diff --git a/[refs] b/[refs] index 32c8ff2de21b..4c8e269decb8 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 16c5ab1d3a6d1b11ed2966fa33a3a4fecd13a2bc +refs/heads/master: 7da58046482fceb17c4a0d4afefd9507ec56de7f diff --git a/trunk/Documentation/DocBook/writing-an-alsa-driver.tmpl b/trunk/Documentation/DocBook/writing-an-alsa-driver.tmpl index bd6fee22c4dd..fb32aead5a0b 100644 --- a/trunk/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ b/trunk/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -871,8 +871,9 @@ This function itself doesn't allocate the data space. The data must be allocated manually beforehand, and its pointer is passed - as the argument. This pointer (chip in the - above example) is used as the identifier for the instance. + as the argument. This pointer is used as the + (chip identifier in the above example) + for the instance. @@ -2303,7 +2304,7 @@ struct _snd_pcm_runtime { SNDRV_PCM_INFO_XXX. Here, at least, you have to specify whether the mmap is supported and which interleaved format is supported. - When the hardware supports mmap, add the + When the is supported, add the SNDRV_PCM_INFO_MMAP flag here. When the hardware supports the interleaved or the non-interleaved formats, SNDRV_PCM_INFO_INTERLEAVED or @@ -2897,7 +2898,7 @@ struct _snd_pcm_runtime { When the pcm supports the pause operation (given in the info - field of the hardware table), the PAUSE_PUSH + field of the hardware table), the PAUSE_PUSE and PAUSE_RELEASE commands must be handled here, too. The former is the command to pause the pcm, and the latter to restart the pcm again. @@ -3084,7 +3085,7 @@ struct _snd_pcm_runtime {
High frequency timer interrupts - This happens when the hardware doesn't generate interrupts + This happense when the hardware doesn't generate interrupts at the period boundary but issues timer interrupts at a fixed timer rate (e.g. es1968 or ymfpci drivers). In this case, you need to check the current hardware @@ -3250,19 +3251,18 @@ struct _snd_pcm_runtime { Example of Hardware Constraints for Channels bits[0] == SNDRV_PCM_FMTBIT_S16_LE) { - ch.min = ch.max = 1; - ch.integer = 1; - return snd_interval_refine(c, &ch); + snd_mask_any(&fmt); /* Init the struct */ + if (c->min < 2) { + fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE; + return snd_mask_refine(f, &fmt); } return 0; } @@ -3278,35 +3278,35 @@ struct _snd_pcm_runtime { runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - hw_rule_channels_by_format, NULL, - SNDRV_PCM_HW_PARAM_FORMAT, -1); + hw_rule_channels_by_format, 0, SNDRV_PCM_HW_PARAM_FORMAT, + -1); ]]> - The rule function is called when an application sets the PCM - format, and it refines the number of channels accordingly. - But an application may set the number of channels before - setting the format. Thus you also need to define the inverse rule: + The rule function is called when an application sets the number of + channels. But an application can set the format before the number of + channels. Thus you also need to define the inverse rule: - Example of Hardware Constraints for Formats + Example of Hardware Constraints for Channels min < 2) { - fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE; - return snd_mask_refine(f, &fmt); + snd_interval_any(&ch); + if (f->bits[0] == SNDRV_PCM_FMTBIT_S16_LE) { + ch.min = ch.max = 1; + ch.integer = 1; + return snd_interval_refine(c, &ch); } return 0; } @@ -3321,8 +3321,8 @@ struct _snd_pcm_runtime { runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - hw_rule_format_by_channels, NULL, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + hw_rule_format_by_channels, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + -1); ]]> diff --git a/trunk/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt b/trunk/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt index 3a268127b054..bc50899e0c81 100644 --- a/trunk/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt +++ b/trunk/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt @@ -81,7 +81,8 @@ PA31 TXD4 Required properties for pin configuration node: - atmel,pins: 4 integers array, represents a group of pins mux and config setting. The format is atmel,pins = . - The PERIPH 0 means gpio. + The PERIPH 0 means gpio, PERIPH 1 is periph A, PERIPH 2 is periph B... + PIN_BANK 0 is pioA, PIN_BANK 1 is pioB... Bits used for CONFIG: PULL_UP (1 << 0): indicate this pin need a pull up. @@ -126,7 +127,7 @@ pinctrl@fffff400 { pinctrl_dbgu: dbgu-0 { atmel,pins = <1 14 0x1 0x0 /* PB14 periph A */ - 1 15 0x1 0x1>; /* PB15 periph with pullup */ + 1 15 0x1 0x1>; /* PB15 periph A with pullup */ }; }; }; diff --git a/trunk/Documentation/filesystems/f2fs.txt b/trunk/Documentation/filesystems/f2fs.txt index 8fbd8b46ee34..dcf338e62b71 100644 --- a/trunk/Documentation/filesystems/f2fs.txt +++ b/trunk/Documentation/filesystems/f2fs.txt @@ -175,9 +175,9 @@ consists of multiple segments as described below. align with the zone size <-| |-> align with the segment size _________________________________________________________________________ - | | | Node | Segment | Segment | | - | Superblock | Checkpoint | Address | Info. | Summary | Main | - | (SB) | (CP) | Table (NAT) | Table (SIT) | Area (SSA) | | + | | | Segment | Node | Segment | | + | Superblock | Checkpoint | Info. | Address | Summary | Main | + | (SB) | (CP) | Table (SIT) | Table (NAT) | Area (SSA) | | |____________|_____2______|______N______|______N______|______N_____|__N___| . . . . @@ -200,14 +200,14 @@ consists of multiple segments as described below. : It contains file system information, bitmaps for valid NAT/SIT sets, orphan inode lists, and summary entries of current active segments. -- Node Address Table (NAT) - : It is composed of a block address table for all the node blocks stored in - Main area. - - Segment Information Table (SIT) : It contains segment information such as valid block count and bitmap for the validity of all the blocks. +- Node Address Table (NAT) + : It is composed of a block address table for all the node blocks stored in + Main area. + - Segment Summary Area (SSA) : It contains summary entries which contains the owner information of all the data and node blocks stored in Main area. @@ -236,13 +236,13 @@ For file system consistency, each CP points to which NAT and SIT copies are valid, as shown as below. +--------+----------+---------+ - | CP | NAT | SIT | + | CP | SIT | NAT | +--------+----------+---------+ . . . . . . . . . . . . +-------+-------+--------+--------+--------+--------+ - | CP #0 | CP #1 | NAT #0 | NAT #1 | SIT #0 | SIT #1 | + | CP #0 | CP #1 | SIT #0 | SIT #1 | NAT #0 | NAT #1 | +-------+-------+--------+--------+--------+--------+ | ^ ^ | | | diff --git a/trunk/Documentation/sound/alsa/ALSA-Configuration.txt b/trunk/Documentation/sound/alsa/ALSA-Configuration.txt index ce6581c8ca26..b9cfd339a6fa 100644 --- a/trunk/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/trunk/Documentation/sound/alsa/ALSA-Configuration.txt @@ -890,9 +890,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. enable_msi - Enable Message Signaled Interrupt (MSI) (default = off) power_save - Automatic power-saving timeout (in second, 0 = disable) - power_save_controller - Support runtime D3 of HD-audio controller - (-1 = on for supported chip (default), false = off, - true = force to on even for unsupported hardware) + power_save_controller - Reset HD-audio controller in power-saving mode + (default = on) align_buffer_size - Force rounding of buffer/period sizes to multiples of 128 bytes. This is more efficient in terms of memory access but isn't required by the HDA spec and prevents diff --git a/trunk/Documentation/sound/alsa/HD-Audio.txt b/trunk/Documentation/sound/alsa/HD-Audio.txt index d4faa63ff352..7813c06a5c71 100644 --- a/trunk/Documentation/sound/alsa/HD-Audio.txt +++ b/trunk/Documentation/sound/alsa/HD-Audio.txt @@ -176,14 +176,14 @@ support the automatic probing (yet as of 2.6.28). And, BIOS is often, yes, pretty often broken. It sets up wrong values and screws up the driver. -The preset model (or recently called as "fix-up") is provided -basically to overcome such a situation. When the matching preset -model is found in the white-list, the driver assumes the static -configuration of that preset with the correct pin setup, etc. -Thus, if you have a newer machine with a slightly different PCI SSID -(or codec SSID) from the existing one, you may have a good chance to -re-use the same model. You can pass the `model` option to specify the -preset model instead of PCI (and codec-) SSID look-up. +The preset model is provided basically to overcome such a situation. +When the matching preset model is found in the white-list, the driver +assumes the static configuration of that preset and builds the mixer +elements and PCM streams based on the static information. Thus, if +you have a newer machine with a slightly different PCI SSID from the +existing one, you may have a good chance to re-use the same model. +You can pass the `model` option to specify the preset model instead of +PCI SSID look-up. What `model` option values are available depends on the codec chip. Check your codec chip from the codec proc file (see "Codec Proc-File" @@ -199,12 +199,17 @@ non-working HD-audio hardware is to check HD-audio codec and several different `model` option values. If you have any luck, some of them might suit with your device well. -There are a few special model option values: -- when 'nofixup' is passed, the device-specific fixups in the codec - parser are skipped. -- when `generic` is passed, the codec-specific parser is skipped and - only the generic parser is used. +Some codecs such as ALC880 have a special model option `model=test`. +This configures the driver to provide as many mixer controls as +possible for every single pin feature except for the unsolicited +events (and maybe some other specials). Adjust each mixer element and +try the I/O in the way of trial-and-error until figuring out the whole +I/O pin mappings. +Note that `model=generic` has a special meaning. It means to use the +generic parser regardless of the codec. Usually the codec-specific +parser is much better than the generic parser (as now). Thus this +option is more about the debugging purpose. Speaker and Headphone Output ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -382,8 +387,9 @@ init_verbs:: (separated with a space). hints:: Shows / stores hint strings for codec parsers for any use. - Its format is `key = value`. For example, passing `jack_detect = no` - will disable the jack detection of the machine completely. + Its format is `key = value`. For example, passing `hp_detect = yes` + to IDT/STAC codec parser will result in the disablement of the + headphone detection. init_pin_configs:: Shows the initial pin default config values set by BIOS. driver_pin_configs:: @@ -415,61 +421,6 @@ re-configure based on that state, run like below: ------------------------------------------------------------------------ -Hint Strings -~~~~~~~~~~~~ -The codec parser have several switches and adjustment knobs for -matching better with the actual codec or device behavior. Many of -them can be adjusted dynamically via "hints" strings as mentioned in -the section above. For example, by passing `jack_detect = no` string -via sysfs or a patch file, you can disable the jack detection, thus -the codec parser will skip the features like auto-mute or mic -auto-switch. As a boolean value, either `yes`, `no`, `true`, `false`, -`1` or `0` can be passed. - -The generic parser supports the following hints: - -- jack_detect (bool): specify whether the jack detection is available - at all on this machine; default true -- inv_jack_detect (bool): indicates that the jack detection logic is - inverted -- trigger_sense (bool): indicates that the jack detection needs the - explicit call of AC_VERB_SET_PIN_SENSE verb -- inv_eapd (bool): indicates that the EAPD is implemented in the - inverted logic -- pcm_format_first (bool): sets the PCM format before the stream tag - and channel ID -- sticky_stream (bool): keep the PCM format, stream tag and ID as long - as possible; default true -- spdif_status_reset (bool): reset the SPDIF status bits at each time - the SPDIF stream is set up -- pin_amp_workaround (bool): the output pin may have multiple amp - values -- single_adc_amp (bool): ADCs can have only single input amps -- auto_mute (bool): enable/disable the headphone auto-mute feature; - default true -- auto_mic (bool): enable/disable the mic auto-switch feature; default - true -- line_in_auto_switch (bool): enable/disable the line-in auto-switch - feature; default false -- need_dac_fix (bool): limits the DACs depending on the channel count -- primary_hp (bool): probe headphone jacks as the primary outputs; - default true -- multi_cap_vol (bool): provide multiple capture volumes -- inv_dmic_split (bool): provide split internal mic volume/switch for - phase-inverted digital mics -- indep_hp (bool): provide the independent headphone PCM stream and - the corresponding mixer control, if available -- add_stereo_mix_input (bool): add the stereo mix (analog-loopback - mix) to the input mux if available -- add_out_jack_modes (bool): add "xxx Jack Mode" enum controls to each - output jack for allowing to change the headphone amp capability -- add_in_jack_modes (bool): add "xxx Jack Mode" enum controls to each - input jack for allowing to change the mic bias vref -- power_down_unused (bool): power down the unused widgets -- mixer_nid (int): specifies the widget NID of the analog-loopback - mixer - - Early Patching ~~~~~~~~~~~~~~ When CONFIG_SND_HDA_PATCH_LOADER=y is set, you can pass a "patch" as a @@ -494,7 +445,7 @@ A patch file is a plain text file which looks like below: 0x20 0x400 0xff [hint] - jack_detect = no + hp_detect = yes ------------------------------------------------------------------------ The file needs to have a line `[codec]`. The next line should contain @@ -580,13 +531,6 @@ cable is unplugged. Thus, if you hear noises, suspect first the power-saving. See /sys/module/snd_hda_intel/parameters/power_save to check the current value. If it's non-zero, the feature is turned on. -The recent kernel supports the runtime PM for the HD-audio controller -chip, too. It means that the HD-audio controller is also powered up / -down dynamically. The feature is enabled only for certain controller -chips like Intel LynxPoint. You can enable/disable this feature -forcibly by setting `power_save_controller` option, which is also -available at /sys/module/snd_hda_intel/parameters directory. - Tracepoints ~~~~~~~~~~~ @@ -643,9 +587,8 @@ The latest development codes for HD-audio are found on sound git tree: - git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git The master branch or for-next branches can be used as the main -development branches in general while the development for the current -and next kernels are found in for-linus and for-next branches, -respectively. +development branches in general while the HD-audio specific patches +are committed in topic/hda branch. If you are using the latest Linus tree, it'd be better to pull the above GIT tree onto it. If you are using the older kernels, an easy @@ -756,11 +699,7 @@ won't be always updated. For example, the volume values are usually cached in the driver, and thus changing the widget amp value directly via hda-verb won't change the mixer value. -The hda-verb program is included now in alsa-tools: - -- git://git.alsa-project.org/alsa-tools.git - -Also, the old stand-alone package is found in the ftp directory: +The hda-verb program is found in the ftp directory: - ftp://ftp.suse.com/pub/people/tiwai/misc/ @@ -838,18 +777,3 @@ A git repository is available: See README file in the tarball for more details about hda-emu program. - - -hda-jack-retask -~~~~~~~~~~~~~~~ -hda-jack-retask is a user-friendly GUI program to manipulate the -HD-audio pin control for jack retasking. If you have a problem about -the jack assignment, try this program and check whether you can get -useful results. Once when you figure out the proper pin assignment, -it can be fixed either in the driver code statically or via passing a -firmware patch file (see "Early Patching" section). - -The program is included in alsa-tools now: - -- git://git.alsa-project.org/alsa-tools.git - diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS index 915564eda145..8ae709e34523 100644 --- a/trunk/MAINTAINERS +++ b/trunk/MAINTAINERS @@ -228,7 +228,7 @@ S: Maintained F: drivers/platform/x86/acerhdf.c ACER WMI LAPTOP EXTRAS -M: Joey Lee +M: "Lee, Chun-Yi" L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/acer-wmi.c @@ -648,7 +648,7 @@ F: arch/arm/ ARM SUB-ARCHITECTURES L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: MAINTAINED +S: Maintained F: arch/arm/mach-*/ F: arch/arm/plat-*/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git @@ -1351,6 +1351,14 @@ W: http://wireless.kernel.org/en/users/Drivers/ath9k S: Supported F: drivers/net/wireless/ath/ath9k/ +WILOCITY WIL6210 WIRELESS DRIVER +M: Vladimir Kondratiev +L: linux-wireless@vger.kernel.org +L: wil6210@qca.qualcomm.com +S: Supported +W: http://wireless.kernel.org/en/users/Drivers/wil6210 +F: drivers/net/wireless/ath/wil6210/ + CARL9170 LINUX COMMUNITY WIRELESS DRIVER M: Christian Lamparter L: linux-wireless@vger.kernel.org @@ -1964,9 +1972,9 @@ S: Maintained F: drivers/usb/host/ohci-ep93xx.c CIRRUS LOGIC CS4270 SOUND DRIVER -M: Timur Tabi +M: Timur Tabi L: alsa-devel@alsa-project.org (moderated for non-subscribers) -S: Supported +S: Odd Fixes F: sound/soc/codecs/cs4270* CLEANCACHE API @@ -3183,9 +3191,9 @@ F: include/uapi/video/ F: include/uapi/linux/fb.h FREESCALE DIU FRAMEBUFFER DRIVER -M: Timur Tabi +M: Timur Tabi L: linux-fbdev@vger.kernel.org -S: Supported +S: Maintained F: drivers/video/fsl-diu-fb.* FREESCALE DMA DRIVER @@ -3220,9 +3228,8 @@ F: drivers/net/ethernet/freescale/fs_enet/ F: include/linux/fs_enet_pd.h FREESCALE QUICC ENGINE LIBRARY -M: Timur Tabi L: linuxppc-dev@lists.ozlabs.org -S: Supported +S: Orphan F: arch/powerpc/sysdev/qe_lib/ F: arch/powerpc/include/asm/*qe.h @@ -3241,16 +3248,16 @@ S: Maintained F: drivers/net/ethernet/freescale/ucc_geth* FREESCALE QUICC ENGINE UCC UART DRIVER -M: Timur Tabi +M: Timur Tabi L: linuxppc-dev@lists.ozlabs.org -S: Supported +S: Maintained F: drivers/tty/serial/ucc_uart.c FREESCALE SOC SOUND DRIVERS -M: Timur Tabi +M: Timur Tabi L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: linuxppc-dev@lists.ozlabs.org -S: Supported +S: Maintained F: sound/soc/fsl/fsl* F: sound/soc/fsl/mpc8610_hpcd.c @@ -5077,7 +5084,7 @@ S: Maintained F: drivers/media/radio/radio-mr800.c MSI LAPTOP SUPPORT -M: "Lee, Chun-Yi" +M: "Lee, Chun-Yi" L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/msi-laptop.c @@ -5507,8 +5514,7 @@ M: Benoît Cousson M: Paul Walmsley L: linux-omap@vger.kernel.org S: Maintained -F: arch/arm/mach-omap2/omap_hwmod.c -F: arch/arm/plat-omap/include/plat/omap_hwmod.h +F: arch/arm/mach-omap2/omap_hwmod.* OMAP HWMOD DATA FOR OMAP4-BASED DEVICES M: Benoît Cousson @@ -6579,7 +6585,7 @@ F: drivers/media/platform/s3c-camif/ F: include/media/s3c_camif.h SERIAL DRIVERS -M: Alan Cox +M: Greg Kroah-Hartman L: linux-serial@vger.kernel.org S: Maintained F: drivers/tty/serial @@ -7334,7 +7340,7 @@ S: Odd Fixes F: drivers/staging/speakup/ STAGING - TI DSP BRIDGE DRIVERS -M: Omar Ramirez Luna +M: Omar Ramirez Luna S: Odd Fixes F: drivers/staging/tidspbridge/ @@ -8526,7 +8532,7 @@ F: Documentation/x86/ F: arch/x86/ X86 PLATFORM DRIVERS -M: Matthew Garrett +M: Matthew Garrett L: platform-driver-x86@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86.git S: Maintained diff --git a/trunk/Makefile b/trunk/Makefile index a1667c4bcce5..2d3c92c774fb 100644 --- a/trunk/Makefile +++ b/trunk/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 8 SUBLEVEL = 0 -EXTRAVERSION = -rc3 +EXTRAVERSION = -rc5 NAME = Terrified Chipmunk # *DOCUMENTATION* @@ -169,7 +169,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -e s/arm.*/arm/ -e s/sa110/arm/ \ -e s/s390x/s390/ -e s/parisc64/parisc/ \ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ - -e s/sh[234].*/sh/ ) + -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ ) # Cross compiling and selecting different set of gcc/bin-utils # --------------------------------------------------------------------------- diff --git a/trunk/arch/arm/boot/dts/Makefile b/trunk/arch/arm/boot/dts/Makefile index e44da40d984f..5ebb44fe826a 100644 --- a/trunk/arch/arm/boot/dts/Makefile +++ b/trunk/arch/arm/boot/dts/Makefile @@ -155,6 +155,7 @@ dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \ dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb targets += dtbs +targets += $(dtb-y) endif # *.dtb used to be generated in the directory above. Clean out the diff --git a/trunk/arch/arm/boot/dts/armada-370-db.dts b/trunk/arch/arm/boot/dts/armada-370-db.dts index 00044026ef1f..9b82facb2561 100644 --- a/trunk/arch/arm/boot/dts/armada-370-db.dts +++ b/trunk/arch/arm/boot/dts/armada-370-db.dts @@ -26,7 +26,7 @@ memory { device_type = "memory"; - reg = <0x00000000 0x20000000>; /* 512 MB */ + reg = <0x00000000 0x40000000>; /* 1 GB */ }; soc { diff --git a/trunk/arch/arm/boot/dts/armada-xp-mv78230.dtsi b/trunk/arch/arm/boot/dts/armada-xp-mv78230.dtsi index 271855a6e224..e041f42ed711 100644 --- a/trunk/arch/arm/boot/dts/armada-xp-mv78230.dtsi +++ b/trunk/arch/arm/boot/dts/armada-xp-mv78230.dtsi @@ -50,27 +50,25 @@ }; gpio0: gpio@d0018100 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018100 0x40>, - <0xd0018800 0x30>; + compatible = "marvell,orion-gpio"; + reg = <0xd0018100 0x40>; ngpios = <32>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupts-cells = <2>; - interrupts = <16>, <17>, <18>, <19>; + interrupts = <82>, <83>, <84>, <85>; }; gpio1: gpio@d0018140 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018140 0x40>, - <0xd0018840 0x30>; + compatible = "marvell,orion-gpio"; + reg = <0xd0018140 0x40>; ngpios = <17>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupts-cells = <2>; - interrupts = <20>, <21>, <22>; + interrupts = <87>, <88>, <89>; }; }; }; diff --git a/trunk/arch/arm/boot/dts/armada-xp-mv78260.dtsi b/trunk/arch/arm/boot/dts/armada-xp-mv78260.dtsi index 1c1937dbce73..9e23bd8c9536 100644 --- a/trunk/arch/arm/boot/dts/armada-xp-mv78260.dtsi +++ b/trunk/arch/arm/boot/dts/armada-xp-mv78260.dtsi @@ -51,39 +51,36 @@ }; gpio0: gpio@d0018100 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018100 0x40>, - <0xd0018800 0x30>; + compatible = "marvell,orion-gpio"; + reg = <0xd0018100 0x40>; ngpios = <32>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupts-cells = <2>; - interrupts = <16>, <17>, <18>, <19>; + interrupts = <82>, <83>, <84>, <85>; }; gpio1: gpio@d0018140 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018140 0x40>, - <0xd0018840 0x30>; + compatible = "marvell,orion-gpio"; + reg = <0xd0018140 0x40>; ngpios = <32>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupts-cells = <2>; - interrupts = <20>, <21>, <22>, <23>; + interrupts = <87>, <88>, <89>, <90>; }; gpio2: gpio@d0018180 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018180 0x40>, - <0xd0018870 0x30>; + compatible = "marvell,orion-gpio"; + reg = <0xd0018180 0x40>; ngpios = <3>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupts-cells = <2>; - interrupts = <24>; + interrupts = <91>; }; ethernet@d0034000 { diff --git a/trunk/arch/arm/boot/dts/armada-xp-mv78460.dtsi b/trunk/arch/arm/boot/dts/armada-xp-mv78460.dtsi index 4905cf3a5ef8..965966110e38 100644 --- a/trunk/arch/arm/boot/dts/armada-xp-mv78460.dtsi +++ b/trunk/arch/arm/boot/dts/armada-xp-mv78460.dtsi @@ -66,39 +66,36 @@ }; gpio0: gpio@d0018100 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018100 0x40>, - <0xd0018800 0x30>; + compatible = "marvell,orion-gpio"; + reg = <0xd0018100 0x40>; ngpios = <32>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupts-cells = <2>; - interrupts = <16>, <17>, <18>, <19>; + interrupts = <82>, <83>, <84>, <85>; }; gpio1: gpio@d0018140 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018140 0x40>, - <0xd0018840 0x30>; + compatible = "marvell,orion-gpio"; + reg = <0xd0018140 0x40>; ngpios = <32>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupts-cells = <2>; - interrupts = <20>, <21>, <22>, <23>; + interrupts = <87>, <88>, <89>, <90>; }; gpio2: gpio@d0018180 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018180 0x40>, - <0xd0018870 0x30>; + compatible = "marvell,orion-gpio"; + reg = <0xd0018180 0x40>; ngpios = <3>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupts-cells = <2>; - interrupts = <24>; + interrupts = <91>; }; ethernet@d0034000 { diff --git a/trunk/arch/arm/boot/dts/at91rm9200.dtsi b/trunk/arch/arm/boot/dts/at91rm9200.dtsi index e154f242c680..222047f1ece9 100644 --- a/trunk/arch/arm/boot/dts/at91rm9200.dtsi +++ b/trunk/arch/arm/boot/dts/at91rm9200.dtsi @@ -336,8 +336,8 @@ i2c@0 { compatible = "i2c-gpio"; - gpios = <&pioA 23 0 /* sda */ - &pioA 24 0 /* scl */ + gpios = <&pioA 25 0 /* sda */ + &pioA 26 0 /* scl */ >; i2c-gpio,sda-open-drain; i2c-gpio,scl-open-drain; diff --git a/trunk/arch/arm/boot/dts/at91sam9x5.dtsi b/trunk/arch/arm/boot/dts/at91sam9x5.dtsi index 3a47cf952146..8ecca6948d81 100644 --- a/trunk/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/trunk/arch/arm/boot/dts/at91sam9x5.dtsi @@ -143,6 +143,11 @@ atmel,pins = <0 3 0x1 0x0>; /* PA3 periph A */ }; + + pinctrl_usart0_sck: usart0_sck-0 { + atmel,pins = + <0 4 0x1 0x0>; /* PA4 periph A */ + }; }; usart1 { @@ -154,12 +159,17 @@ pinctrl_usart1_rts: usart1_rts-0 { atmel,pins = - <3 27 0x3 0x0>; /* PC27 periph C */ + <2 27 0x3 0x0>; /* PC27 periph C */ }; pinctrl_usart1_cts: usart1_cts-0 { atmel,pins = - <3 28 0x3 0x0>; /* PC28 periph C */ + <2 28 0x3 0x0>; /* PC28 periph C */ + }; + + pinctrl_usart1_sck: usart1_sck-0 { + atmel,pins = + <2 28 0x3 0x0>; /* PC29 periph C */ }; }; @@ -172,46 +182,56 @@ pinctrl_uart2_rts: uart2_rts-0 { atmel,pins = - <0 0 0x2 0x0>; /* PB0 periph B */ + <1 0 0x2 0x0>; /* PB0 periph B */ }; pinctrl_uart2_cts: uart2_cts-0 { atmel,pins = - <0 1 0x2 0x0>; /* PB1 periph B */ + <1 1 0x2 0x0>; /* PB1 periph B */ + }; + + pinctrl_usart2_sck: usart2_sck-0 { + atmel,pins = + <1 2 0x2 0x0>; /* PB2 periph B */ }; }; usart3 { pinctrl_uart3: usart3-0 { atmel,pins = - <3 23 0x2 0x1 /* PC22 periph B with pullup */ - 3 23 0x2 0x0>; /* PC23 periph B */ + <2 23 0x2 0x1 /* PC22 periph B with pullup */ + 2 23 0x2 0x0>; /* PC23 periph B */ }; pinctrl_usart3_rts: usart3_rts-0 { atmel,pins = - <3 24 0x2 0x0>; /* PC24 periph B */ + <2 24 0x2 0x0>; /* PC24 periph B */ }; pinctrl_usart3_cts: usart3_cts-0 { atmel,pins = - <3 25 0x2 0x0>; /* PC25 periph B */ + <2 25 0x2 0x0>; /* PC25 periph B */ + }; + + pinctrl_usart3_sck: usart3_sck-0 { + atmel,pins = + <2 26 0x2 0x0>; /* PC26 periph B */ }; }; uart0 { pinctrl_uart0: uart0-0 { atmel,pins = - <3 8 0x3 0x0 /* PC8 periph C */ - 3 9 0x3 0x1>; /* PC9 periph C with pullup */ + <2 8 0x3 0x0 /* PC8 periph C */ + 2 9 0x3 0x1>; /* PC9 periph C with pullup */ }; }; uart1 { pinctrl_uart1: uart1-0 { atmel,pins = - <3 16 0x3 0x0 /* PC16 periph C */ - 3 17 0x3 0x1>; /* PC17 periph C with pullup */ + <2 16 0x3 0x0 /* PC16 periph C */ + 2 17 0x3 0x1>; /* PC17 periph C with pullup */ }; }; @@ -240,14 +260,14 @@ pinctrl_macb0_rmii_mii: macb0_rmii_mii-0 { atmel,pins = - <1 8 0x1 0x0 /* PA8 periph A */ - 1 11 0x1 0x0 /* PA11 periph A */ - 1 12 0x1 0x0 /* PA12 periph A */ - 1 13 0x1 0x0 /* PA13 periph A */ - 1 14 0x1 0x0 /* PA14 periph A */ - 1 15 0x1 0x0 /* PA15 periph A */ - 1 16 0x1 0x0 /* PA16 periph A */ - 1 17 0x1 0x0>; /* PA17 periph A */ + <1 8 0x1 0x0 /* PB8 periph A */ + 1 11 0x1 0x0 /* PB11 periph A */ + 1 12 0x1 0x0 /* PB12 periph A */ + 1 13 0x1 0x0 /* PB13 periph A */ + 1 14 0x1 0x0 /* PB14 periph A */ + 1 15 0x1 0x0 /* PB15 periph A */ + 1 16 0x1 0x0 /* PB16 periph A */ + 1 17 0x1 0x0>; /* PB17 periph A */ }; }; diff --git a/trunk/arch/arm/boot/dts/cros5250-common.dtsi b/trunk/arch/arm/boot/dts/cros5250-common.dtsi index fddd17417433..46c098017036 100644 --- a/trunk/arch/arm/boot/dts/cros5250-common.dtsi +++ b/trunk/arch/arm/boot/dts/cros5250-common.dtsi @@ -96,8 +96,8 @@ fifo-depth = <0x80>; card-detect-delay = <200>; samsung,dw-mshc-ciu-div = <3>; - samsung,dw-mshc-sdr-timing = <2 3 3>; - samsung,dw-mshc-ddr-timing = <1 2 3>; + samsung,dw-mshc-sdr-timing = <2 3>; + samsung,dw-mshc-ddr-timing = <1 2>; slot@0 { reg = <0>; @@ -120,8 +120,8 @@ fifo-depth = <0x80>; card-detect-delay = <200>; samsung,dw-mshc-ciu-div = <3>; - samsung,dw-mshc-sdr-timing = <2 3 3>; - samsung,dw-mshc-ddr-timing = <1 2 3>; + samsung,dw-mshc-sdr-timing = <2 3>; + samsung,dw-mshc-ddr-timing = <1 2>; slot@0 { reg = <0>; @@ -141,8 +141,8 @@ fifo-depth = <0x80>; card-detect-delay = <200>; samsung,dw-mshc-ciu-div = <3>; - samsung,dw-mshc-sdr-timing = <2 3 3>; - samsung,dw-mshc-ddr-timing = <1 2 3>; + samsung,dw-mshc-sdr-timing = <2 3>; + samsung,dw-mshc-ddr-timing = <1 2>; slot@0 { reg = <0>; diff --git a/trunk/arch/arm/boot/dts/dove-cubox.dts b/trunk/arch/arm/boot/dts/dove-cubox.dts index fed7d3f9f431..cdee96fca6e2 100644 --- a/trunk/arch/arm/boot/dts/dove-cubox.dts +++ b/trunk/arch/arm/boot/dts/dove-cubox.dts @@ -26,10 +26,15 @@ }; &uart0 { status = "okay"; }; -&sdio0 { status = "okay"; }; &sata0 { status = "okay"; }; &i2c0 { status = "okay"; }; +&sdio0 { + status = "okay"; + /* sdio0 card detect is connected to wrong pin on CuBox */ + cd-gpios = <&gpio0 12 1>; +}; + &spi0 { status = "okay"; @@ -42,9 +47,14 @@ }; &pinctrl { - pinctrl-0 = <&pmx_gpio_18>; + pinctrl-0 = <&pmx_gpio_12 &pmx_gpio_18>; pinctrl-names = "default"; + pmx_gpio_12: pmx-gpio-12 { + marvell,pins = "mpp12"; + marvell,function = "gpio"; + }; + pmx_gpio_18: pmx-gpio-18 { marvell,pins = "mpp18"; marvell,function = "gpio"; diff --git a/trunk/arch/arm/boot/dts/exynos5250-smdk5250.dts b/trunk/arch/arm/boot/dts/exynos5250-smdk5250.dts index 942d5761ca97..e05b18f3c33d 100644 --- a/trunk/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/trunk/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -115,8 +115,8 @@ fifo-depth = <0x80>; card-detect-delay = <200>; samsung,dw-mshc-ciu-div = <3>; - samsung,dw-mshc-sdr-timing = <2 3 3>; - samsung,dw-mshc-ddr-timing = <1 2 3>; + samsung,dw-mshc-sdr-timing = <2 3>; + samsung,dw-mshc-ddr-timing = <1 2>; slot@0 { reg = <0>; @@ -139,8 +139,8 @@ fifo-depth = <0x80>; card-detect-delay = <200>; samsung,dw-mshc-ciu-div = <3>; - samsung,dw-mshc-sdr-timing = <2 3 3>; - samsung,dw-mshc-ddr-timing = <1 2 3>; + samsung,dw-mshc-sdr-timing = <2 3>; + samsung,dw-mshc-ddr-timing = <1 2>; slot@0 { reg = <0>; diff --git a/trunk/arch/arm/boot/dts/kirkwood-ns2-common.dtsi b/trunk/arch/arm/boot/dts/kirkwood-ns2-common.dtsi index 9bc6785ad228..77d21abfcdf7 100644 --- a/trunk/arch/arm/boot/dts/kirkwood-ns2-common.dtsi +++ b/trunk/arch/arm/boot/dts/kirkwood-ns2-common.dtsi @@ -1,4 +1,5 @@ /include/ "kirkwood.dtsi" +/include/ "kirkwood-6281.dtsi" / { chosen { @@ -6,6 +7,21 @@ }; ocp@f1000000 { + pinctrl: pinctrl@10000 { + pinctrl-0 = < &pmx_spi &pmx_twsi0 &pmx_uart0 + &pmx_ns2_sata0 &pmx_ns2_sata1>; + pinctrl-names = "default"; + + pmx_ns2_sata0: pmx-ns2-sata0 { + marvell,pins = "mpp21"; + marvell,function = "sata0"; + }; + pmx_ns2_sata1: pmx-ns2-sata1 { + marvell,pins = "mpp20"; + marvell,function = "sata1"; + }; + }; + serial@12000 { clock-frequency = <166666667>; status = "okay"; diff --git a/trunk/arch/arm/boot/dts/kirkwood.dtsi b/trunk/arch/arm/boot/dts/kirkwood.dtsi index 110d6cbb795b..d6ab442b7011 100644 --- a/trunk/arch/arm/boot/dts/kirkwood.dtsi +++ b/trunk/arch/arm/boot/dts/kirkwood.dtsi @@ -36,6 +36,7 @@ reg = <0x10100 0x40>; ngpios = <32>; interrupt-controller; + #interrupt-cells = <2>; interrupts = <35>, <36>, <37>, <38>; }; @@ -46,6 +47,7 @@ reg = <0x10140 0x40>; ngpios = <18>; interrupt-controller; + #interrupt-cells = <2>; interrupts = <39>, <40>, <41>; }; diff --git a/trunk/arch/arm/boot/dts/kizbox.dts b/trunk/arch/arm/boot/dts/kizbox.dts index e8814fe0e277..b4dc3ed9a3ec 100644 --- a/trunk/arch/arm/boot/dts/kizbox.dts +++ b/trunk/arch/arm/boot/dts/kizbox.dts @@ -48,6 +48,8 @@ macb0: ethernet@fffc4000 { phy-mode = "mii"; + pinctrl-0 = <&pinctrl_macb_rmii + &pinctrl_macb_rmii_mii_alt>; status = "okay"; }; diff --git a/trunk/arch/arm/boot/dts/sunxi.dtsi b/trunk/arch/arm/boot/dts/sunxi.dtsi index 8bbc2bfef221..8b36abea9f2e 100644 --- a/trunk/arch/arm/boot/dts/sunxi.dtsi +++ b/trunk/arch/arm/boot/dts/sunxi.dtsi @@ -60,19 +60,21 @@ }; uart0: uart@01c28000 { - compatible = "ns8250"; + compatible = "snps,dw-apb-uart"; reg = <0x01c28000 0x400>; interrupts = <1>; reg-shift = <2>; + reg-io-width = <4>; clock-frequency = <24000000>; status = "disabled"; }; uart1: uart@01c28400 { - compatible = "ns8250"; + compatible = "snps,dw-apb-uart"; reg = <0x01c28400 0x400>; interrupts = <2>; reg-shift = <2>; + reg-io-width = <4>; clock-frequency = <24000000>; status = "disabled"; }; diff --git a/trunk/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/trunk/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index 1fc405a9ecfb..cf8071ad22d5 100644 --- a/trunk/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/trunk/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -45,7 +45,6 @@ reg = <1>; }; -/* A7s disabled till big.LITTLE patches are available... cpu2: cpu@2 { device_type = "cpu"; compatible = "arm,cortex-a7"; @@ -63,7 +62,6 @@ compatible = "arm,cortex-a7"; reg = <0x102>; }; -*/ }; memory@80000000 { diff --git a/trunk/arch/arm/configs/at91_dt_defconfig b/trunk/arch/arm/configs/at91_dt_defconfig index b175577d7abb..1ea959019fcd 100644 --- a/trunk/arch/arm/configs/at91_dt_defconfig +++ b/trunk/arch/arm/configs/at91_dt_defconfig @@ -19,6 +19,7 @@ CONFIG_SOC_AT91SAM9260=y CONFIG_SOC_AT91SAM9263=y CONFIG_SOC_AT91SAM9G45=y CONFIG_SOC_AT91SAM9X5=y +CONFIG_SOC_AT91SAM9N12=y CONFIG_MACH_AT91SAM_DT=y CONFIG_AT91_PROGRAMMABLE_CLOCKS=y CONFIG_AT91_TIMER_HZ=128 @@ -31,7 +32,7 @@ CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 CONFIG_ARM_APPENDED_DTB=y CONFIG_ARM_ATAG_DTB_COMPAT=y -CONFIG_CMDLINE="mem=128M console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw" +CONFIG_CMDLINE="console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw" CONFIG_KEXEC=y CONFIG_AUTO_ZRELADDR=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set diff --git a/trunk/arch/arm/kernel/debug.S b/trunk/arch/arm/kernel/debug.S index 6809200c31fb..14f7c3b14632 100644 --- a/trunk/arch/arm/kernel/debug.S +++ b/trunk/arch/arm/kernel/debug.S @@ -100,12 +100,14 @@ ENTRY(printch) b 1b ENDPROC(printch) +#ifdef CONFIG_MMU ENTRY(debug_ll_addr) addruart r2, r3, ip str r2, [r0] str r3, [r1] mov pc, lr ENDPROC(debug_ll_addr) +#endif #else diff --git a/trunk/arch/arm/kernel/head.S b/trunk/arch/arm/kernel/head.S index 4eee351f4668..486a15ae9011 100644 --- a/trunk/arch/arm/kernel/head.S +++ b/trunk/arch/arm/kernel/head.S @@ -246,6 +246,7 @@ __create_page_tables: /* * Then map boot params address in r2 if specified. + * We map 2 sections in case the ATAGs/DTB crosses a section boundary. */ mov r0, r2, lsr #SECTION_SHIFT movs r0, r0, lsl #SECTION_SHIFT @@ -253,6 +254,8 @@ __create_page_tables: addne r3, r3, #PAGE_OFFSET addne r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER) orrne r6, r7, r0 + strne r6, [r3], #1 << PMD_ORDER + addne r6, r6, #1 << SECTION_SHIFT strne r6, [r3] #ifdef CONFIG_DEBUG_LL @@ -331,7 +334,7 @@ ENTRY(secondary_startup) * as it has already been validated by the primary processor. */ #ifdef CONFIG_ARM_VIRT_EXT - bl __hyp_stub_install + bl __hyp_stub_install_secondary #endif safe_svcmode_maskall r9 diff --git a/trunk/arch/arm/kernel/hyp-stub.S b/trunk/arch/arm/kernel/hyp-stub.S index 65b2417aebce..1315c4ccfa56 100644 --- a/trunk/arch/arm/kernel/hyp-stub.S +++ b/trunk/arch/arm/kernel/hyp-stub.S @@ -99,7 +99,7 @@ ENTRY(__hyp_stub_install_secondary) * immediately. */ compare_cpu_mode_with_primary r4, r5, r6, r7 - bxne lr + movne pc, lr /* * Once we have given up on one CPU, we do not try to install the @@ -111,7 +111,7 @@ ENTRY(__hyp_stub_install_secondary) */ cmp r4, #HYP_MODE - bxne lr @ give up if the CPU is not in HYP mode + movne pc, lr @ give up if the CPU is not in HYP mode /* * Configure HSCTLR to set correct exception endianness/instruction set @@ -120,7 +120,8 @@ ENTRY(__hyp_stub_install_secondary) * Eventually, CPU-specific code might be needed -- assume not for now * * This code relies on the "eret" instruction to synchronize the - * various coprocessor accesses. + * various coprocessor accesses. This is done when we switch to SVC + * (see safe_svcmode_maskall). */ @ Now install the hypervisor stub: adr r7, __hyp_stub_vectors @@ -155,14 +156,7 @@ THUMB( orr r7, #(1 << 30) ) @ HSCTLR.TE 1: #endif - bic r7, r4, #MODE_MASK - orr r7, r7, #SVC_MODE -THUMB( orr r7, r7, #PSR_T_BIT ) - msr spsr_cxsf, r7 @ This is SPSR_hyp. - - __MSR_ELR_HYP(14) @ msr elr_hyp, lr - __ERET @ return, switching to SVC mode - @ The boot CPU mode is left in r4. + bx lr @ The boot CPU mode is left in r4. ENDPROC(__hyp_stub_install_secondary) __hyp_stub_do_trap: @@ -200,7 +194,7 @@ ENDPROC(__hyp_get_vectors) @ fall through ENTRY(__hyp_set_vectors) __HVC(0) - bx lr + mov pc, lr ENDPROC(__hyp_set_vectors) #ifndef ZIMAGE diff --git a/trunk/arch/arm/mach-at91/setup.c b/trunk/arch/arm/mach-at91/setup.c index 9ee866ce0478..4b678478cf95 100644 --- a/trunk/arch/arm/mach-at91/setup.c +++ b/trunk/arch/arm/mach-at91/setup.c @@ -105,6 +105,8 @@ static void __init soc_detect(u32 dbgu_base) switch (socid) { case ARCH_ID_AT91RM9200: at91_soc_initdata.type = AT91_SOC_RM9200; + if (at91_soc_initdata.subtype == AT91_SOC_SUBTYPE_NONE) + at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA; at91_boot_soc = at91rm9200_soc; break; diff --git a/trunk/arch/arm/mach-imx/Kconfig b/trunk/arch/arm/mach-imx/Kconfig index 3e628fd7a674..0a2349dc7018 100644 --- a/trunk/arch/arm/mach-imx/Kconfig +++ b/trunk/arch/arm/mach-imx/Kconfig @@ -851,6 +851,7 @@ config SOC_IMX6Q select HAVE_CAN_FLEXCAN if CAN select HAVE_IMX_GPC select HAVE_IMX_MMDC + select HAVE_IMX_SRC select HAVE_SMP select MFD_SYSCON select PINCTRL diff --git a/trunk/arch/arm/mach-imx/clk-imx25.c b/trunk/arch/arm/mach-imx/clk-imx25.c index b197aa73dc4b..2c570cdaae7b 100644 --- a/trunk/arch/arm/mach-imx/clk-imx25.c +++ b/trunk/arch/arm/mach-imx/clk-imx25.c @@ -254,9 +254,9 @@ int __init mx25_clocks_init(void) clk_register_clkdev(clk[ipg], "ipg", "mxc-ehci.2"); clk_register_clkdev(clk[usbotg_ahb], "ahb", "mxc-ehci.2"); clk_register_clkdev(clk[usb_div], "per", "mxc-ehci.2"); - clk_register_clkdev(clk[ipg], "ipg", "fsl-usb2-udc"); - clk_register_clkdev(clk[usbotg_ahb], "ahb", "fsl-usb2-udc"); - clk_register_clkdev(clk[usb_div], "per", "fsl-usb2-udc"); + clk_register_clkdev(clk[ipg], "ipg", "imx-udc-mx27"); + clk_register_clkdev(clk[usbotg_ahb], "ahb", "imx-udc-mx27"); + clk_register_clkdev(clk[usb_div], "per", "imx-udc-mx27"); clk_register_clkdev(clk[nfc_ipg_per], NULL, "imx25-nand.0"); /* i.mx25 has the i.mx35 type cspi */ clk_register_clkdev(clk[cspi1_ipg], NULL, "imx35-cspi.0"); diff --git a/trunk/arch/arm/mach-imx/clk-imx27.c b/trunk/arch/arm/mach-imx/clk-imx27.c index 4c1d1e4efc74..1ffe3b534e51 100644 --- a/trunk/arch/arm/mach-imx/clk-imx27.c +++ b/trunk/arch/arm/mach-imx/clk-imx27.c @@ -236,9 +236,9 @@ int __init mx27_clocks_init(unsigned long fref) clk_register_clkdev(clk[lcdc_ahb_gate], "ahb", "imx21-fb.0"); clk_register_clkdev(clk[csi_ahb_gate], "ahb", "imx27-camera.0"); clk_register_clkdev(clk[per4_gate], "per", "imx27-camera.0"); - clk_register_clkdev(clk[usb_div], "per", "fsl-usb2-udc"); - clk_register_clkdev(clk[usb_ipg_gate], "ipg", "fsl-usb2-udc"); - clk_register_clkdev(clk[usb_ahb_gate], "ahb", "fsl-usb2-udc"); + clk_register_clkdev(clk[usb_div], "per", "imx-udc-mx27"); + clk_register_clkdev(clk[usb_ipg_gate], "ipg", "imx-udc-mx27"); + clk_register_clkdev(clk[usb_ahb_gate], "ahb", "imx-udc-mx27"); clk_register_clkdev(clk[usb_div], "per", "mxc-ehci.0"); clk_register_clkdev(clk[usb_ipg_gate], "ipg", "mxc-ehci.0"); clk_register_clkdev(clk[usb_ahb_gate], "ahb", "mxc-ehci.0"); diff --git a/trunk/arch/arm/mach-imx/clk-imx31.c b/trunk/arch/arm/mach-imx/clk-imx31.c index 8be64e0a4ace..16ccbd41dea9 100644 --- a/trunk/arch/arm/mach-imx/clk-imx31.c +++ b/trunk/arch/arm/mach-imx/clk-imx31.c @@ -139,9 +139,9 @@ int __init mx31_clocks_init(unsigned long fref) clk_register_clkdev(clk[usb_div_post], "per", "mxc-ehci.2"); clk_register_clkdev(clk[usb_gate], "ahb", "mxc-ehci.2"); clk_register_clkdev(clk[ipg], "ipg", "mxc-ehci.2"); - clk_register_clkdev(clk[usb_div_post], "per", "fsl-usb2-udc"); - clk_register_clkdev(clk[usb_gate], "ahb", "fsl-usb2-udc"); - clk_register_clkdev(clk[ipg], "ipg", "fsl-usb2-udc"); + clk_register_clkdev(clk[usb_div_post], "per", "imx-udc-mx27"); + clk_register_clkdev(clk[usb_gate], "ahb", "imx-udc-mx27"); + clk_register_clkdev(clk[ipg], "ipg", "imx-udc-mx27"); clk_register_clkdev(clk[csi_gate], NULL, "mx3-camera.0"); /* i.mx31 has the i.mx21 type uart */ clk_register_clkdev(clk[uart1_gate], "per", "imx21-uart.0"); diff --git a/trunk/arch/arm/mach-imx/clk-imx35.c b/trunk/arch/arm/mach-imx/clk-imx35.c index 66f3d65ea275..f0727e80815d 100644 --- a/trunk/arch/arm/mach-imx/clk-imx35.c +++ b/trunk/arch/arm/mach-imx/clk-imx35.c @@ -251,9 +251,9 @@ int __init mx35_clocks_init() clk_register_clkdev(clk[usb_div], "per", "mxc-ehci.2"); clk_register_clkdev(clk[ipg], "ipg", "mxc-ehci.2"); clk_register_clkdev(clk[usbotg_gate], "ahb", "mxc-ehci.2"); - clk_register_clkdev(clk[usb_div], "per", "fsl-usb2-udc"); - clk_register_clkdev(clk[ipg], "ipg", "fsl-usb2-udc"); - clk_register_clkdev(clk[usbotg_gate], "ahb", "fsl-usb2-udc"); + clk_register_clkdev(clk[usb_div], "per", "imx-udc-mx27"); + clk_register_clkdev(clk[ipg], "ipg", "imx-udc-mx27"); + clk_register_clkdev(clk[usbotg_gate], "ahb", "imx-udc-mx27"); clk_register_clkdev(clk[wdog_gate], NULL, "imx2-wdt.0"); clk_register_clkdev(clk[nfc_div], NULL, "imx25-nand.0"); clk_register_clkdev(clk[csi_gate], NULL, "mx3-camera.0"); diff --git a/trunk/arch/arm/mach-imx/clk-imx51-imx53.c b/trunk/arch/arm/mach-imx/clk-imx51-imx53.c index 579023f59dc1..fb7cb841b64c 100644 --- a/trunk/arch/arm/mach-imx/clk-imx51-imx53.c +++ b/trunk/arch/arm/mach-imx/clk-imx51-imx53.c @@ -269,9 +269,9 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil, clk_register_clkdev(clk[usboh3_per_gate], "per", "mxc-ehci.2"); clk_register_clkdev(clk[usboh3_gate], "ipg", "mxc-ehci.2"); clk_register_clkdev(clk[usboh3_gate], "ahb", "mxc-ehci.2"); - clk_register_clkdev(clk[usboh3_per_gate], "per", "fsl-usb2-udc"); - clk_register_clkdev(clk[usboh3_gate], "ipg", "fsl-usb2-udc"); - clk_register_clkdev(clk[usboh3_gate], "ahb", "fsl-usb2-udc"); + clk_register_clkdev(clk[usboh3_per_gate], "per", "imx-udc-mx51"); + clk_register_clkdev(clk[usboh3_gate], "ipg", "imx-udc-mx51"); + clk_register_clkdev(clk[usboh3_gate], "ahb", "imx-udc-mx51"); clk_register_clkdev(clk[nfc_gate], NULL, "imx51-nand"); clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "imx-ssi.0"); clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "imx-ssi.1"); diff --git a/trunk/arch/arm/mach-imx/clk-imx6q.c b/trunk/arch/arm/mach-imx/clk-imx6q.c index 7f2c10c7413a..c0c4e723b7f5 100644 --- a/trunk/arch/arm/mach-imx/clk-imx6q.c +++ b/trunk/arch/arm/mach-imx/clk-imx6q.c @@ -436,6 +436,9 @@ int __init mx6q_clocks_init(void) for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clk[clks_init_on[i]]); + /* Set initial power mode */ + imx6q_set_lpm(WAIT_CLOCKED); + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpt"); base = of_iomap(np, 0); WARN_ON(!base); diff --git a/trunk/arch/arm/mach-imx/common.h b/trunk/arch/arm/mach-imx/common.h index 7191ab4434e5..fa36fb84ab19 100644 --- a/trunk/arch/arm/mach-imx/common.h +++ b/trunk/arch/arm/mach-imx/common.h @@ -142,6 +142,7 @@ extern int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); extern void imx6q_clock_map_io(void); extern void imx_cpu_die(unsigned int cpu); +extern int imx_cpu_kill(unsigned int cpu); #ifdef CONFIG_PM extern void imx6q_pm_init(void); diff --git a/trunk/arch/arm/mach-imx/devices/devices-common.h b/trunk/arch/arm/mach-imx/devices/devices-common.h index 6277baf1b7be..9bd5777ff0e7 100644 --- a/trunk/arch/arm/mach-imx/devices/devices-common.h +++ b/trunk/arch/arm/mach-imx/devices/devices-common.h @@ -63,6 +63,7 @@ struct platform_device *__init imx_add_flexcan( #include struct imx_fsl_usb2_udc_data { + const char *devid; resource_size_t iobase; resource_size_t irq; }; diff --git a/trunk/arch/arm/mach-imx/devices/platform-fsl-usb2-udc.c b/trunk/arch/arm/mach-imx/devices/platform-fsl-usb2-udc.c index 37e44398197b..3c06bd96e9cc 100644 --- a/trunk/arch/arm/mach-imx/devices/platform-fsl-usb2-udc.c +++ b/trunk/arch/arm/mach-imx/devices/platform-fsl-usb2-udc.c @@ -11,35 +11,36 @@ #include "../hardware.h" #include "devices-common.h" -#define imx_fsl_usb2_udc_data_entry_single(soc) \ +#define imx_fsl_usb2_udc_data_entry_single(soc, _devid) \ { \ + .devid = _devid, \ .iobase = soc ## _USB_OTG_BASE_ADDR, \ .irq = soc ## _INT_USB_OTG, \ } #ifdef CONFIG_SOC_IMX25 const struct imx_fsl_usb2_udc_data imx25_fsl_usb2_udc_data __initconst = - imx_fsl_usb2_udc_data_entry_single(MX25); + imx_fsl_usb2_udc_data_entry_single(MX25, "imx-udc-mx27"); #endif /* ifdef CONFIG_SOC_IMX25 */ #ifdef CONFIG_SOC_IMX27 const struct imx_fsl_usb2_udc_data imx27_fsl_usb2_udc_data __initconst = - imx_fsl_usb2_udc_data_entry_single(MX27); + imx_fsl_usb2_udc_data_entry_single(MX27, "imx-udc-mx27"); #endif /* ifdef CONFIG_SOC_IMX27 */ #ifdef CONFIG_SOC_IMX31 const struct imx_fsl_usb2_udc_data imx31_fsl_usb2_udc_data __initconst = - imx_fsl_usb2_udc_data_entry_single(MX31); + imx_fsl_usb2_udc_data_entry_single(MX31, "imx-udc-mx27"); #endif /* ifdef CONFIG_SOC_IMX31 */ #ifdef CONFIG_SOC_IMX35 const struct imx_fsl_usb2_udc_data imx35_fsl_usb2_udc_data __initconst = - imx_fsl_usb2_udc_data_entry_single(MX35); + imx_fsl_usb2_udc_data_entry_single(MX35, "imx-udc-mx27"); #endif /* ifdef CONFIG_SOC_IMX35 */ #ifdef CONFIG_SOC_IMX51 const struct imx_fsl_usb2_udc_data imx51_fsl_usb2_udc_data __initconst = - imx_fsl_usb2_udc_data_entry_single(MX51); + imx_fsl_usb2_udc_data_entry_single(MX51, "imx-udc-mx51"); #endif struct platform_device *__init imx_add_fsl_usb2_udc( @@ -57,7 +58,7 @@ struct platform_device *__init imx_add_fsl_usb2_udc( .flags = IORESOURCE_IRQ, }, }; - return imx_add_platform_device_dmamask("fsl-usb2-udc", -1, + return imx_add_platform_device_dmamask(data->devid, -1, res, ARRAY_SIZE(res), pdata, sizeof(*pdata), DMA_BIT_MASK(32)); } diff --git a/trunk/arch/arm/mach-imx/devices/platform-imx-fb.c b/trunk/arch/arm/mach-imx/devices/platform-imx-fb.c index 10b0ed39f07f..25a47c616b2d 100644 --- a/trunk/arch/arm/mach-imx/devices/platform-imx-fb.c +++ b/trunk/arch/arm/mach-imx/devices/platform-imx-fb.c @@ -54,7 +54,7 @@ struct platform_device *__init imx_add_imx_fb( .flags = IORESOURCE_IRQ, }, }; - return imx_add_platform_device_dmamask("imx-fb", 0, + return imx_add_platform_device_dmamask(data->devid, 0, res, ARRAY_SIZE(res), pdata, sizeof(*pdata), DMA_BIT_MASK(32)); } diff --git a/trunk/arch/arm/mach-imx/hotplug.c b/trunk/arch/arm/mach-imx/hotplug.c index 3dec962b0770..7bc5fe15dda2 100644 --- a/trunk/arch/arm/mach-imx/hotplug.c +++ b/trunk/arch/arm/mach-imx/hotplug.c @@ -46,9 +46,11 @@ static inline void cpu_enter_lowpower(void) void imx_cpu_die(unsigned int cpu) { cpu_enter_lowpower(); - imx_enable_cpu(cpu, false); + cpu_do_idle(); +} - /* spin here until hardware takes it down */ - while (1) - ; +int imx_cpu_kill(unsigned int cpu) +{ + imx_enable_cpu(cpu, false); + return 1; } diff --git a/trunk/arch/arm/mach-imx/iram_alloc.c b/trunk/arch/arm/mach-imx/iram_alloc.c index 6c80424f678e..e05cf407db65 100644 --- a/trunk/arch/arm/mach-imx/iram_alloc.c +++ b/trunk/arch/arm/mach-imx/iram_alloc.c @@ -22,8 +22,7 @@ #include #include #include - -#include "iram.h" +#include "linux/platform_data/imx-iram.h" static unsigned long iram_phys_base; static void __iomem *iram_virt_base; diff --git a/trunk/arch/arm/mach-imx/platsmp.c b/trunk/arch/arm/mach-imx/platsmp.c index 3777b805b76b..66fae885c842 100644 --- a/trunk/arch/arm/mach-imx/platsmp.c +++ b/trunk/arch/arm/mach-imx/platsmp.c @@ -92,5 +92,6 @@ struct smp_operations imx_smp_ops __initdata = { .smp_boot_secondary = imx_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = imx_cpu_die, + .cpu_kill = imx_cpu_kill, #endif }; diff --git a/trunk/arch/arm/mach-imx/pm-imx6q.c b/trunk/arch/arm/mach-imx/pm-imx6q.c index a17543da602d..ee42d20cba19 100644 --- a/trunk/arch/arm/mach-imx/pm-imx6q.c +++ b/trunk/arch/arm/mach-imx/pm-imx6q.c @@ -41,6 +41,7 @@ static int imx6q_pm_enter(suspend_state_t state) cpu_suspend(0, imx6q_suspend_finish); imx_smp_prepare(); imx_gpc_post_resume(); + imx6q_set_lpm(WAIT_CLOCKED); break; default: return -EINVAL; diff --git a/trunk/arch/arm/mach-integrator/pci_v3.c b/trunk/arch/arm/mach-integrator/pci_v3.c index be50e795536d..e7fcea7f3300 100644 --- a/trunk/arch/arm/mach-integrator/pci_v3.c +++ b/trunk/arch/arm/mach-integrator/pci_v3.c @@ -475,13 +475,12 @@ int __init pci_v3_setup(int nr, struct pci_sys_data *sys) { int ret = 0; + if (!ap_syscon_base) + return -EINVAL; + if (nr == 0) { sys->mem_offset = PHYS_PCI_MEM_BASE; ret = pci_v3_setup_resources(sys); - /* Remap the Integrator system controller */ - ap_syscon_base = ioremap(INTEGRATOR_SC_BASE, 0x100); - if (!ap_syscon_base) - return -EINVAL; } return ret; @@ -497,6 +496,13 @@ void __init pci_v3_preinit(void) unsigned int temp; int ret; + /* Remap the Integrator system controller */ + ap_syscon_base = ioremap(INTEGRATOR_SC_BASE, 0x100); + if (!ap_syscon_base) { + pr_err("unable to remap the AP syscon for PCIv3\n"); + return; + } + pcibios_min_mem = 0x00100000; /* diff --git a/trunk/arch/arm/mach-kirkwood/board-ns2.c b/trunk/arch/arm/mach-kirkwood/board-ns2.c index 8821720ab5a4..f4632a809f68 100644 --- a/trunk/arch/arm/mach-kirkwood/board-ns2.c +++ b/trunk/arch/arm/mach-kirkwood/board-ns2.c @@ -18,47 +18,11 @@ #include #include #include "common.h" -#include "mpp.h" static struct mv643xx_eth_platform_data ns2_ge00_data = { .phy_addr = MV643XX_ETH_PHY_ADDR(8), }; -static unsigned int ns2_mpp_config[] __initdata = { - MPP0_SPI_SCn, - MPP1_SPI_MOSI, - MPP2_SPI_SCK, - MPP3_SPI_MISO, - MPP4_NF_IO6, - MPP5_NF_IO7, - MPP6_SYSRST_OUTn, - MPP7_GPO, /* Fan speed (bit 1) */ - MPP8_TW0_SDA, - MPP9_TW0_SCK, - MPP10_UART0_TXD, - MPP11_UART0_RXD, - MPP12_GPO, /* Red led */ - MPP14_GPIO, /* USB fuse */ - MPP16_GPIO, /* SATA 0 power */ - MPP17_GPIO, /* SATA 1 power */ - MPP18_NF_IO0, - MPP19_NF_IO1, - MPP20_SATA1_ACTn, - MPP21_SATA0_ACTn, - MPP22_GPIO, /* Fan speed (bit 0) */ - MPP23_GPIO, /* Fan power */ - MPP24_GPIO, /* USB mode select */ - MPP25_GPIO, /* Fan rotation fail */ - MPP26_GPIO, /* USB device vbus */ - MPP28_GPIO, /* USB enable host vbus */ - MPP29_GPIO, /* Blue led (slow register) */ - MPP30_GPIO, /* Blue led (command register) */ - MPP31_GPIO, /* Board power off */ - MPP32_GPIO, /* Power button (0 = Released, 1 = Pushed) */ - MPP33_GPO, /* Fan speed (bit 2) */ - 0 -}; - #define NS2_GPIO_POWER_OFF 31 static void ns2_power_off(void) @@ -71,8 +35,6 @@ void __init ns2_init(void) /* * Basic setup. Needs to be called early. */ - kirkwood_mpp_conf(ns2_mpp_config); - if (of_machine_is_compatible("lacie,netspace_lite_v2") || of_machine_is_compatible("lacie,netspace_mini_v2")) ns2_ge00_data.phy_addr = MV643XX_ETH_PHY_ADDR(0); diff --git a/trunk/arch/arm/mach-mvebu/Makefile b/trunk/arch/arm/mach-mvebu/Makefile index 5dcb369b58aa..99df4df680fd 100644 --- a/trunk/arch/arm/mach-mvebu/Makefile +++ b/trunk/arch/arm/mach-mvebu/Makefile @@ -1,6 +1,8 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ -I$(srctree)/arch/arm/plat-orion/include +AFLAGS_coherency_ll.o := -Wa,-march=armv7-a + obj-y += system-controller.o obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o pmsu.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o diff --git a/trunk/arch/arm/mach-omap2/board-omap4panda.c b/trunk/arch/arm/mach-omap2/board-omap4panda.c index 5c8e9cee2c2e..769c1feee1c4 100644 --- a/trunk/arch/arm/mach-omap2/board-omap4panda.c +++ b/trunk/arch/arm/mach-omap2/board-omap4panda.c @@ -397,6 +397,12 @@ static struct omap_board_mux board_mux[] __initdata = { OMAP_PULL_ENA), OMAP4_MUX(ABE_MCBSP1_FSX, OMAP_MUX_MODE0 | OMAP_PIN_INPUT), + /* UART2 - BT/FM/GPS shared transport */ + OMAP4_MUX(UART2_CTS, OMAP_PIN_INPUT | OMAP_MUX_MODE0), + OMAP4_MUX(UART2_RTS, OMAP_PIN_OUTPUT | OMAP_MUX_MODE0), + OMAP4_MUX(UART2_RX, OMAP_PIN_INPUT | OMAP_MUX_MODE0), + OMAP4_MUX(UART2_TX, OMAP_PIN_OUTPUT | OMAP_MUX_MODE0), + { .reg_offset = OMAP_MUX_TERMINATOR }, }; diff --git a/trunk/arch/arm/mach-omap2/cclock2420_data.c b/trunk/arch/arm/mach-omap2/cclock2420_data.c index 7e5febe456d9..ab7e952d2070 100644 --- a/trunk/arch/arm/mach-omap2/cclock2420_data.c +++ b/trunk/arch/arm/mach-omap2/cclock2420_data.c @@ -1935,6 +1935,8 @@ int __init omap2420_clk_init(void) omap2_init_clk_hw_omap_clocks(c->lk.clk); } + omap2xxx_clkt_vps_late_init(); + omap2_clk_disable_autoidle_all(); omap2_clk_enable_init_clocks(enable_init_clks, diff --git a/trunk/arch/arm/mach-omap2/cclock2430_data.c b/trunk/arch/arm/mach-omap2/cclock2430_data.c index eda079b96c6a..eb3dab68d536 100644 --- a/trunk/arch/arm/mach-omap2/cclock2430_data.c +++ b/trunk/arch/arm/mach-omap2/cclock2430_data.c @@ -2050,6 +2050,8 @@ int __init omap2430_clk_init(void) omap2_init_clk_hw_omap_clocks(c->lk.clk); } + omap2xxx_clkt_vps_late_init(); + omap2_clk_disable_autoidle_all(); omap2_clk_enable_init_clocks(enable_init_clks, diff --git a/trunk/arch/arm/mach-omap2/cclock44xx_data.c b/trunk/arch/arm/mach-omap2/cclock44xx_data.c index 5789a5e25563..a2cc046b47f4 100644 --- a/trunk/arch/arm/mach-omap2/cclock44xx_data.c +++ b/trunk/arch/arm/mach-omap2/cclock44xx_data.c @@ -2026,14 +2026,13 @@ int __init omap4xxx_clk_init(void) * On OMAP4460 the ABE DPLL fails to turn on if in idle low-power * state when turning the ABE clock domain. Workaround this by * locking the ABE DPLL on boot. + * Lock the ABE DPLL in any case to avoid issues with audio. */ - if (cpu_is_omap446x()) { - rc = clk_set_parent(&abe_dpll_refclk_mux_ck, &sys_32k_ck); - if (!rc) - rc = clk_set_rate(&dpll_abe_ck, OMAP4_DPLL_ABE_DEFFREQ); - if (rc) - pr_err("%s: failed to configure ABE DPLL!\n", __func__); - } + rc = clk_set_parent(&abe_dpll_refclk_mux_ck, &sys_32k_ck); + if (!rc) + rc = clk_set_rate(&dpll_abe_ck, OMAP4_DPLL_ABE_DEFFREQ); + if (rc) + pr_err("%s: failed to configure ABE DPLL!\n", __func__); return 0; } diff --git a/trunk/arch/arm/mach-omap2/devices.c b/trunk/arch/arm/mach-omap2/devices.c index 5e304d0719a2..626f3ea3142f 100644 --- a/trunk/arch/arm/mach-omap2/devices.c +++ b/trunk/arch/arm/mach-omap2/devices.c @@ -639,7 +639,7 @@ static int count_ocp2scp_devices(struct omap_ocp2scp_dev *ocp2scp_dev) return cnt; } -static void omap_init_ocp2scp(void) +static void __init omap_init_ocp2scp(void) { struct omap_hwmod *oh; struct platform_device *pdev; diff --git a/trunk/arch/arm/mach-omap2/drm.c b/trunk/arch/arm/mach-omap2/drm.c index 4c7566c7e24a..2a2cfa88ddbf 100644 --- a/trunk/arch/arm/mach-omap2/drm.c +++ b/trunk/arch/arm/mach-omap2/drm.c @@ -25,6 +25,7 @@ #include #include +#include "soc.h" #include "omap_device.h" #include "omap_hwmod.h" @@ -56,7 +57,7 @@ static int __init omap_init_drm(void) oh->name); } - platform_data.omaprev = GET_OMAP_REVISION(); + platform_data.omaprev = GET_OMAP_TYPE; return platform_device_register(&omap_drm_device); diff --git a/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 129d5081ed15..793f54ac7d14 100644 --- a/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -2132,8 +2132,12 @@ static struct omap_hwmod omap44xx_mcpdm_hwmod = { * currently reset very early during boot, before I2C is * available, so it doesn't seem that we have any choice in * the kernel other than to avoid resetting it. + * + * Also, McPDM needs to be configured to NO_IDLE mode when it + * is in used otherwise vital clocks will be gated which + * results 'slow motion' audio playback. */ - .flags = HWMOD_EXT_OPT_MAIN_CLK, + .flags = HWMOD_EXT_OPT_MAIN_CLK | HWMOD_SWSUP_SIDLE, .mpu_irqs = omap44xx_mcpdm_irqs, .sdma_reqs = omap44xx_mcpdm_sdma_reqs, .main_clk = "mcpdm_fck", diff --git a/trunk/arch/arm/mach-omap2/timer.c b/trunk/arch/arm/mach-omap2/timer.c index 691aa674665a..b8ad6e632bb8 100644 --- a/trunk/arch/arm/mach-omap2/timer.c +++ b/trunk/arch/arm/mach-omap2/timer.c @@ -165,15 +165,11 @@ static struct device_node * __init omap_get_timer_dt(struct of_device_id *match, struct device_node *np; for_each_matching_node(np, match) { - if (!of_device_is_available(np)) { - of_node_put(np); + if (!of_device_is_available(np)) continue; - } - if (property && !of_get_property(np, property, NULL)) { - of_node_put(np); + if (property && !of_get_property(np, property, NULL)) continue; - } of_add_property(np, &device_disabled); return np; diff --git a/trunk/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/trunk/arch/arm/mach-s3c64xx/mach-crag6410-module.c index 553059f51841..755c0bb119f4 100644 --- a/trunk/arch/arm/mach-s3c64xx/mach-crag6410-module.c +++ b/trunk/arch/arm/mach-s3c64xx/mach-crag6410-module.c @@ -47,7 +47,7 @@ static struct spi_board_info wm1253_devs[] = { .bus_num = 0, .chip_select = 0, .mode = SPI_MODE_0, - .irq = S3C_EINT(5), + .irq = S3C_EINT(4), .controller_data = &wm0010_spi_csinfo, .platform_data = &wm0010_pdata, }, diff --git a/trunk/arch/arm/mach-s3c64xx/pm.c b/trunk/arch/arm/mach-s3c64xx/pm.c index 7feb426fc202..d2e1a16690bd 100644 --- a/trunk/arch/arm/mach-s3c64xx/pm.c +++ b/trunk/arch/arm/mach-s3c64xx/pm.c @@ -338,8 +338,10 @@ int __init s3c64xx_pm_init(void) for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); i++) pm_genpd_init(&s3c64xx_pm_domains[i]->pd, NULL, false); +#ifdef CONFIG_S3C_DEV_FB if (dev_get_platdata(&s3c_device_fb.dev)) pm_genpd_add_device(&s3c64xx_pm_f.pd, &s3c_device_fb.dev); +#endif return 0; } diff --git a/trunk/arch/arm/mm/dma-mapping.c b/trunk/arch/arm/mm/dma-mapping.c index 6b2fb87c8698..076c26d43864 100644 --- a/trunk/arch/arm/mm/dma-mapping.c +++ b/trunk/arch/arm/mm/dma-mapping.c @@ -774,25 +774,27 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset, size_t size, enum dma_data_direction dir, void (*op)(const void *, size_t, int)) { + unsigned long pfn; + size_t left = size; + + pfn = page_to_pfn(page) + offset / PAGE_SIZE; + offset %= PAGE_SIZE; + /* * A single sg entry may refer to multiple physically contiguous * pages. But we still need to process highmem pages individually. * If highmem is not configured then the bulk of this loop gets * optimized out. */ - size_t left = size; do { size_t len = left; void *vaddr; + page = pfn_to_page(pfn); + if (PageHighMem(page)) { - if (len + offset > PAGE_SIZE) { - if (offset >= PAGE_SIZE) { - page += offset / PAGE_SIZE; - offset %= PAGE_SIZE; - } + if (len + offset > PAGE_SIZE) len = PAGE_SIZE - offset; - } vaddr = kmap_high_get(page); if (vaddr) { vaddr += offset; @@ -809,7 +811,7 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset, op(vaddr, len, dir); } offset = 0; - page++; + pfn++; left -= len; } while (left); } diff --git a/trunk/arch/arm/mm/mmu.c b/trunk/arch/arm/mm/mmu.c index 9f0610243bd6..ce328c7f5c94 100644 --- a/trunk/arch/arm/mm/mmu.c +++ b/trunk/arch/arm/mm/mmu.c @@ -283,7 +283,7 @@ static struct mem_type mem_types[] = { }, [MT_MEMORY_SO] = { .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | - L_PTE_MT_UNCACHED, + L_PTE_MT_UNCACHED | L_PTE_XN, .prot_l1 = PMD_TYPE_TABLE, .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_S | PMD_SECT_UNCACHED | PMD_SECT_XN, diff --git a/trunk/arch/arm/plat-versatile/headsmp.S b/trunk/arch/arm/plat-versatile/headsmp.S index dd703ef09b8d..b178d44e9eaa 100644 --- a/trunk/arch/arm/plat-versatile/headsmp.S +++ b/trunk/arch/arm/plat-versatile/headsmp.S @@ -20,7 +20,7 @@ */ ENTRY(versatile_secondary_startup) mrc p15, 0, r0, c0, c0, 5 - and r0, r0, #15 + bic r0, #0xff000000 adr r4, 1f ldmia r4, {r5, r6} sub r4, r4, r5 diff --git a/trunk/arch/arm/vfp/entry.S b/trunk/arch/arm/vfp/entry.S index cc926c985981..323ce1a62bbf 100644 --- a/trunk/arch/arm/vfp/entry.S +++ b/trunk/arch/arm/vfp/entry.S @@ -22,7 +22,7 @@ @ IRQs disabled. @ ENTRY(do_vfp) -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT ldr r4, [r10, #TI_PREEMPT] @ get preempt count add r11, r4, #1 @ increment it str r11, [r10, #TI_PREEMPT] @@ -35,7 +35,7 @@ ENTRY(do_vfp) ENDPROC(do_vfp) ENTRY(vfp_null_entry) -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT get_thread_info r10 ldr r4, [r10, #TI_PREEMPT] @ get preempt count sub r11, r4, #1 @ decrement it @@ -53,7 +53,7 @@ ENDPROC(vfp_null_entry) __INIT ENTRY(vfp_testing_entry) -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT get_thread_info r10 ldr r4, [r10, #TI_PREEMPT] @ get preempt count sub r11, r4, #1 @ decrement it diff --git a/trunk/arch/arm/vfp/vfphw.S b/trunk/arch/arm/vfp/vfphw.S index ea0349f63586..dd5e56f95f3f 100644 --- a/trunk/arch/arm/vfp/vfphw.S +++ b/trunk/arch/arm/vfp/vfphw.S @@ -168,7 +168,7 @@ vfp_hw_state_valid: @ else it's one 32-bit instruction, so @ always subtract 4 from the following @ instruction address. -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT get_thread_info r10 ldr r4, [r10, #TI_PREEMPT] @ get preempt count sub r11, r4, #1 @ decrement it @@ -192,7 +192,7 @@ look_for_VFP_exceptions: @ not recognised by VFP DBGSTR "not VFP" -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT_COUNT get_thread_info r10 ldr r4, [r10, #TI_PREEMPT] @ get preempt count sub r11, r4, #1 @ decrement it diff --git a/trunk/arch/arm64/boot/dts/Makefile b/trunk/arch/arm64/boot/dts/Makefile index 801e2d7fcbc6..32ac0aef0068 100644 --- a/trunk/arch/arm64/boot/dts/Makefile +++ b/trunk/arch/arm64/boot/dts/Makefile @@ -1,4 +1,5 @@ targets += dtbs +targets += $(dtb-y) dtbs: $(addprefix $(obj)/, $(dtb-y)) diff --git a/trunk/arch/arm64/include/asm/elf.h b/trunk/arch/arm64/include/asm/elf.h index 07fea290d7c1..fe32c0e4ac01 100644 --- a/trunk/arch/arm64/include/asm/elf.h +++ b/trunk/arch/arm64/include/asm/elf.h @@ -26,7 +26,10 @@ typedef unsigned long elf_greg_t; -#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t)) +#define ELF_NGREG (sizeof(struct user_pt_regs) / sizeof(elf_greg_t)) +#define ELF_CORE_COPY_REGS(dest, regs) \ + *(struct user_pt_regs *)&(dest) = (regs)->user_regs; + typedef elf_greg_t elf_gregset_t[ELF_NGREG]; typedef struct user_fpsimd_state elf_fpregset_t; diff --git a/trunk/arch/arm64/include/asm/pgtable.h b/trunk/arch/arm64/include/asm/pgtable.h index 64b133949502..e333a243bfcc 100644 --- a/trunk/arch/arm64/include/asm/pgtable.h +++ b/trunk/arch/arm64/include/asm/pgtable.h @@ -24,7 +24,8 @@ /* * Software defined PTE bits definition. */ -#define PTE_VALID (_AT(pteval_t, 1) << 0) /* pte_present() check */ +#define PTE_VALID (_AT(pteval_t, 1) << 0) +#define PTE_PROT_NONE (_AT(pteval_t, 1) << 1) /* only when !PTE_VALID */ #define PTE_FILE (_AT(pteval_t, 1) << 2) /* only when !pte_present() */ #define PTE_DIRTY (_AT(pteval_t, 1) << 55) #define PTE_SPECIAL (_AT(pteval_t, 1) << 56) @@ -60,9 +61,12 @@ extern void __pgd_error(const char *file, int line, unsigned long val); extern pgprot_t pgprot_default; -#define _MOD_PROT(p, b) __pgprot(pgprot_val(p) | (b)) +#define __pgprot_modify(prot,mask,bits) \ + __pgprot((pgprot_val(prot) & ~(mask)) | (bits)) + +#define _MOD_PROT(p, b) __pgprot_modify(p, 0, b) -#define PAGE_NONE _MOD_PROT(pgprot_default, PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY) +#define PAGE_NONE __pgprot_modify(pgprot_default, PTE_TYPE_MASK, PTE_PROT_NONE) #define PAGE_SHARED _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) #define PAGE_SHARED_EXEC _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN) #define PAGE_COPY _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY) @@ -72,7 +76,7 @@ extern pgprot_t pgprot_default; #define PAGE_KERNEL _MOD_PROT(pgprot_default, PTE_PXN | PTE_UXN | PTE_DIRTY) #define PAGE_KERNEL_EXEC _MOD_PROT(pgprot_default, PTE_UXN | PTE_DIRTY) -#define __PAGE_NONE __pgprot(_PAGE_DEFAULT | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY) +#define __PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE) #define __PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) #define __PAGE_SHARED_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN) #define __PAGE_COPY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY) @@ -125,16 +129,15 @@ extern struct page *empty_zero_page; /* * The following only work if pte_present(). Undefined behaviour otherwise. */ -#define pte_present(pte) (pte_val(pte) & PTE_VALID) +#define pte_present(pte) (pte_val(pte) & (PTE_VALID | PTE_PROT_NONE)) #define pte_dirty(pte) (pte_val(pte) & PTE_DIRTY) #define pte_young(pte) (pte_val(pte) & PTE_AF) #define pte_special(pte) (pte_val(pte) & PTE_SPECIAL) #define pte_write(pte) (!(pte_val(pte) & PTE_RDONLY)) #define pte_exec(pte) (!(pte_val(pte) & PTE_UXN)) -#define pte_present_exec_user(pte) \ - ((pte_val(pte) & (PTE_VALID | PTE_USER | PTE_UXN)) == \ - (PTE_VALID | PTE_USER)) +#define pte_valid_user(pte) \ + ((pte_val(pte) & (PTE_VALID | PTE_USER)) == (PTE_VALID | PTE_USER)) #define PTE_BIT_FUNC(fn,op) \ static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; } @@ -157,10 +160,13 @@ extern void __sync_icache_dcache(pte_t pteval, unsigned long addr); static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte) { - if (pte_present_exec_user(pte)) - __sync_icache_dcache(pte, addr); - if (!pte_dirty(pte)) - pte = pte_wrprotect(pte); + if (pte_valid_user(pte)) { + if (pte_exec(pte)) + __sync_icache_dcache(pte, addr); + if (!pte_dirty(pte)) + pte = pte_wrprotect(pte); + } + set_pte(ptep, pte); } @@ -170,9 +176,6 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, #define pte_huge(pte) ((pte_val(pte) & PTE_TYPE_MASK) == PTE_TYPE_HUGEPAGE) #define pte_mkhuge(pte) (__pte((pte_val(pte) & ~PTE_TYPE_MASK) | PTE_TYPE_HUGEPAGE)) -#define __pgprot_modify(prot,mask,bits) \ - __pgprot((pgprot_val(prot) & ~(mask)) | (bits)) - #define __HAVE_ARCH_PTE_SPECIAL /* @@ -264,7 +267,8 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr) static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { - const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY; + const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY | + PTE_PROT_NONE | PTE_VALID; pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask); return pte; } diff --git a/trunk/arch/arm64/include/asm/unistd32.h b/trunk/arch/arm64/include/asm/unistd32.h index 58432625fdb3..5ef47ba3ed45 100644 --- a/trunk/arch/arm64/include/asm/unistd32.h +++ b/trunk/arch/arm64/include/asm/unistd32.h @@ -395,8 +395,13 @@ __SYSCALL(370, sys_name_to_handle_at) __SYSCALL(371, compat_sys_open_by_handle_at) __SYSCALL(372, compat_sys_clock_adjtime) __SYSCALL(373, sys_syncfs) +__SYSCALL(374, compat_sys_sendmmsg) +__SYSCALL(375, sys_setns) +__SYSCALL(376, compat_sys_process_vm_readv) +__SYSCALL(377, compat_sys_process_vm_writev) +__SYSCALL(378, sys_ni_syscall) /* 378 for kcmp */ -#define __NR_compat_syscalls 374 +#define __NR_compat_syscalls 379 /* * Compat syscall numbers used by the AArch64 kernel. diff --git a/trunk/arch/arm64/kernel/vdso.c b/trunk/arch/arm64/kernel/vdso.c index c958cb84d75f..6a389dc1bd49 100644 --- a/trunk/arch/arm64/kernel/vdso.c +++ b/trunk/arch/arm64/kernel/vdso.c @@ -252,10 +252,6 @@ void update_vsyscall(struct timekeeper *tk) void update_vsyscall_tz(void) { - ++vdso_data->tb_seq_count; - smp_wmb(); vdso_data->tz_minuteswest = sys_tz.tz_minuteswest; vdso_data->tz_dsttime = sys_tz.tz_dsttime; - smp_wmb(); - ++vdso_data->tb_seq_count; } diff --git a/trunk/arch/arm64/kernel/vdso/gettimeofday.S b/trunk/arch/arm64/kernel/vdso/gettimeofday.S index 8bf658d974f9..f0a6d10b5211 100644 --- a/trunk/arch/arm64/kernel/vdso/gettimeofday.S +++ b/trunk/arch/arm64/kernel/vdso/gettimeofday.S @@ -73,8 +73,6 @@ ENTRY(__kernel_gettimeofday) /* If tz is NULL, return 0. */ cbz x1, 3f ldp w4, w5, [vdso_data, #VDSO_TZ_MINWEST] - seqcnt_read w9 - seqcnt_check w9, 1b stp w4, w5, [x1, #TZ_MINWEST] 3: mov x0, xzr diff --git a/trunk/arch/ia64/kernel/ptrace.c b/trunk/arch/ia64/kernel/ptrace.c index 4265ff64219b..b7a5fffe0924 100644 --- a/trunk/arch/ia64/kernel/ptrace.c +++ b/trunk/arch/ia64/kernel/ptrace.c @@ -672,33 +672,6 @@ ptrace_attach_sync_user_rbs (struct task_struct *child) read_unlock(&tasklist_lock); } -static inline int -thread_matches (struct task_struct *thread, unsigned long addr) -{ - unsigned long thread_rbs_end; - struct pt_regs *thread_regs; - - if (ptrace_check_attach(thread, 0) < 0) - /* - * If the thread is not in an attachable state, we'll - * ignore it. The net effect is that if ADDR happens - * to overlap with the portion of the thread's - * register backing store that is currently residing - * on the thread's kernel stack, then ptrace() may end - * up accessing a stale value. But if the thread - * isn't stopped, that's a problem anyhow, so we're - * doing as well as we can... - */ - return 0; - - thread_regs = task_pt_regs(thread); - thread_rbs_end = ia64_get_user_rbs_end(thread, thread_regs, NULL); - if (!on_kernel_rbs(addr, thread_regs->ar_bspstore, thread_rbs_end)) - return 0; - - return 1; /* looks like we've got a winner */ -} - /* * Write f32-f127 back to task->thread.fph if it has been modified. */ diff --git a/trunk/arch/m68k/include/asm/dma-mapping.h b/trunk/arch/m68k/include/asm/dma-mapping.h index 17f7a45948ea..3e6b8445af6a 100644 --- a/trunk/arch/m68k/include/asm/dma-mapping.h +++ b/trunk/arch/m68k/include/asm/dma-mapping.h @@ -21,6 +21,22 @@ extern void *dma_alloc_coherent(struct device *, size_t, extern void dma_free_coherent(struct device *, size_t, void *, dma_addr_t); +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) +{ + /* attrs is not supported and ignored */ + return dma_alloc_coherent(dev, size, dma_handle, flag); +} + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle, + struct dma_attrs *attrs) +{ + /* attrs is not supported and ignored */ + dma_free_coherent(dev, size, cpu_addr, dma_handle); +} + static inline void *dma_alloc_noncoherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t flag) { diff --git a/trunk/arch/m68k/include/asm/pgtable_no.h b/trunk/arch/m68k/include/asm/pgtable_no.h index bf86b29fe64a..037028f4ab70 100644 --- a/trunk/arch/m68k/include/asm/pgtable_no.h +++ b/trunk/arch/m68k/include/asm/pgtable_no.h @@ -64,6 +64,8 @@ extern unsigned int kobjsize(const void *objp); */ #define VMALLOC_START 0 #define VMALLOC_END 0xffffffff +#define KMAP_START 0 +#define KMAP_END 0xffffffff #include diff --git a/trunk/arch/m68k/include/asm/unistd.h b/trunk/arch/m68k/include/asm/unistd.h index 847994ce6804..f9337f614660 100644 --- a/trunk/arch/m68k/include/asm/unistd.h +++ b/trunk/arch/m68k/include/asm/unistd.h @@ -4,7 +4,7 @@ #include -#define NR_syscalls 348 +#define NR_syscalls 349 #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT diff --git a/trunk/arch/m68k/include/uapi/asm/unistd.h b/trunk/arch/m68k/include/uapi/asm/unistd.h index b94bfbf90705..625f321001dc 100644 --- a/trunk/arch/m68k/include/uapi/asm/unistd.h +++ b/trunk/arch/m68k/include/uapi/asm/unistd.h @@ -353,5 +353,6 @@ #define __NR_process_vm_readv 345 #define __NR_process_vm_writev 346 #define __NR_kcmp 347 +#define __NR_finit_module 348 #endif /* _UAPI_ASM_M68K_UNISTD_H_ */ diff --git a/trunk/arch/m68k/kernel/syscalltable.S b/trunk/arch/m68k/kernel/syscalltable.S index c30da5b3f2db..3f04ea0ab802 100644 --- a/trunk/arch/m68k/kernel/syscalltable.S +++ b/trunk/arch/m68k/kernel/syscalltable.S @@ -368,4 +368,5 @@ ENTRY(sys_call_table) .long sys_process_vm_readv /* 345 */ .long sys_process_vm_writev .long sys_kcmp + .long sys_finit_module diff --git a/trunk/arch/m68k/mm/init.c b/trunk/arch/m68k/mm/init.c index f0e05bce92f2..afd8106fd83b 100644 --- a/trunk/arch/m68k/mm/init.c +++ b/trunk/arch/m68k/mm/init.c @@ -39,6 +39,11 @@ void *empty_zero_page; EXPORT_SYMBOL(empty_zero_page); +#if !defined(CONFIG_SUN3) && !defined(CONFIG_COLDFIRE) +extern void init_pointer_table(unsigned long ptable); +extern pmd_t *zero_pgtable; +#endif + #ifdef CONFIG_MMU pg_data_t pg_data_map[MAX_NUMNODES]; @@ -69,9 +74,6 @@ void __init m68k_setup_node(int node) node_set_online(node); } -extern void init_pointer_table(unsigned long ptable); -extern pmd_t *zero_pgtable; - #else /* CONFIG_MMU */ /* diff --git a/trunk/arch/mn10300/Kconfig b/trunk/arch/mn10300/Kconfig index aa03f2e13385..e70001cfa05b 100644 --- a/trunk/arch/mn10300/Kconfig +++ b/trunk/arch/mn10300/Kconfig @@ -6,6 +6,7 @@ config MN10300 select ARCH_WANT_IPC_PARSE_VERSION select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_KGDB + select GENERIC_ATOMIC64 select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER select GENERIC_CLOCKEVENTS select MODULES_USE_ELF_RELA diff --git a/trunk/arch/parisc/kernel/entry.S b/trunk/arch/parisc/kernel/entry.S index bfb44247d7a7..eb7850b46c25 100644 --- a/trunk/arch/parisc/kernel/entry.S +++ b/trunk/arch/parisc/kernel/entry.S @@ -1865,7 +1865,7 @@ syscall_restore: /* Are we being ptraced? */ ldw TASK_FLAGS(%r1),%r19 - ldi (_TIF_SINGLESTEP|_TIF_BLOCKSTEP),%r2 + ldi _TIF_SYSCALL_TRACE_MASK,%r2 and,COND(=) %r19,%r2,%r0 b,n syscall_restore_rfi @@ -1978,15 +1978,23 @@ syscall_restore_rfi: /* sr2 should be set to zero for userspace syscalls */ STREG %r0,TASK_PT_SR2(%r1) -pt_regs_ok: LDREG TASK_PT_GR31(%r1),%r2 - depi 3,31,2,%r2 /* ensure return to user mode. */ - STREG %r2,TASK_PT_IAOQ0(%r1) + depi 3,31,2,%r2 /* ensure return to user mode. */ + STREG %r2,TASK_PT_IAOQ0(%r1) ldo 4(%r2),%r2 STREG %r2,TASK_PT_IAOQ1(%r1) + b intr_restore copy %r25,%r16 + +pt_regs_ok: + LDREG TASK_PT_IAOQ0(%r1),%r2 + depi 3,31,2,%r2 /* ensure return to user mode. */ + STREG %r2,TASK_PT_IAOQ0(%r1) + LDREG TASK_PT_IAOQ1(%r1),%r2 + depi 3,31,2,%r2 + STREG %r2,TASK_PT_IAOQ1(%r1) b intr_restore - nop + copy %r25,%r16 .import schedule,code syscall_do_resched: diff --git a/trunk/arch/parisc/kernel/irq.c b/trunk/arch/parisc/kernel/irq.c index c0b1affc06a8..0299d63cd112 100644 --- a/trunk/arch/parisc/kernel/irq.c +++ b/trunk/arch/parisc/kernel/irq.c @@ -410,11 +410,13 @@ void __init init_IRQ(void) { local_irq_disable(); /* PARANOID - should already be disabled */ mtctl(~0UL, 23); /* EIRR : clear all pending external intr */ - claim_cpu_irqs(); #ifdef CONFIG_SMP - if (!cpu_eiem) + if (!cpu_eiem) { + claim_cpu_irqs(); cpu_eiem = EIEM_MASK(IPI_IRQ) | EIEM_MASK(TIMER_IRQ); + } #else + claim_cpu_irqs(); cpu_eiem = EIEM_MASK(TIMER_IRQ); #endif set_eiem(cpu_eiem); /* EIEM : enable all external intr */ diff --git a/trunk/arch/parisc/kernel/ptrace.c b/trunk/arch/parisc/kernel/ptrace.c index 857c2f545470..534abd4936e1 100644 --- a/trunk/arch/parisc/kernel/ptrace.c +++ b/trunk/arch/parisc/kernel/ptrace.c @@ -26,7 +26,7 @@ #include /* PSW bits we allow the debugger to modify */ -#define USER_PSW_BITS (PSW_N | PSW_V | PSW_CB) +#define USER_PSW_BITS (PSW_N | PSW_B | PSW_V | PSW_CB) /* * Called by kernel/ptrace.c when detaching.. diff --git a/trunk/arch/parisc/kernel/signal.c b/trunk/arch/parisc/kernel/signal.c index 537996955998..fd051705a407 100644 --- a/trunk/arch/parisc/kernel/signal.c +++ b/trunk/arch/parisc/kernel/signal.c @@ -190,8 +190,10 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) DBG(1,"get_sigframe: ka = %#lx, sp = %#lx, frame_size = %#lx\n", (unsigned long)ka, sp, frame_size); + /* Align alternate stack and reserve 64 bytes for the signal + handler's frame marker. */ if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! sas_ss_flags(sp)) - sp = current->sas_ss_sp; /* Stacks grow up! */ + sp = (current->sas_ss_sp + 0x7f) & ~0x3f; /* Stacks grow up! */ DBG(1,"get_sigframe: Returning sp = %#lx\n", (unsigned long)sp); return (void __user *) sp; /* Stacks grow up. Fun. */ diff --git a/trunk/arch/parisc/math-emu/cnv_float.h b/trunk/arch/parisc/math-emu/cnv_float.h index 9071e093164a..933423fa5144 100644 --- a/trunk/arch/parisc/math-emu/cnv_float.h +++ b/trunk/arch/parisc/math-emu/cnv_float.h @@ -347,16 +347,15 @@ Sgl_isinexact_to_fix(sgl_value,exponent) #define Duint_from_sgl_mantissa(sgl_value,exponent,dresultA,dresultB) \ - {Sall(sgl_value) <<= SGL_EXP_LENGTH; /* left-justify */ \ + {unsigned int val = Sall(sgl_value) << SGL_EXP_LENGTH; \ if (exponent <= 31) { \ - Dintp1(dresultA) = 0; \ - Dintp2(dresultB) = (unsigned)Sall(sgl_value) >> (31 - exponent); \ + Dintp1(dresultA) = 0; \ + Dintp2(dresultB) = val >> (31 - exponent); \ } \ else { \ - Dintp1(dresultA) = Sall(sgl_value) >> (63 - exponent); \ - Dintp2(dresultB) = Sall(sgl_value) << (exponent - 31); \ + Dintp1(dresultA) = val >> (63 - exponent); \ + Dintp2(dresultB) = exponent <= 62 ? val << (exponent - 31) : 0; \ } \ - Sall(sgl_value) >>= SGL_EXP_LENGTH; /* return to original */ \ } #define Duint_setzero(dresultA,dresultB) \ diff --git a/trunk/arch/powerpc/include/uapi/asm/kvm_para.h b/trunk/arch/powerpc/include/uapi/asm/kvm_para.h index ed0e0254b47f..e3af3286a068 100644 --- a/trunk/arch/powerpc/include/uapi/asm/kvm_para.h +++ b/trunk/arch/powerpc/include/uapi/asm/kvm_para.h @@ -78,7 +78,7 @@ struct kvm_vcpu_arch_shared { #define KVM_HCALL_TOKEN(num) _EV_HCALL_TOKEN(EV_KVM_VENDOR_ID, num) -#include +#include #define KVM_FEATURE_MAGIC_PAGE 1 diff --git a/trunk/arch/powerpc/kvm/book3s_hv_ras.c b/trunk/arch/powerpc/kvm/book3s_hv_ras.c index 35f3cf0269b3..a353c485808c 100644 --- a/trunk/arch/powerpc/kvm/book3s_hv_ras.c +++ b/trunk/arch/powerpc/kvm/book3s_hv_ras.c @@ -79,7 +79,9 @@ static void flush_tlb_power7(struct kvm_vcpu *vcpu) static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu) { unsigned long srr1 = vcpu->arch.shregs.msr; +#ifdef CONFIG_PPC_POWERNV struct opal_machine_check_event *opal_evt; +#endif long handled = 1; if (srr1 & SRR1_MC_LDSTERR) { @@ -117,6 +119,7 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu) handled = 0; } +#ifdef CONFIG_PPC_POWERNV /* * See if OPAL has already handled the condition. * We assume that if the condition is recovered then OPAL @@ -131,6 +134,7 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu) if (handled) opal_evt->in_use = 0; +#endif return handled; } diff --git a/trunk/arch/powerpc/kvm/emulate.c b/trunk/arch/powerpc/kvm/emulate.c index b0855e5d8905..9d9cddc5b346 100644 --- a/trunk/arch/powerpc/kvm/emulate.c +++ b/trunk/arch/powerpc/kvm/emulate.c @@ -39,6 +39,7 @@ #define OP_31_XOP_TRAP 4 #define OP_31_XOP_LWZX 23 #define OP_31_XOP_TRAP_64 68 +#define OP_31_XOP_DCBF 86 #define OP_31_XOP_LBZX 87 #define OP_31_XOP_STWX 151 #define OP_31_XOP_STBX 215 @@ -374,6 +375,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) emulated = kvmppc_emulate_mtspr(vcpu, sprn, rs); break; + case OP_31_XOP_DCBF: case OP_31_XOP_DCBI: /* Do nothing. The guest is performing dcbi because * hardware DMA is not snooped by the dcache, but diff --git a/trunk/arch/s390/Makefile b/trunk/arch/s390/Makefile index 4b8e08b56f49..7e3ce78d4290 100644 --- a/trunk/arch/s390/Makefile +++ b/trunk/arch/s390/Makefile @@ -24,8 +24,8 @@ CHECKFLAGS += -D__s390__ -msize-long else LD_BFD := elf64-s390 LDFLAGS := -m elf64_s390 -KBUILD_AFLAGS_MODULE += -fpic -D__PIC__ -KBUILD_CFLAGS_MODULE += -fpic -D__PIC__ +KBUILD_AFLAGS_MODULE += -fPIC +KBUILD_CFLAGS_MODULE += -fPIC KBUILD_CFLAGS += -m64 KBUILD_AFLAGS += -m64 UTS_MACHINE := s390x diff --git a/trunk/arch/s390/include/asm/dma.h b/trunk/arch/s390/include/asm/dma.h index de015d85e3e5..bb9bdcd20864 100644 --- a/trunk/arch/s390/include/asm/dma.h +++ b/trunk/arch/s390/include/asm/dma.h @@ -10,4 +10,10 @@ */ #define MAX_DMA_ADDRESS 0x80000000 +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; +#else +#define isa_dma_bridge_buggy (0) +#endif + #endif /* _ASM_S390_DMA_H */ diff --git a/trunk/arch/s390/include/asm/io.h b/trunk/arch/s390/include/asm/io.h index 16c3eb164f4f..27cb32185ce1 100644 --- a/trunk/arch/s390/include/asm/io.h +++ b/trunk/arch/s390/include/asm/io.h @@ -85,6 +85,11 @@ static inline void iounmap(volatile void __iomem *addr) #define __raw_writel zpci_write_u32 #define __raw_writeq zpci_write_u64 +#define readb_relaxed readb +#define readw_relaxed readw +#define readl_relaxed readl +#define readq_relaxed readq + #endif /* CONFIG_PCI */ #include diff --git a/trunk/arch/s390/include/asm/irq.h b/trunk/arch/s390/include/asm/irq.h index e6972f85d2b0..7def77302d63 100644 --- a/trunk/arch/s390/include/asm/irq.h +++ b/trunk/arch/s390/include/asm/irq.h @@ -2,43 +2,61 @@ #define _ASM_IRQ_H #include +#include +#include #include -enum interruption_class { +enum interruption_main_class { EXTERNAL_INTERRUPT, IO_INTERRUPT, - EXTINT_CLK, - EXTINT_EXC, - EXTINT_EMS, - EXTINT_TMR, - EXTINT_TLA, - EXTINT_PFL, - EXTINT_DSD, - EXTINT_VRT, - EXTINT_SCP, - EXTINT_IUC, - EXTINT_CMS, - EXTINT_CMC, - EXTINT_CMR, - IOINT_CIO, - IOINT_QAI, - IOINT_DAS, - IOINT_C15, - IOINT_C70, - IOINT_TAP, - IOINT_VMR, - IOINT_LCS, - IOINT_CLW, - IOINT_CTC, - IOINT_APB, - IOINT_ADM, - IOINT_CSC, - IOINT_PCI, - IOINT_MSI, + NR_IRQS +}; + +enum interruption_class { + IRQEXT_CLK, + IRQEXT_EXC, + IRQEXT_EMS, + IRQEXT_TMR, + IRQEXT_TLA, + IRQEXT_PFL, + IRQEXT_DSD, + IRQEXT_VRT, + IRQEXT_SCP, + IRQEXT_IUC, + IRQEXT_CMS, + IRQEXT_CMC, + IRQEXT_CMR, + IRQIO_CIO, + IRQIO_QAI, + IRQIO_DAS, + IRQIO_C15, + IRQIO_C70, + IRQIO_TAP, + IRQIO_VMR, + IRQIO_LCS, + IRQIO_CLW, + IRQIO_CTC, + IRQIO_APB, + IRQIO_ADM, + IRQIO_CSC, + IRQIO_PCI, + IRQIO_MSI, NMI_NMI, - NR_IRQS, + CPU_RST, + NR_ARCH_IRQS }; +struct irq_stat { + unsigned int irqs[NR_ARCH_IRQS]; +}; + +DECLARE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat); + +static __always_inline void inc_irq_stat(enum interruption_class irq) +{ + __get_cpu_var(irq_stat).irqs[irq]++; +} + struct ext_code { unsigned short subcode; unsigned short code; diff --git a/trunk/arch/s390/include/asm/pgtable.h b/trunk/arch/s390/include/asm/pgtable.h index c928dc1938f2..c1d7930a82f4 100644 --- a/trunk/arch/s390/include/asm/pgtable.h +++ b/trunk/arch/s390/include/asm/pgtable.h @@ -1387,10 +1387,7 @@ static inline int has_transparent_hugepage(void) static inline unsigned long pmd_pfn(pmd_t pmd) { - if (pmd_trans_huge(pmd)) - return pmd_val(pmd) >> HPAGE_SHIFT; - else - return pmd_val(pmd) >> PAGE_SHIFT; + return pmd_val(pmd) >> PAGE_SHIFT; } #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ diff --git a/trunk/arch/s390/include/asm/timex.h b/trunk/arch/s390/include/asm/timex.h index fba4d66788a2..4c060bb5b8ea 100644 --- a/trunk/arch/s390/include/asm/timex.h +++ b/trunk/arch/s390/include/asm/timex.h @@ -128,4 +128,32 @@ static inline unsigned long long get_clock_monotonic(void) return get_clock_xt() - sched_clock_base_cc; } +/** + * tod_to_ns - convert a TOD format value to nanoseconds + * @todval: to be converted TOD format value + * Returns: number of nanoseconds that correspond to the TOD format value + * + * Converting a 64 Bit TOD format value to nanoseconds means that the value + * must be divided by 4.096. In order to achieve that we multiply with 125 + * and divide by 512: + * + * ns = (todval * 125) >> 9; + * + * In order to avoid an overflow with the multiplication we can rewrite this. + * With a split todval == 2^32 * th + tl (th upper 32 bits, tl lower 32 bits) + * we end up with + * + * ns = ((2^32 * th + tl) * 125 ) >> 9; + * -> ns = (2^23 * th * 125) + ((tl * 125) >> 9); + * + */ +static inline unsigned long long tod_to_ns(unsigned long long todval) +{ + unsigned long long ns; + + ns = ((todval >> 32) << 23) * 125; + ns += ((todval & 0xffffffff) * 125) >> 9; + return ns; +} + #endif diff --git a/trunk/arch/s390/include/uapi/asm/unistd.h b/trunk/arch/s390/include/uapi/asm/unistd.h index 63e6078699f1..864f693c237f 100644 --- a/trunk/arch/s390/include/uapi/asm/unistd.h +++ b/trunk/arch/s390/include/uapi/asm/unistd.h @@ -279,7 +279,8 @@ #define __NR_process_vm_writev 341 #define __NR_s390_runtime_instr 342 #define __NR_kcmp 343 -#define NR_syscalls 344 +#define __NR_finit_module 344 +#define NR_syscalls 345 /* * There are some system calls that are not present on 64 bit, some diff --git a/trunk/arch/s390/kernel/compat_wrapper.S b/trunk/arch/s390/kernel/compat_wrapper.S index 827e094a2f49..9b9a805656b5 100644 --- a/trunk/arch/s390/kernel/compat_wrapper.S +++ b/trunk/arch/s390/kernel/compat_wrapper.S @@ -1659,3 +1659,9 @@ ENTRY(sys_kcmp_wrapper) llgfr %r5,%r5 # unsigned long llgfr %r6,%r6 # unsigned long jg sys_kcmp + +ENTRY(sys_finit_module_wrapper) + lgfr %r2,%r2 # int + llgtr %r3,%r3 # const char __user * + lgfr %r4,%r4 # int + jg sys_finit_module diff --git a/trunk/arch/s390/kernel/debug.c b/trunk/arch/s390/kernel/debug.c index ba500d8dc392..4e8215e0d4b6 100644 --- a/trunk/arch/s390/kernel/debug.c +++ b/trunk/arch/s390/kernel/debug.c @@ -1127,13 +1127,14 @@ debug_register_view(debug_info_t * id, struct debug_view *view) if (i == DEBUG_MAX_VIEWS) { pr_err("Registering view %s/%s would exceed the maximum " "number of views %i\n", id->name, view->name, i); - debugfs_remove(pde); rc = -1; } else { id->views[i] = view; id->debugfs_entries[i] = pde; } spin_unlock_irqrestore(&id->lock, flags); + if (rc) + debugfs_remove(pde); out: return rc; } @@ -1146,9 +1147,9 @@ EXPORT_SYMBOL(debug_register_view); int debug_unregister_view(debug_info_t * id, struct debug_view *view) { - int rc = 0; - int i; + struct dentry *dentry = NULL; unsigned long flags; + int i, rc = 0; if (!id) goto out; @@ -1160,10 +1161,12 @@ debug_unregister_view(debug_info_t * id, struct debug_view *view) if (i == DEBUG_MAX_VIEWS) rc = -1; else { - debugfs_remove(id->debugfs_entries[i]); + dentry = id->debugfs_entries[i]; id->views[i] = NULL; + id->debugfs_entries[i] = NULL; } spin_unlock_irqrestore(&id->lock, flags); + debugfs_remove(dentry); out: return rc; } diff --git a/trunk/arch/s390/kernel/irq.c b/trunk/arch/s390/kernel/irq.c index bf24293970ce..9df824ea1667 100644 --- a/trunk/arch/s390/kernel/irq.c +++ b/trunk/arch/s390/kernel/irq.c @@ -24,43 +24,65 @@ #include #include "entry.h" +DEFINE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat); +EXPORT_PER_CPU_SYMBOL_GPL(irq_stat); + struct irq_class { char *name; char *desc; }; -static const struct irq_class intrclass_names[] = { +/* + * The list of "main" irq classes on s390. This is the list of interrrupts + * that appear both in /proc/stat ("intr" line) and /proc/interrupts. + * Historically only external and I/O interrupts have been part of /proc/stat. + * We can't add the split external and I/O sub classes since the first field + * in the "intr" line in /proc/stat is supposed to be the sum of all other + * fields. + * Since the external and I/O interrupt fields are already sums we would end + * up with having a sum which accounts each interrupt twice. + */ +static const struct irq_class irqclass_main_desc[NR_IRQS] = { [EXTERNAL_INTERRUPT] = {.name = "EXT"}, - [IO_INTERRUPT] = {.name = "I/O"}, - [EXTINT_CLK] = {.name = "CLK", .desc = "[EXT] Clock Comparator"}, - [EXTINT_EXC] = {.name = "EXC", .desc = "[EXT] External Call"}, - [EXTINT_EMS] = {.name = "EMS", .desc = "[EXT] Emergency Signal"}, - [EXTINT_TMR] = {.name = "TMR", .desc = "[EXT] CPU Timer"}, - [EXTINT_TLA] = {.name = "TAL", .desc = "[EXT] Timing Alert"}, - [EXTINT_PFL] = {.name = "PFL", .desc = "[EXT] Pseudo Page Fault"}, - [EXTINT_DSD] = {.name = "DSD", .desc = "[EXT] DASD Diag"}, - [EXTINT_VRT] = {.name = "VRT", .desc = "[EXT] Virtio"}, - [EXTINT_SCP] = {.name = "SCP", .desc = "[EXT] Service Call"}, - [EXTINT_IUC] = {.name = "IUC", .desc = "[EXT] IUCV"}, - [EXTINT_CMS] = {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"}, - [EXTINT_CMC] = {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"}, - [EXTINT_CMR] = {.name = "CMR", .desc = "[EXT] CPU-Measurement: RI"}, - [IOINT_CIO] = {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"}, - [IOINT_QAI] = {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"}, - [IOINT_DAS] = {.name = "DAS", .desc = "[I/O] DASD"}, - [IOINT_C15] = {.name = "C15", .desc = "[I/O] 3215"}, - [IOINT_C70] = {.name = "C70", .desc = "[I/O] 3270"}, - [IOINT_TAP] = {.name = "TAP", .desc = "[I/O] Tape"}, - [IOINT_VMR] = {.name = "VMR", .desc = "[I/O] Unit Record Devices"}, - [IOINT_LCS] = {.name = "LCS", .desc = "[I/O] LCS"}, - [IOINT_CLW] = {.name = "CLW", .desc = "[I/O] CLAW"}, - [IOINT_CTC] = {.name = "CTC", .desc = "[I/O] CTC"}, - [IOINT_APB] = {.name = "APB", .desc = "[I/O] AP Bus"}, - [IOINT_ADM] = {.name = "ADM", .desc = "[I/O] EADM Subchannel"}, - [IOINT_CSC] = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"}, - [IOINT_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" }, - [IOINT_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" }, + [IO_INTERRUPT] = {.name = "I/O"} +}; + +/* + * The list of split external and I/O interrupts that appear only in + * /proc/interrupts. + * In addition this list contains non external / I/O events like NMIs. + */ +static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = { + [IRQEXT_CLK] = {.name = "CLK", .desc = "[EXT] Clock Comparator"}, + [IRQEXT_EXC] = {.name = "EXC", .desc = "[EXT] External Call"}, + [IRQEXT_EMS] = {.name = "EMS", .desc = "[EXT] Emergency Signal"}, + [IRQEXT_TMR] = {.name = "TMR", .desc = "[EXT] CPU Timer"}, + [IRQEXT_TLA] = {.name = "TAL", .desc = "[EXT] Timing Alert"}, + [IRQEXT_PFL] = {.name = "PFL", .desc = "[EXT] Pseudo Page Fault"}, + [IRQEXT_DSD] = {.name = "DSD", .desc = "[EXT] DASD Diag"}, + [IRQEXT_VRT] = {.name = "VRT", .desc = "[EXT] Virtio"}, + [IRQEXT_SCP] = {.name = "SCP", .desc = "[EXT] Service Call"}, + [IRQEXT_IUC] = {.name = "IUC", .desc = "[EXT] IUCV"}, + [IRQEXT_CMS] = {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"}, + [IRQEXT_CMC] = {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"}, + [IRQEXT_CMR] = {.name = "CMR", .desc = "[EXT] CPU-Measurement: RI"}, + [IRQIO_CIO] = {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"}, + [IRQIO_QAI] = {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"}, + [IRQIO_DAS] = {.name = "DAS", .desc = "[I/O] DASD"}, + [IRQIO_C15] = {.name = "C15", .desc = "[I/O] 3215"}, + [IRQIO_C70] = {.name = "C70", .desc = "[I/O] 3270"}, + [IRQIO_TAP] = {.name = "TAP", .desc = "[I/O] Tape"}, + [IRQIO_VMR] = {.name = "VMR", .desc = "[I/O] Unit Record Devices"}, + [IRQIO_LCS] = {.name = "LCS", .desc = "[I/O] LCS"}, + [IRQIO_CLW] = {.name = "CLW", .desc = "[I/O] CLAW"}, + [IRQIO_CTC] = {.name = "CTC", .desc = "[I/O] CTC"}, + [IRQIO_APB] = {.name = "APB", .desc = "[I/O] AP Bus"}, + [IRQIO_ADM] = {.name = "ADM", .desc = "[I/O] EADM Subchannel"}, + [IRQIO_CSC] = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"}, + [IRQIO_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" }, + [IRQIO_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" }, [NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"}, + [CPU_RST] = {.name = "RST", .desc = "[CPU] CPU Restart"}, }; /* @@ -68,30 +90,34 @@ static const struct irq_class intrclass_names[] = { */ int show_interrupts(struct seq_file *p, void *v) { - int i = *(loff_t *) v, j; + int irq = *(loff_t *) v; + int cpu; get_online_cpus(); - if (i == 0) { + if (irq == 0) { seq_puts(p, " "); - for_each_online_cpu(j) - seq_printf(p, "CPU%d ",j); + for_each_online_cpu(cpu) + seq_printf(p, "CPU%d ", cpu); seq_putc(p, '\n'); } - - if (i < NR_IRQS) { - seq_printf(p, "%s: ", intrclass_names[i].name); -#ifndef CONFIG_SMP - seq_printf(p, "%10u ", kstat_irqs(i)); -#else - for_each_online_cpu(j) - seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); -#endif - if (intrclass_names[i].desc) - seq_printf(p, " %s", intrclass_names[i].desc); - seq_putc(p, '\n'); - } + if (irq < NR_IRQS) { + seq_printf(p, "%s: ", irqclass_main_desc[irq].name); + for_each_online_cpu(cpu) + seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[irq]); + seq_putc(p, '\n'); + goto skip_arch_irqs; + } + for (irq = 0; irq < NR_ARCH_IRQS; irq++) { + seq_printf(p, "%s: ", irqclass_sub_desc[irq].name); + for_each_online_cpu(cpu) + seq_printf(p, "%10u ", per_cpu(irq_stat, cpu).irqs[irq]); + if (irqclass_sub_desc[irq].desc) + seq_printf(p, " %s", irqclass_sub_desc[irq].desc); + seq_putc(p, '\n'); + } +skip_arch_irqs: put_online_cpus(); - return 0; + return 0; } /* @@ -222,7 +248,7 @@ void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code, /* Serve timer interrupts first. */ clock_comparator_work(); } - kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++; + kstat_incr_irqs_this_cpu(EXTERNAL_INTERRUPT, NULL); if (ext_code.code != 0x1004) __get_cpu_var(s390_idle).nohz_delay = 1; diff --git a/trunk/arch/s390/kernel/nmi.c b/trunk/arch/s390/kernel/nmi.c index a6daa5c5cdb0..7918fbea36bb 100644 --- a/trunk/arch/s390/kernel/nmi.c +++ b/trunk/arch/s390/kernel/nmi.c @@ -254,7 +254,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs) int umode; nmi_enter(); - kstat_cpu(smp_processor_id()).irqs[NMI_NMI]++; + inc_irq_stat(NMI_NMI); mci = (struct mci *) &S390_lowcore.mcck_interruption_code; mcck = &__get_cpu_var(cpu_mcck); umode = user_mode(regs); diff --git a/trunk/arch/s390/kernel/perf_cpum_cf.c b/trunk/arch/s390/kernel/perf_cpum_cf.c index c4e7269d4a09..86ec7447e1f5 100644 --- a/trunk/arch/s390/kernel/perf_cpum_cf.c +++ b/trunk/arch/s390/kernel/perf_cpum_cf.c @@ -229,7 +229,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code, if (!(alert & CPU_MF_INT_CF_MASK)) return; - kstat_cpu(smp_processor_id()).irqs[EXTINT_CMC]++; + inc_irq_stat(IRQEXT_CMC); cpuhw = &__get_cpu_var(cpu_hw_events); /* Measurement alerts are shared and might happen when the PMU diff --git a/trunk/arch/s390/kernel/runtime_instr.c b/trunk/arch/s390/kernel/runtime_instr.c index 61066f6f71a5..077a99389b07 100644 --- a/trunk/arch/s390/kernel/runtime_instr.c +++ b/trunk/arch/s390/kernel/runtime_instr.c @@ -71,7 +71,7 @@ static void runtime_instr_int_handler(struct ext_code ext_code, if (!(param32 & CPU_MF_INT_RI_MASK)) return; - kstat_cpu(smp_processor_id()).irqs[EXTINT_CMR]++; + inc_irq_stat(IRQEXT_CMR); if (!current->thread.ri_cb) return; diff --git a/trunk/arch/s390/kernel/setup.c b/trunk/arch/s390/kernel/setup.c index 2568590973ad..a5360de85ec7 100644 --- a/trunk/arch/s390/kernel/setup.c +++ b/trunk/arch/s390/kernel/setup.c @@ -16,7 +16,7 @@ #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include -#include +#include #include #include #include @@ -289,6 +289,7 @@ void machine_power_off(void) * Dummy power off function. */ void (*pm_power_off)(void) = machine_power_off; +EXPORT_SYMBOL_GPL(pm_power_off); static int __init early_parse_mem(char *p) { diff --git a/trunk/arch/s390/kernel/smp.c b/trunk/arch/s390/kernel/smp.c index 0b45baa55438..7433a2f9e5cc 100644 --- a/trunk/arch/s390/kernel/smp.c +++ b/trunk/arch/s390/kernel/smp.c @@ -433,9 +433,9 @@ static void do_ext_call_interrupt(struct ext_code ext_code, cpu = smp_processor_id(); if (ext_code.code == 0x1202) - kstat_cpu(cpu).irqs[EXTINT_EXC]++; + inc_irq_stat(IRQEXT_EXC); else - kstat_cpu(cpu).irqs[EXTINT_EMS]++; + inc_irq_stat(IRQEXT_EMS); /* * handle bit signal external calls */ @@ -623,9 +623,10 @@ static struct sclp_cpu_info *smp_get_cpu_info(void) return info; } -static int smp_add_present_cpu(int cpu); +static int __cpuinit smp_add_present_cpu(int cpu); -static int __smp_rescan_cpus(struct sclp_cpu_info *info, int sysfs_add) +static int __cpuinit __smp_rescan_cpus(struct sclp_cpu_info *info, + int sysfs_add) { struct pcpu *pcpu; cpumask_t avail; @@ -708,6 +709,7 @@ static void __cpuinit smp_start_secondary(void *cpuvoid) pfault_init(); notify_cpu_starting(smp_processor_id()); set_cpu_online(smp_processor_id(), true); + inc_irq_stat(CPU_RST); local_irq_enable(); /* cpu_idle will call schedule for us */ cpu_idle(); @@ -985,7 +987,7 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self, return notifier_from_errno(err); } -static int smp_add_present_cpu(int cpu) +static int __cpuinit smp_add_present_cpu(int cpu) { struct cpu *c = &pcpu_devices[cpu].cpu; struct device *s = &c->dev; diff --git a/trunk/arch/s390/kernel/syscalls.S b/trunk/arch/s390/kernel/syscalls.S index 48174850f3b0..6a6c61f94dd3 100644 --- a/trunk/arch/s390/kernel/syscalls.S +++ b/trunk/arch/s390/kernel/syscalls.S @@ -352,3 +352,4 @@ SYSCALL(sys_process_vm_readv,sys_process_vm_readv,compat_sys_process_vm_readv_wr SYSCALL(sys_process_vm_writev,sys_process_vm_writev,compat_sys_process_vm_writev_wrapper) SYSCALL(sys_ni_syscall,sys_s390_runtime_instr,sys_s390_runtime_instr_wrapper) SYSCALL(sys_kcmp,sys_kcmp,sys_kcmp_wrapper) +SYSCALL(sys_finit_module,sys_finit_module,sys_finit_module_wrapper) diff --git a/trunk/arch/s390/kernel/time.c b/trunk/arch/s390/kernel/time.c index 7fcd690d42c7..a5f4f5a1d24b 100644 --- a/trunk/arch/s390/kernel/time.c +++ b/trunk/arch/s390/kernel/time.c @@ -63,7 +63,7 @@ static DEFINE_PER_CPU(struct clock_event_device, comparators); */ unsigned long long notrace __kprobes sched_clock(void) { - return (get_clock_monotonic() * 125) >> 9; + return tod_to_ns(get_clock_monotonic()); } /* @@ -168,7 +168,7 @@ static void clock_comparator_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { - kstat_cpu(smp_processor_id()).irqs[EXTINT_CLK]++; + inc_irq_stat(IRQEXT_CLK); if (S390_lowcore.clock_comparator == -1ULL) set_clock_comparator(S390_lowcore.clock_comparator); } @@ -179,7 +179,7 @@ static void stp_timing_alert(struct stp_irq_parm *); static void timing_alert_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { - kstat_cpu(smp_processor_id()).irqs[EXTINT_TLA]++; + inc_irq_stat(IRQEXT_TLA); if (param32 & 0x00c40000) etr_timing_alert((struct etr_irq_parm *) ¶m32); if (param32 & 0x00038000) diff --git a/trunk/arch/s390/kernel/topology.c b/trunk/arch/s390/kernel/topology.c index f1aba87cceb8..4b2e3e317004 100644 --- a/trunk/arch/s390/kernel/topology.c +++ b/trunk/arch/s390/kernel/topology.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ static struct mask_info socket_info; static struct mask_info book_info; struct cpu_topology_s390 cpu_topology[NR_CPUS]; +EXPORT_SYMBOL_GPL(cpu_topology); static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu) { diff --git a/trunk/arch/s390/kvm/interrupt.c b/trunk/arch/s390/kvm/interrupt.c index c30615e605ac..82c481ddef76 100644 --- a/trunk/arch/s390/kvm/interrupt.c +++ b/trunk/arch/s390/kvm/interrupt.c @@ -408,7 +408,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) return 0; } - sltime = ((vcpu->arch.sie_block->ckc - now)*125)>>9; + sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now); hrtimer_start(&vcpu->arch.ckc_timer, ktime_set (0, sltime) , HRTIMER_MODE_REL); VCPU_EVENT(vcpu, 5, "enabled wait via clock comparator: %llx ns", sltime); diff --git a/trunk/arch/s390/kvm/kvm-s390.c b/trunk/arch/s390/kvm/kvm-s390.c index c9011bfaabbe..f090e819bf71 100644 --- a/trunk/arch/s390/kvm/kvm-s390.c +++ b/trunk/arch/s390/kvm/kvm-s390.c @@ -613,7 +613,9 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) kvm_s390_deliver_pending_interrupts(vcpu); vcpu->arch.sie_block->icptcode = 0; + preempt_disable(); kvm_guest_enter(); + preempt_enable(); VCPU_EVENT(vcpu, 6, "entering sie flags %x", atomic_read(&vcpu->arch.sie_block->cpuflags)); trace_kvm_s390_sie_enter(vcpu, diff --git a/trunk/arch/s390/mm/fault.c b/trunk/arch/s390/mm/fault.c index 42601d6e166f..2fb9e63b8fc4 100644 --- a/trunk/arch/s390/mm/fault.c +++ b/trunk/arch/s390/mm/fault.c @@ -569,7 +569,7 @@ static void pfault_interrupt(struct ext_code ext_code, subcode = ext_code.subcode; if ((subcode & 0xff00) != __SUBCODE_MASK) return; - kstat_cpu(smp_processor_id()).irqs[EXTINT_PFL]++; + inc_irq_stat(IRQEXT_PFL); /* Get the token (= pid of the affected task). */ pid = sizeof(void *) == 4 ? param32 : param64; rcu_read_lock(); diff --git a/trunk/arch/s390/oprofile/hwsampler.c b/trunk/arch/s390/oprofile/hwsampler.c index 0cb385da202c..b5b2916895e0 100644 --- a/trunk/arch/s390/oprofile/hwsampler.c +++ b/trunk/arch/s390/oprofile/hwsampler.c @@ -233,7 +233,7 @@ static void hws_ext_handler(struct ext_code ext_code, if (!(param32 & CPU_MF_INT_SF_MASK)) return; - kstat_cpu(smp_processor_id()).irqs[EXTINT_CMS]++; + inc_irq_stat(IRQEXT_CMS); atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32); if (hws_wq) diff --git a/trunk/arch/s390/pci/pci.c b/trunk/arch/s390/pci/pci.c index ff49427e9941..60e0372545d2 100644 --- a/trunk/arch/s390/pci/pci.c +++ b/trunk/arch/s390/pci/pci.c @@ -160,35 +160,6 @@ int pci_proc_domain(struct pci_bus *bus) } EXPORT_SYMBOL_GPL(pci_proc_domain); -/* Store PCI function information block */ -static int zpci_store_fib(struct zpci_dev *zdev, u8 *fc) -{ - struct zpci_fib *fib; - u8 status, cc; - - fib = (void *) get_zeroed_page(GFP_KERNEL); - if (!fib) - return -ENOMEM; - - do { - cc = __stpcifc(zdev->fh, 0, fib, &status); - if (cc == 2) { - msleep(ZPCI_INSN_BUSY_DELAY); - memset(fib, 0, PAGE_SIZE); - } - } while (cc == 2); - - if (cc) - pr_err_once("%s: cc: %u status: %u\n", - __func__, cc, status); - - /* Return PCI function controls */ - *fc = fib->fc; - - free_page((unsigned long) fib); - return (cc) ? -EIO : 0; -} - /* Modify PCI: Register adapter interruptions */ static int zpci_register_airq(struct zpci_dev *zdev, unsigned int aisb, u64 aibv) @@ -469,7 +440,7 @@ static void zpci_irq_handler(void *dont, void *need) int rescan = 0, max = aisb_max; struct zdev_irq_map *imap; - kstat_cpu(smp_processor_id()).irqs[IOINT_PCI]++; + inc_irq_stat(IRQIO_PCI); sbit = start; scan: @@ -481,7 +452,7 @@ static void zpci_irq_handler(void *dont, void *need) /* find vector bit */ imap = bucket->imap[sbit]; for_each_set_bit_left(mbit, &imap->aibv, imap->msi_vecs) { - kstat_cpu(smp_processor_id()).irqs[IOINT_MSI]++; + inc_irq_stat(IRQIO_MSI); clear_bit(63 - mbit, &imap->aibv); spin_lock(&imap->lock); diff --git a/trunk/arch/s390/pci/pci_dma.c b/trunk/arch/s390/pci/pci_dma.c index 6138468b420f..a547419907c3 100644 --- a/trunk/arch/s390/pci/pci_dma.c +++ b/trunk/arch/s390/pci/pci_dma.c @@ -13,8 +13,6 @@ #include #include -static enum zpci_ioat_dtype zpci_ioat_dt = ZPCI_IOTA_RTTO; - static struct kmem_cache *dma_region_table_cache; static struct kmem_cache *dma_page_table_cache; diff --git a/trunk/arch/sh/boards/mach-ecovec24/setup.c b/trunk/arch/sh/boards/mach-ecovec24/setup.c index 3fede4556c91..a0fa5791cd44 100644 --- a/trunk/arch/sh/boards/mach-ecovec24/setup.c +++ b/trunk/arch/sh/boards/mach-ecovec24/setup.c @@ -70,6 +70,16 @@ * OFF-ON : MMC */ +/* + * FSI - DA7210 + * + * it needs amixer settings for playing + * + * amixer set 'HeadPhone' 80 + * amixer set 'Out Mixer Left DAC Left' on + * amixer set 'Out Mixer Right DAC Right' on + */ + /* Heartbeat */ static unsigned char led_pos[] = { 0, 1, 2, 3 }; diff --git a/trunk/arch/sh/include/asm/elf.h b/trunk/arch/sh/include/asm/elf.h index 37924afa8d8a..bf9f44f17c29 100644 --- a/trunk/arch/sh/include/asm/elf.h +++ b/trunk/arch/sh/include/asm/elf.h @@ -203,9 +203,9 @@ extern void __kernel_vsyscall; if (vdso_enabled) \ NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_BASE); \ else \ - NEW_AUX_ENT(AT_IGNORE, 0); + NEW_AUX_ENT(AT_IGNORE, 0) #else -#define VSYSCALL_AUX_ENT +#define VSYSCALL_AUX_ENT NEW_AUX_ENT(AT_IGNORE, 0) #endif /* CONFIG_VSYSCALL */ #ifdef CONFIG_SH_FPU diff --git a/trunk/arch/sh/include/asm/processor_32.h b/trunk/arch/sh/include/asm/processor_32.h index b1320d55ca30..e699a12cdcca 100644 --- a/trunk/arch/sh/include/asm/processor_32.h +++ b/trunk/arch/sh/include/asm/processor_32.h @@ -39,7 +39,7 @@ /* This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) /* * Bit of SR register diff --git a/trunk/arch/sh/include/asm/processor_64.h b/trunk/arch/sh/include/asm/processor_64.h index 1ee8946f0952..1cc7d3197143 100644 --- a/trunk/arch/sh/include/asm/processor_64.h +++ b/trunk/arch/sh/include/asm/processor_64.h @@ -47,7 +47,7 @@ pc; }) /* This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) +#define TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) /* * Bit of SR register diff --git a/trunk/arch/sh/include/uapi/asm/unistd_32.h b/trunk/arch/sh/include/uapi/asm/unistd_32.h index 9e465f246dc1..d13a1d623736 100644 --- a/trunk/arch/sh/include/uapi/asm/unistd_32.h +++ b/trunk/arch/sh/include/uapi/asm/unistd_32.h @@ -379,7 +379,8 @@ #define __NR_process_vm_readv 365 #define __NR_process_vm_writev 366 #define __NR_kcmp 367 +#define __NR_finit_module 368 -#define NR_syscalls 368 +#define NR_syscalls 369 #endif /* __ASM_SH_UNISTD_32_H */ diff --git a/trunk/arch/sh/include/uapi/asm/unistd_64.h b/trunk/arch/sh/include/uapi/asm/unistd_64.h index 8e3a2edd284e..e6820c86e8c7 100644 --- a/trunk/arch/sh/include/uapi/asm/unistd_64.h +++ b/trunk/arch/sh/include/uapi/asm/unistd_64.h @@ -399,7 +399,8 @@ #define __NR_process_vm_readv 376 #define __NR_process_vm_writev 377 #define __NR_kcmp 378 +#define __NR_finit_module 379 -#define NR_syscalls 379 +#define NR_syscalls 380 #endif /* __ASM_SH_UNISTD_64_H */ diff --git a/trunk/arch/sh/kernel/syscalls_32.S b/trunk/arch/sh/kernel/syscalls_32.S index fe97ae5e56f1..734234be2f01 100644 --- a/trunk/arch/sh/kernel/syscalls_32.S +++ b/trunk/arch/sh/kernel/syscalls_32.S @@ -385,3 +385,4 @@ ENTRY(sys_call_table) .long sys_process_vm_readv /* 365 */ .long sys_process_vm_writev .long sys_kcmp + .long sys_finit_module diff --git a/trunk/arch/sh/kernel/syscalls_64.S b/trunk/arch/sh/kernel/syscalls_64.S index 5c7b1c67bdc1..579fcb9a896b 100644 --- a/trunk/arch/sh/kernel/syscalls_64.S +++ b/trunk/arch/sh/kernel/syscalls_64.S @@ -405,3 +405,4 @@ sys_call_table: .long sys_process_vm_readv .long sys_process_vm_writev .long sys_kcmp + .long sys_finit_module diff --git a/trunk/arch/sh/lib/mcount.S b/trunk/arch/sh/lib/mcount.S index 60164e65d665..52aa2011d753 100644 --- a/trunk/arch/sh/lib/mcount.S +++ b/trunk/arch/sh/lib/mcount.S @@ -294,6 +294,8 @@ stack_panic: .align 2 .L_init_thread_union: .long init_thread_union +.L_ebss: + .long __bss_stop .Lpanic: .long panic .Lpanic_s: diff --git a/trunk/arch/sparc/include/uapi/asm/unistd.h b/trunk/arch/sparc/include/uapi/asm/unistd.h index cac719d1bc5c..62ced589bcf7 100644 --- a/trunk/arch/sparc/include/uapi/asm/unistd.h +++ b/trunk/arch/sparc/include/uapi/asm/unistd.h @@ -407,8 +407,9 @@ #define __NR_process_vm_writev 339 #define __NR_kern_features 340 #define __NR_kcmp 341 +#define __NR_finit_module 342 -#define NR_syscalls 342 +#define NR_syscalls 343 /* Bitmask values returned from kern_features system call. */ #define KERN_FEATURE_MIXED_MODE_STACK 0x00000001 diff --git a/trunk/arch/sparc/kernel/pci.c b/trunk/arch/sparc/kernel/pci.c index 04bacce76fe6..baf4366e2d6a 100644 --- a/trunk/arch/sparc/kernel/pci.c +++ b/trunk/arch/sparc/kernel/pci.c @@ -378,7 +378,8 @@ static void apb_calc_first_last(u8 map, u32 *first_p, u32 *last_p) /* Cook up fake bus resources for SUNW,simba PCI bridges which lack * a proper 'ranges' property. */ -static void apb_fake_ranges(struct pci_dev *dev, struct pci_bus *bus, +static void apb_fake_ranges(struct pci_dev *dev, + struct pci_bus *bus, struct pci_pbm_info *pbm) { struct pci_bus_region region; @@ -403,13 +404,15 @@ static void apb_fake_ranges(struct pci_dev *dev, struct pci_bus *bus, pcibios_bus_to_resource(dev, res, ®ion); } -static void pci_of_scan_bus(struct pci_pbm_info *pbm, struct device_node *node, +static void pci_of_scan_bus(struct pci_pbm_info *pbm, + struct device_node *node, struct pci_bus *bus); #define GET_64BIT(prop, i) ((((u64) (prop)[(i)]) << 32) | (prop)[(i)+1]) static void of_scan_pci_bridge(struct pci_pbm_info *pbm, - struct device_node *node, struct pci_dev *dev) + struct device_node *node, + struct pci_dev *dev) { struct pci_bus *bus; const u32 *busrange, *ranges; @@ -500,7 +503,8 @@ static void of_scan_pci_bridge(struct pci_pbm_info *pbm, pci_of_scan_bus(pbm, node, bus); } -static void pci_of_scan_bus(struct pci_pbm_info *pbm, struct device_node *node, +static void pci_of_scan_bus(struct pci_pbm_info *pbm, + struct device_node *node, struct pci_bus *bus) { struct device_node *child; diff --git a/trunk/arch/sparc/kernel/pci_psycho.c b/trunk/arch/sparc/kernel/pci_psycho.c index b85238289717..c647634ead2b 100644 --- a/trunk/arch/sparc/kernel/pci_psycho.c +++ b/trunk/arch/sparc/kernel/pci_psycho.c @@ -366,7 +366,8 @@ static void pbm_config_busmastering(struct pci_pbm_info *pbm) pci_config_write8(addr, 64); } -static void psycho_scan_bus(struct pci_pbm_info *pbm, struct device *parent) +static void psycho_scan_bus(struct pci_pbm_info *pbm, + struct device *parent) { pbm_config_busmastering(pbm); pbm->is_66mhz_capable = 0; diff --git a/trunk/arch/sparc/kernel/pci_sabre.c b/trunk/arch/sparc/kernel/pci_sabre.c index 531186d7c9ab..6f00d27e8dac 100644 --- a/trunk/arch/sparc/kernel/pci_sabre.c +++ b/trunk/arch/sparc/kernel/pci_sabre.c @@ -442,7 +442,8 @@ static void sabre_scan_bus(struct pci_pbm_info *pbm, struct device *parent) sabre_register_error_handlers(pbm); } -static void sabre_pbm_init(struct pci_pbm_info *pbm, struct platform_device *op) +static void sabre_pbm_init(struct pci_pbm_info *pbm, + struct platform_device *op) { psycho_pbm_init_common(pbm, op, "SABRE", PBM_CHIP_TYPE_SABRE); pbm->pci_afsr = pbm->controller_regs + SABRE_PIOAFSR; diff --git a/trunk/arch/sparc/kernel/pci_schizo.c b/trunk/arch/sparc/kernel/pci_schizo.c index 29e888158ae6..8f76f23dac38 100644 --- a/trunk/arch/sparc/kernel/pci_schizo.c +++ b/trunk/arch/sparc/kernel/pci_schizo.c @@ -1306,8 +1306,9 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) } } -static int schizo_pbm_init(struct pci_pbm_info *pbm, struct platform_device *op, - u32 portid, int chip_type) +static int schizo_pbm_init(struct pci_pbm_info *pbm, + struct platform_device *op, u32 portid, + int chip_type) { const struct linux_prom64_registers *regs; struct device_node *dp = op->dev.of_node; diff --git a/trunk/arch/sparc/kernel/systbls_32.S b/trunk/arch/sparc/kernel/systbls_32.S index 5147f574f125..6ac43c36bbbf 100644 --- a/trunk/arch/sparc/kernel/systbls_32.S +++ b/trunk/arch/sparc/kernel/systbls_32.S @@ -85,4 +85,4 @@ sys_call_table: /*325*/ .long sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open, sys_recvmmsg, sys_fanotify_init /*330*/ .long sys_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, sys_open_by_handle_at, sys_clock_adjtime /*335*/ .long sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev -/*340*/ .long sys_ni_syscall, sys_kcmp +/*340*/ .long sys_ni_syscall, sys_kcmp, sys_finit_module diff --git a/trunk/arch/sparc/kernel/systbls_64.S b/trunk/arch/sparc/kernel/systbls_64.S index cdbd9b817751..1009ecb92678 100644 --- a/trunk/arch/sparc/kernel/systbls_64.S +++ b/trunk/arch/sparc/kernel/systbls_64.S @@ -86,7 +86,7 @@ sys_call_table32: .word compat_sys_pwritev, compat_sys_rt_tgsigqueueinfo, sys_perf_event_open, compat_sys_recvmmsg, sys_fanotify_init /*330*/ .word sys32_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, compat_sys_open_by_handle_at, compat_sys_clock_adjtime .word sys_syncfs, compat_sys_sendmmsg, sys_setns, compat_sys_process_vm_readv, compat_sys_process_vm_writev -/*340*/ .word sys_kern_features, sys_kcmp +/*340*/ .word sys_kern_features, sys_kcmp, sys_finit_module #endif /* CONFIG_COMPAT */ @@ -164,4 +164,4 @@ sys_call_table: .word sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open, sys_recvmmsg, sys_fanotify_init /*330*/ .word sys_fanotify_mark, sys_prlimit64, sys_name_to_handle_at, sys_open_by_handle_at, sys_clock_adjtime .word sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev -/*340*/ .word sys_kern_features, sys_kcmp +/*340*/ .word sys_kern_features, sys_kcmp, sys_finit_module diff --git a/trunk/arch/x86/boot/compressed/eboot.c b/trunk/arch/x86/boot/compressed/eboot.c index b1942e222768..18e329ca108e 100644 --- a/trunk/arch/x86/boot/compressed/eboot.c +++ b/trunk/arch/x86/boot/compressed/eboot.c @@ -302,7 +302,7 @@ static efi_status_t setup_efi_pci(struct boot_params *params) if (status != EFI_SUCCESS) continue; - if (!attributes & EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM) + if (!(attributes & EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM)) continue; if (!pci->romimage || !pci->romsize) diff --git a/trunk/arch/x86/kernel/cpu/perf_event.c b/trunk/arch/x86/kernel/cpu/perf_event.c index 4428fd178bce..6774c17a5576 100644 --- a/trunk/arch/x86/kernel/cpu/perf_event.c +++ b/trunk/arch/x86/kernel/cpu/perf_event.c @@ -340,9 +340,6 @@ int x86_setup_perfctr(struct perf_event *event) /* BTS is currently only allowed for user-mode. */ if (!attr->exclude_kernel) return -EOPNOTSUPP; - - if (!attr->exclude_guest) - return -EOPNOTSUPP; } hwc->config |= config; @@ -385,9 +382,6 @@ int x86_pmu_hw_config(struct perf_event *event) if (event->attr.precise_ip) { int precise = 0; - if (!event->attr.exclude_guest) - return -EOPNOTSUPP; - /* Support for constant skid */ if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) { precise++; diff --git a/trunk/arch/x86/kernel/entry_32.S b/trunk/arch/x86/kernel/entry_32.S index ff84d5469d77..6ed91d9980e2 100644 --- a/trunk/arch/x86/kernel/entry_32.S +++ b/trunk/arch/x86/kernel/entry_32.S @@ -1065,7 +1065,6 @@ ENTRY(xen_failsafe_callback) lea 16(%esp),%esp CFI_ADJUST_CFA_OFFSET -16 jz 5f - addl $16,%esp jmp iret_exc 5: pushl_cfi $-1 /* orig_ax = -1 => not a system call */ SAVE_ALL diff --git a/trunk/arch/x86/kernel/kvm.c b/trunk/arch/x86/kernel/kvm.c index 08b973f64032..9c2bd8bd4b4c 100644 --- a/trunk/arch/x86/kernel/kvm.c +++ b/trunk/arch/x86/kernel/kvm.c @@ -43,6 +43,7 @@ #include #include #include +#include static int kvmapf = 1; @@ -121,6 +122,8 @@ void kvm_async_pf_task_wait(u32 token) struct kvm_task_sleep_node n, *e; DEFINE_WAIT(wait); + rcu_irq_enter(); + spin_lock(&b->lock); e = _find_apf_task(b, token); if (e) { @@ -128,6 +131,8 @@ void kvm_async_pf_task_wait(u32 token) hlist_del(&e->link); kfree(e); spin_unlock(&b->lock); + + rcu_irq_exit(); return; } @@ -152,13 +157,16 @@ void kvm_async_pf_task_wait(u32 token) /* * We cannot reschedule. So halt. */ + rcu_irq_exit(); native_safe_halt(); + rcu_irq_enter(); local_irq_disable(); } } if (!n.halted) finish_wait(&n.wq, &wait); + rcu_irq_exit(); return; } EXPORT_SYMBOL_GPL(kvm_async_pf_task_wait); @@ -252,10 +260,10 @@ do_async_page_fault(struct pt_regs *regs, unsigned long error_code) break; case KVM_PV_REASON_PAGE_NOT_PRESENT: /* page is swapped out by the host. */ - rcu_irq_enter(); + exception_enter(regs); exit_idle(); kvm_async_pf_task_wait((u32)read_cr2()); - rcu_irq_exit(); + exception_exit(regs); break; case KVM_PV_REASON_PAGE_READY: rcu_irq_enter(); diff --git a/trunk/arch/x86/kernel/setup.c b/trunk/arch/x86/kernel/setup.c index 23ddd558fbd5..00f6c1472b85 100644 --- a/trunk/arch/x86/kernel/setup.c +++ b/trunk/arch/x86/kernel/setup.c @@ -610,6 +610,83 @@ static __init void reserve_ibft_region(void) static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10; +static bool __init snb_gfx_workaround_needed(void) +{ +#ifdef CONFIG_PCI + int i; + u16 vendor, devid; + static const __initconst u16 snb_ids[] = { + 0x0102, + 0x0112, + 0x0122, + 0x0106, + 0x0116, + 0x0126, + 0x010a, + }; + + /* Assume no if something weird is going on with PCI */ + if (!early_pci_allowed()) + return false; + + vendor = read_pci_config_16(0, 2, 0, PCI_VENDOR_ID); + if (vendor != 0x8086) + return false; + + devid = read_pci_config_16(0, 2, 0, PCI_DEVICE_ID); + for (i = 0; i < ARRAY_SIZE(snb_ids); i++) + if (devid == snb_ids[i]) + return true; +#endif + + return false; +} + +/* + * Sandy Bridge graphics has trouble with certain ranges, exclude + * them from allocation. + */ +static void __init trim_snb_memory(void) +{ + static const __initconst unsigned long bad_pages[] = { + 0x20050000, + 0x20110000, + 0x20130000, + 0x20138000, + 0x40004000, + }; + int i; + + if (!snb_gfx_workaround_needed()) + return; + + printk(KERN_DEBUG "reserving inaccessible SNB gfx pages\n"); + + /* + * Reserve all memory below the 1 MB mark that has not + * already been reserved. + */ + memblock_reserve(0, 1<<20); + + for (i = 0; i < ARRAY_SIZE(bad_pages); i++) { + if (memblock_reserve(bad_pages[i], PAGE_SIZE)) + printk(KERN_WARNING "failed to reserve 0x%08lx\n", + bad_pages[i]); + } +} + +/* + * Here we put platform-specific memory range workarounds, i.e. + * memory known to be corrupt or otherwise in need to be reserved on + * specific platforms. + * + * If this gets used more widely it could use a real dispatch mechanism. + */ +static void __init trim_platform_memory_ranges(void) +{ + trim_snb_memory(); +} + static void __init trim_bios_range(void) { /* @@ -630,6 +707,7 @@ static void __init trim_bios_range(void) * take them out. */ e820_remove_range(BIOS_BEGIN, BIOS_END - BIOS_BEGIN, E820_RAM, 1); + sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); } @@ -908,6 +986,8 @@ void __init setup_arch(char **cmdline_p) setup_real_mode(); + trim_platform_memory_ranges(); + init_gbpages(); /* max_pfn_mapped is updated here */ diff --git a/trunk/arch/x86/kernel/step.c b/trunk/arch/x86/kernel/step.c index cd3b2438a980..9b4d51d0c0d0 100644 --- a/trunk/arch/x86/kernel/step.c +++ b/trunk/arch/x86/kernel/step.c @@ -165,10 +165,11 @@ void set_task_blockstep(struct task_struct *task, bool on) * Ensure irq/preemption can't change debugctl in between. * Note also that both TIF_BLOCKSTEP and debugctl should * be changed atomically wrt preemption. - * FIXME: this means that set/clear TIF_BLOCKSTEP is simply - * wrong if task != current, SIGKILL can wakeup the stopped - * tracee and set/clear can play with the running task, this - * can confuse the next __switch_to_xtra(). + * + * NOTE: this means that set/clear TIF_BLOCKSTEP is only safe if + * task is current or it can't be running, otherwise we can race + * with __switch_to_xtra(). We rely on ptrace_freeze_traced() but + * PTRACE_KILL is not safe. */ local_irq_disable(); debugctl = get_debugctlmsr(); diff --git a/trunk/arch/x86/kvm/x86.c b/trunk/arch/x86/kvm/x86.c index 76f54461f7cb..c243b81e3c74 100644 --- a/trunk/arch/x86/kvm/x86.c +++ b/trunk/arch/x86/kvm/x86.c @@ -120,7 +120,7 @@ struct kvm_shared_msrs { }; static struct kvm_shared_msrs_global __read_mostly shared_msrs_global; -static DEFINE_PER_CPU(struct kvm_shared_msrs, shared_msrs); +static struct kvm_shared_msrs __percpu *shared_msrs; struct kvm_stats_debugfs_item debugfs_entries[] = { { "pf_fixed", VCPU_STAT(pf_fixed) }, @@ -191,10 +191,10 @@ static void kvm_on_user_return(struct user_return_notifier *urn) static void shared_msr_update(unsigned slot, u32 msr) { - struct kvm_shared_msrs *smsr; u64 value; + unsigned int cpu = smp_processor_id(); + struct kvm_shared_msrs *smsr = per_cpu_ptr(shared_msrs, cpu); - smsr = &__get_cpu_var(shared_msrs); /* only read, and nobody should modify it at this time, * so don't need lock */ if (slot >= shared_msrs_global.nr) { @@ -226,7 +226,8 @@ static void kvm_shared_msr_cpu_online(void) void kvm_set_shared_msr(unsigned slot, u64 value, u64 mask) { - struct kvm_shared_msrs *smsr = &__get_cpu_var(shared_msrs); + unsigned int cpu = smp_processor_id(); + struct kvm_shared_msrs *smsr = per_cpu_ptr(shared_msrs, cpu); if (((value ^ smsr->values[slot].curr) & mask) == 0) return; @@ -242,7 +243,8 @@ EXPORT_SYMBOL_GPL(kvm_set_shared_msr); static void drop_user_return_notifiers(void *ignore) { - struct kvm_shared_msrs *smsr = &__get_cpu_var(shared_msrs); + unsigned int cpu = smp_processor_id(); + struct kvm_shared_msrs *smsr = per_cpu_ptr(shared_msrs, cpu); if (smsr->registered) kvm_on_user_return(&smsr->urn); @@ -5233,9 +5235,16 @@ int kvm_arch_init(void *opaque) goto out; } + r = -ENOMEM; + shared_msrs = alloc_percpu(struct kvm_shared_msrs); + if (!shared_msrs) { + printk(KERN_ERR "kvm: failed to allocate percpu kvm_shared_msrs\n"); + goto out; + } + r = kvm_mmu_module_init(); if (r) - goto out; + goto out_free_percpu; kvm_set_mmio_spte_mask(); kvm_init_msr_list(); @@ -5258,6 +5267,8 @@ int kvm_arch_init(void *opaque) return 0; +out_free_percpu: + free_percpu(shared_msrs); out: return r; } @@ -5275,6 +5286,7 @@ void kvm_arch_exit(void) #endif kvm_x86_ops = NULL; kvm_mmu_module_exit(); + free_percpu(shared_msrs); } int kvm_emulate_halt(struct kvm_vcpu *vcpu) diff --git a/trunk/arch/x86/xen/smp.c b/trunk/arch/x86/xen/smp.c index 4f7d2599b484..34bc4cee8887 100644 --- a/trunk/arch/x86/xen/smp.c +++ b/trunk/arch/x86/xen/smp.c @@ -432,13 +432,6 @@ static void __cpuinit xen_play_dead(void) /* used only with HOTPLUG_CPU */ play_dead_common(); HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); cpu_bringup(); - /* - * Balance out the preempt calls - as we are running in cpu_idle - * loop which has been called at bootup from cpu_bringup_and_idle. - * The cpucpu_bringup_and_idle called cpu_bringup which made a - * preempt_disable() So this preempt_enable will balance it out. - */ - preempt_enable(); } #else /* !CONFIG_HOTPLUG_CPU */ diff --git a/trunk/drivers/acpi/apei/apei-base.c b/trunk/drivers/acpi/apei/apei-base.c index 00a783661d0b..46f80e2c92f7 100644 --- a/trunk/drivers/acpi/apei/apei-base.c +++ b/trunk/drivers/acpi/apei/apei-base.c @@ -590,6 +590,9 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr, if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 && *access_bit_width < 32) *access_bit_width = 32; + else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 && + *access_bit_width < 64) + *access_bit_width = 64; if ((bit_width + bit_offset) > *access_bit_width) { pr_warning(FW_BUG APEI_PFX diff --git a/trunk/drivers/acpi/glue.c b/trunk/drivers/acpi/glue.c index 95af6f674a6c..35da18113216 100644 --- a/trunk/drivers/acpi/glue.c +++ b/trunk/drivers/acpi/glue.c @@ -297,7 +297,7 @@ static int acpi_platform_notify(struct device *dev) if (!ret) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - acpi_get_name(dev->acpi_handle, ACPI_FULL_PATHNAME, &buffer); + acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer); DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer); kfree(buffer.pointer); } else diff --git a/trunk/drivers/acpi/processor_idle.c b/trunk/drivers/acpi/processor_idle.c index f1a5da44591d..ed9a1cc690be 100644 --- a/trunk/drivers/acpi/processor_idle.c +++ b/trunk/drivers/acpi/processor_idle.c @@ -958,6 +958,9 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr) return -EINVAL; } + if (!dev) + return -EINVAL; + dev->cpu = pr->id; if (max_cstate == 0) @@ -1149,6 +1152,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) } /* Populate Updated C-state information */ + acpi_processor_get_power_info(pr); acpi_processor_setup_cpuidle_states(pr); /* Enable all cpuidle devices */ diff --git a/trunk/drivers/acpi/processor_perflib.c b/trunk/drivers/acpi/processor_perflib.c index 836bfe069042..53e7ac9403a7 100644 --- a/trunk/drivers/acpi/processor_perflib.c +++ b/trunk/drivers/acpi/processor_perflib.c @@ -340,6 +340,13 @@ static void amd_fixup_frequency(struct acpi_processor_px *px, int i) if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) || boot_cpu_data.x86 == 0x11) { rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi); + /* + * MSR C001_0064+: + * Bit 63: PstateEn. Read-write. If set, the P-state is valid. + */ + if (!(hi & BIT(31))) + return; + fid = lo & 0x3f; did = (lo >> 6) & 7; if (boot_cpu_data.x86 == 0x10) diff --git a/trunk/drivers/ata/ahci.c b/trunk/drivers/ata/ahci.c index 7862d17976b7..497912732566 100644 --- a/trunk/drivers/ata/ahci.c +++ b/trunk/drivers/ata/ahci.c @@ -53,6 +53,7 @@ enum { AHCI_PCI_BAR_STA2X11 = 0, + AHCI_PCI_BAR_ENMOTUS = 2, AHCI_PCI_BAR_STANDARD = 5, }; @@ -410,6 +411,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci }, /* ASM1061 */ { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */ + /* Enmotus */ + { PCI_DEVICE(0x1c44, 0x8000), board_ahci }, + /* Generic, PCI class code for AHCI */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci }, @@ -1098,9 +1102,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(&pdev->dev, "PDC42819 can only drive SATA devices with this driver\n"); - /* The Connext uses non-standard BAR */ + /* Both Connext and Enmotus devices use non-standard BARs */ if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == 0xCC06) ahci_pci_bar = AHCI_PCI_BAR_STA2X11; + else if (pdev->vendor == 0x1c44 && pdev->device == 0x8000) + ahci_pci_bar = AHCI_PCI_BAR_ENMOTUS; /* acquire resources */ rc = pcim_enable_device(pdev); diff --git a/trunk/drivers/ata/libahci.c b/trunk/drivers/ata/libahci.c index 320712a7b9ea..6cd7805e47ca 100644 --- a/trunk/drivers/ata/libahci.c +++ b/trunk/drivers/ata/libahci.c @@ -1951,13 +1951,13 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) /* Use the nominal value 10 ms if the read MDAT is zero, * the nominal value of DETO is 20 ms. */ - if (dev->sata_settings[ATA_LOG_DEVSLP_VALID] & + if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] & ATA_LOG_DEVSLP_VALID_MASK) { - mdat = dev->sata_settings[ATA_LOG_DEVSLP_MDAT] & + mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] & ATA_LOG_DEVSLP_MDAT_MASK; if (!mdat) mdat = 10; - deto = dev->sata_settings[ATA_LOG_DEVSLP_DETO]; + deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO]; if (!deto) deto = 20; } else { diff --git a/trunk/drivers/ata/libata-core.c b/trunk/drivers/ata/libata-core.c index 9e8b99af400d..46cd3f4c6aaa 100644 --- a/trunk/drivers/ata/libata-core.c +++ b/trunk/drivers/ata/libata-core.c @@ -2325,24 +2325,28 @@ int ata_dev_configure(struct ata_device *dev) } } - /* check and mark DevSlp capability */ - if (ata_id_has_devslp(dev->id)) - dev->flags |= ATA_DFLAG_DEVSLP; - - /* Obtain SATA Settings page from Identify Device Data Log, - * which contains DevSlp timing variables etc. - * Exclude old devices with ata_id_has_ncq() + /* Check and mark DevSlp capability. Get DevSlp timing variables + * from SATA Settings page of Identify Device Data Log. */ - if (ata_id_has_ncq(dev->id)) { + if (ata_id_has_devslp(dev->id)) { + u8 sata_setting[ATA_SECT_SIZE]; + int i, j; + + dev->flags |= ATA_DFLAG_DEVSLP; err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA, ATA_LOG_SATA_SETTINGS, - dev->sata_settings, + sata_setting, 1); if (err_mask) ata_dev_dbg(dev, "failed to get Identify Device Data, Emask 0x%x\n", err_mask); + else + for (i = 0; i < ATA_LOG_DEVSLP_SIZE; i++) { + j = ATA_LOG_DEVSLP_OFFSET + i; + dev->devslp_timing[i] = sata_setting[j]; + } } dev->cdb_len = 16; diff --git a/trunk/drivers/ata/libata-eh.c b/trunk/drivers/ata/libata-eh.c index bf039b0e97b7..bcf4437214f5 100644 --- a/trunk/drivers/ata/libata-eh.c +++ b/trunk/drivers/ata/libata-eh.c @@ -2094,7 +2094,7 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev, */ static inline int ata_eh_worth_retry(struct ata_queued_cmd *qc) { - if (qc->flags & AC_ERR_MEDIA) + if (qc->err_mask & AC_ERR_MEDIA) return 0; /* don't retry media errors */ if (qc->flags & ATA_QCFLAG_IO) return 1; /* otherwise retry anything from fs stack */ diff --git a/trunk/drivers/base/cpu.c b/trunk/drivers/base/cpu.c index 63452943abd1..fb10728f6372 100644 --- a/trunk/drivers/base/cpu.c +++ b/trunk/drivers/base/cpu.c @@ -224,7 +224,7 @@ static void cpu_device_release(struct device *dev) * by the cpu device. * * Never copy this way of doing things, or you too will be made fun of - * on the linux-kerenl list, you have been warned. + * on the linux-kernel list, you have been warned. */ } diff --git a/trunk/drivers/base/firmware_class.c b/trunk/drivers/base/firmware_class.c index d81460309182..b392b353be39 100644 --- a/trunk/drivers/base/firmware_class.c +++ b/trunk/drivers/base/firmware_class.c @@ -305,7 +305,7 @@ static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf char *buf; size = fw_file_size(file); - if (size < 0) + if (size <= 0) return false; buf = vmalloc(size); if (!buf) diff --git a/trunk/drivers/base/regmap/regmap-debugfs.c b/trunk/drivers/base/regmap/regmap-debugfs.c index 07aad786f817..d9a6c94ce423 100644 --- a/trunk/drivers/base/regmap/regmap-debugfs.c +++ b/trunk/drivers/base/regmap/regmap-debugfs.c @@ -56,6 +56,19 @@ static const struct file_operations regmap_name_fops = { .llseek = default_llseek, }; +static void regmap_debugfs_free_dump_cache(struct regmap *map) +{ + struct regmap_debugfs_off_cache *c; + + while (!list_empty(&map->debugfs_off_cache)) { + c = list_first_entry(&map->debugfs_off_cache, + struct regmap_debugfs_off_cache, + list); + list_del(&c->list); + kfree(c); + } +} + /* * Work out where the start offset maps into register numbers, bearing * in mind that we suppress hidden registers. @@ -91,8 +104,10 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, /* No cache entry? Start a new one */ if (!c) { c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - break; + if (!c) { + regmap_debugfs_free_dump_cache(map); + return base; + } c->min = p; c->base_reg = i; } @@ -101,14 +116,32 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, } } + /* Close the last entry off if we didn't scan beyond it */ + if (c) { + c->max = p - 1; + list_add_tail(&c->list, + &map->debugfs_off_cache); + } + + /* + * This should never happen; we return above if we fail to + * allocate and we should never be in this code if there are + * no registers at all. + */ + if (list_empty(&map->debugfs_off_cache)) { + WARN_ON(list_empty(&map->debugfs_off_cache)); + return base; + } + /* Find the relevant block */ list_for_each_entry(c, &map->debugfs_off_cache, list) { - if (*pos >= c->min && *pos <= c->max) { + if (from >= c->min && from <= c->max) { *pos = c->min; return c->base_reg; } - ret = c->max; + *pos = c->min; + ret = c->base_reg; } return ret; @@ -387,16 +420,8 @@ void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_exit(struct regmap *map) { - struct regmap_debugfs_off_cache *c; - debugfs_remove_recursive(map->debugfs); - while (!list_empty(&map->debugfs_off_cache)) { - c = list_first_entry(&map->debugfs_off_cache, - struct regmap_debugfs_off_cache, - list); - list_del(&c->list); - kfree(c); - } + regmap_debugfs_free_dump_cache(map); kfree(map->debugfs_name); } diff --git a/trunk/drivers/base/regmap/regmap.c b/trunk/drivers/base/regmap/regmap.c index 42d5cb0f503f..f00b059c057a 100644 --- a/trunk/drivers/base/regmap/regmap.c +++ b/trunk/drivers/base/regmap/regmap.c @@ -1106,7 +1106,7 @@ EXPORT_SYMBOL_GPL(regmap_raw_write); * @val_count: Number of registers to write * * This function is intended to be used for writing a large block of - * data to be device either in single transfer or multiple transfer. + * data to the device either in single transfer or multiple transfer. * * A value of zero will be returned on success, a negative errno will * be returned in error cases. diff --git a/trunk/drivers/block/virtio_blk.c b/trunk/drivers/block/virtio_blk.c index 9d8409c02082..8ad21a25bc0d 100644 --- a/trunk/drivers/block/virtio_blk.c +++ b/trunk/drivers/block/virtio_blk.c @@ -889,6 +889,7 @@ static void virtblk_remove(struct virtio_device *vdev) { struct virtio_blk *vblk = vdev->priv; int index = vblk->index; + int refc; /* Prevent config work handler from accessing the device. */ mutex_lock(&vblk->config_lock); @@ -903,11 +904,15 @@ static void virtblk_remove(struct virtio_device *vdev) flush_work(&vblk->config_work); + refc = atomic_read(&disk_to_dev(vblk->disk)->kobj.kref.refcount); put_disk(vblk->disk); mempool_destroy(vblk->pool); vdev->config->del_vqs(vdev); kfree(vblk); - ida_simple_remove(&vd_index_ida, index); + + /* Only free device id if we don't have any users */ + if (refc == 1) + ida_simple_remove(&vd_index_ida, index); } #ifdef CONFIG_PM diff --git a/trunk/drivers/clk/mvebu/clk-cpu.c b/trunk/drivers/clk/mvebu/clk-cpu.c index ff004578a119..9dd2551a0a41 100644 --- a/trunk/drivers/clk/mvebu/clk-cpu.c +++ b/trunk/drivers/clk/mvebu/clk-cpu.c @@ -124,7 +124,7 @@ void __init of_cpu_clk_setup(struct device_node *node) clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL); if (WARN_ON(!clks)) - return; + goto clks_out; for_each_node_by_type(dn, "cpu") { struct clk_init_data init; @@ -134,11 +134,11 @@ void __init of_cpu_clk_setup(struct device_node *node) int cpu, err; if (WARN_ON(!clk_name)) - return; + goto bail_out; err = of_property_read_u32(dn, "reg", &cpu); if (WARN_ON(err)) - return; + goto bail_out; sprintf(clk_name, "cpu%d", cpu); parent_clk = of_clk_get(node, 0); @@ -167,6 +167,9 @@ void __init of_cpu_clk_setup(struct device_node *node) return; bail_out: kfree(clks); + while(ncpus--) + kfree(cpuclk[ncpus].clk_name); +clks_out: kfree(cpuclk); } diff --git a/trunk/drivers/cpufreq/Kconfig.x86 b/trunk/drivers/cpufreq/Kconfig.x86 index 934854ae5eb4..7227cd734042 100644 --- a/trunk/drivers/cpufreq/Kconfig.x86 +++ b/trunk/drivers/cpufreq/Kconfig.x86 @@ -106,7 +106,7 @@ config X86_POWERNOW_K7_ACPI config X86_POWERNOW_K8 tristate "AMD Opteron/Athlon64 PowerNow!" select CPU_FREQ_TABLE - depends on ACPI && ACPI_PROCESSOR + depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ help This adds the CPUFreq driver for K8/early Opteron/Athlon64 processors. Support for K10 and newer processors is now in acpi-cpufreq. diff --git a/trunk/drivers/cpufreq/acpi-cpufreq.c b/trunk/drivers/cpufreq/acpi-cpufreq.c index 0d048f6a2b23..7b0d49d78c61 100644 --- a/trunk/drivers/cpufreq/acpi-cpufreq.c +++ b/trunk/drivers/cpufreq/acpi-cpufreq.c @@ -1030,4 +1030,11 @@ MODULE_PARM_DESC(acpi_pstate_strict, late_initcall(acpi_cpufreq_init); module_exit(acpi_cpufreq_exit); +static const struct x86_cpu_id acpi_cpufreq_ids[] = { + X86_FEATURE_MATCH(X86_FEATURE_ACPI), + X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, acpi_cpufreq_ids); + MODULE_ALIAS("acpi"); diff --git a/trunk/drivers/cpufreq/cpufreq-cpu0.c b/trunk/drivers/cpufreq/cpufreq-cpu0.c index 52bf36d599f5..debc5a7c8db6 100644 --- a/trunk/drivers/cpufreq/cpufreq-cpu0.c +++ b/trunk/drivers/cpufreq/cpufreq-cpu0.c @@ -71,12 +71,15 @@ static int cpu0_set_target(struct cpufreq_policy *policy, } if (cpu_reg) { + rcu_read_lock(); opp = opp_find_freq_ceil(cpu_dev, &freq_Hz); if (IS_ERR(opp)) { + rcu_read_unlock(); pr_err("failed to find OPP for %ld\n", freq_Hz); return PTR_ERR(opp); } volt = opp_get_voltage(opp); + rcu_read_unlock(); tol = volt * voltage_tolerance / 100; volt_old = regulator_get_voltage(cpu_reg); } @@ -236,12 +239,14 @@ static int cpu0_cpufreq_driver_init(void) */ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) ; + rcu_read_lock(); opp = opp_find_freq_exact(cpu_dev, freq_table[0].frequency * 1000, true); min_uV = opp_get_voltage(opp); opp = opp_find_freq_exact(cpu_dev, freq_table[i-1].frequency * 1000, true); max_uV = opp_get_voltage(opp); + rcu_read_unlock(); ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV); if (ret > 0) transition_latency += ret * 1000; diff --git a/trunk/drivers/cpufreq/omap-cpufreq.c b/trunk/drivers/cpufreq/omap-cpufreq.c index 1f3417a8322d..97102b05843f 100644 --- a/trunk/drivers/cpufreq/omap-cpufreq.c +++ b/trunk/drivers/cpufreq/omap-cpufreq.c @@ -110,13 +110,16 @@ static int omap_target(struct cpufreq_policy *policy, freq = ret; if (mpu_reg) { + rcu_read_lock(); opp = opp_find_freq_ceil(mpu_dev, &freq); if (IS_ERR(opp)) { + rcu_read_unlock(); dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n", __func__, freqs.new); return -EINVAL; } volt = opp_get_voltage(opp); + rcu_read_unlock(); tol = volt * OPP_TOLERANCE / 100; volt_old = regulator_get_voltage(mpu_reg); } diff --git a/trunk/drivers/cpuidle/cpuidle.c b/trunk/drivers/cpuidle/cpuidle.c index fb4a7dd57f94..e1f6860e069c 100644 --- a/trunk/drivers/cpuidle/cpuidle.c +++ b/trunk/drivers/cpuidle/cpuidle.c @@ -69,24 +69,15 @@ int cpuidle_play_dead(void) { struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); - int i, dead_state = -1; - int power_usage = INT_MAX; + int i; if (!drv) return -ENODEV; /* Find lowest-power state that supports long-term idle */ - for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { - struct cpuidle_state *s = &drv->states[i]; - - if (s->power_usage < power_usage && s->enter_dead) { - power_usage = s->power_usage; - dead_state = i; - } - } - - if (dead_state != -1) - return drv->states[dead_state].enter_dead(dev, dead_state); + for (i = drv->state_count - 1; i >= CPUIDLE_DRIVER_STATE_START; i--) + if (drv->states[i].enter_dead) + return drv->states[i].enter_dead(dev, i); return -ENODEV; } diff --git a/trunk/drivers/cpuidle/driver.c b/trunk/drivers/cpuidle/driver.c index c2b281afe0ed..422c7b69ba7c 100644 --- a/trunk/drivers/cpuidle/driver.c +++ b/trunk/drivers/cpuidle/driver.c @@ -19,34 +19,9 @@ DEFINE_SPINLOCK(cpuidle_driver_lock); static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); -static void set_power_states(struct cpuidle_driver *drv) -{ - int i; - - /* - * cpuidle driver should set the drv->power_specified bit - * before registering if the driver provides - * power_usage numbers. - * - * If power_specified is not set, - * we fill in power_usage with decreasing values as the - * cpuidle code has an implicit assumption that state Cn - * uses less power than C(n-1). - * - * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned - * an power value of -1. So we use -2, -3, etc, for other - * c-states. - */ - for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) - drv->states[i].power_usage = -1 - i; -} - static void __cpuidle_driver_init(struct cpuidle_driver *drv) { drv->refcnt = 0; - - if (!drv->power_specified) - set_power_states(drv); } static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) diff --git a/trunk/drivers/cpuidle/governors/menu.c b/trunk/drivers/cpuidle/governors/menu.c index 20ea33afdda1..fe343a06b7da 100644 --- a/trunk/drivers/cpuidle/governors/menu.c +++ b/trunk/drivers/cpuidle/governors/menu.c @@ -312,7 +312,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) { struct menu_device *data = &__get_cpu_var(menu_devices); int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); - int power_usage = INT_MAX; int i; int multiplier; struct timespec t; @@ -383,11 +382,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) if (s->exit_latency * multiplier > data->predicted_us) continue; - if (s->power_usage < power_usage) { - power_usage = s->power_usage; - data->last_state_idx = i; - data->exit_us = s->exit_latency; - } + data->last_state_idx = i; + data->exit_us = s->exit_latency; } /* not deepest C-state chosen for low predicted residency */ diff --git a/trunk/drivers/cpuidle/sysfs.c b/trunk/drivers/cpuidle/sysfs.c index 340942946106..428754af6236 100644 --- a/trunk/drivers/cpuidle/sysfs.c +++ b/trunk/drivers/cpuidle/sysfs.c @@ -374,7 +374,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); /* state statistics */ - for (i = 0; i < drv->state_count; i++) { + for (i = 0; i < device->state_count; i++) { kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); if (!kobj) goto error_state; diff --git a/trunk/drivers/devfreq/devfreq.c b/trunk/drivers/devfreq/devfreq.c index 53766f39aadd..3b367973a802 100644 --- a/trunk/drivers/devfreq/devfreq.c +++ b/trunk/drivers/devfreq/devfreq.c @@ -994,6 +994,11 @@ module_exit(devfreq_exit); * @freq: The frequency given to target function * @flags: Flags handed from devfreq framework. * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. */ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, u32 flags) diff --git a/trunk/drivers/devfreq/exynos4_bus.c b/trunk/drivers/devfreq/exynos4_bus.c index 80c745e83082..46d94e9e95b5 100644 --- a/trunk/drivers/devfreq/exynos4_bus.c +++ b/trunk/drivers/devfreq/exynos4_bus.c @@ -73,6 +73,16 @@ enum busclk_level_idx { #define EX4210_LV_NUM (LV_2 + 1) #define EX4x12_LV_NUM (LV_4 + 1) +/** + * struct busfreq_opp_info - opp information for bus + * @rate: Frequency in hertz + * @volt: Voltage in microvolts corresponding to this OPP + */ +struct busfreq_opp_info { + unsigned long rate; + unsigned long volt; +}; + struct busfreq_data { enum exynos4_busf_type type; struct device *dev; @@ -80,7 +90,7 @@ struct busfreq_data { bool disabled; struct regulator *vdd_int; struct regulator *vdd_mif; /* Exynos4412/4212 only */ - struct opp *curr_opp; + struct busfreq_opp_info curr_oppinfo; struct exynos4_ppmu dmc[2]; struct notifier_block pm_notifier; @@ -296,13 +306,14 @@ static unsigned int exynos4x12_clkdiv_sclkip[][3] = { }; -static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp) +static int exynos4210_set_busclk(struct busfreq_data *data, + struct busfreq_opp_info *oppi) { unsigned int index; unsigned int tmp; for (index = LV_0; index < EX4210_LV_NUM; index++) - if (opp_get_freq(opp) == exynos4210_busclk_table[index].clk) + if (oppi->rate == exynos4210_busclk_table[index].clk) break; if (index == EX4210_LV_NUM) @@ -361,13 +372,14 @@ static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp) return 0; } -static int exynos4x12_set_busclk(struct busfreq_data *data, struct opp *opp) +static int exynos4x12_set_busclk(struct busfreq_data *data, + struct busfreq_opp_info *oppi) { unsigned int index; unsigned int tmp; for (index = LV_0; index < EX4x12_LV_NUM; index++) - if (opp_get_freq(opp) == exynos4x12_mifclk_table[index].clk) + if (oppi->rate == exynos4x12_mifclk_table[index].clk) break; if (index == EX4x12_LV_NUM) @@ -576,11 +588,12 @@ static int exynos4x12_get_intspec(unsigned long mifclk) return -EINVAL; } -static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp, - struct opp *oldopp) +static int exynos4_bus_setvolt(struct busfreq_data *data, + struct busfreq_opp_info *oppi, + struct busfreq_opp_info *oldoppi) { int err = 0, tmp; - unsigned long volt = opp_get_voltage(opp); + unsigned long volt = oppi->volt; switch (data->type) { case TYPE_BUSF_EXYNOS4210: @@ -595,11 +608,11 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp, if (err) break; - tmp = exynos4x12_get_intspec(opp_get_freq(opp)); + tmp = exynos4x12_get_intspec(oppi->rate); if (tmp < 0) { err = tmp; regulator_set_voltage(data->vdd_mif, - opp_get_voltage(oldopp), + oldoppi->volt, MAX_SAFEVOLT); break; } @@ -609,7 +622,7 @@ static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp, /* Try to recover */ if (err) regulator_set_voltage(data->vdd_mif, - opp_get_voltage(oldopp), + oldoppi->volt, MAX_SAFEVOLT); break; default: @@ -626,17 +639,26 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq, struct platform_device *pdev = container_of(dev, struct platform_device, dev); struct busfreq_data *data = platform_get_drvdata(pdev); - struct opp *opp = devfreq_recommended_opp(dev, _freq, flags); - unsigned long freq = opp_get_freq(opp); - unsigned long old_freq = opp_get_freq(data->curr_opp); + struct opp *opp; + unsigned long freq; + unsigned long old_freq = data->curr_oppinfo.rate; + struct busfreq_opp_info new_oppinfo; - if (IS_ERR(opp)) + rcu_read_lock(); + opp = devfreq_recommended_opp(dev, _freq, flags); + if (IS_ERR(opp)) { + rcu_read_unlock(); return PTR_ERR(opp); + } + new_oppinfo.rate = opp_get_freq(opp); + new_oppinfo.volt = opp_get_voltage(opp); + rcu_read_unlock(); + freq = new_oppinfo.rate; if (old_freq == freq) return 0; - dev_dbg(dev, "targetting %lukHz %luuV\n", freq, opp_get_voltage(opp)); + dev_dbg(dev, "targetting %lukHz %luuV\n", freq, new_oppinfo.volt); mutex_lock(&data->lock); @@ -644,17 +666,18 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq, goto out; if (old_freq < freq) - err = exynos4_bus_setvolt(data, opp, data->curr_opp); + err = exynos4_bus_setvolt(data, &new_oppinfo, + &data->curr_oppinfo); if (err) goto out; if (old_freq != freq) { switch (data->type) { case TYPE_BUSF_EXYNOS4210: - err = exynos4210_set_busclk(data, opp); + err = exynos4210_set_busclk(data, &new_oppinfo); break; case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_set_busclk(data, opp); + err = exynos4x12_set_busclk(data, &new_oppinfo); break; default: err = -EINVAL; @@ -664,11 +687,12 @@ static int exynos4_bus_target(struct device *dev, unsigned long *_freq, goto out; if (old_freq > freq) - err = exynos4_bus_setvolt(data, opp, data->curr_opp); + err = exynos4_bus_setvolt(data, &new_oppinfo, + &data->curr_oppinfo); if (err) goto out; - data->curr_opp = opp; + data->curr_oppinfo = new_oppinfo; out: mutex_unlock(&data->lock); return err; @@ -702,7 +726,7 @@ static int exynos4_bus_get_dev_status(struct device *dev, exynos4_read_ppmu(data); busier_dmc = exynos4_get_busier_dmc(data); - stat->current_frequency = opp_get_freq(data->curr_opp); + stat->current_frequency = data->curr_oppinfo.rate; if (busier_dmc) addr = S5P_VA_DMC1; @@ -933,6 +957,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, struct busfreq_data *data = container_of(this, struct busfreq_data, pm_notifier); struct opp *opp; + struct busfreq_opp_info new_oppinfo; unsigned long maxfreq = ULONG_MAX; int err = 0; @@ -943,18 +968,29 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, data->disabled = true; + rcu_read_lock(); opp = opp_find_freq_floor(data->dev, &maxfreq); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(data->dev, "%s: unable to find a min freq\n", + __func__); + return PTR_ERR(opp); + } + new_oppinfo.rate = opp_get_freq(opp); + new_oppinfo.volt = opp_get_voltage(opp); + rcu_read_unlock(); - err = exynos4_bus_setvolt(data, opp, data->curr_opp); + err = exynos4_bus_setvolt(data, &new_oppinfo, + &data->curr_oppinfo); if (err) goto unlock; switch (data->type) { case TYPE_BUSF_EXYNOS4210: - err = exynos4210_set_busclk(data, opp); + err = exynos4210_set_busclk(data, &new_oppinfo); break; case TYPE_BUSF_EXYNOS4x12: - err = exynos4x12_set_busclk(data, opp); + err = exynos4x12_set_busclk(data, &new_oppinfo); break; default: err = -EINVAL; @@ -962,7 +998,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, if (err) goto unlock; - data->curr_opp = opp; + data->curr_oppinfo = new_oppinfo; unlock: mutex_unlock(&data->lock); if (err) @@ -1027,13 +1063,17 @@ static int exynos4_busfreq_probe(struct platform_device *pdev) } } + rcu_read_lock(); opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq); if (IS_ERR(opp)) { + rcu_read_unlock(); dev_err(dev, "Invalid initial frequency %lu kHz.\n", exynos4_devfreq_profile.initial_freq); return PTR_ERR(opp); } - data->curr_opp = opp; + data->curr_oppinfo.rate = opp_get_freq(opp); + data->curr_oppinfo.volt = opp_get_voltage(opp); + rcu_read_unlock(); platform_set_drvdata(pdev, data); diff --git a/trunk/drivers/dma/imx-dma.c b/trunk/drivers/dma/imx-dma.c index dbf0e6f8de8a..a7dcf78b1ff8 100644 --- a/trunk/drivers/dma/imx-dma.c +++ b/trunk/drivers/dma/imx-dma.c @@ -684,9 +684,8 @@ static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, break; } - imxdmac->hw_chaining = 1; - if (!imxdma_hw_chain(imxdmac)) - return -EINVAL; + imxdmac->hw_chaining = 0; + imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) | ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) | CCR_REN; diff --git a/trunk/drivers/dma/ioat/dma_v3.c b/trunk/drivers/dma/ioat/dma_v3.c index e5fc944de1f0..3e9d66920eb3 100644 --- a/trunk/drivers/dma/ioat/dma_v3.c +++ b/trunk/drivers/dma/ioat/dma_v3.c @@ -951,7 +951,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device) goto free_resources; } } - dma_sync_single_for_device(dev, dest_dma, PAGE_SIZE, DMA_TO_DEVICE); + dma_sync_single_for_device(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); /* skip validate if the capability is not present */ if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask)) diff --git a/trunk/drivers/dma/tegra20-apb-dma.c b/trunk/drivers/dma/tegra20-apb-dma.c index c39e61bc8172..3cad856fe67f 100644 --- a/trunk/drivers/dma/tegra20-apb-dma.c +++ b/trunk/drivers/dma/tegra20-apb-dma.c @@ -266,6 +266,7 @@ static struct tegra_dma_desc *tegra_dma_desc_get( if (async_tx_test_ack(&dma_desc->txd)) { list_del(&dma_desc->node); spin_unlock_irqrestore(&tdc->lock, flags); + dma_desc->txd.flags = 0; return dma_desc; } } @@ -1050,7 +1051,9 @@ struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT; ahb_seq |= TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32; - csr |= TEGRA_APBDMA_CSR_FLOW | TEGRA_APBDMA_CSR_IE_EOC; + csr |= TEGRA_APBDMA_CSR_FLOW; + if (flags & DMA_PREP_INTERRUPT) + csr |= TEGRA_APBDMA_CSR_IE_EOC; csr |= tdc->dma_sconfig.slave_id << TEGRA_APBDMA_CSR_REQ_SEL_SHIFT; apb_seq |= TEGRA_APBDMA_APBSEQ_WRAP_WORD_1; @@ -1095,7 +1098,8 @@ struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( mem += len; } sg_req->last_sg = true; - dma_desc->txd.flags = 0; + if (flags & DMA_CTRL_ACK) + dma_desc->txd.flags = DMA_CTRL_ACK; /* * Make sure that mode should not be conflicting with currently diff --git a/trunk/drivers/gpio/gpio-mvebu.c b/trunk/drivers/gpio/gpio-mvebu.c index 7d9bd94be8d2..6819d63cb167 100644 --- a/trunk/drivers/gpio/gpio-mvebu.c +++ b/trunk/drivers/gpio/gpio-mvebu.c @@ -547,7 +547,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->membase = devm_request_and_ioremap(&pdev->dev, res); if (! mvchip->membase) { dev_err(&pdev->dev, "Cannot ioremap\n"); - kfree(mvchip->chip.label); return -ENOMEM; } @@ -557,14 +556,12 @@ static int mvebu_gpio_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (! res) { dev_err(&pdev->dev, "Cannot get memory resource\n"); - kfree(mvchip->chip.label); return -ENODEV; } mvchip->percpu_membase = devm_request_and_ioremap(&pdev->dev, res); if (! mvchip->percpu_membase) { dev_err(&pdev->dev, "Cannot ioremap\n"); - kfree(mvchip->chip.label); return -ENOMEM; } } @@ -625,7 +622,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1); if (mvchip->irqbase < 0) { dev_err(&pdev->dev, "no irqs\n"); - kfree(mvchip->chip.label); return -ENOMEM; } @@ -633,7 +629,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->membase, handle_level_irq); if (! gc) { dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n"); - kfree(mvchip->chip.label); return -ENOMEM; } @@ -668,7 +663,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev) irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); kfree(gc); - kfree(mvchip->chip.label); return -ENODEV; } diff --git a/trunk/drivers/gpio/gpio-samsung.c b/trunk/drivers/gpio/gpio-samsung.c index 01f7fe955590..76be7eed79de 100644 --- a/trunk/drivers/gpio/gpio-samsung.c +++ b/trunk/drivers/gpio/gpio-samsung.c @@ -32,7 +32,6 @@ #include #include -#include #include #include @@ -446,7 +445,7 @@ static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = { }; #endif -#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_EXYNOS5) +#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_SOC_EXYNOS5250) static struct samsung_gpio_cfg exynos_gpio_cfg = { .set_pull = exynos_gpio_setpull, .get_pull = exynos_gpio_getpull, @@ -2446,7 +2445,7 @@ static struct samsung_gpio_chip exynos4_gpios_3[] = { }; #endif -#ifdef CONFIG_ARCH_EXYNOS5 +#ifdef CONFIG_SOC_EXYNOS5250 static struct samsung_gpio_chip exynos5_gpios_1[] = { { .chip = { @@ -2614,7 +2613,7 @@ static struct samsung_gpio_chip exynos5_gpios_1[] = { }; #endif -#ifdef CONFIG_ARCH_EXYNOS5 +#ifdef CONFIG_SOC_EXYNOS5250 static struct samsung_gpio_chip exynos5_gpios_2[] = { { .chip = { @@ -2675,7 +2674,7 @@ static struct samsung_gpio_chip exynos5_gpios_2[] = { }; #endif -#ifdef CONFIG_ARCH_EXYNOS5 +#ifdef CONFIG_SOC_EXYNOS5250 static struct samsung_gpio_chip exynos5_gpios_3[] = { { .chip = { @@ -2711,7 +2710,7 @@ static struct samsung_gpio_chip exynos5_gpios_3[] = { }; #endif -#ifdef CONFIG_ARCH_EXYNOS5 +#ifdef CONFIG_SOC_EXYNOS5250 static struct samsung_gpio_chip exynos5_gpios_4[] = { { .chip = { @@ -3010,7 +3009,7 @@ static __init int samsung_gpiolib_init(void) int i, nr_chips; int group = 0; -#ifdef CONFIG_PINCTRL_SAMSUNG +#if defined(CONFIG_PINCTRL_EXYNOS) || defined(CONFIG_PINCTRL_EXYNOS5440) /* * This gpio driver includes support for device tree support and there * are platforms using it. In order to maintain compatibility with those @@ -3026,6 +3025,7 @@ static __init int samsung_gpiolib_init(void) static const struct of_device_id exynos_pinctrl_ids[] = { { .compatible = "samsung,pinctrl-exynos4210", }, { .compatible = "samsung,pinctrl-exynos4x12", }, + { .compatible = "samsung,pinctrl-exynos5440", }, }; for_each_matching_node(pctrl_np, exynos_pinctrl_ids) if (pctrl_np && of_device_is_available(pctrl_np)) diff --git a/trunk/drivers/gpu/drm/drm_mm.c b/trunk/drivers/gpu/drm/drm_mm.c index 2bf9670ba29b..2aa331499f81 100644 --- a/trunk/drivers/gpu/drm/drm_mm.c +++ b/trunk/drivers/gpu/drm/drm_mm.c @@ -221,11 +221,13 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, BUG_ON(!hole_node->hole_follows || node->allocated); - if (mm->color_adjust) - mm->color_adjust(hole_node, color, &adj_start, &adj_end); - if (adj_start < start) adj_start = start; + if (adj_end > end) + adj_end = end; + + if (mm->color_adjust) + mm->color_adjust(hole_node, color, &adj_start, &adj_end); if (alignment) { unsigned tmp = adj_start % alignment; @@ -506,7 +508,7 @@ void drm_mm_init_scan(struct drm_mm *mm, mm->scan_size = size; mm->scanned_blocks = 0; mm->scan_hit_start = 0; - mm->scan_hit_size = 0; + mm->scan_hit_end = 0; mm->scan_check_range = 0; mm->prev_scanned_node = NULL; } @@ -533,7 +535,7 @@ void drm_mm_init_scan_with_range(struct drm_mm *mm, mm->scan_size = size; mm->scanned_blocks = 0; mm->scan_hit_start = 0; - mm->scan_hit_size = 0; + mm->scan_hit_end = 0; mm->scan_start = start; mm->scan_end = end; mm->scan_check_range = 1; @@ -552,8 +554,7 @@ int drm_mm_scan_add_block(struct drm_mm_node *node) struct drm_mm *mm = node->mm; struct drm_mm_node *prev_node; unsigned long hole_start, hole_end; - unsigned long adj_start; - unsigned long adj_end; + unsigned long adj_start, adj_end; mm->scanned_blocks++; @@ -570,14 +571,8 @@ int drm_mm_scan_add_block(struct drm_mm_node *node) node->node_list.next = &mm->prev_scanned_node->node_list; mm->prev_scanned_node = node; - hole_start = drm_mm_hole_node_start(prev_node); - hole_end = drm_mm_hole_node_end(prev_node); - - adj_start = hole_start; - adj_end = hole_end; - - if (mm->color_adjust) - mm->color_adjust(prev_node, mm->scan_color, &adj_start, &adj_end); + adj_start = hole_start = drm_mm_hole_node_start(prev_node); + adj_end = hole_end = drm_mm_hole_node_end(prev_node); if (mm->scan_check_range) { if (adj_start < mm->scan_start) @@ -586,11 +581,14 @@ int drm_mm_scan_add_block(struct drm_mm_node *node) adj_end = mm->scan_end; } + if (mm->color_adjust) + mm->color_adjust(prev_node, mm->scan_color, + &adj_start, &adj_end); + if (check_free_hole(adj_start, adj_end, mm->scan_size, mm->scan_alignment)) { mm->scan_hit_start = hole_start; - mm->scan_hit_size = hole_end; - + mm->scan_hit_end = hole_end; return 1; } @@ -626,19 +624,10 @@ int drm_mm_scan_remove_block(struct drm_mm_node *node) node_list); prev_node->hole_follows = node->scanned_preceeds_hole; - INIT_LIST_HEAD(&node->node_list); list_add(&node->node_list, &prev_node->node_list); - /* Only need to check for containement because start&size for the - * complete resulting free block (not just the desired part) is - * stored. */ - if (node->start >= mm->scan_hit_start && - node->start + node->size - <= mm->scan_hit_start + mm->scan_hit_size) { - return 1; - } - - return 0; + return (drm_mm_hole_node_end(node) > mm->scan_hit_start && + node->start < mm->scan_hit_end); } EXPORT_SYMBOL(drm_mm_scan_remove_block); diff --git a/trunk/drivers/gpu/drm/i915/i915_debugfs.c b/trunk/drivers/gpu/drm/i915/i915_debugfs.c index e6a11ca85eaf..7944d301518a 100644 --- a/trunk/drivers/gpu/drm/i915/i915_debugfs.c +++ b/trunk/drivers/gpu/drm/i915/i915_debugfs.c @@ -641,6 +641,7 @@ static void i915_ring_error_state(struct seq_file *m, seq_printf(m, "%s command stream:\n", ring_str(ring)); seq_printf(m, " HEAD: 0x%08x\n", error->head[ring]); seq_printf(m, " TAIL: 0x%08x\n", error->tail[ring]); + seq_printf(m, " CTL: 0x%08x\n", error->ctl[ring]); seq_printf(m, " ACTHD: 0x%08x\n", error->acthd[ring]); seq_printf(m, " IPEIR: 0x%08x\n", error->ipeir[ring]); seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr[ring]); @@ -693,6 +694,8 @@ static int i915_error_state(struct seq_file *m, void *unused) seq_printf(m, "EIR: 0x%08x\n", error->eir); seq_printf(m, "IER: 0x%08x\n", error->ier); seq_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er); + seq_printf(m, "FORCEWAKE: 0x%08x\n", error->forcewake); + seq_printf(m, "DERRMR: 0x%08x\n", error->derrmr); seq_printf(m, "CCID: 0x%08x\n", error->ccid); for (i = 0; i < dev_priv->num_fence_regs; i++) diff --git a/trunk/drivers/gpu/drm/i915/i915_drv.h b/trunk/drivers/gpu/drm/i915/i915_drv.h index ed3059575576..12ab3bdea54d 100644 --- a/trunk/drivers/gpu/drm/i915/i915_drv.h +++ b/trunk/drivers/gpu/drm/i915/i915_drv.h @@ -188,10 +188,13 @@ struct drm_i915_error_state { u32 pgtbl_er; u32 ier; u32 ccid; + u32 derrmr; + u32 forcewake; bool waiting[I915_NUM_RINGS]; u32 pipestat[I915_MAX_PIPES]; u32 tail[I915_NUM_RINGS]; u32 head[I915_NUM_RINGS]; + u32 ctl[I915_NUM_RINGS]; u32 ipeir[I915_NUM_RINGS]; u32 ipehr[I915_NUM_RINGS]; u32 instdone[I915_NUM_RINGS]; diff --git a/trunk/drivers/gpu/drm/i915/i915_gem.c b/trunk/drivers/gpu/drm/i915/i915_gem.c index da3c82e301b1..8febea6daa08 100644 --- a/trunk/drivers/gpu/drm/i915/i915_gem.c +++ b/trunk/drivers/gpu/drm/i915/i915_gem.c @@ -1717,7 +1717,8 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj) } static long -i915_gem_purge(struct drm_i915_private *dev_priv, long target) +__i915_gem_shrink(struct drm_i915_private *dev_priv, long target, + bool purgeable_only) { struct drm_i915_gem_object *obj, *next; long count = 0; @@ -1725,7 +1726,7 @@ i915_gem_purge(struct drm_i915_private *dev_priv, long target) list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list) { - if (i915_gem_object_is_purgeable(obj) && + if ((i915_gem_object_is_purgeable(obj) || !purgeable_only) && i915_gem_object_put_pages(obj) == 0) { count += obj->base.size >> PAGE_SHIFT; if (count >= target) @@ -1736,7 +1737,7 @@ i915_gem_purge(struct drm_i915_private *dev_priv, long target) list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list) { - if (i915_gem_object_is_purgeable(obj) && + if ((i915_gem_object_is_purgeable(obj) || !purgeable_only) && i915_gem_object_unbind(obj) == 0 && i915_gem_object_put_pages(obj) == 0) { count += obj->base.size >> PAGE_SHIFT; @@ -1748,6 +1749,12 @@ i915_gem_purge(struct drm_i915_private *dev_priv, long target) return count; } +static long +i915_gem_purge(struct drm_i915_private *dev_priv, long target) +{ + return __i915_gem_shrink(dev_priv, target, true); +} + static void i915_gem_shrink_all(struct drm_i915_private *dev_priv) { @@ -3522,14 +3529,15 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, goto out; } - obj->user_pin_count++; - obj->pin_filp = file; - if (obj->user_pin_count == 1) { + if (obj->user_pin_count == 0) { ret = i915_gem_object_pin(obj, args->alignment, true, false); if (ret) goto out; } + obj->user_pin_count++; + obj->pin_filp = file; + /* XXX - flush the CPU caches for pinned objects * as the X server doesn't manage domains yet */ @@ -4394,6 +4402,9 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc) if (nr_to_scan) { nr_to_scan -= i915_gem_purge(dev_priv, nr_to_scan); + if (nr_to_scan > 0) + nr_to_scan -= __i915_gem_shrink(dev_priv, nr_to_scan, + false); if (nr_to_scan > 0) i915_gem_shrink_all(dev_priv); } @@ -4402,7 +4413,7 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc) list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) if (obj->pages_pin_count == 0) cnt += obj->base.size >> PAGE_SHIFT; - list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) + list_for_each_entry(obj, &dev_priv->mm.inactive_list, gtt_list) if (obj->pin_count == 0 && obj->pages_pin_count == 0) cnt += obj->base.size >> PAGE_SHIFT; diff --git a/trunk/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/trunk/drivers/gpu/drm/i915/i915_gem_execbuffer.c index d6a994a07393..26d08bb58218 100644 --- a/trunk/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/trunk/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -539,6 +539,8 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, total = 0; for (i = 0; i < count; i++) { struct drm_i915_gem_relocation_entry __user *user_relocs; + u64 invalid_offset = (u64)-1; + int j; user_relocs = (void __user *)(uintptr_t)exec[i].relocs_ptr; @@ -549,6 +551,25 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, goto err; } + /* As we do not update the known relocation offsets after + * relocating (due to the complexities in lock handling), + * we need to mark them as invalid now so that we force the + * relocation processing next time. Just in case the target + * object is evicted and then rebound into its old + * presumed_offset before the next execbuffer - if that + * happened we would make the mistake of assuming that the + * relocations were valid. + */ + for (j = 0; j < exec[i].relocation_count; j++) { + if (copy_to_user(&user_relocs[j].presumed_offset, + &invalid_offset, + sizeof(invalid_offset))) { + ret = -EFAULT; + mutex_lock(&dev->struct_mutex); + goto err; + } + } + reloc_offset[i] = total; total += exec[i].relocation_count; } diff --git a/trunk/drivers/gpu/drm/i915/i915_irq.c b/trunk/drivers/gpu/drm/i915/i915_irq.c index 2220dec3e5d9..fe843389c7b4 100644 --- a/trunk/drivers/gpu/drm/i915/i915_irq.c +++ b/trunk/drivers/gpu/drm/i915/i915_irq.c @@ -1157,6 +1157,7 @@ static void i915_record_ring_state(struct drm_device *dev, error->acthd[ring->id] = intel_ring_get_active_head(ring); error->head[ring->id] = I915_READ_HEAD(ring); error->tail[ring->id] = I915_READ_TAIL(ring); + error->ctl[ring->id] = I915_READ_CTL(ring); error->cpu_ring_head[ring->id] = ring->head; error->cpu_ring_tail[ring->id] = ring->tail; @@ -1251,6 +1252,16 @@ static void i915_capture_error_state(struct drm_device *dev) else error->ier = I915_READ(IER); + if (INTEL_INFO(dev)->gen >= 6) + error->derrmr = I915_READ(DERRMR); + + if (IS_VALLEYVIEW(dev)) + error->forcewake = I915_READ(FORCEWAKE_VLV); + else if (INTEL_INFO(dev)->gen >= 7) + error->forcewake = I915_READ(FORCEWAKE_MT); + else if (INTEL_INFO(dev)->gen == 6) + error->forcewake = I915_READ(FORCEWAKE); + for_each_pipe(pipe) error->pipestat[pipe] = I915_READ(PIPESTAT(pipe)); diff --git a/trunk/drivers/gpu/drm/i915/i915_reg.h b/trunk/drivers/gpu/drm/i915/i915_reg.h index 186ee5c85b51..b401788e1791 100644 --- a/trunk/drivers/gpu/drm/i915/i915_reg.h +++ b/trunk/drivers/gpu/drm/i915/i915_reg.h @@ -512,6 +512,8 @@ #define GEN7_ERR_INT 0x44040 #define ERR_INT_MMIO_UNCLAIMED (1<<13) +#define DERRMR 0x44050 + /* GM45+ chicken bits -- debug workaround bits that may be required * for various sorts of correct behavior. The top 16 bits of each are * the enables for writing to the corresponding low bit. diff --git a/trunk/drivers/gpu/drm/i915/intel_display.c b/trunk/drivers/gpu/drm/i915/intel_display.c index a9fb046b94a1..da1ad9c80bb5 100644 --- a/trunk/drivers/gpu/drm/i915/intel_display.c +++ b/trunk/drivers/gpu/drm/i915/intel_display.c @@ -8598,19 +8598,30 @@ int intel_framebuffer_init(struct drm_device *dev, { int ret; - if (obj->tiling_mode == I915_TILING_Y) + if (obj->tiling_mode == I915_TILING_Y) { + DRM_DEBUG("hardware does not support tiling Y\n"); return -EINVAL; + } - if (mode_cmd->pitches[0] & 63) + if (mode_cmd->pitches[0] & 63) { + DRM_DEBUG("pitch (%d) must be at least 64 byte aligned\n", + mode_cmd->pitches[0]); return -EINVAL; + } /* FIXME <= Gen4 stride limits are bit unclear */ - if (mode_cmd->pitches[0] > 32768) + if (mode_cmd->pitches[0] > 32768) { + DRM_DEBUG("pitch (%d) must be at less than 32768\n", + mode_cmd->pitches[0]); return -EINVAL; + } if (obj->tiling_mode != I915_TILING_NONE && - mode_cmd->pitches[0] != obj->stride) + mode_cmd->pitches[0] != obj->stride) { + DRM_DEBUG("pitch (%d) must match tiling stride (%d)\n", + mode_cmd->pitches[0], obj->stride); return -EINVAL; + } /* Reject formats not supported by any plane early. */ switch (mode_cmd->pixel_format) { @@ -8621,8 +8632,10 @@ int intel_framebuffer_init(struct drm_device *dev, break; case DRM_FORMAT_XRGB1555: case DRM_FORMAT_ARGB1555: - if (INTEL_INFO(dev)->gen > 3) + if (INTEL_INFO(dev)->gen > 3) { + DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format); return -EINVAL; + } break; case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: @@ -8630,18 +8643,22 @@ int intel_framebuffer_init(struct drm_device *dev, case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_ABGR2101010: - if (INTEL_INFO(dev)->gen < 4) + if (INTEL_INFO(dev)->gen < 4) { + DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format); return -EINVAL; + } break; case DRM_FORMAT_YUYV: case DRM_FORMAT_UYVY: case DRM_FORMAT_YVYU: case DRM_FORMAT_VYUY: - if (INTEL_INFO(dev)->gen < 6) + if (INTEL_INFO(dev)->gen < 5) { + DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format); return -EINVAL; + } break; default: - DRM_DEBUG_KMS("unsupported pixel format 0x%08x\n", mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format 0x%08x\n", mode_cmd->pixel_format); return -EINVAL; } diff --git a/trunk/drivers/gpu/drm/i915/intel_dp.c b/trunk/drivers/gpu/drm/i915/intel_dp.c index 1b63d55318a0..fb3715b4b09d 100644 --- a/trunk/drivers/gpu/drm/i915/intel_dp.c +++ b/trunk/drivers/gpu/drm/i915/intel_dp.c @@ -2579,7 +2579,8 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect static void intel_dp_init_panel_power_sequencer(struct drm_device *dev, - struct intel_dp *intel_dp) + struct intel_dp *intel_dp, + struct edp_power_seq *out) { struct drm_i915_private *dev_priv = dev->dev_private; struct edp_power_seq cur, vbt, spec, final; @@ -2650,16 +2651,35 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev, intel_dp->panel_power_cycle_delay = get_delay(t11_t12); #undef get_delay + DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", + intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, + intel_dp->panel_power_cycle_delay); + + DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", + intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); + + if (out) + *out = final; +} + +static void +intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, + struct intel_dp *intel_dp, + struct edp_power_seq *seq) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_on, pp_off, pp_div; + /* And finally store the new values in the power sequencer. */ - pp_on = (final.t1_t3 << PANEL_POWER_UP_DELAY_SHIFT) | - (final.t8 << PANEL_LIGHT_ON_DELAY_SHIFT); - pp_off = (final.t9 << PANEL_LIGHT_OFF_DELAY_SHIFT) | - (final.t10 << PANEL_POWER_DOWN_DELAY_SHIFT); + pp_on = (seq->t1_t3 << PANEL_POWER_UP_DELAY_SHIFT) | + (seq->t8 << PANEL_LIGHT_ON_DELAY_SHIFT); + pp_off = (seq->t9 << PANEL_LIGHT_OFF_DELAY_SHIFT) | + (seq->t10 << PANEL_POWER_DOWN_DELAY_SHIFT); /* Compute the divisor for the pp clock, simply match the Bspec * formula. */ pp_div = ((100 * intel_pch_rawclk(dev))/2 - 1) << PP_REFERENCE_DIVIDER_SHIFT; - pp_div |= (DIV_ROUND_UP(final.t11_t12, 1000) + pp_div |= (DIV_ROUND_UP(seq->t11_t12, 1000) << PANEL_POWER_CYCLE_DELAY_SHIFT); /* Haswell doesn't have any port selection bits for the panel @@ -2675,14 +2695,6 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev, I915_WRITE(PCH_PP_OFF_DELAYS, pp_off); I915_WRITE(PCH_PP_DIVISOR, pp_div); - - DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", - intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, - intel_dp->panel_power_cycle_delay); - - DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", - intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); - DRM_DEBUG_KMS("panel power sequencer register settings: PP_ON %#x, PP_OFF %#x, PP_DIV %#x\n", I915_READ(PCH_PP_ON_DELAYS), I915_READ(PCH_PP_OFF_DELAYS), @@ -2699,6 +2711,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct drm_device *dev = intel_encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_display_mode *fixed_mode = NULL; + struct edp_power_seq power_seq = { 0 }; enum port port = intel_dig_port->port; const char *name = NULL; int type; @@ -2771,7 +2784,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, } if (is_edp(intel_dp)) - intel_dp_init_panel_power_sequencer(dev, intel_dp); + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); intel_dp_i2c_init(intel_dp, intel_connector, name); @@ -2798,6 +2811,10 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, return; } + /* We now know it's not a ghost, init power sequence regs. */ + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); + ironlake_edp_panel_vdd_on(intel_dp); edid = drm_get_edid(connector, &intel_dp->adapter); if (edid) { diff --git a/trunk/drivers/gpu/drm/i915/intel_lvds.c b/trunk/drivers/gpu/drm/i915/intel_lvds.c index b9a660a53677..17aee74258ad 100644 --- a/trunk/drivers/gpu/drm/i915/intel_lvds.c +++ b/trunk/drivers/gpu/drm/i915/intel_lvds.c @@ -774,14 +774,6 @@ static const struct dmi_system_id intel_no_lvds[] = { DMI_MATCH(DMI_BOARD_NAME, "MS-7469"), }, }, - { - .callback = intel_no_lvds_dmi_callback, - .ident = "ZOTAC ZBOXSD-ID12/ID13", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ZOTAC"), - DMI_MATCH(DMI_BOARD_NAME, "ZBOXSD-ID12/ID13"), - }, - }, { .callback = intel_no_lvds_dmi_callback, .ident = "Gigabyte GA-D525TUD", diff --git a/trunk/drivers/gpu/drm/i915/intel_pm.c b/trunk/drivers/gpu/drm/i915/intel_pm.c index e6f54ffab3ba..3280cffe50f4 100644 --- a/trunk/drivers/gpu/drm/i915/intel_pm.c +++ b/trunk/drivers/gpu/drm/i915/intel_pm.c @@ -44,6 +44,14 @@ * i915.i915_enable_fbc parameter */ +static bool intel_crtc_active(struct drm_crtc *crtc) +{ + /* Be paranoid as we can arrive here with only partial + * state retrieved from the hardware during setup. + */ + return to_intel_crtc(crtc)->active && crtc->fb && crtc->mode.clock; +} + static void i8xx_disable_fbc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -405,9 +413,8 @@ void intel_update_fbc(struct drm_device *dev) * - going to an unsupported config (interlace, pixel multiply, etc.) */ list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list, head) { - if (to_intel_crtc(tmp_crtc)->active && - !to_intel_crtc(tmp_crtc)->primary_disabled && - tmp_crtc->fb) { + if (intel_crtc_active(tmp_crtc) && + !to_intel_crtc(tmp_crtc)->primary_disabled) { if (crtc) { DRM_DEBUG_KMS("more than one pipe active, disabling compression\n"); dev_priv->no_fbc_reason = FBC_MULTIPLE_PIPES; @@ -992,7 +999,7 @@ static struct drm_crtc *single_enabled_crtc(struct drm_device *dev) struct drm_crtc *crtc, *enabled = NULL; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (to_intel_crtc(crtc)->active && crtc->fb) { + if (intel_crtc_active(crtc)) { if (enabled) return NULL; enabled = crtc; @@ -1086,7 +1093,7 @@ static bool g4x_compute_wm0(struct drm_device *dev, int entries, tlb_miss; crtc = intel_get_crtc_for_plane(dev, plane); - if (crtc->fb == NULL || !to_intel_crtc(crtc)->active) { + if (!intel_crtc_active(crtc)) { *cursor_wm = cursor->guard_size; *plane_wm = display->guard_size; return false; @@ -1215,7 +1222,7 @@ static bool vlv_compute_drain_latency(struct drm_device *dev, int entries; crtc = intel_get_crtc_for_plane(dev, plane); - if (crtc->fb == NULL || !to_intel_crtc(crtc)->active) + if (!intel_crtc_active(crtc)) return false; clock = crtc->mode.clock; /* VESA DOT Clock */ @@ -1476,7 +1483,7 @@ static void i9xx_update_wm(struct drm_device *dev) fifo_size = dev_priv->display.get_fifo_size(dev, 0); crtc = intel_get_crtc_for_plane(dev, 0); - if (to_intel_crtc(crtc)->active && crtc->fb) { + if (intel_crtc_active(crtc)) { int cpp = crtc->fb->bits_per_pixel / 8; if (IS_GEN2(dev)) cpp = 4; @@ -1490,7 +1497,7 @@ static void i9xx_update_wm(struct drm_device *dev) fifo_size = dev_priv->display.get_fifo_size(dev, 1); crtc = intel_get_crtc_for_plane(dev, 1); - if (to_intel_crtc(crtc)->active && crtc->fb) { + if (intel_crtc_active(crtc)) { int cpp = crtc->fb->bits_per_pixel / 8; if (IS_GEN2(dev)) cpp = 4; @@ -2044,7 +2051,7 @@ sandybridge_compute_sprite_wm(struct drm_device *dev, int plane, int entries, tlb_miss; crtc = intel_get_crtc_for_plane(dev, plane); - if (crtc->fb == NULL || !to_intel_crtc(crtc)->active) { + if (!intel_crtc_active(crtc)) { *sprite_wm = display->guard_size; return false; } @@ -4243,7 +4250,8 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) static void __gen6_gt_force_wake_mt_reset(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(0xffff)); - POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */ + /* something from same cacheline, but !FORCEWAKE_MT */ + POSTING_READ(ECOBUS); } static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) @@ -4260,7 +4268,8 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(FORCEWAKE_KERNEL)); - POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */ + /* something from same cacheline, but !FORCEWAKE_MT */ + POSTING_READ(ECOBUS); if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1), FORCEWAKE_ACK_TIMEOUT_MS)) @@ -4297,14 +4306,16 @@ void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv) static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE, 0); - /* gen6_gt_check_fifodbg doubles as the POSTING_READ */ + /* something from same cacheline, but !FORCEWAKE */ + POSTING_READ(ECOBUS); gen6_gt_check_fifodbg(dev_priv); } static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(FORCEWAKE_KERNEL)); - /* gen6_gt_check_fifodbg doubles as the POSTING_READ */ + /* something from same cacheline, but !FORCEWAKE_MT */ + POSTING_READ(ECOBUS); gen6_gt_check_fifodbg(dev_priv); } @@ -4344,6 +4355,8 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) static void vlv_force_wake_reset(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(0xffff)); + /* something from same cacheline, but !FORCEWAKE_VLV */ + POSTING_READ(FORCEWAKE_ACK_VLV); } static void vlv_force_wake_get(struct drm_i915_private *dev_priv) @@ -4364,7 +4377,8 @@ static void vlv_force_wake_get(struct drm_i915_private *dev_priv) static void vlv_force_wake_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(FORCEWAKE_KERNEL)); - /* The below doubles as a POSTING_READ */ + /* something from same cacheline, but !FORCEWAKE_VLV */ + POSTING_READ(FORCEWAKE_ACK_VLV); gen6_gt_check_fifodbg(dev_priv); } diff --git a/trunk/drivers/gpu/drm/i915/intel_sprite.c b/trunk/drivers/gpu/drm/i915/intel_sprite.c index 827dcd4edf1c..d7b060e0a231 100644 --- a/trunk/drivers/gpu/drm/i915/intel_sprite.c +++ b/trunk/drivers/gpu/drm/i915/intel_sprite.c @@ -120,11 +120,10 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x); - linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * pixel_size; sprsurf_offset = intel_gen4_compute_offset_xtiled(&x, &y, - fb->bits_per_pixel / 8, - fb->pitches[0]); + pixel_size, fb->pitches[0]); linear_offset -= sprsurf_offset; /* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET @@ -286,11 +285,10 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]); I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x); - linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * pixel_size; dvssurf_offset = intel_gen4_compute_offset_xtiled(&x, &y, - fb->bits_per_pixel / 8, - fb->pitches[0]); + pixel_size, fb->pitches[0]); linear_offset -= dvssurf_offset; if (obj->tiling_mode != I915_TILING_NONE) diff --git a/trunk/drivers/gpu/drm/nouveau/core/core/client.c b/trunk/drivers/gpu/drm/nouveau/core/core/client.c index c617f0480071..8bbb58f94a19 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/core/client.c +++ b/trunk/drivers/gpu/drm/nouveau/core/core/client.c @@ -66,10 +66,8 @@ nouveau_client_create_(const char *name, u64 devname, const char *cfg, ret = nouveau_handle_create(nv_object(client), ~0, ~0, nv_object(client), &client->root); - if (ret) { - nouveau_namedb_destroy(&client->base); + if (ret) return ret; - } /* prevent init/fini being called, os in in charge of this */ atomic_set(&nv_object(client)->usecount, 2); diff --git a/trunk/drivers/gpu/drm/nouveau/core/core/handle.c b/trunk/drivers/gpu/drm/nouveau/core/core/handle.c index b8d2cbf8a7a7..264c2b338ac3 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/core/handle.c +++ b/trunk/drivers/gpu/drm/nouveau/core/core/handle.c @@ -109,7 +109,7 @@ nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle, while (!nv_iclass(namedb, NV_NAMEDB_CLASS)) namedb = namedb->parent; - handle = *phandle = kzalloc(sizeof(*handle), GFP_KERNEL); + handle = kzalloc(sizeof(*handle), GFP_KERNEL); if (!handle) return -ENOMEM; @@ -146,6 +146,9 @@ nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle, } hprintk(handle, TRACE, "created\n"); + + *phandle = handle; + return 0; } diff --git a/trunk/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/trunk/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 0f09af135415..ca1a7d76a95b 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/trunk/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -851,20 +851,23 @@ exec_script(struct nv50_disp_priv *priv, int head, int id) for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) ctrl = nv_rd32(priv, 0x610b5c + (i * 8)); - if (nv_device(priv)->chipset < 0x90 || - nv_device(priv)->chipset == 0x92 || - nv_device(priv)->chipset == 0xa0) { - for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) - ctrl = nv_rd32(priv, 0x610b74 + (i * 8)); - i += 3; - } else { - for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) - ctrl = nv_rd32(priv, 0x610798 + (i * 8)); - i += 3; + if (!(ctrl & (1 << head))) { + if (nv_device(priv)->chipset < 0x90 || + nv_device(priv)->chipset == 0x92 || + nv_device(priv)->chipset == 0xa0) { + for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) + ctrl = nv_rd32(priv, 0x610b74 + (i * 8)); + i += 4; + } else { + for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) + ctrl = nv_rd32(priv, 0x610798 + (i * 8)); + i += 4; + } } if (!(ctrl & (1 << head))) return false; + i--; data = exec_lookup(priv, head, i, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info); if (data) { @@ -898,20 +901,23 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) ctrl = nv_rd32(priv, 0x610b58 + (i * 8)); - if (nv_device(priv)->chipset < 0x90 || - nv_device(priv)->chipset == 0x92 || - nv_device(priv)->chipset == 0xa0) { - for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) - ctrl = nv_rd32(priv, 0x610b70 + (i * 8)); - i += 3; - } else { - for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) - ctrl = nv_rd32(priv, 0x610794 + (i * 8)); - i += 3; + if (!(ctrl & (1 << head))) { + if (nv_device(priv)->chipset < 0x90 || + nv_device(priv)->chipset == 0x92 || + nv_device(priv)->chipset == 0xa0) { + for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) + ctrl = nv_rd32(priv, 0x610b70 + (i * 8)); + i += 4; + } else { + for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) + ctrl = nv_rd32(priv, 0x610794 + (i * 8)); + i += 4; + } } if (!(ctrl & (1 << head))) return 0x0000; + i--; data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1); if (!data) diff --git a/trunk/drivers/gpu/drm/nouveau/core/include/core/client.h b/trunk/drivers/gpu/drm/nouveau/core/include/core/client.h index 0193532ceac9..63acc0346ff2 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/include/core/client.h +++ b/trunk/drivers/gpu/drm/nouveau/core/include/core/client.h @@ -36,6 +36,9 @@ nouveau_client(void *obj) int nouveau_client_create_(const char *name, u64 device, const char *cfg, const char *dbg, int, void **); +#define nouveau_client_destroy(p) \ + nouveau_namedb_destroy(&(p)->base) + int nouveau_client_init(struct nouveau_client *); int nouveau_client_fini(struct nouveau_client *, bool suspend); diff --git a/trunk/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h b/trunk/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h index c345097592f2..b2f3d4d0aa49 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h +++ b/trunk/drivers/gpu/drm/nouveau/core/include/subdev/bios/pll.h @@ -38,6 +38,8 @@ enum nvbios_pll_type { PLL_UNK42 = 0x42, PLL_VPLL0 = 0x80, PLL_VPLL1 = 0x81, + PLL_VPLL2 = 0x82, + PLL_VPLL3 = 0x83, PLL_MAX = 0xff }; diff --git a/trunk/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/trunk/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index 2917d552689b..690ed438b2ad 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/trunk/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -1534,7 +1534,6 @@ init_io(struct nvbios_init *init) mdelay(10); init_wr32(init, 0x614100, 0x10000018); init_wr32(init, 0x614900, 0x10000018); - return; } value = init_rdport(init, port) & mask; diff --git a/trunk/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c b/trunk/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c index f6962c9b6c36..7c9626258a46 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c +++ b/trunk/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c @@ -52,6 +52,8 @@ nvc0_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq) switch (info.type) { case PLL_VPLL0: case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100); nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M); nv_wr32(priv, info.reg + 0x10, fN << 16); diff --git a/trunk/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c b/trunk/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c index 306bdf121452..7606ed15b6fa 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c +++ b/trunk/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c @@ -145,14 +145,14 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, mem->memtype = type; mem->size = size; - mutex_lock(&mm->mutex); + mutex_lock(&pfb->base.mutex); do { if (back) ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r); else ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r); if (ret) { - mutex_unlock(&mm->mutex); + mutex_unlock(&pfb->base.mutex); pfb->ram.put(pfb, &mem); return ret; } @@ -160,7 +160,7 @@ nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, list_add_tail(&r->rl_entry, &mem->regions); size -= r->length; } while (size); - mutex_unlock(&mm->mutex); + mutex_unlock(&pfb->base.mutex); r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry); mem->offset = (u64)r->offset << 12; diff --git a/trunk/drivers/gpu/drm/nouveau/core/subdev/instmem/base.c b/trunk/drivers/gpu/drm/nouveau/core/subdev/instmem/base.c index 1188227ca6aa..6565f3dbbe04 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/subdev/instmem/base.c +++ b/trunk/drivers/gpu/drm/nouveau/core/subdev/instmem/base.c @@ -40,15 +40,21 @@ nouveau_instobj_create_(struct nouveau_object *parent, if (ret) return ret; + mutex_lock(&imem->base.mutex); list_add(&iobj->head, &imem->list); + mutex_unlock(&imem->base.mutex); return 0; } void nouveau_instobj_destroy(struct nouveau_instobj *iobj) { - if (iobj->head.prev) - list_del(&iobj->head); + struct nouveau_subdev *subdev = nv_subdev(iobj->base.engine); + + mutex_lock(&subdev->mutex); + list_del(&iobj->head); + mutex_unlock(&subdev->mutex); + return nouveau_object_destroy(&iobj->base); } @@ -88,6 +94,8 @@ nouveau_instmem_init(struct nouveau_instmem *imem) if (ret) return ret; + mutex_lock(&imem->base.mutex); + list_for_each_entry(iobj, &imem->list, head) { if (iobj->suspend) { for (i = 0; i < iobj->size; i += 4) @@ -97,6 +105,8 @@ nouveau_instmem_init(struct nouveau_instmem *imem) } } + mutex_unlock(&imem->base.mutex); + return 0; } @@ -104,17 +114,26 @@ int nouveau_instmem_fini(struct nouveau_instmem *imem, bool suspend) { struct nouveau_instobj *iobj; - int i; + int i, ret = 0; if (suspend) { + mutex_lock(&imem->base.mutex); + list_for_each_entry(iobj, &imem->list, head) { iobj->suspend = vmalloc(iobj->size); - if (iobj->suspend) { - for (i = 0; i < iobj->size; i += 4) - iobj->suspend[i / 4] = nv_ro32(iobj, i); - } else - return -ENOMEM; + if (!iobj->suspend) { + ret = -ENOMEM; + break; + } + + for (i = 0; i < iobj->size; i += 4) + iobj->suspend[i / 4] = nv_ro32(iobj, i); } + + mutex_unlock(&imem->base.mutex); + + if (ret) + return ret; } return nouveau_subdev_fini(&imem->base, suspend); diff --git a/trunk/drivers/gpu/drm/nouveau/core/subdev/vm/base.c b/trunk/drivers/gpu/drm/nouveau/core/subdev/vm/base.c index 082c11b75acb..77c67fc970e6 100644 --- a/trunk/drivers/gpu/drm/nouveau/core/subdev/vm/base.c +++ b/trunk/drivers/gpu/drm/nouveau/core/subdev/vm/base.c @@ -352,7 +352,7 @@ nouveau_vm_create(struct nouveau_vmmgr *vmm, u64 offset, u64 length, u64 mm_length = (offset + length) - mm_offset; int ret; - vm = *pvm = kzalloc(sizeof(*vm), GFP_KERNEL); + vm = kzalloc(sizeof(*vm), GFP_KERNEL); if (!vm) return -ENOMEM; @@ -376,6 +376,8 @@ nouveau_vm_create(struct nouveau_vmmgr *vmm, u64 offset, u64 length, return ret; } + *pvm = vm; + return 0; } diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_connector.c b/trunk/drivers/gpu/drm/nouveau/nouveau_connector.c index ac340ba32017..e620ba8271b4 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -127,12 +127,26 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, struct nouveau_encoder **pnv_encoder) { struct drm_device *dev = connector->dev; + struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_gpio *gpio = nouveau_gpio(drm->device); struct nouveau_i2c *i2c = nouveau_i2c(drm->device); - int i; + struct nouveau_i2c_port *port = NULL; + int i, panel = -ENODEV; + + /* eDP panels need powering on by us (if the VBIOS doesn't default it + * to on) before doing any AUX channel transactions. LVDS panel power + * is handled by the SOR itself, and not required for LVDS DDC. + */ + if (nv_connector->type == DCB_CONNECTOR_eDP) { + panel = gpio->get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); + if (panel == 0) { + gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); + msleep(300); + } + } for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - struct nouveau_i2c_port *port = NULL; struct nouveau_encoder *nv_encoder; struct drm_mode_object *obj; int id; @@ -150,11 +164,19 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, port = i2c->find(i2c, nv_encoder->dcb->i2c_index); if (port && nv_probe_i2c(port, 0x50)) { *pnv_encoder = nv_encoder; - return port; + break; } + + port = NULL; } - return NULL; + /* eDP panel not detected, restore panel power GPIO to previous + * state to avoid confusing the SOR for other output types. + */ + if (!port && panel == 0) + gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel); + + return port; } static struct nouveau_encoder * diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_display.c b/trunk/drivers/gpu/drm/nouveau/nouveau_display.c index e4188f24fc75..508b00a2ce0d 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_display.c @@ -225,15 +225,6 @@ nouveau_display_init(struct drm_device *dev) if (ret) return ret; - /* power on internal panel if it's not already. the init tables of - * some vbios default this to off for some reason, causing the - * panel to not work after resume - */ - if (gpio && gpio->get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff) == 0) { - gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); - msleep(300); - } - /* enable polling for external displays */ drm_kms_helper_poll_enable(dev); diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_drm.c b/trunk/drivers/gpu/drm/nouveau/nouveau_drm.c index 180a45e3b525..8b090f1eb51d 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -84,11 +84,16 @@ nouveau_cli_create(struct pci_dev *pdev, const char *name, struct nouveau_cli *cli; int ret; + *pcli = NULL; ret = nouveau_client_create_(name, nouveau_name(pdev), nouveau_config, nouveau_debug, size, pcli); cli = *pcli; - if (ret) + if (ret) { + if (cli) + nouveau_client_destroy(&cli->base); + *pcli = NULL; return ret; + } mutex_init(&cli->mutex); return 0; diff --git a/trunk/drivers/gpu/drm/nouveau/nouveau_fence.h b/trunk/drivers/gpu/drm/nouveau/nouveau_fence.h index bedafd1c9539..cdb83acdffe2 100644 --- a/trunk/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/trunk/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -60,6 +60,7 @@ u32 nv10_fence_read(struct nouveau_channel *); void nv10_fence_context_del(struct nouveau_channel *); void nv10_fence_destroy(struct nouveau_drm *); int nv10_fence_create(struct nouveau_drm *); +void nv17_fence_resume(struct nouveau_drm *drm); int nv50_fence_create(struct nouveau_drm *); int nv84_fence_create(struct nouveau_drm *); diff --git a/trunk/drivers/gpu/drm/nouveau/nv04_dfp.c b/trunk/drivers/gpu/drm/nouveau/nv04_dfp.c index 184cdf806761..39ffc07f906b 100644 --- a/trunk/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/trunk/drivers/gpu/drm/nouveau/nv04_dfp.c @@ -505,7 +505,7 @@ static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode) static inline bool is_powersaving_dpms(int mode) { - return (mode != DRM_MODE_DPMS_ON); + return mode != DRM_MODE_DPMS_ON && mode != NV_DPMS_CLEARED; } static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode) diff --git a/trunk/drivers/gpu/drm/nouveau/nv10_fence.c b/trunk/drivers/gpu/drm/nouveau/nv10_fence.c index 7ae7f97a6d4d..03017f24d593 100644 --- a/trunk/drivers/gpu/drm/nouveau/nv10_fence.c +++ b/trunk/drivers/gpu/drm/nouveau/nv10_fence.c @@ -162,6 +162,13 @@ nv10_fence_destroy(struct nouveau_drm *drm) kfree(priv); } +void nv17_fence_resume(struct nouveau_drm *drm) +{ + struct nv10_fence_priv *priv = drm->fence; + + nouveau_bo_wr32(priv->bo, 0, priv->sequence); +} + int nv10_fence_create(struct nouveau_drm *drm) { @@ -197,6 +204,7 @@ nv10_fence_create(struct nouveau_drm *drm) if (ret == 0) { nouveau_bo_wr32(priv->bo, 0x000, 0x00000000); priv->base.sync = nv17_fence_sync; + priv->base.resume = nv17_fence_resume; } } diff --git a/trunk/drivers/gpu/drm/nouveau/nv50_fence.c b/trunk/drivers/gpu/drm/nouveau/nv50_fence.c index c20f2727ea0b..d889f3ac0d41 100644 --- a/trunk/drivers/gpu/drm/nouveau/nv50_fence.c +++ b/trunk/drivers/gpu/drm/nouveau/nv50_fence.c @@ -122,6 +122,7 @@ nv50_fence_create(struct nouveau_drm *drm) if (ret == 0) { nouveau_bo_wr32(priv->bo, 0x000, 0x00000000); priv->base.sync = nv17_fence_sync; + priv->base.resume = nv17_fence_resume; } if (ret) diff --git a/trunk/drivers/gpu/drm/radeon/evergreen.c b/trunk/drivers/gpu/drm/radeon/evergreen.c index 061fa0a28900..4d0e60adbc6d 100644 --- a/trunk/drivers/gpu/drm/radeon/evergreen.c +++ b/trunk/drivers/gpu/drm/radeon/evergreen.c @@ -2401,6 +2401,12 @@ static int evergreen_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask) { struct evergreen_mc_save save; + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + reset_mask &= ~(RADEON_RESET_GFX | RADEON_RESET_COMPUTE); + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + reset_mask &= ~RADEON_RESET_DMA; + if (reset_mask == 0) return 0; diff --git a/trunk/drivers/gpu/drm/radeon/ni.c b/trunk/drivers/gpu/drm/radeon/ni.c index 896f1cbc58a5..59acabb45c9b 100644 --- a/trunk/drivers/gpu/drm/radeon/ni.c +++ b/trunk/drivers/gpu/drm/radeon/ni.c @@ -1409,6 +1409,12 @@ static int cayman_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask) { struct evergreen_mc_save save; + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + reset_mask &= ~(RADEON_RESET_GFX | RADEON_RESET_COMPUTE); + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + reset_mask &= ~RADEON_RESET_DMA; + if (reset_mask == 0) return 0; diff --git a/trunk/drivers/gpu/drm/radeon/r600.c b/trunk/drivers/gpu/drm/radeon/r600.c index 537e259b3837..3cb9d6089373 100644 --- a/trunk/drivers/gpu/drm/radeon/r600.c +++ b/trunk/drivers/gpu/drm/radeon/r600.c @@ -1378,6 +1378,12 @@ static int r600_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask) { struct rv515_mc_save save; + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + reset_mask &= ~(RADEON_RESET_GFX | RADEON_RESET_COMPUTE); + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + reset_mask &= ~RADEON_RESET_DMA; + if (reset_mask == 0) return 0; diff --git a/trunk/drivers/gpu/drm/radeon/r600_cs.c b/trunk/drivers/gpu/drm/radeon/r600_cs.c index 03191a56eb44..69ec24ab8d63 100644 --- a/trunk/drivers/gpu/drm/radeon/r600_cs.c +++ b/trunk/drivers/gpu/drm/radeon/r600_cs.c @@ -2476,8 +2476,10 @@ static void r600_cs_parser_fini(struct radeon_cs_parser *parser, int error) kfree(parser->relocs); for (i = 0; i < parser->nchunks; i++) { kfree(parser->chunks[i].kdata); - kfree(parser->chunks[i].kpage[0]); - kfree(parser->chunks[i].kpage[1]); + if (parser->rdev && (parser->rdev->flags & RADEON_IS_AGP)) { + kfree(parser->chunks[i].kpage[0]); + kfree(parser->chunks[i].kpage[1]); + } } kfree(parser->chunks); kfree(parser->chunks_array); @@ -2561,16 +2563,16 @@ int r600_dma_cs_next_reloc(struct radeon_cs_parser *p, struct radeon_cs_chunk *relocs_chunk; unsigned idx; + *cs_reloc = NULL; if (p->chunk_relocs_idx == -1) { DRM_ERROR("No relocation chunk !\n"); return -EINVAL; } - *cs_reloc = NULL; relocs_chunk = &p->chunks[p->chunk_relocs_idx]; idx = p->dma_reloc_idx; - if (idx >= relocs_chunk->length_dw) { + if (idx >= p->nrelocs) { DRM_ERROR("Relocs at %d after relocations chunk end %d !\n", - idx, relocs_chunk->length_dw); + idx, p->nrelocs); return -EINVAL; } *cs_reloc = p->relocs_ptr[idx]; diff --git a/trunk/drivers/gpu/drm/radeon/radeon.h b/trunk/drivers/gpu/drm/radeon/radeon.h index 34e52304a525..a08f657329a0 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon.h +++ b/trunk/drivers/gpu/drm/radeon/radeon.h @@ -324,7 +324,6 @@ struct radeon_bo { struct list_head list; /* Protected by tbo.reserved */ u32 placements[3]; - u32 busy_placements[3]; struct ttm_placement placement; struct ttm_buffer_object tbo; struct ttm_bo_kmap_obj kmap; @@ -654,6 +653,8 @@ struct radeon_ring { u32 ptr_reg_mask; u32 nop; u32 idx; + u64 last_semaphore_signal_addr; + u64 last_semaphore_wait_addr; }; /* diff --git a/trunk/drivers/gpu/drm/radeon/radeon_cs.c b/trunk/drivers/gpu/drm/radeon/radeon_cs.c index 396baba0141a..469661fd1903 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_cs.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_cs.c @@ -279,13 +279,13 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) p->chunks[p->chunk_ib_idx].length_dw); return -EINVAL; } - if ((p->rdev->flags & RADEON_IS_AGP)) { + if (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) { p->chunks[p->chunk_ib_idx].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL); p->chunks[p->chunk_ib_idx].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL); if (p->chunks[p->chunk_ib_idx].kpage[0] == NULL || p->chunks[p->chunk_ib_idx].kpage[1] == NULL) { - kfree(p->chunks[i].kpage[0]); - kfree(p->chunks[i].kpage[1]); + kfree(p->chunks[p->chunk_ib_idx].kpage[0]); + kfree(p->chunks[p->chunk_ib_idx].kpage[1]); return -ENOMEM; } } @@ -583,7 +583,8 @@ static int radeon_cs_update_pages(struct radeon_cs_parser *p, int pg_idx) struct radeon_cs_chunk *ibc = &p->chunks[p->chunk_ib_idx]; int i; int size = PAGE_SIZE; - bool copy1 = (p->rdev->flags & RADEON_IS_AGP) ? false : true; + bool copy1 = (p->rdev && (p->rdev->flags & RADEON_IS_AGP)) ? + false : true; for (i = ibc->last_copied_page + 1; i < pg_idx; i++) { if (DRM_COPY_FROM_USER(p->ib.ptr + (i * (PAGE_SIZE/4)), diff --git a/trunk/drivers/gpu/drm/radeon/radeon_drv.c b/trunk/drivers/gpu/drm/radeon/radeon_drv.c index dff6cf77f953..d9bf96ee299a 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_drv.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_drv.c @@ -69,9 +69,10 @@ * 2.26.0 - r600-eg: fix htile size computation * 2.27.0 - r600-SI: Add CS ioctl support for async DMA * 2.28.0 - r600-eg: Add MEM_WRITE packet support + * 2.29.0 - R500 FP16 color clear registers */ #define KMS_DRIVER_MAJOR 2 -#define KMS_DRIVER_MINOR 28 +#define KMS_DRIVER_MINOR 29 #define KMS_DRIVER_PATCHLEVEL 0 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); int radeon_driver_unload_kms(struct drm_device *dev); diff --git a/trunk/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/trunk/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index f5ba2241dacc..62cd512f5c8d 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_legacy_encoders.c @@ -640,6 +640,14 @@ static enum drm_connector_status radeon_legacy_primary_dac_detect(struct drm_enc enum drm_connector_status found = connector_status_disconnected; bool color = true; + /* just don't bother on RN50 those chip are often connected to remoting + * console hw and often we get failure to load detect those. So to make + * everyone happy report the encoder as always connected. + */ + if (ASIC_IS_RN50(rdev)) { + return connector_status_connected; + } + /* save the regs we need */ vclk_ecp_cntl = RREG32_PLL(RADEON_VCLK_ECP_CNTL); crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); diff --git a/trunk/drivers/gpu/drm/radeon/radeon_object.c b/trunk/drivers/gpu/drm/radeon/radeon_object.c index 883c95d8d90f..d3aface2d12d 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_object.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_object.c @@ -84,6 +84,7 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain) rbo->placement.fpfn = 0; rbo->placement.lpfn = 0; rbo->placement.placement = rbo->placements; + rbo->placement.busy_placement = rbo->placements; if (domain & RADEON_GEM_DOMAIN_VRAM) rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; @@ -104,14 +105,6 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain) if (!c) rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; rbo->placement.num_placement = c; - - c = 0; - rbo->placement.busy_placement = rbo->busy_placements; - if (rbo->rdev->flags & RADEON_IS_AGP) { - rbo->busy_placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_TT; - } else { - rbo->busy_placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT; - } rbo->placement.num_busy_placement = c; } @@ -357,6 +350,7 @@ int radeon_bo_list_validate(struct list_head *head) { struct radeon_bo_list *lobj; struct radeon_bo *bo; + u32 domain; int r; r = ttm_eu_reserve_buffers(head); @@ -366,9 +360,17 @@ int radeon_bo_list_validate(struct list_head *head) list_for_each_entry(lobj, head, tv.head) { bo = lobj->bo; if (!bo->pin_count) { + domain = lobj->wdomain ? lobj->wdomain : lobj->rdomain; + + retry: + radeon_ttm_placement_from_domain(bo, domain); r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); if (unlikely(r)) { + if (r != -ERESTARTSYS && domain == RADEON_GEM_DOMAIN_VRAM) { + domain |= RADEON_GEM_DOMAIN_GTT; + goto retry; + } return r; } } diff --git a/trunk/drivers/gpu/drm/radeon/radeon_ring.c b/trunk/drivers/gpu/drm/radeon/radeon_ring.c index 141f2b6a9cf2..2430d80b1871 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_ring.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_ring.c @@ -784,6 +784,8 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data) } seq_printf(m, "driver's copy of the wptr: 0x%08x [%5d]\n", ring->wptr, ring->wptr); seq_printf(m, "driver's copy of the rptr: 0x%08x [%5d]\n", ring->rptr, ring->rptr); + seq_printf(m, "last semaphore signal addr : 0x%016llx\n", ring->last_semaphore_signal_addr); + seq_printf(m, "last semaphore wait addr : 0x%016llx\n", ring->last_semaphore_wait_addr); seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw); seq_printf(m, "%u dwords in ring\n", count); /* print 8 dw before current rptr as often it's the last executed diff --git a/trunk/drivers/gpu/drm/radeon/radeon_semaphore.c b/trunk/drivers/gpu/drm/radeon/radeon_semaphore.c index 97f3ece81cd2..8dcc20f53d73 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_semaphore.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_semaphore.c @@ -95,6 +95,10 @@ int radeon_semaphore_sync_rings(struct radeon_device *rdev, /* we assume caller has already allocated space on waiters ring */ radeon_semaphore_emit_wait(rdev, waiter, semaphore); + /* for debugging lockup only, used by sysfs debug files */ + rdev->ring[signaler].last_semaphore_signal_addr = semaphore->gpu_addr; + rdev->ring[waiter].last_semaphore_wait_addr = semaphore->gpu_addr; + return 0; } diff --git a/trunk/drivers/gpu/drm/radeon/reg_srcs/rv515 b/trunk/drivers/gpu/drm/radeon/reg_srcs/rv515 index 911a8fbd32bb..78d5e99d759d 100644 --- a/trunk/drivers/gpu/drm/radeon/reg_srcs/rv515 +++ b/trunk/drivers/gpu/drm/radeon/reg_srcs/rv515 @@ -324,6 +324,8 @@ rv515 0x6d40 0x46AC US_OUT_FMT_2 0x46B0 US_OUT_FMT_3 0x46B4 US_W_FMT +0x46C0 RB3D_COLOR_CLEAR_VALUE_AR +0x46C4 RB3D_COLOR_CLEAR_VALUE_GB 0x4BC0 FG_FOG_BLEND 0x4BC4 FG_FOG_FACTOR 0x4BC8 FG_FOG_COLOR_R diff --git a/trunk/drivers/gpu/drm/radeon/si.c b/trunk/drivers/gpu/drm/radeon/si.c index 3240a3d64f30..ae8b48205a6c 100644 --- a/trunk/drivers/gpu/drm/radeon/si.c +++ b/trunk/drivers/gpu/drm/radeon/si.c @@ -2215,6 +2215,12 @@ static int si_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask) { struct evergreen_mc_save save; + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + reset_mask &= ~(RADEON_RESET_GFX | RADEON_RESET_COMPUTE); + + if (RREG32(DMA_STATUS_REG) & DMA_IDLE) + reset_mask &= ~RADEON_RESET_DMA; + if (reset_mask == 0) return 0; diff --git a/trunk/drivers/gpu/drm/ttm/ttm_bo.c b/trunk/drivers/gpu/drm/ttm/ttm_bo.c index 33d20be87db5..52b20b12c83a 100644 --- a/trunk/drivers/gpu/drm/ttm/ttm_bo.c +++ b/trunk/drivers/gpu/drm/ttm/ttm_bo.c @@ -434,6 +434,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, bo->mem = tmp_mem; bdev->driver->move_notify(bo, mem); bo->mem = *mem; + *mem = tmp_mem; } goto out_err; diff --git a/trunk/drivers/gpu/drm/ttm/ttm_bo_util.c b/trunk/drivers/gpu/drm/ttm/ttm_bo_util.c index d73d6e3e17b2..44420fca7dfa 100644 --- a/trunk/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/trunk/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -344,8 +344,12 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, if (ttm->state == tt_unpopulated) { ret = ttm->bdev->driver->ttm_tt_populate(ttm); - if (ret) + if (ret) { + /* if we fail here don't nuke the mm node + * as the bo still owns it */ + old_copy.mm_node = NULL; goto out1; + } } add = 0; @@ -371,8 +375,11 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, prot); } else ret = ttm_copy_io_page(new_iomap, old_iomap, page); - if (ret) + if (ret) { + /* failing here, means keep old copy as-is */ + old_copy.mm_node = NULL; goto out1; + } } mb(); out2: diff --git a/trunk/drivers/gpu/drm/udl/udl_connector.c b/trunk/drivers/gpu/drm/udl/udl_connector.c index 512f44add89f..fe5cdbcf2636 100644 --- a/trunk/drivers/gpu/drm/udl/udl_connector.c +++ b/trunk/drivers/gpu/drm/udl/udl_connector.c @@ -22,13 +22,17 @@ static u8 *udl_get_edid(struct udl_device *udl) { u8 *block; - char rbuf[3]; + char *rbuf; int ret, i; block = kmalloc(EDID_LENGTH, GFP_KERNEL); if (block == NULL) return NULL; + rbuf = kmalloc(2, GFP_KERNEL); + if (rbuf == NULL) + goto error; + for (i = 0; i < EDID_LENGTH; i++) { ret = usb_control_msg(udl->ddev->usbdev, usb_rcvctrlpipe(udl->ddev->usbdev, 0), (0x02), @@ -36,16 +40,17 @@ static u8 *udl_get_edid(struct udl_device *udl) HZ); if (ret < 1) { DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); - i--; goto error; } block[i] = rbuf[1]; } + kfree(rbuf); return block; error: kfree(block); + kfree(rbuf); return NULL; } @@ -57,6 +62,14 @@ static int udl_get_modes(struct drm_connector *connector) edid = (struct edid *)udl_get_edid(udl); + /* + * We only read the main block, but if the monitor reports extension + * blocks then the drm edid code expects them to be present, so patch + * the extension count to 0. + */ + edid->checksum += edid->extensions; + edid->extensions = 0; + drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); kfree(edid); diff --git a/trunk/drivers/hv/hv_balloon.c b/trunk/drivers/hv/hv_balloon.c index f6c0011a0337..dd289fd179ca 100644 --- a/trunk/drivers/hv/hv_balloon.c +++ b/trunk/drivers/hv/hv_balloon.c @@ -403,7 +403,7 @@ struct dm_info_header { */ struct dm_info_msg { - struct dm_info_header header; + struct dm_header hdr; __u32 reserved; __u32 info_size; __u8 info[]; @@ -503,13 +503,17 @@ static void hot_add_req(struct hv_dynmem_device *dm, struct dm_hot_add *msg) static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) { - switch (msg->header.type) { + struct dm_info_header *info_hdr; + + info_hdr = (struct dm_info_header *)msg->info; + + switch (info_hdr->type) { case INFO_TYPE_MAX_PAGE_CNT: pr_info("Received INFO_TYPE_MAX_PAGE_CNT\n"); - pr_info("Data Size is %d\n", msg->header.data_size); + pr_info("Data Size is %d\n", info_hdr->data_size); break; default: - pr_info("Received Unknown type: %d\n", msg->header.type); + pr_info("Received Unknown type: %d\n", info_hdr->type); } } @@ -879,7 +883,7 @@ static int balloon_probe(struct hv_device *dev, balloon_onchannelcallback, dev); if (ret) - return ret; + goto probe_error0; dm_device.dev = dev; dm_device.state = DM_INITIALIZING; @@ -891,7 +895,7 @@ static int balloon_probe(struct hv_device *dev, kthread_run(dm_thread_func, &dm_device, "hv_balloon"); if (IS_ERR(dm_device.thread)) { ret = PTR_ERR(dm_device.thread); - goto probe_error0; + goto probe_error1; } hv_set_drvdata(dev, &dm_device); @@ -914,12 +918,12 @@ static int balloon_probe(struct hv_device *dev, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret) - goto probe_error1; + goto probe_error2; t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; - goto probe_error1; + goto probe_error2; } /* @@ -928,7 +932,7 @@ static int balloon_probe(struct hv_device *dev, */ if (dm_device.state == DM_INIT_ERROR) { ret = -ETIMEDOUT; - goto probe_error1; + goto probe_error2; } /* * Now submit our capabilities to the host. @@ -961,12 +965,12 @@ static int balloon_probe(struct hv_device *dev, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret) - goto probe_error1; + goto probe_error2; t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; - goto probe_error1; + goto probe_error2; } /* @@ -975,18 +979,20 @@ static int balloon_probe(struct hv_device *dev, */ if (dm_device.state == DM_INIT_ERROR) { ret = -ETIMEDOUT; - goto probe_error1; + goto probe_error2; } dm_device.state = DM_INITIALIZED; return 0; -probe_error1: +probe_error2: kthread_stop(dm_device.thread); -probe_error0: +probe_error1: vmbus_close(dev->channel); +probe_error0: + kfree(send_buffer); return ret; } @@ -999,6 +1005,7 @@ static int balloon_remove(struct hv_device *dev) vmbus_close(dev->channel); kthread_stop(dm->thread); + kfree(send_buffer); return 0; } diff --git a/trunk/drivers/hwmon/vexpress.c b/trunk/drivers/hwmon/vexpress.c index 86d7f6d858b1..d867e6bb2be1 100644 --- a/trunk/drivers/hwmon/vexpress.c +++ b/trunk/drivers/hwmon/vexpress.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/trunk/drivers/i2c/busses/i2c-designware-core.c b/trunk/drivers/i2c/busses/i2c-designware-core.c index cbba7db9ad59..f5258c205de5 100644 --- a/trunk/drivers/i2c/busses/i2c-designware-core.c +++ b/trunk/drivers/i2c/busses/i2c-designware-core.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "i2c-designware-core.h" /* @@ -725,3 +726,6 @@ u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev) return dw_readl(dev, DW_IC_COMP_PARAM_1); } EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param); + +MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/i2c/busses/i2c-mxs.c b/trunk/drivers/i2c/busses/i2c-mxs.c index 1b1a936eccc9..d6abaf2cf2e3 100644 --- a/trunk/drivers/i2c/busses/i2c-mxs.c +++ b/trunk/drivers/i2c/busses/i2c-mxs.c @@ -127,7 +127,7 @@ struct mxs_i2c_dev { struct device *dev; void __iomem *regs; struct completion cmd_complete; - u32 cmd_err; + int cmd_err; struct i2c_adapter adapter; const struct mxs_i2c_speed_config *speed; @@ -316,7 +316,7 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, if (msg->len == 0) return -EINVAL; - init_completion(&i2c->cmd_complete); + INIT_COMPLETION(i2c->cmd_complete); i2c->cmd_err = 0; ret = mxs_i2c_dma_setup_xfer(adap, msg, flags); @@ -473,6 +473,8 @@ static int mxs_i2c_probe(struct platform_device *pdev) i2c->dev = dev; i2c->speed = &mxs_i2c_95kHz_config; + init_completion(&i2c->cmd_complete); + if (dev->of_node) { err = mxs_i2c_get_ofdata(i2c); if (err) diff --git a/trunk/drivers/i2c/busses/i2c-omap.c b/trunk/drivers/i2c/busses/i2c-omap.c index 20d41bfa7c19..4cc2f0528c88 100644 --- a/trunk/drivers/i2c/busses/i2c-omap.c +++ b/trunk/drivers/i2c/busses/i2c-omap.c @@ -803,7 +803,7 @@ static int errata_omap3_i462(struct omap_i2c_dev *dev) if (stat & OMAP_I2C_STAT_AL) { dev_err(dev->dev, "Arbitration lost\n"); dev->cmd_err |= OMAP_I2C_STAT_AL; - omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK); + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL); } return -EIO; @@ -963,7 +963,7 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) i2c_omap_errata_i207(dev, stat); omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR); - break; + continue; } if (stat & OMAP_I2C_STAT_RRDY) { @@ -989,7 +989,7 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) break; omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XDR); - break; + continue; } if (stat & OMAP_I2C_STAT_XRDY) { diff --git a/trunk/drivers/i2c/busses/i2c-sirf.c b/trunk/drivers/i2c/busses/i2c-sirf.c index 3f1818b87974..e03381aee34f 100644 --- a/trunk/drivers/i2c/busses/i2c-sirf.c +++ b/trunk/drivers/i2c/busses/i2c-sirf.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -328,6 +329,7 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev) adap->algo = &i2c_sirfsoc_algo; adap->algo_data = siic; + adap->dev.of_node = pdev->dev.of_node; adap->dev.parent = &pdev->dev; adap->nr = pdev->id; @@ -371,6 +373,8 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev) clk_disable(clk); + of_i2c_register_devices(adap); + dev_info(&pdev->dev, " I2C adapter ready to operate\n"); return 0; diff --git a/trunk/drivers/i2c/muxes/i2c-mux-pinctrl.c b/trunk/drivers/i2c/muxes/i2c-mux-pinctrl.c index 1e44d04d1b22..a43c0ce5e3d8 100644 --- a/trunk/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/trunk/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -167,7 +167,7 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev) } mux->busses = devm_kzalloc(&pdev->dev, - sizeof(mux->busses) * mux->pdata->bus_count, + sizeof(*mux->busses) * mux->pdata->bus_count, GFP_KERNEL); if (!mux->busses) { dev_err(&pdev->dev, "Cannot allocate busses\n"); diff --git a/trunk/drivers/idle/intel_idle.c b/trunk/drivers/idle/intel_idle.c index 4ba384f1ab54..2df9414a72f7 100644 --- a/trunk/drivers/idle/intel_idle.c +++ b/trunk/drivers/idle/intel_idle.c @@ -448,8 +448,6 @@ static int intel_idle_probe(void) else on_each_cpu(__setup_broadcast_timer, (void *)true, 1); - register_cpu_notifier(&cpu_hotplug_notifier); - pr_debug(PREFIX "v" INTEL_IDLE_VERSION " model 0x%X\n", boot_cpu_data.x86_model); @@ -612,6 +610,7 @@ static int __init intel_idle_init(void) return retval; } } + register_cpu_notifier(&cpu_hotplug_notifier); return 0; } diff --git a/trunk/drivers/iio/accel/Kconfig b/trunk/drivers/iio/accel/Kconfig index fe4bcd7c5b12..05e996fafc9d 100644 --- a/trunk/drivers/iio/accel/Kconfig +++ b/trunk/drivers/iio/accel/Kconfig @@ -8,6 +8,7 @@ config HID_SENSOR_ACCEL_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID Accelerometers 3D" help Say yes here to build support for the HID SENSOR diff --git a/trunk/drivers/iio/adc/ad7266.c b/trunk/drivers/iio/adc/ad7266.c index 4a5f639bc684..bbad9b94cd75 100644 --- a/trunk/drivers/iio/adc/ad7266.c +++ b/trunk/drivers/iio/adc/ad7266.c @@ -411,7 +411,11 @@ static int ad7266_probe(struct spi_device *spi) if (ret) goto error_put_reg; - st->vref_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + st->vref_uv = ret; } else { /* Use internal reference */ st->vref_uv = 2500000; diff --git a/trunk/drivers/iio/adc/at91_adc.c b/trunk/drivers/iio/adc/at91_adc.c index 04b013561f0f..a526c0e3aaa8 100644 --- a/trunk/drivers/iio/adc/at91_adc.c +++ b/trunk/drivers/iio/adc/at91_adc.c @@ -80,7 +80,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - iio_push_to_buffers(indio_dev, (u8 *)st->buffer); + iio_push_to_buffers(idev, (u8 *)st->buffer); iio_trigger_notify_done(idev->trig); diff --git a/trunk/drivers/iio/adc/max1363.c b/trunk/drivers/iio/adc/max1363.c index b5669be6f396..03b25b3dc71e 100644 --- a/trunk/drivers/iio/adc/max1363.c +++ b/trunk/drivers/iio/adc/max1363.c @@ -1605,19 +1605,20 @@ static int max1363_probe(struct i2c_client *client, return 0; error_free_irq: - free_irq(st->client->irq, indio_dev); + if (client->irq) + free_irq(st->client->irq, indio_dev); error_uninit_buffer: iio_buffer_unregister(indio_dev); error_cleanup_buffer: max1363_buffer_cleanup(indio_dev); error_free_available_scan_masks: kfree(indio_dev->available_scan_masks); -error_unregister_map: - iio_map_array_unregister(indio_dev, client->dev.platform_data); error_disable_reg: regulator_disable(st->reg); error_put_reg: regulator_put(st->reg); +error_unregister_map: + iio_map_array_unregister(indio_dev, client->dev.platform_data); error_free_device: iio_device_free(indio_dev); error_out: @@ -1635,10 +1636,8 @@ static int max1363_remove(struct i2c_client *client) iio_buffer_unregister(indio_dev); max1363_buffer_cleanup(indio_dev); kfree(indio_dev->available_scan_masks); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } + regulator_disable(st->reg); + regulator_put(st->reg); iio_map_array_unregister(indio_dev, client->dev.platform_data); iio_device_free(indio_dev); diff --git a/trunk/drivers/iio/common/hid-sensors/Kconfig b/trunk/drivers/iio/common/hid-sensors/Kconfig index ae10778da7aa..1178121b55b0 100644 --- a/trunk/drivers/iio/common/hid-sensors/Kconfig +++ b/trunk/drivers/iio/common/hid-sensors/Kconfig @@ -6,7 +6,7 @@ menu "Hid Sensor IIO Common" config HID_SENSOR_IIO_COMMON tristate "Common modules for all HID Sensor IIO drivers" depends on HID_SENSOR_HUB - select IIO_TRIGGER if IIO_BUFFER + select HID_SENSOR_IIO_TRIGGER if IIO_BUFFER help Say yes here to build support for HID sensor to use HID sensor common processing for attributes and IIO triggers. @@ -14,6 +14,17 @@ config HID_SENSOR_IIO_COMMON HID sensor drivers, this module contains processing for those attributes. +config HID_SENSOR_IIO_TRIGGER + tristate "Common module (trigger) for all HID Sensor IIO drivers" + depends on HID_SENSOR_HUB && HID_SENSOR_IIO_COMMON + select IIO_TRIGGER + help + Say yes here to build trigger support for HID sensors. + Triggers will be send if all requested attributes were read. + + If this driver is compiled as a module, it will be named + hid-sensor-trigger. + config HID_SENSOR_ENUM_BASE_QUIRKS bool "ENUM base quirks for HID Sensor IIO drivers" depends on HID_SENSOR_IIO_COMMON diff --git a/trunk/drivers/iio/common/hid-sensors/Makefile b/trunk/drivers/iio/common/hid-sensors/Makefile index 1f463e00c242..22e7c5a82325 100644 --- a/trunk/drivers/iio/common/hid-sensors/Makefile +++ b/trunk/drivers/iio/common/hid-sensors/Makefile @@ -3,4 +3,5 @@ # obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o -hid-sensor-iio-common-y := hid-sensor-attributes.o hid-sensor-trigger.o +obj-$(CONFIG_HID_SENSOR_IIO_TRIGGER) += hid-sensor-trigger.o +hid-sensor-iio-common-y := hid-sensor-attributes.o diff --git a/trunk/drivers/iio/dac/ad5380.c b/trunk/drivers/iio/dac/ad5380.c index 6c7898c765d9..483fc379a2da 100644 --- a/trunk/drivers/iio/dac/ad5380.c +++ b/trunk/drivers/iio/dac/ad5380.c @@ -406,7 +406,11 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap, goto error_free_reg; } - st->vref = regulator_get_voltage(st->vref_reg); + ret = regulator_get_voltage(st->vref_reg); + if (ret < 0) + goto error_disable_reg; + + st->vref = ret; } else { st->vref = st->chip_info->int_vref; ctrl |= AD5380_CTRL_INT_VREF_EN; diff --git a/trunk/drivers/iio/dac/ad5446.c b/trunk/drivers/iio/dac/ad5446.c index 29f653dab2f7..f5583aedfb59 100644 --- a/trunk/drivers/iio/dac/ad5446.c +++ b/trunk/drivers/iio/dac/ad5446.c @@ -226,7 +226,11 @@ static int ad5446_probe(struct device *dev, const char *name, if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(reg); + ret = regulator_get_voltage(reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } indio_dev = iio_device_alloc(sizeof(*st)); diff --git a/trunk/drivers/iio/dac/ad5504.c b/trunk/drivers/iio/dac/ad5504.c index b2a31a0468ed..0661829f2773 100644 --- a/trunk/drivers/iio/dac/ad5504.c +++ b/trunk/drivers/iio/dac/ad5504.c @@ -296,7 +296,11 @@ static int ad5504_probe(struct spi_device *spi) if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(reg); + ret = regulator_get_voltage(reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } spi_set_drvdata(spi, indio_dev); diff --git a/trunk/drivers/iio/dac/ad5624r_spi.c b/trunk/drivers/iio/dac/ad5624r_spi.c index e9947969f9fe..f6e116627b71 100644 --- a/trunk/drivers/iio/dac/ad5624r_spi.c +++ b/trunk/drivers/iio/dac/ad5624r_spi.c @@ -238,7 +238,11 @@ static int ad5624r_probe(struct spi_device *spi) if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } spi_set_drvdata(spi, indio_dev); diff --git a/trunk/drivers/iio/dac/ad5686.c b/trunk/drivers/iio/dac/ad5686.c index 36e51382ae52..ca9609d7a15c 100644 --- a/trunk/drivers/iio/dac/ad5686.c +++ b/trunk/drivers/iio/dac/ad5686.c @@ -332,7 +332,11 @@ static int ad5686_probe(struct spi_device *spi) if (ret) goto error_put_reg; - voltage_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + voltage_uv = ret; } st->chip_info = diff --git a/trunk/drivers/iio/dac/ad5791.c b/trunk/drivers/iio/dac/ad5791.c index c84180f23139..6407b5407ddd 100644 --- a/trunk/drivers/iio/dac/ad5791.c +++ b/trunk/drivers/iio/dac/ad5791.c @@ -365,7 +365,11 @@ static int ad5791_probe(struct spi_device *spi) if (ret) goto error_put_reg_pos; - pos_voltage_uv = regulator_get_voltage(st->reg_vdd); + ret = regulator_get_voltage(st->reg_vdd); + if (ret < 0) + goto error_disable_reg_pos; + + pos_voltage_uv = ret; } st->reg_vss = regulator_get(&spi->dev, "vss"); @@ -374,7 +378,11 @@ static int ad5791_probe(struct spi_device *spi) if (ret) goto error_put_reg_neg; - neg_voltage_uv = regulator_get_voltage(st->reg_vss); + ret = regulator_get_voltage(st->reg_vss); + if (ret < 0) + goto error_disable_reg_neg; + + neg_voltage_uv = ret; } st->pwr_down = true; @@ -428,6 +436,7 @@ static int ad5791_probe(struct spi_device *spi) if (!IS_ERR(st->reg_vss)) regulator_put(st->reg_vss); +error_disable_reg_pos: if (!IS_ERR(st->reg_vdd)) regulator_disable(st->reg_vdd); error_put_reg_pos: diff --git a/trunk/drivers/iio/frequency/adf4350.c b/trunk/drivers/iio/frequency/adf4350.c index e5033b4cfba0..a884252ac66b 100644 --- a/trunk/drivers/iio/frequency/adf4350.c +++ b/trunk/drivers/iio/frequency/adf4350.c @@ -173,7 +173,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt); } while (r_cnt == 0); - tmp = freq * (u64)st->r1_mod + (st->fpfd > 1); + tmp = freq * (u64)st->r1_mod + (st->fpfd >> 1); do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */ st->r0_fract = do_div(tmp, st->r1_mod); st->r0_int = tmp; diff --git a/trunk/drivers/iio/gyro/Kconfig b/trunk/drivers/iio/gyro/Kconfig index 48ed1483ff27..96b68f63a902 100644 --- a/trunk/drivers/iio/gyro/Kconfig +++ b/trunk/drivers/iio/gyro/Kconfig @@ -17,6 +17,7 @@ config HID_SENSOR_GYRO_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID Gyroscope 3D" help Say yes here to build support for the HID SENSOR diff --git a/trunk/drivers/iio/light/Kconfig b/trunk/drivers/iio/light/Kconfig index 1763c9bcb98a..dbf80abc834f 100644 --- a/trunk/drivers/iio/light/Kconfig +++ b/trunk/drivers/iio/light/Kconfig @@ -47,6 +47,7 @@ config HID_SENSOR_ALS select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID ALS" help Say yes here to build support for the HID SENSOR diff --git a/trunk/drivers/iio/magnetometer/Kconfig b/trunk/drivers/iio/magnetometer/Kconfig index c1f0cdd57037..ff11d68225cf 100644 --- a/trunk/drivers/iio/magnetometer/Kconfig +++ b/trunk/drivers/iio/magnetometer/Kconfig @@ -8,6 +8,7 @@ config HID_SENSOR_MAGNETOMETER_3D select IIO_BUFFER select IIO_TRIGGERED_BUFFER select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER tristate "HID Magenetometer 3D" help Say yes here to build support for the HID SENSOR diff --git a/trunk/drivers/media/i2c/m5mols/m5mols_core.c b/trunk/drivers/media/i2c/m5mols/m5mols_core.c index 8a8d42fe2633..d4e7567b367c 100644 --- a/trunk/drivers/media/i2c/m5mols/m5mols_core.c +++ b/trunk/drivers/media/i2c/m5mols/m5mols_core.c @@ -556,7 +556,7 @@ static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, mutex_lock(&info->lock); format = __find_format(info, fh, fmt->which, info->res_type); - if (!format) + if (format) fmt->format = *format; else ret = -EINVAL; diff --git a/trunk/drivers/media/platform/coda.c b/trunk/drivers/media/platform/coda.c index 1cf8293c0fb0..4a980e029ca7 100644 --- a/trunk/drivers/media/platform/coda.c +++ b/trunk/drivers/media/platform/coda.c @@ -23,8 +23,8 @@ #include #include #include +#include -#include #include #include #include diff --git a/trunk/drivers/media/platform/omap3isp/ispvideo.c b/trunk/drivers/media/platform/omap3isp/ispvideo.c index e0d73a642186..8dac17511e61 100644 --- a/trunk/drivers/media/platform/omap3isp/ispvideo.c +++ b/trunk/drivers/media/platform/omap3isp/ispvideo.c @@ -35,9 +35,6 @@ #include #include #include -#include -#include -#include #include "ispvideo.h" #include "isp.h" diff --git a/trunk/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/trunk/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 4ab99f3a7b09..b4a68ecf0ca7 100644 --- a/trunk/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/trunk/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -593,7 +593,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) { struct media_entity *source, *sink; unsigned int flags = MEDIA_LNK_FL_ENABLED; - int i, ret; + int i, ret = 0; for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { struct fimc_lite *fimc = fmd->fimc_lite[i]; diff --git a/trunk/drivers/media/platform/s5p-mfc/s5p_mfc.c b/trunk/drivers/media/platform/s5p-mfc/s5p_mfc.c index 379f57433711..681bc6ba149d 100644 --- a/trunk/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/trunk/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -412,62 +412,48 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, } /* Error handling for interrupt */ -static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx, - unsigned int reason, unsigned int err) +static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev, + struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err) { - struct s5p_mfc_dev *dev; unsigned long flags; - /* If no context is available then all necessary - * processing has been done. */ - if (ctx == NULL) - return; - - dev = ctx->dev; mfc_err("Interrupt Error: %08x\n", err); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - wake_up_dev(dev, reason, err); - /* Error recovery is dependent on the state of context */ - switch (ctx->state) { - case MFCINST_INIT: - /* This error had to happen while acquireing instance */ - case MFCINST_GOT_INST: - /* This error had to happen while parsing the header */ - case MFCINST_HEAD_PARSED: - /* This error had to happen while setting dst buffers */ - case MFCINST_RETURN_INST: - /* This error had to happen while releasing instance */ - clear_work_bit(ctx); - wake_up_ctx(ctx, reason, err); - if (test_and_clear_bit(0, &dev->hw_lock) == 0) - BUG(); - s5p_mfc_clock_off(); - ctx->state = MFCINST_ERROR; - break; - case MFCINST_FINISHING: - case MFCINST_FINISHED: - case MFCINST_RUNNING: - /* It is higly probable that an error occured - * while decoding a frame */ - clear_work_bit(ctx); - ctx->state = MFCINST_ERROR; - /* Mark all dst buffers as having an error */ - spin_lock_irqsave(&dev->irqlock, flags); - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue, - &ctx->vq_dst); - /* Mark all src buffers as having an error */ - s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue, - &ctx->vq_src); - spin_unlock_irqrestore(&dev->irqlock, flags); - if (test_and_clear_bit(0, &dev->hw_lock) == 0) - BUG(); - s5p_mfc_clock_off(); - break; - default: - mfc_err("Encountered an error interrupt which had not been handled\n"); - break; + if (ctx != NULL) { + /* Error recovery is dependent on the state of context */ + switch (ctx->state) { + case MFCINST_RES_CHANGE_INIT: + case MFCINST_RES_CHANGE_FLUSH: + case MFCINST_RES_CHANGE_END: + case MFCINST_FINISHING: + case MFCINST_FINISHED: + case MFCINST_RUNNING: + /* It is higly probable that an error occured + * while decoding a frame */ + clear_work_bit(ctx); + ctx->state = MFCINST_ERROR; + /* Mark all dst buffers as having an error */ + spin_lock_irqsave(&dev->irqlock, flags); + s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, + &ctx->dst_queue, &ctx->vq_dst); + /* Mark all src buffers as having an error */ + s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, + &ctx->src_queue, &ctx->vq_src); + spin_unlock_irqrestore(&dev->irqlock, flags); + wake_up_ctx(ctx, reason, err); + break; + default: + clear_work_bit(ctx); + ctx->state = MFCINST_ERROR; + wake_up_ctx(ctx, reason, err); + break; + } } + if (test_and_clear_bit(0, &dev->hw_lock) == 0) + BUG(); + s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); + s5p_mfc_clock_off(); + wake_up_dev(dev, reason, err); return; } @@ -632,7 +618,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) dev->warn_start) s5p_mfc_handle_frame(ctx, reason, err); else - s5p_mfc_handle_error(ctx, reason, err); + s5p_mfc_handle_error(dev, ctx, reason, err); clear_bit(0, &dev->enter_suspend); break; diff --git a/trunk/drivers/media/usb/gspca/kinect.c b/trunk/drivers/media/usb/gspca/kinect.c index 40ad6687ee5d..3773a8a745df 100644 --- a/trunk/drivers/media/usb/gspca/kinect.c +++ b/trunk/drivers/media/usb/gspca/kinect.c @@ -381,6 +381,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x045e, 0x02ae)}, + {USB_DEVICE(0x045e, 0x02bf)}, {} }; diff --git a/trunk/drivers/media/usb/gspca/sonixb.c b/trunk/drivers/media/usb/gspca/sonixb.c index 70511d5f9538..1220340e7602 100644 --- a/trunk/drivers/media/usb/gspca/sonixb.c +++ b/trunk/drivers/media/usb/gspca/sonixb.c @@ -496,7 +496,7 @@ static void reg_w(struct gspca_dev *gspca_dev, } } -static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) +static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buf) { int retry = 60; @@ -504,16 +504,19 @@ static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) return; /* is i2c ready */ - reg_w(gspca_dev, 0x08, buffer, 8); + reg_w(gspca_dev, 0x08, buf, 8); while (retry--) { if (gspca_dev->usb_err < 0) return; - msleep(10); + msleep(1); reg_r(gspca_dev, 0x08); if (gspca_dev->usb_buf[0] & 0x04) { if (gspca_dev->usb_buf[0] & 0x08) { dev_err(gspca_dev->v4l2_dev.dev, - "i2c write error\n"); + "i2c error writing %02x %02x %02x %02x" + " %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); gspca_dev->usb_err = -EIO; } return; @@ -530,7 +533,7 @@ static void i2c_w_vector(struct gspca_dev *gspca_dev, for (;;) { if (gspca_dev->usb_err < 0) return; - reg_w(gspca_dev, 0x08, *buffer, 8); + i2c_w(gspca_dev, *buffer); len -= 8; if (len <= 0) break; diff --git a/trunk/drivers/media/usb/gspca/sonixj.c b/trunk/drivers/media/usb/gspca/sonixj.c index 5a86047b846f..36307a9028a9 100644 --- a/trunk/drivers/media/usb/gspca/sonixj.c +++ b/trunk/drivers/media/usb/gspca/sonixj.c @@ -1550,6 +1550,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) 0, gspca_dev->usb_buf, 8, 500); + msleep(2); if (ret < 0) { pr_err("i2c_w1 err %d\n", ret); gspca_dev->usb_err = ret; diff --git a/trunk/drivers/media/usb/uvc/uvc_ctrl.c b/trunk/drivers/media/usb/uvc/uvc_ctrl.c index 2bb7613ddebb..d5baab17a5ef 100644 --- a/trunk/drivers/media/usb/uvc/uvc_ctrl.c +++ b/trunk/drivers/media/usb/uvc/uvc_ctrl.c @@ -1431,8 +1431,10 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, int ret; ctrl = uvc_find_control(chain, xctrl->id, &mapping); - if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) == 0) + if (ctrl == NULL) return -EINVAL; + if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) + return -EACCES; /* Clamp out of range values. */ switch (mapping->v4l2_type) { diff --git a/trunk/drivers/media/usb/uvc/uvc_v4l2.c b/trunk/drivers/media/usb/uvc/uvc_v4l2.c index f2ee8c6b0d8d..68d59b527492 100644 --- a/trunk/drivers/media/usb/uvc/uvc_v4l2.c +++ b/trunk/drivers/media/usb/uvc/uvc_v4l2.c @@ -657,8 +657,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ret = uvc_ctrl_get(chain, ctrl); if (ret < 0) { uvc_ctrl_rollback(handle); - ctrls->error_idx = ret == -ENOENT - ? ctrls->count : i; + ctrls->error_idx = i; return ret; } } @@ -686,8 +685,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ret = uvc_ctrl_set(chain, ctrl); if (ret < 0) { uvc_ctrl_rollback(handle); - ctrls->error_idx = (ret == -ENOENT && - cmd == VIDIOC_S_EXT_CTRLS) + ctrls->error_idx = cmd == VIDIOC_S_EXT_CTRLS ? ctrls->count : i; return ret; } diff --git a/trunk/drivers/media/v4l2-core/videobuf2-core.c b/trunk/drivers/media/v4l2-core/videobuf2-core.c index 9f81be23a81f..e02c4797b1c6 100644 --- a/trunk/drivers/media/v4l2-core/videobuf2-core.c +++ b/trunk/drivers/media/v4l2-core/videobuf2-core.c @@ -921,8 +921,10 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b * In videobuf we use our internal V4l2_planes struct for * single-planar buffers as well, for simplicity. */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) + if (V4L2_TYPE_IS_OUTPUT(b->type)) { v4l2_planes[0].bytesused = b->bytesused; + v4l2_planes[0].data_offset = 0; + } if (b->memory == V4L2_MEMORY_USERPTR) { v4l2_planes[0].m.userptr = b->m.userptr; diff --git a/trunk/drivers/mfd/Kconfig b/trunk/drivers/mfd/Kconfig index 1c0abd4dfc43..47ad4e270877 100644 --- a/trunk/drivers/mfd/Kconfig +++ b/trunk/drivers/mfd/Kconfig @@ -292,6 +292,7 @@ config TWL4030_CORE bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support" depends on I2C=y && GENERIC_HARDIRQS select IRQ_DOMAIN + select REGMAP_I2C help Say yes here if you have TWL4030 / TWL6030 family chip on your board. This core driver provides register access and IRQ handling diff --git a/trunk/drivers/mfd/vexpress-sysreg.c b/trunk/drivers/mfd/vexpress-sysreg.c index e5d8f63b252a..77048b18439e 100644 --- a/trunk/drivers/mfd/vexpress-sysreg.c +++ b/trunk/drivers/mfd/vexpress-sysreg.c @@ -313,19 +313,11 @@ static void vexpress_sysreg_config_complete(unsigned long data) } -void __init vexpress_sysreg_early_init(void __iomem *base) +void __init vexpress_sysreg_setup(struct device_node *node) { - struct device_node *node = of_find_compatible_node(NULL, NULL, - "arm,vexpress-sysreg"); - - if (node) - base = of_iomap(node, 0); - - if (WARN_ON(!base)) + if (WARN_ON(!vexpress_sysreg_base)) return; - vexpress_sysreg_base = base; - if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) vexpress_master_site = VEXPRESS_SITE_DB2; else @@ -336,9 +328,23 @@ void __init vexpress_sysreg_early_init(void __iomem *base) WARN_ON(!vexpress_sysreg_config_bridge); } +void __init vexpress_sysreg_early_init(void __iomem *base) +{ + vexpress_sysreg_base = base; + vexpress_sysreg_setup(NULL); +} + void __init vexpress_sysreg_of_early_init(void) { - vexpress_sysreg_early_init(NULL); + struct device_node *node = of_find_compatible_node(NULL, NULL, + "arm,vexpress-sysreg"); + + if (node) { + vexpress_sysreg_base = of_iomap(node, 0); + vexpress_sysreg_setup(node); + } else { + pr_info("vexpress-sysreg: No Device Tree node found."); + } } @@ -426,9 +432,11 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) return -EBUSY; } - if (!vexpress_sysreg_base) + if (!vexpress_sysreg_base) { vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + vexpress_sysreg_setup(pdev->dev.of_node); + } if (!vexpress_sysreg_base) { dev_err(&pdev->dev, "Failed to obtain base address!\n"); diff --git a/trunk/drivers/misc/mei/amthif.c b/trunk/drivers/misc/mei/amthif.c index 18794aea6062..e40ffd9502d1 100644 --- a/trunk/drivers/misc/mei/amthif.c +++ b/trunk/drivers/misc/mei/amthif.c @@ -187,13 +187,13 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, wait_ret = wait_event_interruptible(dev->iamthif_cl.wait, (cb = mei_amthif_find_read_list_entry(dev, file))); + /* Locking again the Mutex */ + mutex_lock(&dev->device_lock); + if (wait_ret) return -ERESTARTSYS; dev_dbg(&dev->pdev->dev, "woke up from sleep\n"); - - /* Locking again the Mutex */ - mutex_lock(&dev->device_lock); } diff --git a/trunk/drivers/misc/ti-st/st_kim.c b/trunk/drivers/misc/ti-st/st_kim.c index 9ff942a346ed..83269f1d16e3 100644 --- a/trunk/drivers/misc/ti-st/st_kim.c +++ b/trunk/drivers/misc/ti-st/st_kim.c @@ -468,6 +468,11 @@ long st_kim_start(void *kim_data) if (pdata->chip_enable) pdata->chip_enable(kim_gdata); + /* Configure BT nShutdown to HIGH state */ + gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); + mdelay(5); /* FIXME: a proper toggle */ + gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH); + mdelay(100); /* re-initialize the completion */ INIT_COMPLETION(kim_gdata->ldisc_installed); /* send notification to UIM */ @@ -509,7 +514,8 @@ long st_kim_start(void *kim_data) * (b) upon failure to either install ldisc or download firmware. * The function is responsible to (a) notify UIM about un-installation, * (b) flush UART if the ldisc was installed. - * (c) invoke platform's chip disabling routine. + * (c) reset BT_EN - pull down nshutdown at the end. + * (d) invoke platform's chip disabling routine. */ long st_kim_stop(void *kim_data) { @@ -541,6 +547,13 @@ long st_kim_stop(void *kim_data) err = -ETIMEDOUT; } + /* By default configure BT nShutdown to LOW state */ + gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); + mdelay(1); + gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH); + mdelay(1); + gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); + /* platform specific disable */ if (pdata->chip_disable) pdata->chip_disable(kim_gdata); @@ -733,6 +746,20 @@ static int kim_probe(struct platform_device *pdev) /* refer to itself */ kim_gdata->core_data->kim_data = kim_gdata; + /* Claim the chip enable nShutdown gpio from the system */ + kim_gdata->nshutdown = pdata->nshutdown_gpio; + err = gpio_request(kim_gdata->nshutdown, "kim"); + if (unlikely(err)) { + pr_err(" gpio %ld request failed ", kim_gdata->nshutdown); + return err; + } + + /* Configure nShutdown GPIO as output=0 */ + err = gpio_direction_output(kim_gdata->nshutdown, 0); + if (unlikely(err)) { + pr_err(" unable to configure gpio %ld", kim_gdata->nshutdown); + return err; + } /* get reference of pdev for request_firmware */ kim_gdata->kim_pdev = pdev; @@ -779,10 +806,18 @@ static int kim_probe(struct platform_device *pdev) static int kim_remove(struct platform_device *pdev) { + /* free the GPIOs requested */ + struct ti_st_plat_data *pdata = pdev->dev.platform_data; struct kim_data_s *kim_gdata; kim_gdata = dev_get_drvdata(&pdev->dev); + /* Free the Bluetooth/FM/GPIO + * nShutdown gpio from the system + */ + gpio_free(pdata->nshutdown_gpio); + pr_info("nshutdown GPIO Freed"); + debugfs_remove_recursive(kim_debugfs_dir); sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp); pr_info("sysfs entries removed"); diff --git a/trunk/drivers/mmc/host/mvsdio.c b/trunk/drivers/mmc/host/mvsdio.c index de4c20b3936c..f8dd36102949 100644 --- a/trunk/drivers/mmc/host/mvsdio.c +++ b/trunk/drivers/mmc/host/mvsdio.c @@ -50,8 +50,6 @@ struct mvsd_host { struct timer_list timer; struct mmc_host *mmc; struct device *dev; - struct resource *res; - int irq; struct clk *clk; int gpio_card_detect; int gpio_write_protect; @@ -718,10 +716,6 @@ static int __init mvsd_probe(struct platform_device *pdev) if (!r || irq < 0 || !mvsd_data) return -ENXIO; - r = request_mem_region(r->start, SZ_1K, DRIVER_NAME); - if (!r) - return -EBUSY; - mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; @@ -731,8 +725,8 @@ static int __init mvsd_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; host->dev = &pdev->dev; - host->res = r; host->base_clock = mvsd_data->clock / 2; + host->clk = ERR_PTR(-EINVAL); mmc->ops = &mvsd_ops; @@ -752,7 +746,7 @@ static int __init mvsd_probe(struct platform_device *pdev) spin_lock_init(&host->lock); - host->base = ioremap(r->start, SZ_4K); + host->base = devm_request_and_ioremap(&pdev->dev, r); if (!host->base) { ret = -ENOMEM; goto out; @@ -765,44 +759,45 @@ static int __init mvsd_probe(struct platform_device *pdev) mvsd_power_down(host); - ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host); + ret = devm_request_irq(&pdev->dev, irq, mvsd_irq, 0, DRIVER_NAME, host); if (ret) { pr_err("%s: cannot assign irq %d\n", DRIVER_NAME, irq); goto out; - } else - host->irq = irq; + } /* Not all platforms can gate the clock, so it is not an error if the clock does not exists. */ - host->clk = clk_get(&pdev->dev, NULL); - if (!IS_ERR(host->clk)) { + host->clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(host->clk)) clk_prepare_enable(host->clk); - } if (mvsd_data->gpio_card_detect) { - ret = gpio_request(mvsd_data->gpio_card_detect, - DRIVER_NAME " cd"); + ret = devm_gpio_request_one(&pdev->dev, + mvsd_data->gpio_card_detect, + GPIOF_IN, DRIVER_NAME " cd"); if (ret == 0) { - gpio_direction_input(mvsd_data->gpio_card_detect); irq = gpio_to_irq(mvsd_data->gpio_card_detect); - ret = request_irq(irq, mvsd_card_detect_irq, - IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING, - DRIVER_NAME " cd", host); + ret = devm_request_irq(&pdev->dev, irq, + mvsd_card_detect_irq, + IRQ_TYPE_EDGE_RISING | + IRQ_TYPE_EDGE_FALLING, + DRIVER_NAME " cd", host); if (ret == 0) host->gpio_card_detect = mvsd_data->gpio_card_detect; else - gpio_free(mvsd_data->gpio_card_detect); + devm_gpio_free(&pdev->dev, + mvsd_data->gpio_card_detect); } } if (!host->gpio_card_detect) mmc->caps |= MMC_CAP_NEEDS_POLL; if (mvsd_data->gpio_write_protect) { - ret = gpio_request(mvsd_data->gpio_write_protect, - DRIVER_NAME " wp"); + ret = devm_gpio_request_one(&pdev->dev, + mvsd_data->gpio_write_protect, + GPIOF_IN, DRIVER_NAME " wp"); if (ret == 0) { - gpio_direction_input(mvsd_data->gpio_write_protect); host->gpio_write_protect = mvsd_data->gpio_write_protect; } @@ -824,26 +819,11 @@ static int __init mvsd_probe(struct platform_device *pdev) return 0; out: - if (host) { - if (host->irq) - free_irq(host->irq, host); - if (host->gpio_card_detect) { - free_irq(gpio_to_irq(host->gpio_card_detect), host); - gpio_free(host->gpio_card_detect); - } - if (host->gpio_write_protect) - gpio_free(host->gpio_write_protect); - if (host->base) - iounmap(host->base); - } - if (r) - release_resource(r); - if (mmc) - if (!IS_ERR_OR_NULL(host->clk)) { + if (mmc) { + if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); - clk_put(host->clk); - } mmc_free_host(mmc); + } return ret; } @@ -852,28 +832,16 @@ static int __exit mvsd_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); - if (mmc) { - struct mvsd_host *host = mmc_priv(mmc); + struct mvsd_host *host = mmc_priv(mmc); - if (host->gpio_card_detect) { - free_irq(gpio_to_irq(host->gpio_card_detect), host); - gpio_free(host->gpio_card_detect); - } - mmc_remove_host(mmc); - free_irq(host->irq, host); - if (host->gpio_write_protect) - gpio_free(host->gpio_write_protect); - del_timer_sync(&host->timer); - mvsd_power_down(host); - iounmap(host->base); - release_resource(host->res); + mmc_remove_host(mmc); + del_timer_sync(&host->timer); + mvsd_power_down(host); + + if (!IS_ERR(host->clk)) + clk_disable_unprepare(host->clk); + mmc_free_host(mmc); - if (!IS_ERR(host->clk)) { - clk_disable_unprepare(host->clk); - clk_put(host->clk); - } - mmc_free_host(mmc); - } platform_set_drvdata(pdev, NULL); return 0; } diff --git a/trunk/drivers/net/ethernet/adi/Kconfig b/trunk/drivers/net/ethernet/adi/Kconfig index e49c0eff040b..a9481606bbcd 100644 --- a/trunk/drivers/net/ethernet/adi/Kconfig +++ b/trunk/drivers/net/ethernet/adi/Kconfig @@ -61,6 +61,7 @@ config BFIN_RX_DESC_NUM config BFIN_MAC_USE_HWSTAMP bool "Use IEEE 1588 hwstamp" + depends on BFIN_MAC && BF518 select PTP_1588_CLOCK default y ---help--- diff --git a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 01588b66a38c..f771ddfba646 100644 --- a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -80,12 +80,37 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to) new_txdata_index = new_max_eth_txqs + FCOE_TXQ_IDX_OFFSET; } - memcpy(&bp->bnx2x_txq[old_txdata_index], - &bp->bnx2x_txq[new_txdata_index], + memcpy(&bp->bnx2x_txq[new_txdata_index], + &bp->bnx2x_txq[old_txdata_index], sizeof(struct bnx2x_fp_txdata)); to_fp->txdata_ptr[0] = &bp->bnx2x_txq[new_txdata_index]; } +/** + * bnx2x_shrink_eth_fp - guarantees fastpath structures stay intact + * + * @bp: driver handle + * @delta: number of eth queues which were not allocated + */ +static void bnx2x_shrink_eth_fp(struct bnx2x *bp, int delta) +{ + int i, cos, old_eth_num = BNX2X_NUM_ETH_QUEUES(bp); + + /* Queue pointer cannot be re-set on an fp-basis, as moving pointer + * backward along the array could cause memory to be overriden + */ + for (cos = 1; cos < bp->max_cos; cos++) { + for (i = 0; i < old_eth_num - delta; i++) { + struct bnx2x_fastpath *fp = &bp->fp[i]; + int new_idx = cos * (old_eth_num - delta) + i; + + memcpy(&bp->bnx2x_txq[new_idx], fp->txdata_ptr[cos], + sizeof(struct bnx2x_fp_txdata)); + fp->txdata_ptr[cos] = &bp->bnx2x_txq[new_idx]; + } + } +} + int load_count[2][3] = { {0} }; /* per-path: 0-common, 1-port0, 2-port1 */ /* free skb in the packet ring at pos idx @@ -3863,6 +3888,7 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp) int delta = BNX2X_NUM_ETH_QUEUES(bp) - i; WARN_ON(delta < 0); + bnx2x_shrink_eth_fp(bp, delta); if (CNIC_SUPPORT(bp)) /* move non eth FPs next to last eth FP * must be done in that order diff --git a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 277f17e3c8f8..a427b49a886c 100644 --- a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -2777,10 +2777,10 @@ static int bnx2x_set_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info) } else if ((info->flow_type == UDP_V6_FLOW) && (bp->rss_conf_obj.udp_rss_v6 != udp_rss_requested)) { bp->rss_conf_obj.udp_rss_v6 = udp_rss_requested; - return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0); DP(BNX2X_MSG_ETHTOOL, "rss re-configured, UDP 4-tupple %s\n", udp_rss_requested ? "enabled" : "disabled"); + return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0); } else { return 0; } diff --git a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 940ef859dc60..5523da3afcdc 100644 --- a/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/trunk/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -127,6 +127,17 @@ MODULE_PARM_DESC(debug, " Default debug msglevel"); struct workqueue_struct *bnx2x_wq; +struct bnx2x_mac_vals { + u32 xmac_addr; + u32 xmac_val; + u32 emac_addr; + u32 emac_val; + u32 umac_addr; + u32 umac_val; + u32 bmac_addr; + u32 bmac_val[2]; +}; + enum bnx2x_board_type { BCM57710 = 0, BCM57711, @@ -9420,12 +9431,19 @@ static inline void bnx2x_undi_int_disable(struct bnx2x *bp) bnx2x_undi_int_disable_e1h(bp); } -static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) +static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, + struct bnx2x_mac_vals *vals) { u32 val, base_addr, offset, mask, reset_reg; bool mac_stopped = false; u8 port = BP_PORT(bp); + /* reset addresses as they also mark which values were changed */ + vals->bmac_addr = 0; + vals->umac_addr = 0; + vals->xmac_addr = 0; + vals->emac_addr = 0; + reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2); if (!CHIP_IS_E3(bp)) { @@ -9447,14 +9465,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) */ wb_data[0] = REG_RD(bp, base_addr + offset); wb_data[1] = REG_RD(bp, base_addr + offset + 0x4); + vals->bmac_addr = base_addr + offset; + vals->bmac_val[0] = wb_data[0]; + vals->bmac_val[1] = wb_data[1]; wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE; - REG_WR(bp, base_addr + offset, wb_data[0]); - REG_WR(bp, base_addr + offset + 0x4, wb_data[1]); + REG_WR(bp, vals->bmac_addr, wb_data[0]); + REG_WR(bp, vals->bmac_addr + 0x4, wb_data[1]); } BNX2X_DEV_INFO("Disable emac Rx\n"); - REG_WR(bp, NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4, 0); - + vals->emac_addr = NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4; + vals->emac_val = REG_RD(bp, vals->emac_addr); + REG_WR(bp, vals->emac_addr, 0); mac_stopped = true; } else { if (reset_reg & MISC_REGISTERS_RESET_REG_2_XMAC) { @@ -9465,14 +9487,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) val & ~(1 << 1)); REG_WR(bp, base_addr + XMAC_REG_PFC_CTRL_HI, val | (1 << 1)); - REG_WR(bp, base_addr + XMAC_REG_CTRL, 0); + vals->xmac_addr = base_addr + XMAC_REG_CTRL; + vals->xmac_val = REG_RD(bp, vals->xmac_addr); + REG_WR(bp, vals->xmac_addr, 0); mac_stopped = true; } mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port; if (mask & reset_reg) { BNX2X_DEV_INFO("Disable umac Rx\n"); base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0; - REG_WR(bp, base_addr + UMAC_REG_COMMAND_CONFIG, 0); + vals->umac_addr = base_addr + UMAC_REG_COMMAND_CONFIG; + vals->umac_val = REG_RD(bp, vals->umac_addr); + REG_WR(bp, vals->umac_addr, 0); mac_stopped = true; } } @@ -9664,12 +9690,16 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) { u32 reset_reg, tmp_reg = 0, rc; bool prev_undi = false; + struct bnx2x_mac_vals mac_vals; + /* It is possible a previous function received 'common' answer, * but hasn't loaded yet, therefore creating a scenario of * multiple functions receiving 'common' on the same path. */ BNX2X_DEV_INFO("Common unload Flow\n"); + memset(&mac_vals, 0, sizeof(mac_vals)); + if (bnx2x_prev_is_path_marked(bp)) return bnx2x_prev_mcp_done(bp); @@ -9680,7 +9710,10 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) u32 timer_count = 1000; /* Close the MAC Rx to prevent BRB from filling up */ - bnx2x_prev_unload_close_mac(bp); + bnx2x_prev_unload_close_mac(bp, &mac_vals); + + /* close LLH filters towards the BRB */ + bnx2x_set_rx_filter(&bp->link_params, 0); /* Check if the UNDI driver was previously loaded * UNDI driver initializes CID offset for normal bell to 0x7 @@ -9727,6 +9760,17 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) /* No packets are in the pipeline, path is ready for reset */ bnx2x_reset_common(bp); + if (mac_vals.xmac_addr) + REG_WR(bp, mac_vals.xmac_addr, mac_vals.xmac_val); + if (mac_vals.umac_addr) + REG_WR(bp, mac_vals.umac_addr, mac_vals.umac_val); + if (mac_vals.emac_addr) + REG_WR(bp, mac_vals.emac_addr, mac_vals.emac_val); + if (mac_vals.bmac_addr) { + REG_WR(bp, mac_vals.bmac_addr, mac_vals.bmac_val[0]); + REG_WR(bp, mac_vals.bmac_addr + 4, mac_vals.bmac_val[1]); + } + rc = bnx2x_prev_mark_path(bp, prev_undi); if (rc) { bnx2x_prev_mcp_done(bp); diff --git a/trunk/drivers/net/ethernet/emulex/benet/be.h b/trunk/drivers/net/ethernet/emulex/benet/be.h index 3bc1912afba9..4eba17b83ba8 100644 --- a/trunk/drivers/net/ethernet/emulex/benet/be.h +++ b/trunk/drivers/net/ethernet/emulex/benet/be.h @@ -190,6 +190,7 @@ struct be_eq_obj { u8 idx; /* array index */ u16 tx_budget; + u16 spurious_intr; struct napi_struct napi; struct be_adapter *adapter; } ____cacheline_aligned_in_smp; diff --git a/trunk/drivers/net/ethernet/emulex/benet/be_main.c b/trunk/drivers/net/ethernet/emulex/benet/be_main.c index 9dca22be8125..5c995700e534 100644 --- a/trunk/drivers/net/ethernet/emulex/benet/be_main.c +++ b/trunk/drivers/net/ethernet/emulex/benet/be_main.c @@ -2026,19 +2026,30 @@ static irqreturn_t be_intx(int irq, void *dev) struct be_adapter *adapter = eqo->adapter; int num_evts = 0; - /* On Lancer, clear-intr bit of the EQ DB does not work. - * INTx is de-asserted only on notifying num evts. + /* IRQ is not expected when NAPI is scheduled as the EQ + * will not be armed. + * But, this can happen on Lancer INTx where it takes + * a while to de-assert INTx or in BE2 where occasionaly + * an interrupt may be raised even when EQ is unarmed. + * If NAPI is already scheduled, then counting & notifying + * events will orphan them. */ - if (lancer_chip(adapter)) + if (napi_schedule_prep(&eqo->napi)) { num_evts = events_get(eqo); + __napi_schedule(&eqo->napi); + if (num_evts) + eqo->spurious_intr = 0; + } + be_eq_notify(adapter, eqo->q.id, false, true, num_evts); - /* The EQ-notify may not de-assert INTx rightaway, causing - * the ISR to be invoked again. So, return HANDLED even when - * num_evts is zero. + /* Return IRQ_HANDLED only for the the first spurious intr + * after a valid intr to stop the kernel from branding + * this irq as a bad one! */ - be_eq_notify(adapter, eqo->q.id, false, true, num_evts); - napi_schedule(&eqo->napi); - return IRQ_HANDLED; + if (num_evts || eqo->spurious_intr++ == 0) + return IRQ_HANDLED; + else + return IRQ_NONE; } static irqreturn_t be_msix(int irq, void *dev) diff --git a/trunk/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/trunk/drivers/net/ethernet/qlogic/qlge/qlge_main.c index f80cd975daed..3e73742024b0 100644 --- a/trunk/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/trunk/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4678,7 +4678,7 @@ static int qlge_probe(struct pci_dev *pdev, qdev = netdev_priv(ndev); SET_NETDEV_DEV(ndev, &pdev->dev); ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | - NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN | + NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM; ndev->features = ndev->hw_features | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; diff --git a/trunk/drivers/net/ethernet/xilinx/Kconfig b/trunk/drivers/net/ethernet/xilinx/Kconfig index 5778a4ae1164..122d60c0481b 100644 --- a/trunk/drivers/net/ethernet/xilinx/Kconfig +++ b/trunk/drivers/net/ethernet/xilinx/Kconfig @@ -27,7 +27,7 @@ config XILINX_EMACLITE config XILINX_AXI_EMAC tristate "Xilinx 10/100/1000 AXI Ethernet support" - depends on (PPC32 || MICROBLAZE) + depends on MICROBLAZE select PHYLIB ---help--- This driver supports the 10/100/1000 Ethernet from Xilinx for the diff --git a/trunk/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/trunk/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d9f69b82cc4f..6f47100e58d7 100644 --- a/trunk/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/trunk/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1590,7 +1590,7 @@ static int axienet_of_probe(struct platform_device *op) lp->rx_irq = irq_of_parse_and_map(np, 1); lp->tx_irq = irq_of_parse_and_map(np, 0); of_node_put(np); - if ((lp->rx_irq == NO_IRQ) || (lp->tx_irq == NO_IRQ)) { + if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) { dev_err(&op->dev, "could not determine irqs\n"); ret = -ENOMEM; goto err_iounmap_2; diff --git a/trunk/drivers/net/tun.c b/trunk/drivers/net/tun.c index fbd106edbe59..af372d0957fe 100644 --- a/trunk/drivers/net/tun.c +++ b/trunk/drivers/net/tun.c @@ -404,8 +404,8 @@ static void __tun_detach(struct tun_file *tfile, bool clean) struct tun_struct *tun; struct net_device *dev; - tun = rcu_dereference_protected(tfile->tun, - lockdep_rtnl_is_held()); + tun = rtnl_dereference(tfile->tun); + if (tun) { u16 index = tfile->queue_index; BUG_ON(index >= tun->numqueues); @@ -414,8 +414,7 @@ static void __tun_detach(struct tun_file *tfile, bool clean) rcu_assign_pointer(tun->tfiles[index], tun->tfiles[tun->numqueues - 1]); rcu_assign_pointer(tfile->tun, NULL); - ntfile = rcu_dereference_protected(tun->tfiles[index], - lockdep_rtnl_is_held()); + ntfile = rtnl_dereference(tun->tfiles[index]); ntfile->queue_index = index; --tun->numqueues; @@ -429,8 +428,10 @@ static void __tun_detach(struct tun_file *tfile, bool clean) /* Drop read queue */ skb_queue_purge(&tfile->sk.sk_receive_queue); tun_set_real_num_queues(tun); - } else if (tfile->detached && clean) + } else if (tfile->detached && clean) { tun = tun_enable_queue(tfile); + sock_put(&tfile->sk); + } if (clean) { if (tun && tun->numqueues == 0 && tun->numdisabled == 0 && @@ -458,8 +459,7 @@ static void tun_detach_all(struct net_device *dev) int i, n = tun->numqueues; for (i = 0; i < n; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); BUG_ON(!tfile); wake_up_all(&tfile->wq.wait); rcu_assign_pointer(tfile->tun, NULL); @@ -469,8 +469,7 @@ static void tun_detach_all(struct net_device *dev) synchronize_net(); for (i = 0; i < n; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); /* Drop read queue */ skb_queue_purge(&tfile->sk.sk_receive_queue); sock_put(&tfile->sk); @@ -481,6 +480,9 @@ static void tun_detach_all(struct net_device *dev) sock_put(&tfile->sk); } BUG_ON(tun->numdisabled != 0); + + if (tun->flags & TUN_PERSIST) + module_put(THIS_MODULE); } static int tun_attach(struct tun_struct *tun, struct file *file) @@ -489,7 +491,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file) int err; err = -EINVAL; - if (rcu_dereference_protected(tfile->tun, lockdep_rtnl_is_held())) + if (rtnl_dereference(tfile->tun)) goto out; err = -EBUSY; @@ -1544,6 +1546,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) struct net_device *dev; int err; + if (tfile->detached) + return -EINVAL; + dev = __dev_get_by_name(net, ifr->ifr_name); if (dev) { if (ifr->ifr_flags & IFF_TUN_EXCL) @@ -1738,8 +1743,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n) struct tun_file *tfile; for (i = 0; i < n; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); sk_detach_filter(tfile->socket.sk); } @@ -1752,8 +1756,7 @@ static int tun_attach_filter(struct tun_struct *tun) struct tun_file *tfile; for (i = 0; i < tun->numqueues; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); ret = sk_attach_filter(&tun->fprog, tfile->socket.sk); if (ret) { tun_detach_filter(tun, i); @@ -1771,8 +1774,7 @@ static void tun_set_sndbuf(struct tun_struct *tun) int i; for (i = 0; i < tun->numqueues; i++) { - tfile = rcu_dereference_protected(tun->tfiles[i], - lockdep_rtnl_is_held()); + tfile = rtnl_dereference(tun->tfiles[i]); tfile->socket.sk->sk_sndbuf = tun->sndbuf; } } @@ -1789,13 +1791,10 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) tun = tfile->detached; if (!tun) ret = -EINVAL; - else if (tun_not_capable(tun)) - ret = -EPERM; else ret = tun_attach(tun, file); } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { - tun = rcu_dereference_protected(tfile->tun, - lockdep_rtnl_is_held()); + tun = rtnl_dereference(tfile->tun); if (!tun || !(tun->flags & TUN_TAP_MQ)) ret = -EINVAL; else @@ -1880,10 +1879,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, /* Disable/Enable persist mode. Keep an extra reference to the * module to prevent the module being unprobed. */ - if (arg) { + if (arg && !(tun->flags & TUN_PERSIST)) { tun->flags |= TUN_PERSIST; __module_get(THIS_MODULE); - } else { + } + if (!arg && (tun->flags & TUN_PERSIST)) { tun->flags &= ~TUN_PERSIST; module_put(THIS_MODULE); } diff --git a/trunk/drivers/net/wireless/ath/Kconfig b/trunk/drivers/net/wireless/ath/Kconfig index 1a67a4f829fe..2c02b4e84094 100644 --- a/trunk/drivers/net/wireless/ath/Kconfig +++ b/trunk/drivers/net/wireless/ath/Kconfig @@ -30,5 +30,6 @@ source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" source "drivers/net/wireless/ath/ath6kl/Kconfig" source "drivers/net/wireless/ath/ar5523/Kconfig" +source "drivers/net/wireless/ath/wil6210/Kconfig" endif diff --git a/trunk/drivers/net/wireless/ath/Makefile b/trunk/drivers/net/wireless/ath/Makefile index 1e18621326dc..97b964ded2be 100644 --- a/trunk/drivers/net/wireless/ath/Makefile +++ b/trunk/drivers/net/wireless/ath/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_AR5523) += ar5523/ +obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/trunk/drivers/net/wireless/ath/wil6210/Kconfig b/trunk/drivers/net/wireless/ath/wil6210/Kconfig new file mode 100644 index 000000000000..bac3d98a0cfb --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/Kconfig @@ -0,0 +1,29 @@ +config WIL6210 + tristate "Wilocity 60g WiFi card wil6210 support" + depends on CFG80211 + depends on PCI + default n + ---help--- + This module adds support for wireless adapter based on + wil6210 chip by Wilocity. It supports operation on the + 60 GHz band, covered by the IEEE802.11ad standard. + + http://wireless.kernel.org/en/users/Drivers/wil6210 + + If you choose to build it as a module, it will be called + wil6210 + +config WIL6210_ISR_COR + bool "Use Clear-On-Read mode for ISR registers for wil6210" + depends on WIL6210 + default y + ---help--- + ISR registers on wil6210 chip may operate in either + COR (Clear-On-Read) or W1C (Write-1-to-Clear) mode. + For production code, use COR (say y); is default since + it saves extra target transaction; + For ISR debug, use W1C (say n); is allows to monitor ISR + registers with debugfs. If COR were used, ISR would + self-clear when accessed for debug purposes, it makes + such monitoring impossible. + Say y unless you debug interrupts diff --git a/trunk/drivers/net/wireless/ath/wil6210/Makefile b/trunk/drivers/net/wireless/ath/wil6210/Makefile new file mode 100644 index 000000000000..9396dc9fe3c5 --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_WIL6210) += wil6210.o + +wil6210-objs := main.o +wil6210-objs += netdev.o +wil6210-objs += cfg80211.o +wil6210-objs += pcie_bus.o +wil6210-objs += debugfs.o +wil6210-objs += wmi.o +wil6210-objs += interrupt.o +wil6210-objs += txrx.o + +subdir-ccflags-y += -Werror +subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/trunk/drivers/net/wireless/ath/wil6210/cfg80211.c b/trunk/drivers/net/wireless/ath/wil6210/cfg80211.c new file mode 100644 index 000000000000..116f4e807ae1 --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" + +#define CHAN60G(_channel, _flags) { \ + .band = IEEE80211_BAND_60GHZ, \ + .center_freq = 56160 + (2160 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 40, \ +} + +static struct ieee80211_channel wil_60ghz_channels[] = { + CHAN60G(1, 0), + CHAN60G(2, 0), + CHAN60G(3, 0), +/* channel 4 not supported yet */ +}; + +static struct ieee80211_supported_band wil_band_60ghz = { + .channels = wil_60ghz_channels, + .n_channels = ARRAY_SIZE(wil_60ghz_channels), + .ht_cap = { + .ht_supported = true, + .cap = 0, /* TODO */ + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */ + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */ + .mcs = { + /* MCS 1..12 - SC PHY */ + .rx_mask = {0xfe, 0x1f}, /* 1..12 */ + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */ + }, + }, +}; + +static const struct ieee80211_txrx_stypes +wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_AP] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, +}; + +static const u32 wil_cipher_suites[] = { + WLAN_CIPHER_SUITE_GCMP, +}; + +int wil_iftype_nl2wmi(enum nl80211_iftype type) +{ + static const struct { + enum nl80211_iftype nl; + enum wmi_network_type wmi; + } __nl2wmi[] = { + {NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC}, + {NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA}, + {NL80211_IFTYPE_AP, WMI_NETTYPE_AP}, + {NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P}, + {NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P}, + {NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */ + }; + uint i; + + for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) { + if (__nl2wmi[i].nl == type) + return __nl2wmi[i].wmi; + } + + return -EOPNOTSUPP; +} + +static int wil_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *ndev, + u8 *mac, struct station_info *sinfo) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + struct wmi_notify_req_cmd cmd = { + .cid = 0, + .interval_usec = 0, + }; + + if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) + return -ENOENT; + + /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */ + rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), + WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); + if (rc) + return rc; + + sinfo->generation = wil->sinfo_gen; + + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->txrate.mcs = wil->stats.bf_mcs; + sinfo->filled |= STATION_INFO_RX_BITRATE; + sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->rxrate.mcs = wil->stats.last_mcs_rx; + + if (test_bit(wil_status_fwconnected, &wil->status)) { + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = 12; /* TODO: provide real value */ + } + + return 0; +} + +static int wil_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + + switch (type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + break; + case NL80211_IFTYPE_MONITOR: + if (flags) + wil->monitor_flags = *flags; + else + wil->monitor_flags = 0; + + break; + default: + return -EOPNOTSUPP; + } + + wdev->iftype = type; + + return 0; +} + +static int wil_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + struct { + struct wmi_start_scan_cmd cmd; + u16 chnl[4]; + } __packed cmd; + uint i, n; + + if (wil->scan_request) { + wil_err(wil, "Already scanning\n"); + return -EAGAIN; + } + + /* check we are client side */ + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + break; + default: + return -EOPNOTSUPP; + + } + + /* FW don't support scan after connection attempt */ + if (test_bit(wil_status_dontscan, &wil->status)) { + wil_err(wil, "Scan after connect attempt not supported\n"); + return -EBUSY; + } + + wil->scan_request = request; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd.num_channels = 0; + n = min(request->n_channels, 4U); + for (i = 0; i < n; i++) { + int ch = request->channels[i]->hw_value; + if (ch == 0) { + wil_err(wil, + "Scan requested for unknown frequency %dMhz\n", + request->channels[i]->center_freq); + continue; + } + /* 0-based channel indexes */ + cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; + wil_dbg(wil, "Scan for ch %d : %d MHz\n", ch, + request->channels[i]->center_freq); + } + + return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + + cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); +} + +static int wil_cfg80211_connect(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct cfg80211_bss *bss; + struct wmi_connect_cmd conn; + const u8 *ssid_eid; + const u8 *rsn_eid; + int ch; + int rc = 0; + + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, + sme->ssid, sme->ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!bss) { + wil_err(wil, "Unable to find BSS\n"); + return -ENOENT; + } + + ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (!ssid_eid) { + wil_err(wil, "No SSID\n"); + rc = -ENOENT; + goto out; + } + + rsn_eid = sme->ie ? + cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : + NULL; + if (rsn_eid) { + if (sme->ie_len > WMI_MAX_IE_LEN) { + rc = -ERANGE; + wil_err(wil, "IE too large (%td bytes)\n", + sme->ie_len); + goto out; + } + /* + * For secure assoc, send: + * (1) WMI_DELETE_CIPHER_KEY_CMD + * (2) WMI_SET_APPIE_CMD + */ + rc = wmi_del_cipher_key(wil, 0, bss->bssid); + if (rc) { + wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n"); + goto out; + } + /* WMI_SET_APPIE_CMD */ + rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); + if (rc) { + wil_err(wil, "WMI_SET_APPIE_CMD failed\n"); + goto out; + } + } + + /* WMI_CONNECT_CMD */ + memset(&conn, 0, sizeof(conn)); + switch (bss->capability & 0x03) { + case WLAN_CAPABILITY_DMG_TYPE_AP: + conn.network_type = WMI_NETTYPE_INFRA; + break; + case WLAN_CAPABILITY_DMG_TYPE_PBSS: + conn.network_type = WMI_NETTYPE_P2P; + break; + default: + wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", + bss->capability); + goto out; + } + if (rsn_eid) { + conn.dot11_auth_mode = WMI_AUTH11_SHARED; + conn.auth_mode = WMI_AUTH_WPA2_PSK; + conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; + conn.pairwise_crypto_len = 16; + } else { + conn.dot11_auth_mode = WMI_AUTH11_OPEN; + conn.auth_mode = WMI_AUTH_NONE; + } + + conn.ssid_len = min_t(u8, ssid_eid[1], 32); + memcpy(conn.ssid, ssid_eid+2, conn.ssid_len); + + ch = bss->channel->hw_value; + if (ch == 0) { + wil_err(wil, "BSS at unknown frequency %dMhz\n", + bss->channel->center_freq); + rc = -EOPNOTSUPP; + goto out; + } + conn.channel = ch - 1; + + memcpy(conn.bssid, bss->bssid, 6); + memcpy(conn.dst_mac, bss->bssid, 6); + /* + * FW don't support scan after connection attempt + */ + set_bit(wil_status_dontscan, &wil->status); + + rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); + if (rc == 0) { + /* Connect can take lots of time */ + mod_timer(&wil->connect_timer, + jiffies + msecs_to_jiffies(2000)); + } + + out: + cfg80211_put_bss(bss); + + return rc; +} + +static int wil_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *ndev, + u16 reason_code) +{ + int rc; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); + + return rc; +} + +static int wil_cfg80211_set_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + + wdev->preset_chandef = *chandef; + + return 0; +} + +static int wil_cfg80211_add_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, + struct key_params *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + /* group key is not used */ + if (!pairwise) + return 0; + + return wmi_add_cipher_key(wil, key_index, mac_addr, + params->key_len, params->key); +} + +static int wil_cfg80211_del_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + /* group key is not used */ + if (!pairwise) + return 0; + + return wmi_del_cipher_key(wil, key_index, mac_addr); +} + +/* Need to be present or wiphy_new() will WARN */ +static int wil_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool unicast, + bool multicast) +{ + return 0; +} + +static int wil_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_ap_settings *info) +{ + int rc = 0; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + struct ieee80211_channel *channel = info->chandef.chan; + struct cfg80211_beacon_data *bcon = &info->beacon; + u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + if (!channel) { + wil_err(wil, "AP: No channel???\n"); + return -EINVAL; + } + + wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, + channel->center_freq, info->privacy ? "secure" : "open"); + print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, + info->ssid, info->ssid_len); + + rc = wil_reset(wil); + if (rc) + return rc; + + rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); + if (rc) + return rc; + + rc = wmi_set_channel(wil, channel->hw_value); + if (rc) + return rc; + + /* MAC address - pre-requisite for other commands */ + wmi_set_mac_address(wil, ndev->dev_addr); + + /* IE's */ + /* bcon 'head IE's are not relevant for 60g band */ + wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, + bcon->beacon_ies); + wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, + bcon->proberesp_ies); + wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, + bcon->assocresp_ies); + + wil->secure_pcp = info->privacy; + + rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype); + if (rc) + return rc; + + /* Rx VRING. After MAC and beacon */ + rc = wil_rx_init(wil); + + netif_carrier_on(ndev); + + return rc; +} + +static int wil_cfg80211_stop_ap(struct wiphy *wiphy, + struct net_device *ndev) +{ + int rc = 0; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + /* To stop beaconing, set BI to 0 */ + rc = wmi_set_bcon(wil, 0, wmi_nettype); + + return rc; +} + +static struct cfg80211_ops wil_cfg80211_ops = { + .scan = wil_cfg80211_scan, + .connect = wil_cfg80211_connect, + .disconnect = wil_cfg80211_disconnect, + .change_virtual_intf = wil_cfg80211_change_iface, + .get_station = wil_cfg80211_get_station, + .set_monitor_channel = wil_cfg80211_set_channel, + .add_key = wil_cfg80211_add_key, + .del_key = wil_cfg80211_del_key, + .set_default_key = wil_cfg80211_set_default_key, + /* AP mode */ + .start_ap = wil_cfg80211_start_ap, + .stop_ap = wil_cfg80211_stop_ap, +}; + +static void wil_wiphy_init(struct wiphy *wiphy) +{ + /* TODO: set real value */ + wiphy->max_scan_ssids = 10; + wiphy->max_num_pmkids = 0 /* TODO: */; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MONITOR); + /* TODO: enable P2P when integrated with supplicant: + * BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) + */ + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n", + __func__, wiphy->flags); + wiphy->probe_resp_offload = + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + + wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; + + /* TODO: figure this out */ + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->cipher_suites = wil_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); + wiphy->mgmt_stypes = wil_mgmt_stypes; +} + +struct wireless_dev *wil_cfg80211_init(struct device *dev) +{ + int rc = 0; + struct wireless_dev *wdev; + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + wdev->wiphy = wiphy_new(&wil_cfg80211_ops, + sizeof(struct wil6210_priv)); + if (!wdev->wiphy) { + rc = -ENOMEM; + goto out; + } + + set_wiphy_dev(wdev->wiphy, dev); + wil_wiphy_init(wdev->wiphy); + + rc = wiphy_register(wdev->wiphy); + if (rc < 0) + goto out_failed_reg; + + return wdev; + +out_failed_reg: + wiphy_free(wdev->wiphy); +out: + kfree(wdev); + + return ERR_PTR(rc); +} + +void wil_wdev_free(struct wil6210_priv *wil) +{ + struct wireless_dev *wdev = wil_to_wdev(wil); + + if (!wdev) + return; + + wiphy_unregister(wdev->wiphy); + wiphy_free(wdev->wiphy); + kfree(wdev); +} diff --git a/trunk/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/trunk/drivers/net/wireless/ath/wil6210/dbg_hexdump.h new file mode 100644 index 000000000000..6a315ba5aa7d --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/dbg_hexdump.h @@ -0,0 +1,30 @@ +#ifndef WIL_DBG_HEXDUMP_H_ +#define WIL_DBG_HEXDUMP_H_ + +#if defined(CONFIG_DYNAMIC_DEBUG) +#define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ +do { \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, \ + __builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ + print_hex_dump(KERN_DEBUG, prefix_str, \ + prefix_type, rowsize, groupsize, \ + buf, len, ascii); \ +} while (0) + +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +#define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \ + wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true) +#else /* defined(CONFIG_DYNAMIC_DEBUG) */ +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) +#endif /* defined(CONFIG_DYNAMIC_DEBUG) */ + +#endif /* WIL_DBG_HEXDUMP_H_ */ diff --git a/trunk/drivers/net/wireless/ath/wil6210/debugfs.c b/trunk/drivers/net/wireless/ath/wil6210/debugfs.c new file mode 100644 index 000000000000..65fc9683bfd8 --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/debugfs.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "txrx.h" + +/* Nasty hack. Better have per device instances */ +static u32 mem_addr; +static u32 dbg_txdesc_index; + +static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, + const char *name, struct vring *vring) +{ + void __iomem *x = wmi_addr(wil, vring->hwtail); + + seq_printf(s, "VRING %s = {\n", name); + seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa); + seq_printf(s, " va = 0x%p\n", vring->va); + seq_printf(s, " size = %d\n", vring->size); + seq_printf(s, " swtail = %d\n", vring->swtail); + seq_printf(s, " swhead = %d\n", vring->swhead); + seq_printf(s, " hwtail = [0x%08x] -> ", vring->hwtail); + if (x) + seq_printf(s, "0x%08x\n", ioread32(x)); + else + seq_printf(s, "???\n"); + + if (vring->va && (vring->size < 1025)) { + uint i; + for (i = 0; i < vring->size; i++) { + volatile struct vring_tx_desc *d = &vring->va[i].tx; + if ((i % 64) == 0 && (i != 0)) + seq_printf(s, "\n"); + seq_printf(s, "%s", (d->dma.status & BIT(0)) ? + "S" : (vring->ctx[i] ? "H" : "h")); + } + seq_printf(s, "\n"); + } + seq_printf(s, "}\n"); +} + +static int wil_vring_debugfs_show(struct seq_file *s, void *data) +{ + uint i; + struct wil6210_priv *wil = s->private; + + wil_print_vring(s, wil, "rx", &wil->vring_rx); + + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + struct vring *vring = &(wil->vring_tx[i]); + if (vring->va) { + char name[10]; + snprintf(name, sizeof(name), "tx_%2d", i); + wil_print_vring(s, wil, name, vring); + } + } + + return 0; +} + +static int wil_vring_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_vring_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_vring = { + .open = wil_vring_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static void wil_print_ring(struct seq_file *s, const char *prefix, + void __iomem *off) +{ + struct wil6210_priv *wil = s->private; + struct wil6210_mbox_ring r; + int rsize; + uint i; + + wil_memcpy_fromio_32(&r, off, sizeof(r)); + wil_mbox_ring_le2cpus(&r); + /* + * we just read memory block from NIC. This memory may be + * garbage. Check validity before using it. + */ + rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); + + seq_printf(s, "ring %s = {\n", prefix); + seq_printf(s, " base = 0x%08x\n", r.base); + seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize); + seq_printf(s, " tail = 0x%08x\n", r.tail); + seq_printf(s, " head = 0x%08x\n", r.head); + seq_printf(s, " entry size = %d\n", r.entry_size); + + if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { + seq_printf(s, " ??? size is not multiple of %zd, garbage?\n", + sizeof(struct wil6210_mbox_ring_desc)); + goto out; + } + + if (!wmi_addr(wil, r.base) || + !wmi_addr(wil, r.tail) || + !wmi_addr(wil, r.head)) { + seq_printf(s, " ??? pointers are garbage?\n"); + goto out; + } + + for (i = 0; i < rsize; i++) { + struct wil6210_mbox_ring_desc d; + struct wil6210_mbox_hdr hdr; + size_t delta = i * sizeof(d); + void __iomem *x = wil->csr + HOSTADDR(r.base) + delta; + + wil_memcpy_fromio_32(&d, x, sizeof(d)); + + seq_printf(s, " [%2x] %s %s%s 0x%08x", i, + d.sync ? "F" : "E", + (r.tail - r.base == delta) ? "t" : " ", + (r.head - r.base == delta) ? "h" : " ", + le32_to_cpu(d.addr)); + if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { + u16 len = le16_to_cpu(hdr.len); + seq_printf(s, " -> %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, + le16_to_cpu(hdr.type), hdr.flags); + if (len <= MAX_MBOXITEM_SIZE) { + int n = 0; + unsigned char printbuf[16 * 3 + 2]; + unsigned char databuf[MAX_MBOXITEM_SIZE]; + void __iomem *src = wmi_buffer(wil, d.addr) + + sizeof(struct wil6210_mbox_hdr); + /* + * No need to check @src for validity - + * we already validated @d.addr while + * reading header + */ + wil_memcpy_fromio_32(databuf, src, len); + while (n < len) { + int l = min(len - n, 16); + hex_dump_to_buffer(databuf + n, l, + 16, 1, printbuf, + sizeof(printbuf), + false); + seq_printf(s, " : %s\n", printbuf); + n += l; + } + } + } else { + seq_printf(s, "\n"); + } + } + out: + seq_printf(s, "}\n"); +} + +static int wil_mbox_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + + wil_print_ring(s, "tx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx)); + wil_print_ring(s, "rx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx)); + + return 0; +} + +static int wil_mbox_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_mbox_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_mbox = { + .open = wil_mbox_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int wil_debugfs_iomem_x32_set(void *data, u64 val) +{ + iowrite32(val, (void __iomem *)data); + wmb(); /* make sure write propagated to HW */ + + return 0; +} + +static int wil_debugfs_iomem_x32_get(void *data, u64 *val) +{ + *val = ioread32((void __iomem *)data); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, + wil_debugfs_iomem_x32_set, "0x%08llx\n"); + +static struct dentry *wil_debugfs_create_iomem_x32(const char *name, + mode_t mode, + struct dentry *parent, + void __iomem *value) +{ + return debugfs_create_file(name, mode, parent, (void * __force)value, + &fops_iomem_x32); +} + +static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, + const char *name, + struct dentry *parent, u32 off) +{ + struct dentry *d = debugfs_create_dir(name, parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d, + wil->csr + off); + wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d, + wil->csr + off + 4); + wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d, + wil->csr + off + 8); + wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d, + wil->csr + off + 12); + wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d, + wil->csr + off + 16); + wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d, + wil->csr + off + 20); + wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d, + wil->csr + off + 24); + + return 0; +} + +static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, + struct dentry *parent) +{ + struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW)); + + return 0; +} + +static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, + struct dentry *parent) +{ + struct dentry *d = debugfs_create_dir("ITR_CNT", parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); + wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_DATA)); + wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); + + return 0; +} + +static int wil_memread_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr)); + + if (a) + seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a)); + else + seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); + + return 0; +} + +static int wil_memread_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_memread_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_memread = { + .open = wil_memread_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int wil_default_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + enum { max_count = 4096 }; + struct debugfs_blob_wrapper *blob = file->private_data; + loff_t pos = *ppos; + size_t available = blob->size; + void *buf; + size_t ret; + + if (pos < 0) + return -EINVAL; + + if (pos >= available || !count) + return 0; + + if (count > available - pos) + count = available - pos; + if (count > max_count) + count = max_count; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + wil_memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data + + pos, count); + + ret = copy_to_user(user_buf, buf, count); + kfree(buf); + if (ret == count) + return -EFAULT; + + count -= ret; + *ppos = pos + count; + + return count; +} + +static const struct file_operations fops_ioblob = { + .read = wil_read_file_ioblob, + .open = wil_default_open, + .llseek = default_llseek, +}; + +static +struct dentry *wil_debugfs_create_ioblob(const char *name, + mode_t mode, + struct dentry *parent, + struct debugfs_blob_wrapper *blob) +{ + return debugfs_create_file(name, mode, parent, blob, &fops_ioblob); +} +/*---reset---*/ +static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct net_device *ndev = wil_to_ndev(wil); + + /** + * BUG: + * this code does NOT sync device state with the rest of system + * use with care, debug only!!! + */ + rtnl_lock(); + dev_close(ndev); + ndev->flags &= ~IFF_UP; + rtnl_unlock(); + wil_reset(wil); + + return len; +} + +static const struct file_operations fops_reset = { + .write = wil_write_file_reset, + .open = wil_default_open, +}; +/*---------Tx descriptor------------*/ + +static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + struct vring *vring = &(wil->vring_tx[0]); + + if (!vring->va) { + seq_printf(s, "No Tx VRING\n"); + return 0; + } + + if (dbg_txdesc_index < vring->size) { + volatile struct vring_tx_desc *d = + &(vring->va[dbg_txdesc_index].tx); + volatile u32 *u = (volatile u32 *)d; + struct sk_buff *skb = vring->ctx[dbg_txdesc_index]; + + seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); + seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[0], u[1], u[2], u[3]); + seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[4], u[5], u[6], u[7]); + seq_printf(s, " SKB = %p\n", skb); + + if (skb) { + unsigned char printbuf[16 * 3 + 2]; + int i = 0; + int len = skb_headlen(skb); + void *p = skb->data; + + seq_printf(s, " len = %d\n", len); + + while (i < len) { + int l = min(len - i, 16); + hex_dump_to_buffer(p + i, l, 16, 1, printbuf, + sizeof(printbuf), false); + seq_printf(s, " : %s\n", printbuf); + i += l; + } + } + seq_printf(s, "}\n"); + } else { + seq_printf(s, "TxDesc index (%d) >= size (%d)\n", + dbg_txdesc_index, vring->size); + } + + return 0; +} + +static int wil_txdesc_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_txdesc_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_txdesc = { + .open = wil_txdesc_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +/*---------beamforming------------*/ +static int wil_bf_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + seq_printf(s, + "TSF : 0x%016llx\n" + "TxMCS : %d\n" + "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n", + wil->stats.tsf, wil->stats.bf_mcs, + wil->stats.my_rx_sector, wil->stats.my_tx_sector, + wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); + return 0; +} + +static int wil_bf_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_bf_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_bf = { + .open = wil_bf_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; +/*---------SSID------------*/ +static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wireless_dev *wdev = wil_to_wdev(wil); + + return simple_read_from_buffer(user_buf, count, ppos, + wdev->ssid, wdev->ssid_len); +} + +static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wireless_dev *wdev = wil_to_wdev(wil); + struct net_device *ndev = wil_to_ndev(wil); + + if (*ppos != 0) { + wil_err(wil, "Unable to set SSID substring from [%d]\n", + (int)*ppos); + return -EINVAL; + } + + if (count > sizeof(wdev->ssid)) { + wil_err(wil, "SSID too long, len = %d\n", (int)count); + return -EINVAL; + } + if (netif_running(ndev)) { + wil_err(wil, "Unable to change SSID on running interface\n"); + return -EINVAL; + } + + wdev->ssid_len = count; + return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, + buf, count); +} + +static const struct file_operations fops_ssid = { + .read = wil_read_file_ssid, + .write = wil_write_file_ssid, + .open = wil_default_open, +}; + +/*----------------*/ +int wil6210_debugfs_init(struct wil6210_priv *wil) +{ + struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, + wil_to_wiphy(wil)->debugfsdir); + + if (IS_ERR_OR_NULL(dbg)) + return -ENODEV; + + debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); + debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); + debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); + debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg, + &dbg_txdesc_index); + debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf); + debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); + debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, + &wil->secure_pcp); + + wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, + HOSTADDR(RGF_USER_USER_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg, + HOSTADDR(RGF_DMA_EP_TX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg, + HOSTADDR(RGF_DMA_EP_RX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg, + HOSTADDR(RGF_DMA_EP_MISC_ICR)); + wil6210_debugfs_create_pseudo_ISR(wil, dbg); + wil6210_debugfs_create_ITR_CNT(wil, dbg); + + debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr); + debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); + + debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); + + wil->rgf_blob.data = (void * __force)wil->csr + 0; + wil->rgf_blob.size = 0xa000; + wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob); + + wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000; + wil->fw_code_blob.size = 0x40000; + wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg, + &wil->fw_code_blob); + + wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000; + wil->fw_data_blob.size = 0x8000; + wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg, + &wil->fw_data_blob); + + wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000; + wil->fw_peri_blob.size = 0x18000; + wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg, + &wil->fw_peri_blob); + + wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000; + wil->uc_code_blob.size = 0x10000; + wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg, + &wil->uc_code_blob); + + wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000; + wil->uc_data_blob.size = 0x4000; + wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg, + &wil->uc_data_blob); + + return 0; +} + +void wil6210_debugfs_remove(struct wil6210_priv *wil) +{ + debugfs_remove_recursive(wil->debug); + wil->debug = NULL; +} diff --git a/trunk/drivers/net/wireless/ath/wil6210/interrupt.c b/trunk/drivers/net/wireless/ath/wil6210/interrupt.c new file mode 100644 index 000000000000..38049da71049 --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/interrupt.c @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "wil6210.h" + +/** + * Theory of operation: + * + * There is ISR pseudo-cause register, + * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE + * Its bits represents OR'ed bits from 3 real ISR registers: + * TX, RX, and MISC. + * + * Registers may be configured to either "write 1 to clear" or + * "clear on read" mode + * + * When handling interrupt, one have to mask/unmask interrupts for the + * real ISR registers, or hardware may malfunction. + * + */ + +#define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) +#define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE +#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ + BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) +#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT) + +#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ + BIT_DMA_PSEUDO_CAUSE_TX | \ + BIT_DMA_PSEUDO_CAUSE_MISC)) + +#if defined(CONFIG_WIL6210_ISR_COR) +/* configure to Clear-On-Read mode */ +#define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ + +} +#else /* defined(CONFIG_WIL6210_ISR_COR) */ +/* configure to Write-1-to-Clear mode */ +#define WIL_ICR_ICC_VALUE (0UL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ + iowrite32(x, addr); +} +#endif /* defined(CONFIG_WIL6210_ISR_COR) */ + +static inline u32 wil_ioread32_and_clear(void __iomem *addr) +{ + u32 x = ioread32(addr); + + wil_icr_clear(x, addr); + + return x; +} + +static void wil6210_mask_irq_tx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_rx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_misc(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + + clear_bit(wil_status_irqen, &wil->status); +} + +static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_TX, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_RX, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_misc(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_MISC, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + set_bit(wil_status_irqen, &wil->status); + + iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); +} + +void wil6210_disable_irq(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + wil6210_mask_irq_tx(wil); + wil6210_mask_irq_rx(wil); + wil6210_mask_irq_misc(wil); + wil6210_mask_irq_pseudo(wil); +} + +void wil6210_enable_irq(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICC)); + + wil6210_unmask_irq_pseudo(wil); + wil6210_unmask_irq_tx(wil); + wil6210_unmask_irq_rx(wil); + wil6210_unmask_irq_misc(wil); +} + +static irqreturn_t wil6210_irq_rx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: RX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_rx(wil); + + if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { + wil_dbg_IRQ(wil, "RX done\n"); + isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; + wil_rx_handle(wil); + } + + if (isr) + wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); + + wil6210_unmask_irq_rx(wil); + + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_tx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: TX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_tx(wil); + + if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { + uint i; + wil_dbg_IRQ(wil, "TX done\n"); + isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; + for (i = 0; i < 24; i++) { + u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); + if (isr & mask) { + isr &= ~mask; + wil_dbg_IRQ(wil, "TX done(%i)\n", i); + wil_tx_complete(wil, i); + } + } + } + + if (isr) + wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); + + wil6210_unmask_irq_tx(wil); + + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_misc(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: MISC\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_misc(wil); + + if (isr & ISR_MISC_FW_READY) { + wil_dbg_IRQ(wil, "IRQ: FW ready\n"); + /** + * Actual FW ready indicated by the + * WMI_FW_READY_EVENTID + */ + isr &= ~ISR_MISC_FW_READY; + } + + wil->isr_misc = isr; + + if (isr) { + return IRQ_WAKE_THREAD; + } else { + wil6210_unmask_irq_misc(wil); + return IRQ_HANDLED; + } +} + +static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil->isr_misc; + + wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr); + + if (isr & ISR_MISC_MBOX_EVT) { + wil_dbg_IRQ(wil, "MBOX event\n"); + wmi_recv_cmd(wil); + isr &= ~ISR_MISC_MBOX_EVT; + } + + if (isr) + wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); + + wil->isr_misc = 0; + + wil6210_unmask_irq_misc(wil); + + return IRQ_HANDLED; +} + +/** + * thread IRQ handler + */ +static irqreturn_t wil6210_thread_irq(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + + wil_dbg_IRQ(wil, "Thread IRQ\n"); + /* Discover real IRQ cause */ + if (wil->isr_misc) + wil6210_irq_misc_thread(irq, cookie); + + wil6210_unmask_irq_pseudo(wil); + + return IRQ_HANDLED; +} + +/* DEBUG + * There is subtle bug in hardware that causes IRQ to raise when it should be + * masked. It is quite rare and hard to debug. + * + * Catch irq issue if it happens and print all I can. + */ +static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) +{ + if (!test_bit(wil_status_irqen, &wil->status)) { + u32 icm_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_rx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_tx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_misc = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_misc = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_misc = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMV)); + wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" + "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", + pseudo_cause, + icm_rx, icr_rx, imv_rx, + icm_tx, icr_tx, imv_tx, + icm_misc, icr_misc, imv_misc); + + return -EINVAL; + } + + return 0; +} + +static irqreturn_t wil6210_hardirq(int irq, void *cookie) +{ + irqreturn_t rc = IRQ_HANDLED; + struct wil6210_priv *wil = cookie; + u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + + /** + * pseudo_cause is Clear-On-Read, no need to ACK + */ + if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)) + return IRQ_NONE; + + /* FIXME: IRQ mask debug */ + if (wil6210_debug_irq_mask(wil, pseudo_cause)) + return IRQ_NONE; + + wil6210_mask_irq_pseudo(wil); + + /* Discover real IRQ cause + * There are 2 possible phases for every IRQ: + * - hard IRQ handler called right here + * - threaded handler called later + * + * Hard IRQ handler reads and clears ISR. + * + * If threaded handler requested, hard IRQ handler + * returns IRQ_WAKE_THREAD and saves ISR register value + * for the threaded handler use. + * + * voting for wake thread - need at least 1 vote + */ + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && + (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && + (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && + (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + /* if thread is requested, it will unmask IRQ */ + if (rc != IRQ_WAKE_THREAD) + wil6210_unmask_irq_pseudo(wil); + + wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause); + + return rc; +} + +static int wil6210_request_3msi(struct wil6210_priv *wil, int irq) +{ + int rc; + /* + * IRQ's are in the following order: + * - Tx + * - Rx + * - Misc + */ + + rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED, + WIL_NAME"_tx", wil); + if (rc) + return rc; + + rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED, + WIL_NAME"_rx", wil); + if (rc) + goto free0; + + rc = request_threaded_irq(irq + 2, wil6210_irq_misc, + wil6210_irq_misc_thread, + IRQF_SHARED, WIL_NAME"_misc", wil); + if (rc) + goto free1; + + return 0; + /* error branch */ +free1: + free_irq(irq + 1, wil); +free0: + free_irq(irq, wil); + + return rc; +} + +int wil6210_init_irq(struct wil6210_priv *wil, int irq) +{ + int rc; + if (wil->n_msi == 3) + rc = wil6210_request_3msi(wil, irq); + else + rc = request_threaded_irq(irq, wil6210_hardirq, + wil6210_thread_irq, + wil->n_msi ? 0 : IRQF_SHARED, + WIL_NAME, wil); + if (rc) + return rc; + + wil6210_enable_irq(wil); + + return 0; +} + +void wil6210_fini_irq(struct wil6210_priv *wil, int irq) +{ + wil6210_disable_irq(wil); + free_irq(irq, wil); + if (wil->n_msi == 3) { + free_irq(irq + 1, wil); + free_irq(irq + 2, wil); + } +} diff --git a/trunk/drivers/net/wireless/ath/wil6210/main.c b/trunk/drivers/net/wireless/ath/wil6210/main.c new file mode 100644 index 000000000000..95fcd361322b --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/main.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" + +/* + * Due to a hardware issue, + * one has to read/write to/from NIC in 32-bit chunks; + * regular memcpy_fromio and siblings will + * not work on 64-bit platform - it uses 64-bit transactions + * + * Force 32-bit transactions to enable NIC on 64-bit platforms + * + * To avoid byte swap on big endian host, __raw_{read|write}l + * should be used - {read|write}l would swap bytes to provide + * little endian on PCI value in host endianness. + */ +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, + size_t count) +{ + u32 *d = dst; + const volatile u32 __iomem *s = src; + + /* size_t is unsigned, if (count%4 != 0) it will wrap */ + for (count += 4; count > 4; count -= 4) + *d++ = __raw_readl(s++); +} + +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, + size_t count) +{ + volatile u32 __iomem *d = dst; + const u32 *s = src; + + for (count += 4; count > 4; count -= 4) + __raw_writel(*s++, d++); +} + +static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + uint i; + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + + wil_dbg(wil, "%s()\n", __func__); + + wil_link_off(wil); + clear_bit(wil_status_fwconnected, &wil->status); + + switch (wdev->sme_state) { + case CFG80211_SME_CONNECTED: + cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, + NULL, 0, GFP_KERNEL); + break; + case CFG80211_SME_CONNECTING: + cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + break; + default: + ; + } + + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) + wil_vring_fini_tx(wil, i); +} + +static void wil_disconnect_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, + struct wil6210_priv, disconnect_worker); + + _wil6210_disconnect(wil, NULL); +} + +static void wil_connect_timer_fn(ulong x) +{ + struct wil6210_priv *wil = (void *)x; + + wil_dbg(wil, "Connect timeout\n"); + + /* reschedule to thread context - disconnect won't + * run from atomic context + */ + schedule_work(&wil->disconnect_worker); +} + +int wil_priv_init(struct wil6210_priv *wil) +{ + wil_dbg(wil, "%s()\n", __func__); + + mutex_init(&wil->mutex); + mutex_init(&wil->wmi_mutex); + + init_completion(&wil->wmi_ready); + + wil->pending_connect_cid = -1; + setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); + + INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker); + INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); + INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); + + INIT_LIST_HEAD(&wil->pending_wmi_ev); + spin_lock_init(&wil->wmi_ev_lock); + + wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); + if (!wil->wmi_wq) + return -EAGAIN; + + wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); + if (!wil->wmi_wq_conn) { + destroy_workqueue(wil->wmi_wq); + return -EAGAIN; + } + + /* make shadow copy of registers that should not change on run time */ + wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + + return 0; +} + +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + del_timer_sync(&wil->connect_timer); + _wil6210_disconnect(wil, bssid); +} + +void wil_priv_deinit(struct wil6210_priv *wil) +{ + cancel_work_sync(&wil->disconnect_worker); + wil6210_disconnect(wil, NULL); + wmi_event_flush(wil); + destroy_workqueue(wil->wmi_wq_conn); + destroy_workqueue(wil->wmi_wq); +} + +static void wil_target_reset(struct wil6210_priv *wil) +{ + wil_dbg(wil, "Resetting...\n"); + + /* register write */ +#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) + /* register set = read, OR, write */ +#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ + wil->csr + HOSTADDR(a)) + + /* hpal_perst_from_pad_src_n_mask */ + S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); + /* car_perst_rst_src_n_mask */ + S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); + + W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ + W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ + + msleep(100); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); + + msleep(100); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + + msleep(2000); + + W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */ + + msleep(2000); + + wil_dbg(wil, "Reset completed\n"); + +#undef W +#undef S +} + +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) +{ + le32_to_cpus(&r->base); + le16_to_cpus(&r->entry_size); + le16_to_cpus(&r->size); + le32_to_cpus(&r->tail); + le32_to_cpus(&r->head); +} + +static int wil_wait_for_fw_ready(struct wil6210_priv *wil) +{ + ulong to = msecs_to_jiffies(1000); + ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); + if (0 == left) { + wil_err(wil, "Firmware not ready\n"); + return -ETIME; + } else { + wil_dbg(wil, "FW ready after %d ms\n", + jiffies_to_msecs(to-left)); + } + return 0; +} + +/* + * We reset all the structures, and we reset the UMAC. + * After calling this routine, you're expected to reload + * the firmware. + */ +int wil_reset(struct wil6210_priv *wil) +{ + int rc; + + cancel_work_sync(&wil->disconnect_worker); + wil6210_disconnect(wil, NULL); + + wmi_event_flush(wil); + + flush_workqueue(wil->wmi_wq); + flush_workqueue(wil->wmi_wq_conn); + + wil6210_disable_irq(wil); + wil->status = 0; + + /* TODO: put MAC in reset */ + wil_target_reset(wil); + + /* init after reset */ + wil->pending_connect_cid = -1; + INIT_COMPLETION(wil->wmi_ready); + + /* make shadow copy of registers that should not change on run time */ + wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + + /* TODO: release MAC reset */ + wil6210_enable_irq(wil); + + /* we just started MAC, wait for FW ready */ + rc = wil_wait_for_fw_ready(wil); + + return rc; +} + + +void wil_link_on(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + wil_dbg(wil, "%s()\n", __func__); + + netif_carrier_on(ndev); + netif_tx_wake_all_queues(ndev); +} + +void wil_link_off(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + wil_dbg(wil, "%s()\n", __func__); + + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); +} + +static int __wil_up(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct ieee80211_channel *channel = wdev->preset_chandef.chan; + int rc; + int bi; + u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + rc = wil_reset(wil); + if (rc) + return rc; + + /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ + wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + wil_dbg(wil, "type: STATION\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_AP: + wil_dbg(wil, "type: AP\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_CLIENT: + wil_dbg(wil, "type: P2P_CLIENT\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_GO: + wil_dbg(wil, "type: P2P_GO\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_MONITOR: + wil_dbg(wil, "type: Monitor\n"); + bi = 0; + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ + break; + default: + return -EOPNOTSUPP; + } + + /* Apply profile in the following order: */ + /* SSID and channel for the AP */ + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (wdev->ssid_len == 0) { + wil_err(wil, "SSID not set\n"); + return -EINVAL; + } + wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); + if (channel) + wmi_set_channel(wil, channel->hw_value); + break; + default: + ; + } + + /* MAC address - pre-requisite for other commands */ + wmi_set_mac_address(wil, ndev->dev_addr); + + /* Set up beaconing if required. */ + rc = wmi_set_bcon(wil, bi, wmi_nettype); + if (rc) + return rc; + + /* Rx VRING. After MAC and beacon */ + wil_rx_init(wil); + + return 0; +} + +int wil_up(struct wil6210_priv *wil) +{ + int rc; + + mutex_lock(&wil->mutex); + rc = __wil_up(wil); + mutex_unlock(&wil->mutex); + + return rc; +} + +static int __wil_down(struct wil6210_priv *wil) +{ + if (wil->scan_request) { + cfg80211_scan_done(wil->scan_request, true); + wil->scan_request = NULL; + } + + wil6210_disconnect(wil, NULL); + wil_rx_fini(wil); + + return 0; +} + +int wil_down(struct wil6210_priv *wil) +{ + int rc; + + mutex_lock(&wil->mutex); + rc = __wil_down(wil); + mutex_unlock(&wil->mutex); + + return rc; +} diff --git a/trunk/drivers/net/wireless/ath/wil6210/netdev.c b/trunk/drivers/net/wireless/ath/wil6210/netdev.c new file mode 100644 index 000000000000..3068b5cb53a7 --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/netdev.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "wil6210.h" + +static int wil_open(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + return wil_up(wil); +} + +static int wil_stop(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + return wil_down(wil); +} + +/* + * AC to queue mapping + * + * AC_VO -> queue 3 + * AC_VI -> queue 2 + * AC_BE -> queue 1 + * AC_BK -> queue 0 + */ +static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb) +{ + static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; + struct wil6210_priv *wil = ndev_to_wil(ndev); + u16 rc; + + skb->priority = cfg80211_classify8021d(skb); + + rc = wil_1d_to_queue[skb->priority]; + + wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority, + (int)rc); + + return rc; +} + +static const struct net_device_ops wil_netdev_ops = { + .ndo_open = wil_open, + .ndo_stop = wil_stop, + .ndo_start_xmit = wil_start_xmit, + .ndo_select_queue = wil_select_queue, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +void *wil_if_alloc(struct device *dev, void __iomem *csr) +{ + struct net_device *ndev; + struct wireless_dev *wdev; + struct wil6210_priv *wil; + struct ieee80211_channel *ch; + int rc = 0; + + wdev = wil_cfg80211_init(dev); + if (IS_ERR(wdev)) { + dev_err(dev, "wil_cfg80211_init failed\n"); + return wdev; + } + + wil = wdev_to_wil(wdev); + wil->csr = csr; + wil->wdev = wdev; + + rc = wil_priv_init(wil); + if (rc) { + dev_err(dev, "wil_priv_init failed\n"); + goto out_wdev; + } + + wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */ + /* default monitor channel */ + ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; + cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); + + ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1); + if (!ndev) { + dev_err(dev, "alloc_netdev_mqs failed\n"); + rc = -ENOMEM; + goto out_priv; + } + + ndev->netdev_ops = &wil_netdev_ops; + ndev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); + wdev->netdev = ndev; + + wil_link_off(wil); + + return wil; + + out_priv: + wil_priv_deinit(wil); + + out_wdev: + wil_wdev_free(wil); + + return ERR_PTR(rc); +} + +void wil_if_free(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + if (!ndev) + return; + + free_netdev(ndev); + wil_priv_deinit(wil); + wil_wdev_free(wil); +} + +int wil_if_add(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + int rc; + + rc = register_netdev(ndev); + if (rc < 0) { + dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); + return rc; + } + + wil_link_off(wil); + + return 0; +} + +void wil_if_remove(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + unregister_netdev(ndev); +} diff --git a/trunk/drivers/net/wireless/ath/wil6210/pcie_bus.c b/trunk/drivers/net/wireless/ath/wil6210/pcie_bus.c new file mode 100644 index 000000000000..0fc83edd6bad --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" + +static int use_msi = 1; +module_param(use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, + " Use MSI interrupt: " + "0 - don't, 1 - (default) - single, or 3"); + +/* Bus ops */ +static int wil_if_pcie_enable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + int rc; + + pci_set_master(pdev); + + /* + * how many MSI interrupts to request? + */ + switch (use_msi) { + case 3: + case 1: + case 0: + break; + default: + wil_err(wil, "Invalid use_msi=%d, default to 1\n", + use_msi); + use_msi = 1; + } + wil->n_msi = use_msi; + if (wil->n_msi) { + wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi); + rc = pci_enable_msi_block(pdev, wil->n_msi); + if (rc && (wil->n_msi == 3)) { + wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); + wil->n_msi = 1; + rc = pci_enable_msi_block(pdev, wil->n_msi); + } + if (rc) { + wil_err(wil, "pci_enable_msi failed, use INTx\n"); + wil->n_msi = 0; + } + } else { + wil_dbg(wil, "MSI interrupts disabled, use INTx\n"); + } + + rc = wil6210_init_irq(wil, pdev->irq); + if (rc) + goto stop_master; + + /* need reset here to obtain MAC */ + rc = wil_reset(wil); + if (rc) + goto release_irq; + + return 0; + + release_irq: + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + stop_master: + pci_clear_master(pdev); + return rc; +} + +static int wil_if_pcie_disable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + + pci_clear_master(pdev); + /* disable and release IRQ */ + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + /* TODO: disable HW */ + + return 0; +} + +static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct wil6210_priv *wil; + struct device *dev = &pdev->dev; + void __iomem *csr; + int rc; + + /* check HW */ + dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", + (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); + + if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { + dev_err(&pdev->dev, "Not " WIL_NAME "? " + "BAR0 size is %lu while expecting %lu\n", + (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); + return -ENODEV; + } + + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + /* rollback to err_disable_pdev */ + + rc = pci_request_region(pdev, 0, WIL_NAME); + if (rc) { + dev_err(&pdev->dev, "pci_request_region failed\n"); + goto err_disable_pdev; + } + /* rollback to err_release_reg */ + + csr = pci_ioremap_bar(pdev, 0); + if (!csr) { + dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); + rc = -ENODEV; + goto err_release_reg; + } + /* rollback to err_iounmap */ + dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); + + wil = wil_if_alloc(dev, csr); + if (IS_ERR(wil)) { + rc = (int)PTR_ERR(wil); + dev_err(dev, "wil_if_alloc failed: %d\n", rc); + goto err_iounmap; + } + /* rollback to if_free */ + + pci_set_drvdata(pdev, wil); + wil->pdev = pdev; + + /* FW should raise IRQ when ready */ + rc = wil_if_pcie_enable(wil); + if (rc) { + wil_err(wil, "Enable device failed\n"); + goto if_free; + } + /* rollback to bus_disable */ + + rc = wil_if_add(wil); + if (rc) { + wil_err(wil, "wil_if_add failed: %d\n", rc); + goto bus_disable; + } + + wil6210_debugfs_init(wil); + + /* check FW is alive */ + wmi_echo(wil); + + return 0; + + bus_disable: + wil_if_pcie_disable(wil); + if_free: + wil_if_free(wil); + err_iounmap: + pci_iounmap(pdev, csr); + err_release_reg: + pci_release_region(pdev, 0); + err_disable_pdev: + pci_disable_device(pdev); + + return rc; +} + +static void wil_pcie_remove(struct pci_dev *pdev) +{ + struct wil6210_priv *wil = pci_get_drvdata(pdev); + + wil6210_debugfs_remove(wil); + wil_if_pcie_disable(wil); + wil_if_remove(wil); + wil_if_free(wil); + pci_iounmap(pdev, wil->csr); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { + { PCI_DEVICE(0x1ae9, 0x0301) }, + { /* end: all zeroes */ }, +}; +MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); + +static struct pci_driver wil6210_driver = { + .probe = wil_pcie_probe, + .remove = wil_pcie_remove, + .id_table = wil6210_pcie_ids, + .name = WIL_NAME, +}; + +module_pci_driver(wil6210_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Qualcomm Atheros "); +MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card"); diff --git a/trunk/drivers/net/wireless/ath/wil6210/txrx.c b/trunk/drivers/net/wireless/ath/wil6210/txrx.c new file mode 100644 index 000000000000..f29c294413cf --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/txrx.c @@ -0,0 +1,871 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" +#include "txrx.h" + +static bool rtap_include_phy_info; +module_param(rtap_include_phy_info, bool, S_IRUGO); +MODULE_PARM_DESC(rtap_include_phy_info, + " Include PHY info in the radiotap header, default - no"); + +static inline int wil_vring_is_empty(struct vring *vring) +{ + return vring->swhead == vring->swtail; +} + +static inline u32 wil_vring_next_tail(struct vring *vring) +{ + return (vring->swtail + 1) % vring->size; +} + +static inline void wil_vring_advance_head(struct vring *vring, int n) +{ + vring->swhead = (vring->swhead + n) % vring->size; +} + +static inline int wil_vring_is_full(struct vring *vring) +{ + return wil_vring_next_tail(vring) == vring->swhead; +} +/* + * Available space in Tx Vring + */ +static inline int wil_vring_avail_tx(struct vring *vring) +{ + u32 swhead = vring->swhead; + u32 swtail = vring->swtail; + int used = (vring->size + swhead - swtail) % vring->size; + + return vring->size - used - 1; +} + +static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + uint i; + + BUILD_BUG_ON(sizeof(vring->va[0]) != 32); + + vring->swhead = 0; + vring->swtail = 0; + vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL); + if (!vring->ctx) { + wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n", + vring->size); + vring->va = NULL; + return -ENOMEM; + } + /* + * vring->va should be aligned on its size rounded up to power of 2 + * This is granted by the dma_alloc_coherent + */ + vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); + if (!vring->va) { + wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n", + vring->size); + kfree(vring->ctx); + vring->ctx = NULL; + return -ENOMEM; + } + /* initially, all descriptors are SW owned + * For Tx and Rx, ownership bit is at the same location, thus + * we can use any + */ + for (i = 0; i < vring->size; i++) { + volatile struct vring_tx_desc *d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + } + + wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, + vring->va, (unsigned long long)vring->pa, vring->ctx); + + return 0; +} + +static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, + int tx) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + + while (!wil_vring_is_empty(vring)) { + if (tx) { + volatile struct vring_tx_desc *d = + &vring->va[vring->swtail].tx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, + DMA_TO_DEVICE); + } + vring->swtail = wil_vring_next_tail(vring); + } else { /* rx */ + volatile struct vring_rx_desc *d = + &vring->va[vring->swtail].rx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, d->dma.length, + DMA_FROM_DEVICE); + kfree_skb(skb); + wil_vring_advance_head(vring, 1); + } + } + dma_free_coherent(dev, sz, (void *)vring->va, vring->pa); + kfree(vring->ctx); + vring->pa = 0; + vring->va = NULL; + vring->ctx = NULL; +} + +/** + * Allocate one skb for Rx VRING + * + * Safe to call from IRQ + */ +static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, + u32 i, int headroom) +{ + struct device *dev = wil_to_dev(wil); + unsigned int sz = RX_BUF_LEN; + volatile struct vring_rx_desc *d = &(vring->va[i].rx); + dma_addr_t pa; + + /* TODO align */ + struct sk_buff *skb = dev_alloc_skb(sz + headroom); + if (unlikely(!skb)) + return -ENOMEM; + + skb_reserve(skb, headroom); + skb_put(skb, sz); + + pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) { + kfree_skb(skb); + return -ENOMEM; + } + + d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + /* ip_length don't care */ + /* b11 don't care */ + /* error don't care */ + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = sz; + vring->ctx[i] = skb; + + return 0; +} + +/** + * Adds radiotap header + * + * Any error indicated as "Bad FCS" + * + * Vendor data for 04:ce:14-1 (Wilocity-1) consists of: + * - Rx descriptor: 32 bytes + * - Phy info + */ +static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, + struct sk_buff *skb, + volatile struct vring_rx_desc *d) +{ + struct wireless_dev *wdev = wil->wdev; + struct wil6210_rtap { + struct ieee80211_radiotap_header rthdr; + /* fields should be in the order of bits in rthdr.it_present */ + /* flags */ + u8 flags; + /* channel */ + __le16 chnl_freq __aligned(2); + __le16 chnl_flags; + /* MCS */ + u8 mcs_present; + u8 mcs_flags; + u8 mcs_index; + } __packed; + struct wil6210_rtap_vendor { + struct wil6210_rtap rtap; + /* vendor */ + u8 vendor_oui[3] __aligned(2); + u8 vendor_ns; + __le16 vendor_skip; + u8 vendor_data[0]; + } __packed; + struct wil6210_rtap_vendor *rtap_vendor; + int rtap_len = sizeof(struct wil6210_rtap); + int phy_length = 0; /* phy info header size, bytes */ + static char phy_data[128]; + struct ieee80211_channel *ch = wdev->preset_chandef.chan; + + if (rtap_include_phy_info) { + rtap_len = sizeof(*rtap_vendor) + sizeof(*d); + /* calculate additional length */ + if (d->dma.status & RX_DMA_STATUS_PHY_INFO) { + /** + * PHY info starts from 8-byte boundary + * there are 8-byte lines, last line may be partially + * written (HW bug), thus FW configures for last line + * to be excessive. Driver skips this last line. + */ + int len = min_t(int, 8 + sizeof(phy_data), + wil_rxdesc_phy_length(d)); + if (len > 8) { + void *p = skb_tail_pointer(skb); + void *pa = PTR_ALIGN(p, 8); + if (skb_tailroom(skb) >= len + (pa - p)) { + phy_length = len - 8; + memcpy(phy_data, pa, phy_length); + } + } + } + rtap_len += phy_length; + } + + if (skb_headroom(skb) < rtap_len && + pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) { + wil_err(wil, "Unable to expand headrom to %d\n", rtap_len); + return; + } + + rtap_vendor = (void *)skb_push(skb, rtap_len); + memset(rtap_vendor, 0, rtap_len); + + rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION; + rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len); + rtap_vendor->rtap.rthdr.it_present = cpu_to_le32( + (1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_MCS)); + if (d->dma.status & RX_DMA_STATUS_ERROR) + rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS; + + rtap_vendor->rtap.chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320); + rtap_vendor->rtap.chnl_flags = cpu_to_le16(0); + + rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS; + rtap_vendor->rtap.mcs_flags = 0; + rtap_vendor->rtap.mcs_index = wil_rxdesc_mcs(d); + + if (rtap_include_phy_info) { + rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 << + IEEE80211_RADIOTAP_VENDOR_NAMESPACE); + /* OUI for Wilocity 04:ce:14 */ + rtap_vendor->vendor_oui[0] = 0x04; + rtap_vendor->vendor_oui[1] = 0xce; + rtap_vendor->vendor_oui[2] = 0x14; + rtap_vendor->vendor_ns = 1; + /* Rx descriptor + PHY data */ + rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) + + phy_length); + memcpy(rtap_vendor->vendor_data, (void *)d, sizeof(*d)); + memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data, + phy_length); + } +} + +/* + * Fast swap in place between 2 registers + */ +static void wil_swap_u16(u16 *a, u16 *b) +{ + *a ^= *b; + *b ^= *a; + *a ^= *b; +} + +static void wil_swap_ethaddr(void *data) +{ + struct ethhdr *eth = data; + u16 *s = (u16 *)eth->h_source; + u16 *d = (u16 *)eth->h_dest; + + wil_swap_u16(s++, d++); + wil_swap_u16(s++, d++); + wil_swap_u16(s, d); +} + +/** + * reap 1 frame from @swhead + * + * Safe to call from IRQ + */ +static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, + struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + struct net_device *ndev = wil_to_ndev(wil); + volatile struct vring_rx_desc *d; + struct sk_buff *skb; + dma_addr_t pa; + unsigned int sz = RX_BUF_LEN; + u8 ftype; + u8 ds_bits; + + if (wil_vring_is_empty(vring)) + return NULL; + + d = &(vring->va[vring->swhead].rx); + if (!(d->dma.status & RX_DMA_STATUS_DU)) { + /* it is not error, we just reached end of Rx done area */ + return NULL; + } + + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); + skb_trim(skb, d->dma.length); + + wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); + + /* use radiotap header only if required */ + if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) + wil_rx_add_radiotap_header(wil, skb, d); + + wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); + wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + wil_vring_advance_head(vring, 1); + + /* no extra checks if in sniffer mode */ + if (ndev->type != ARPHRD_ETHER) + return skb; + /* + * Non-data frames may be delivered through Rx DMA channel (ex: BAR) + * Driver should recognize it by frame type, that is found + * in Rx descriptor. If type is not data, it is 802.11 frame as is + */ + ftype = wil_rxdesc_ftype(d) << 2; + if (ftype != IEEE80211_FTYPE_DATA) { + wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype); + /* TODO: process it */ + kfree_skb(skb); + return NULL; + } + + if (skb->len < ETH_HLEN) { + wil_err(wil, "Short frame, len = %d\n", skb->len); + /* TODO: process it (i.e. BAR) */ + kfree_skb(skb); + return NULL; + } + + ds_bits = wil_rxdesc_ds_bits(d); + if (ds_bits == 1) { + /* + * HW bug - in ToDS mode, i.e. Rx on AP side, + * addresses get swapped + */ + wil_swap_ethaddr(skb->data); + } + + return skb; +} + +/** + * allocate and fill up to @count buffers in rx ring + * buffers posted at @swtail + */ +static int wil_rx_refill(struct wil6210_priv *wil, int count) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + u32 next_tail; + int rc = 0; + int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ? + WIL6210_RTAP_SIZE : 0; + + for (; next_tail = wil_vring_next_tail(v), + (next_tail != v->swhead) && (count-- > 0); + v->swtail = next_tail) { + rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom); + if (rc) { + wil_err(wil, "Error %d in wil_rx_refill[%d]\n", + rc, v->swtail); + break; + } + } + iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail)); + + return rc; +} + +/* + * Pass Rx packet to the netif. Update statistics. + */ +static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) +{ + int rc; + unsigned int len = skb->len; + + if (in_interrupt()) + rc = netif_rx(skb); + else + rc = netif_rx_ni(skb); + + if (likely(rc == NET_RX_SUCCESS)) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; + + } else { + ndev->stats.rx_dropped++; + } +} + +/** + * Proceed all completed skb's from Rx VRING + * + * Safe to call from IRQ + */ +void wil_rx_handle(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + struct sk_buff *skb; + + if (!v->va) { + wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); + return; + } + wil_dbg_TXRX(wil, "%s()\n", __func__); + while (NULL != (skb = wil_vring_reap_rx(wil, v))) { + wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + skb_orphan(skb); + + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + skb->dev = ndev; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + + } else { + skb->protocol = eth_type_trans(skb, ndev); + } + + wil_netif_rx_any(skb, ndev); + } + wil_rx_refill(wil, v->size); +} + +int wil_rx_init(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct vring *vring = &wil->vring_rx; + int rc; + struct wmi_cfg_rx_chain_cmd cmd = { + .action = WMI_RX_CHAIN_ADD, + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + }, + .mid = 0, /* TODO - what is it? */ + .decap_trans_type = WMI_DECAP_TYPE_802_3, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event evt; + } __packed evt; + + vring->size = WIL6210_RX_RING_SIZE; + rc = wil_vring_alloc(wil, vring); + if (rc) + return rc; + + cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size); + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { + struct ieee80211_channel *ch = wdev->preset_chandef.chan; + + cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); + if (ch) + cmd.sniffer_cfg.channel = ch->hw_value - 1; + cmd.sniffer_cfg.phy_info_mode = + cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); + cmd.sniffer_cfg.phy_support = + cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) + ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); + } + /* typical time for secure PCP is 840ms */ + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); + if (rc) + goto err_free; + + vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); + + wil_dbg(wil, "Rx init: status %d tail 0x%08x\n", + le32_to_cpu(evt.evt.status), vring->hwtail); + + rc = wil_rx_refill(wil, vring->size); + if (rc) + goto err_free; + + return 0; + err_free: + wil_vring_free(wil, vring, 0); + + return rc; +} + +void wil_rx_fini(struct wil6210_priv *wil) +{ + struct vring *vring = &wil->vring_rx; + + if (vring->va) { + int rc; + struct wmi_cfg_rx_chain_cmd cmd = { + .action = cpu_to_le32(WMI_RX_CHAIN_DEL), + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event cfg; + } __packed wmi_rx_cfg_reply; + + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, + &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), + 100); + wil_vring_free(wil, vring, 0); + } +} + +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, + int cid, int tid) +{ + int rc; + struct wmi_vring_cfg_cmd cmd = { + .action = cpu_to_le32(WMI_VRING_CMD_ADD), + .vring_cfg = { + .tx_sw_ring = { + .max_mpdu_size = cpu_to_le16(TX_BUF_LEN), + }, + .ringid = id, + .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), + .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, + .mac_ctrl = 0, + .to_resolution = 0, + .agg_max_wsize = 16, + .schd_params = { + .priority = cpu_to_le16(0), + .timeslot_us = cpu_to_le16(0xfff), + }, + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_vring_cfg_done_event cmd; + } __packed reply; + struct vring *vring = &wil->vring_tx[id]; + + if (vring->va) { + wil_err(wil, "Tx ring [%d] already allocated\n", id); + rc = -EINVAL; + goto out; + } + + vring->size = size; + rc = wil_vring_alloc(wil, vring); + if (rc) + goto out; + + cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + cmd.vring_cfg.tx_sw_ring.ring_size = cpu_to_le16(vring->size); + + rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), + WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); + if (rc) + goto out_free; + + if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) { + wil_err(wil, "Tx config failed, status 0x%02x\n", + reply.cmd.status); + goto out_free; + } + vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); + + return 0; + out_free: + wil_vring_free(wil, vring, 1); + out: + + return rc; +} + +void wil_vring_fini_tx(struct wil6210_priv *wil, int id) +{ + struct vring *vring = &wil->vring_tx[id]; + + if (!vring->va) + return; + + wil_vring_free(wil, vring, 1); +} + +static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct vring *v = &wil->vring_tx[0]; + + if (v->va) + return v; + + return NULL; +} + +static int wil_tx_desc_map(volatile struct vring_tx_desc *d, + dma_addr_t pa, u32 len) +{ + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + d->dma.ip_length = 0; + /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ + d->dma.b11 = 0/*14 | BIT(7)*/; + d->dma.error = 0; + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = len; + d->dma.d0 = 0; + d->mac.d[0] = 0; + d->mac.d[1] = 0; + d->mac.d[2] = 0; + d->mac.ucode_cmd = 0; + /* use dst index 0 */ + d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) | + (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS); + /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ + d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | + (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); + + return 0; +} + +static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, + struct sk_buff *skb) +{ + struct device *dev = wil_to_dev(wil); + volatile struct vring_tx_desc *d; + u32 swhead = vring->swhead; + int avail = wil_vring_avail_tx(vring); + int nr_frags = skb_shinfo(skb)->nr_frags; + uint f; + int vring_index = vring - wil->vring_tx; + uint i = swhead; + dma_addr_t pa; + + wil_dbg_TXRX(wil, "%s()\n", __func__); + + if (avail < vring->size/8) + netif_tx_stop_all_queues(wil_to_ndev(wil)); + if (avail < 1 + nr_frags) { + wil_err(wil, "Tx ring full. No space for %d fragments\n", + 1 + nr_frags); + return -ENOMEM; + } + d = &(vring->va[i].tx); + + /* FIXME FW can accept only unicast frames for the peer */ + memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); + + pa = dma_map_single(dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); + + wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), + skb->data, (unsigned long long)pa); + wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + if (unlikely(dma_mapping_error(dev, pa))) + return -EINVAL; + /* 1-st segment */ + wil_tx_desc_map(d, pa, skb_headlen(skb)); + d->mac.d[2] |= ((nr_frags + 1) << + MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); + /* middle segments */ + for (f = 0; f < nr_frags; f++) { + const struct skb_frag_struct *frag = + &skb_shinfo(skb)->frags[f]; + int len = skb_frag_size(frag); + i = (swhead + f + 1) % vring->size; + d = &(vring->va[i].tx); + pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) + goto dma_error; + wil_tx_desc_map(d, pa, len); + vring->ctx[i] = NULL; + } + /* for the last seg only */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); + d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); + d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); + + wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + /* advance swhead */ + wil_vring_advance_head(vring, nr_frags + 1); + wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); + iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); + /* hold reference to skb + * to prevent skb release before accounting + * in case of immediate "tx done" + */ + vring->ctx[i] = skb_get(skb); + + return 0; + dma_error: + /* unmap what we have mapped */ + /* Note: increment @f to operate with positive index */ + for (f++; f > 0; f--) { + i = (swhead + f) % vring->size; + d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + if (vring->ctx[i]) + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + else + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + + return -EINVAL; +} + + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + struct vring *vring; + int rc; + + wil_dbg_TXRX(wil, "%s()\n", __func__); + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + goto drop; + } + if (!test_bit(wil_status_fwconnected, &wil->status)) { + wil_err(wil, "FW not connected\n"); + goto drop; + } + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + wil_err(wil, "Xmit in monitor mode not supported\n"); + goto drop; + } + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + rc = wmi_tx_eapol(wil, skb); + } else { + /* find vring */ + vring = wil_find_tx_vring(wil, skb); + if (!vring) { + wil_err(wil, "No Tx VRING available\n"); + goto drop; + } + /* set up vring entry */ + rc = wil_tx_vring(wil, vring, skb); + } + switch (rc) { + case 0: + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + case -ENOMEM: + return NETDEV_TX_BUSY; + default: + ; /* goto drop; */ + break; + } + drop: + netif_tx_stop_all_queues(ndev); + ndev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + + return NET_XMIT_DROP; +} + +/** + * Clean up transmitted skb's from the Tx VRING + * + * Safe to call from IRQ + */ +void wil_tx_complete(struct wil6210_priv *wil, int ringid) +{ + struct device *dev = wil_to_dev(wil); + struct vring *vring = &wil->vring_tx[ringid]; + + if (!vring->va) { + wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); + return; + } + + wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid); + + while (!wil_vring_is_empty(vring)) { + volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx; + dma_addr_t pa; + struct sk_buff *skb; + if (!(d->dma.status & TX_DMA_STATUS_DU)) + break; + + wil_dbg_TXRX(wil, + "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", + vring->swtail, d->dma.length, d->dma.status, + d->dma.error); + wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + d->dma.addr_low = 0; + d->dma.addr_high = 0; + d->dma.length = 0; + d->dma.status = TX_DMA_STATUS_DU; + vring->swtail = wil_vring_next_tail(vring); + } + if (wil_vring_avail_tx(vring) > vring->size/4) + netif_tx_wake_all_queues(wil_to_ndev(wil)); +} diff --git a/trunk/drivers/net/wireless/ath/wil6210/txrx.h b/trunk/drivers/net/wireless/ath/wil6210/txrx.h new file mode 100644 index 000000000000..45a61f597c5c --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/txrx.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WIL6210_TXRX_H +#define WIL6210_TXRX_H + +#define BUF_SW_OWNED (1) +#define BUF_HW_OWNED (0) + +/* size of max. Rx packet */ +#define RX_BUF_LEN (2048) +#define TX_BUF_LEN (2048) +/* how many bytes to reserve for rtap header? */ +#define WIL6210_RTAP_SIZE (128) + +/* Tx/Rx path */ +/* + * Tx descriptor - MAC part + * [dword 0] + * bit 0.. 9 : lifetime_expiry_value:10 + * bit 10 : interrup_en:1 + * bit 11 : status_en:1 + * bit 12..13 : txss_override:2 + * bit 14 : timestamp_insertion:1 + * bit 15 : duration_preserve:1 + * bit 16..21 : reserved0:6 + * bit 22..26 : mcs_index:5 + * bit 27 : mcs_en:1 + * bit 28..29 : reserved1:2 + * bit 30 : reserved2:1 + * bit 31 : sn_preserved:1 + * [dword 1] + * bit 0.. 3 : pkt_mode:4 + * bit 4 : pkt_mode_en:1 + * bit 5.. 7 : reserved0:3 + * bit 8..13 : reserved1:6 + * bit 14 : reserved2:1 + * bit 15 : ack_policy_en:1 + * bit 16..19 : dst_index:4 + * bit 20 : dst_index_en:1 + * bit 21..22 : ack_policy:2 + * bit 23 : lifetime_en:1 + * bit 24..30 : max_retry:7 + * bit 31 : max_retry_en:1 + * [dword 2] + * bit 0.. 7 : num_of_descriptors:8 + * bit 8..17 : reserved:10 + * bit 18..19 : l2_translation_type:2 + * bit 20 : snap_hdr_insertion_en:1 + * bit 21 : vlan_removal_en:1 + * bit 22..31 : reserved0:10 + * [dword 3] + * bit 0.. 31: ucode_cmd:32 + */ +struct vring_tx_mac { + u32 d[3]; + u32 ucode_cmd; +} __packed; + +/* TX MAC Dword 0 */ +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_POS 0 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_LEN 10 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF + +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_POS 10 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_MSK 0x400 + +#define MAC_CFG_DESC_TX_0_STATUS_EN_POS 11 +#define MAC_CFG_DESC_TX_0_STATUS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_STATUS_EN_MSK 0x800 + +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_POS 12 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_LEN 2 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_MSK 0x3000 + +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_POS 14 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_LEN 1 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_MSK 0x4000 + +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_POS 15 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_LEN 1 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_MSK 0x8000 + +#define MAC_CFG_DESC_TX_0_MCS_INDEX_POS 22 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_LEN 5 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_MSK 0x7C00000 + +#define MAC_CFG_DESC_TX_0_MCS_EN_POS 27 +#define MAC_CFG_DESC_TX_0_MCS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_MCS_EN_MSK 0x8000000 + +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_POS 31 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_LEN 1 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_MSK 0x80000000 + +/* TX MAC Dword 1 */ +#define MAC_CFG_DESC_TX_1_PKT_MODE_POS 0 +#define MAC_CFG_DESC_TX_1_PKT_MODE_LEN 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_MSK 0xF + +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_POS 16 +#define MAC_CFG_DESC_TX_1_DST_INDEX_LEN 4 +#define MAC_CFG_DESC_TX_1_DST_INDEX_MSK 0xF0000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS 20 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_POS 21 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_LEN 2 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_MSK 0x600000 + +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_POS 23 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_MSK 0x800000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_POS 24 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_LEN 7 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_MSK 0x7F000000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_POS 31 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_MSK 0x80000000 + +/* TX MAC Dword 2 */ +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS 0 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_LEN 8 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_MSK 0xFF + +#define MAC_CFG_DESC_TX_2_RESERVED_POS 8 +#define MAC_CFG_DESC_TX_2_RESERVED_LEN 10 +#define MAC_CFG_DESC_TX_2_RESERVED_MSK 0x3FF00 + +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS 18 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_LEN 2 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_MSK 0xC0000 + +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS 20 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_POS 21 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_MSK 0x200000 + +/* TX MAC Dword 3 */ +#define MAC_CFG_DESC_TX_3_UCODE_CMD_POS 0 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_LEN 32 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_MSK 0xFFFFFFFF + +/* TX DMA Dword 0 */ +#define DMA_CFG_DESC_TX_0_L4_LENGTH_POS 0 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_LEN 8 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_MSK 0xFF + +#define DMA_CFG_DESC_TX_0_CMD_EOP_POS 8 +#define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100 + +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400 + +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS 11 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_LEN 2 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_MSK 0x1800 + +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS 13 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_MSK 0x2000 + +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS 14 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_MSK 0x4000 + +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS 15 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000 + +#define DMA_CFG_DESC_TX_0_QID_POS 16 +#define DMA_CFG_DESC_TX_0_QID_LEN 5 +#define DMA_CFG_DESC_TX_0_QID_MSK 0x1F0000 + +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS 21 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000 + +#define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30 +#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2 +#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 + + +#define TX_DMA_STATUS_DU BIT(0) + +struct vring_tx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; /* 0..6: mac_length; 7:ip_version */ + u8 error; /* 0..2: err; 3..7: reserved; */ + u8 status; /* 0: used; 1..7; reserved */ + u16 length; +} __packed; + +/* + * Rx descriptor - MAC part + * [dword 0] + * bit 0.. 3 : tid:4 The QoS (b3-0) TID Field + * bit 4.. 6 : connection_id:3 :The Source index that was found during + * Parsing the TA. This field is used to define the source of the packet + * bit 7 : reserved:1 + * bit 8.. 9 : mac_id:2 : The MAC virtual Ring number (always zero) + * bit 10..11 : frame_type:2 : The FC Control (b3-2) - MPDU Type + * (management, data, control and extension) + * bit 12..15 : frame_subtype:4 : The FC Control (b7-4) - Frame Subtype + * bit 16..27 : seq_number:12 The received Sequence number field + * bit 28..31 : extended:4 extended subtype + * [dword 1] + * bit 0.. 3 : reserved + * bit 4.. 5 : key_id:2 + * bit 6 : decrypt_bypass:1 + * bit 7 : security:1 + * bit 8.. 9 : ds_bits:2 + * bit 10 : a_msdu_present:1 from qos header + * bit 11 : a_msdu_type:1 from qos header + * bit 12 : a_mpdu:1 part of AMPDU aggregation + * bit 13 : broadcast:1 + * bit 14 : mutlicast:1 + * bit 15 : reserved:1 + * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet + * is received from + * bit 21..24 : mcs:4 + * bit 25..28 : mic_icr:4 + * bit 29..31 : reserved:3 + * [dword 2] + * bit 0.. 2 : time_slot:3 The timeslot that the MPDU is received + * bit 3 : fc_protocol_ver:1 The FC Control (b0) - Protocol Version + * bit 4 : fc_order:1 The FC Control (b15) -Order + * bit 5.. 7 : qos_ack_policy:3 The QoS (b6-5) ack policy Field + * bit 8 : esop:1 The QoS (b4) ESOP field + * bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field + * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field + * bit 15 : qos_ac_constraint:1 + * bit 16..31 : pn_15_0:16 low 2 bytes of PN + * [dword 3] + * bit 0..31 : pn_47_16:32 high 4 bytes of PN + */ +struct vring_rx_mac { + u32 d0; + u32 d1; + u16 w4; + u16 pn_15_0; + u32 pn_47_16; +} __packed; + +/* + * Rx descriptor - DMA part + * [dword 0] + * bit 0.. 7 : l4_length:8 layer 4 length + * bit 8.. 9 : reserved:2 + * bit 10 : cmd_dma_it:1 + * bit 11..15 : reserved:5 + * bit 16..29 : phy_info_length:14 + * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field + * [dword 1] + * bit 0..31 : addr_low:32 The payload buffer low address + * [dword 2] + * bit 0..15 : addr_high:16 The payload buffer high address + * bit 16..23 : ip_length:8 + * bit 24..30 : mac_length:7 + * bit 31 : ip_version:1 + * [dword 3] + * [byte 12] error + * [byte 13] status + * bit 0 : du:1 + * bit 1 : eop:1 + * bit 2 : error:1 + * bit 3 : mi:1 + * bit 4 : l3_identified:1 + * bit 5 : l4_identified:1 + * bit 6 : phy_info_included:1 + * bit 7 : reserved:1 + * [word 7] length + * + */ + +#define RX_DMA_D0_CMD_DMA_IT BIT(10) + +#define RX_DMA_STATUS_DU BIT(0) +#define RX_DMA_STATUS_ERROR BIT(2) +#define RX_DMA_STATUS_PHY_INFO BIT(6) + +struct vring_rx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; + u8 error; + u8 status; + u16 length; +} __packed; + +struct vring_tx_desc { + struct vring_tx_mac mac; + struct vring_tx_dma dma; +} __packed; + +struct vring_rx_desc { + struct vring_rx_mac mac; + struct vring_rx_dma dma; +} __packed; + +union vring_desc { + struct vring_tx_desc tx; + struct vring_rx_desc rx; +} __packed; + +static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->dma.d0, 16, 29); +} + +static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d1, 21, 24); +} + +static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d1, 8, 9); +} + +static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d0, 10, 11); +} + +#endif /* WIL6210_TXRX_H */ diff --git a/trunk/drivers/net/wireless/ath/wil6210/wil6210.h b/trunk/drivers/net/wireless/ath/wil6210/wil6210.h new file mode 100644 index 000000000000..9bcfffa4006c --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/wil6210.h @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WIL6210_H__ +#define __WIL6210_H__ + +#include +#include +#include + +#include "dbg_hexdump.h" + +#define WIL_NAME "wil6210" + +/** + * extract bits [@b0:@b1] (inclusive) from the value @x + * it should be @b0 <= @b1, or result is incorrect + */ +static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) +{ + return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); +} + +#define WIL6210_MEM_SIZE (2*1024*1024UL) + +#define WIL6210_TX_QUEUES (4) + +#define WIL6210_RX_RING_SIZE (128) +#define WIL6210_TX_RING_SIZE (128) +#define WIL6210_MAX_TX_RINGS (24) + +/* Hardware definitions begin */ + +/* + * Mapping + * RGF File | Host addr | FW addr + * | | + * user_rgf | 0x000000 | 0x880000 + * dma_rgf | 0x001000 | 0x881000 + * pcie_rgf | 0x002000 | 0x882000 + * | | + */ + +/* Where various structures placed in host address space */ +#define WIL6210_FW_HOST_OFF (0x880000UL) + +#define HOSTADDR(fwaddr) (fwaddr - WIL6210_FW_HOST_OFF) + +/* + * Interrupt control registers block + * + * each interrupt controlled by the same bit in all registers + */ +struct RGF_ICR { + u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */ + u32 ICR; /* Cause, W1C/COR depending on ICC */ + u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */ + u32 ICS; /* Cause Set, WO */ + u32 IMV; /* Mask, RW+S/C */ + u32 IMS; /* Mask Set, write 1 to set */ + u32 IMC; /* Mask Clear, write 1 to clear */ +} __packed; + +/* registers - FW addresses */ +#define RGF_USER_USER_SCRATCH_PAD (0x8802bc) +#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ + #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) +#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) +#define RGF_USER_MAC_CPU_0 (0x8801fc) +#define RGF_USER_USER_CPU_0 (0x8801e0) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10) + +#define RGF_DMA_PSEUDO_CAUSE (0x881c68) +#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) +#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) + #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0) + #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) + #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) + +#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */ + #define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0) + #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */ +#define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */ + #define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0) +#define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */ + #define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0) + #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1) + #define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28) + #define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29) + +/* Interrupt moderation control */ +#define RGF_DMA_ITR_CNT_TRSH (0x881c5c) +#define RGF_DMA_ITR_CNT_DATA (0x881c60) +#define RGF_DMA_ITR_CNT_CRL (0x881C64) + #define BIT_DMA_ITR_CNT_CRL_EN BIT(0) + #define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1) + #define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2) + #define BIT_DMA_ITR_CNT_CRL_CLR BIT(3) + #define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4) + +/* popular locations */ +#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) +#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ + offsetof(struct RGF_ICR, ICS)) +#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2 + +/* ISR register bits */ +#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0 +#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1 + +/* Hardware definitions end */ + +struct wil6210_mbox_ring { + u32 base; + u16 entry_size; /* max. size of mbox entry, incl. all headers */ + u16 size; + u32 tail; + u32 head; +} __packed; + +struct wil6210_mbox_ring_desc { + __le32 sync; + __le32 addr; +} __packed; + +/* at HOST_OFF_WIL6210_MBOX_CTL */ +struct wil6210_mbox_ctl { + struct wil6210_mbox_ring tx; + struct wil6210_mbox_ring rx; +} __packed; + +struct wil6210_mbox_hdr { + __le16 seq; + __le16 len; /* payload, bytes after this header */ + __le16 type; + u8 flags; + u8 reserved; +} __packed; + +#define WIL_MBOX_HDR_TYPE_WMI (0) + +/* max. value for wil6210_mbox_hdr.len */ +#define MAX_MBOXITEM_SIZE (240) + +struct wil6210_mbox_hdr_wmi { + u8 reserved0[2]; + __le16 id; + __le16 info1; /* bits [0..3] - device_id, rest - unused */ + u8 reserved1[2]; +} __packed; + +struct pending_wmi_event { + struct list_head list; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + u8 data[0]; + } __packed event; +}; + +union vring_desc; + +struct vring { + dma_addr_t pa; + volatile union vring_desc *va; /* vring_desc[size], WriteBack by DMA */ + u16 size; /* number of vring_desc elements */ + u32 swtail; + u32 swhead; + u32 hwtail; /* write here to inform hw */ + void **ctx; /* void *ctx[size] - software context */ +}; + +enum { /* for wil6210_priv.status */ + wil_status_fwready = 0, + wil_status_fwconnected, + wil_status_dontscan, + wil_status_irqen, /* FIXME: interrupts enabled - for debug */ +}; + +struct pci_dev; + +struct wil6210_stats { + u64 tsf; + u32 snr; + u16 last_mcs_rx; + u16 bf_mcs; /* last BF, used for Tx */ + u16 my_rx_sector; + u16 my_tx_sector; + u16 peer_rx_sector; + u16 peer_tx_sector; +}; + +struct wil6210_priv { + struct pci_dev *pdev; + int n_msi; + struct wireless_dev *wdev; + void __iomem *csr; + ulong status; + /* profile */ + u32 monitor_flags; + u32 secure_pcp; /* create secure PCP? */ + int sinfo_gen; + /* cached ISR registers */ + u32 isr_misc; + /* mailbox related */ + struct mutex wmi_mutex; + struct wil6210_mbox_ctl mbox_ctl; + struct completion wmi_ready; + u16 wmi_seq; + u16 reply_id; /**< wait for this WMI event */ + void *reply_buf; + u16 reply_size; + struct workqueue_struct *wmi_wq; /* for deferred calls */ + struct work_struct wmi_event_worker; + struct workqueue_struct *wmi_wq_conn; /* for connect worker */ + struct work_struct wmi_connect_worker; + struct work_struct disconnect_worker; + struct timer_list connect_timer; + int pending_connect_cid; + struct list_head pending_wmi_ev; + /* + * protect pending_wmi_ev + * - fill in IRQ from wil6210_irq_misc, + * - consumed in thread by wmi_event_worker + */ + spinlock_t wmi_ev_lock; + /* DMA related */ + struct vring vring_rx; + struct vring vring_tx[WIL6210_MAX_TX_RINGS]; + u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; + /* scan */ + struct cfg80211_scan_request *scan_request; + + struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ + /* statistics */ + struct wil6210_stats stats; + /* debugfs */ + struct dentry *debug; + struct debugfs_blob_wrapper fw_code_blob; + struct debugfs_blob_wrapper fw_data_blob; + struct debugfs_blob_wrapper fw_peri_blob; + struct debugfs_blob_wrapper uc_code_blob; + struct debugfs_blob_wrapper uc_data_blob; + struct debugfs_blob_wrapper rgf_blob; +}; + +#define wil_to_wiphy(i) (i->wdev->wiphy) +#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i))) +#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w)) +#define wil_to_wdev(i) (i->wdev) +#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w)) +#define wil_to_ndev(i) (wil_to_wdev(i)->netdev) +#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) + +#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) +#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) +#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) + +#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) +#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) +#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) + +#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, + size_t count); +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, + size_t count); + +void *wil_if_alloc(struct device *dev, void __iomem *csr); +void wil_if_free(struct wil6210_priv *wil); +int wil_if_add(struct wil6210_priv *wil); +void wil_if_remove(struct wil6210_priv *wil); +int wil_priv_init(struct wil6210_priv *wil); +void wil_priv_deinit(struct wil6210_priv *wil); +int wil_reset(struct wil6210_priv *wil); +void wil_link_on(struct wil6210_priv *wil); +void wil_link_off(struct wil6210_priv *wil); +int wil_up(struct wil6210_priv *wil); +int wil_down(struct wil6210_priv *wil); +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); + +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, + struct wil6210_mbox_hdr *hdr); +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len); +void wmi_recv_cmd(struct wil6210_priv *wil); +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, + u16 reply_id, void *reply, u8 reply_size, int to_msec); +void wmi_connect_worker(struct work_struct *work); +void wmi_event_worker(struct work_struct *work); +void wmi_event_flush(struct wil6210_priv *wil); +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); +int wmi_set_channel(struct wil6210_priv *wil, int channel); +int wmi_get_channel(struct wil6210_priv *wil, int *channel); +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb); +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr); +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr, int key_len, const void *key); +int wmi_echo(struct wil6210_priv *wil); +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); + +int wil6210_init_irq(struct wil6210_priv *wil, int irq); +void wil6210_fini_irq(struct wil6210_priv *wil, int irq); +void wil6210_disable_irq(struct wil6210_priv *wil); +void wil6210_enable_irq(struct wil6210_priv *wil); + +int wil6210_debugfs_init(struct wil6210_priv *wil); +void wil6210_debugfs_remove(struct wil6210_priv *wil); + +struct wireless_dev *wil_cfg80211_init(struct device *dev); +void wil_wdev_free(struct wil6210_priv *wil); + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype); +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); + +int wil_rx_init(struct wil6210_priv *wil); +void wil_rx_fini(struct wil6210_priv *wil); + +/* TX API */ +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, + int cid, int tid); +void wil_vring_fini_tx(struct wil6210_priv *wil, int id); + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); +void wil_tx_complete(struct wil6210_priv *wil, int ringid); + +/* RX API */ +void wil_rx_handle(struct wil6210_priv *wil); + +int wil_iftype_nl2wmi(enum nl80211_iftype type); + +#endif /* __WIL6210_H__ */ diff --git a/trunk/drivers/net/wireless/ath/wil6210/wmi.c b/trunk/drivers/net/wireless/ath/wil6210/wmi.c new file mode 100644 index 000000000000..12915f6e7617 --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/wmi.c @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" + +/** + * WMI event receiving - theory of operations + * + * When firmware about to report WMI event, it fills memory area + * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for + * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler. + * + * @wmi_recv_cmd reads event, allocates memory chunk and attaches it to the + * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up + * and handles events within the @wmi_event_worker. Every event get detached + * from list, processed and deleted. + * + * Purpose for this mechanism is to release IRQ thread; otherwise, + * if WMI event handling involves another WMI command flow, this 2-nd flow + * won't be completed because of blocked IRQ thread. + */ + +/** + * Addressing - theory of operations + * + * There are several buses present on the WIL6210 card. + * Same memory areas are visible at different address on + * the different busses. There are 3 main bus masters: + * - MAC CPU (ucode) + * - User CPU (firmware) + * - AHB (host) + * + * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing + * AHB addresses starting from 0x880000 + * + * Internally, firmware uses addresses that allows faster access but + * are invisible from the host. To read from these addresses, alternative + * AHB address must be used. + * + * Memory mapping + * Linker address PCI/Host address + * 0x880000 .. 0xa80000 2Mb BAR0 + * 0x800000 .. 0x807000 0x900000 .. 0x907000 28k DCCM + * 0x840000 .. 0x857000 0x908000 .. 0x91f000 92k PERIPH + */ + +/** + * @fw_mapping provides memory remapping table + */ +static const struct { + u32 from; /* linker address - from, inclusive */ + u32 to; /* linker address - to, exclusive */ + u32 host; /* PCI/Host address - BAR0 + 0x880000 */ +} fw_mapping[] = { + {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */ + {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ + {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ + {0x880000, 0x88a000, 0x880000}, /* various RGF */ + {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */ + /* + * 920000..930000 ucode code RAM + * 930000..932000 ucode data RAM + */ +}; + +/** + * return AHB address for given firmware/ucode internal (linker) address + * @x - internal address + * If address have no valid AHB mapping, return 0 + */ +static u32 wmi_addr_remap(u32 x) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { + if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)) + return x + fw_mapping[i].host - fw_mapping[i].from; + } + + return 0; +} + +/** + * Check address validity for WMI buffer; remap if needed + * @ptr - internal (linker) fw/ucode address + * + * Valid buffer should be DWORD aligned + * + * return address for accessing buffer from the host; + * if buffer is not valid, return NULL. + */ +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) +{ + u32 off; + u32 ptr = le32_to_cpu(ptr_); + + if (ptr % 4) + return NULL; + + ptr = wmi_addr_remap(ptr); + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + + return wil->csr + off; +} + +/** + * Check address validity + */ +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) +{ + u32 off; + + if (ptr % 4) + return NULL; + + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + + return wil->csr + off; +} + +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, + struct wil6210_mbox_hdr *hdr) +{ + void __iomem *src = wmi_buffer(wil, ptr); + if (!src) + return -EINVAL; + + wil_memcpy_fromio_32(hdr, src, sizeof(*hdr)); + + return 0; +} + +static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + } __packed cmd = { + .hdr = { + .type = WIL_MBOX_HDR_TYPE_WMI, + .flags = 0, + .len = cpu_to_le16(sizeof(cmd.wmi) + len), + }, + .wmi = { + .id = cpu_to_le16(cmdid), + .info1 = 0, + }, + }; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; + struct wil6210_mbox_ring_desc d_head; + u32 next_head; + void __iomem *dst; + void __iomem *head = wmi_addr(wil, r->head); + uint retry; + + if (sizeof(cmd) + len > r->entry_size) { + wil_err(wil, "WMI size too large: %d bytes, max is %d\n", + (int)(sizeof(cmd) + len), r->entry_size); + return -ERANGE; + + } + + might_sleep(); + + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + return -EAGAIN; + } + + if (!head) { + wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); + return -EINVAL; + } + /* read Tx head till it is not busy */ + for (retry = 5; retry > 0; retry--) { + wil_memcpy_fromio_32(&d_head, head, sizeof(d_head)); + if (d_head.sync == 0) + break; + msleep(20); + } + if (d_head.sync != 0) { + wil_err(wil, "WMI head busy\n"); + return -EBUSY; + } + /* next head */ + next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); + wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); + /* wait till FW finish with previous command */ + for (retry = 5; retry > 0; retry--) { + r->tail = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.tail)); + if (next_head != r->tail) + break; + msleep(20); + } + if (next_head == r->tail) { + wil_err(wil, "WMI ring full\n"); + return -EBUSY; + } + dst = wmi_buffer(wil, d_head.addr); + if (!dst) { + wil_err(wil, "invalid WMI buffer: 0x%08x\n", + le32_to_cpu(d_head.addr)); + return -EINVAL; + } + cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); + /* set command */ + wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len); + wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, + sizeof(cmd), true); + wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, + len, true); + wil_memcpy_toio_32(dst, &cmd, sizeof(cmd)); + wil_memcpy_toio_32(dst + sizeof(cmd), buf, len); + /* mark entry as full */ + iowrite32(1, wil->csr + HOSTADDR(r->head) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* advance next ptr */ + iowrite32(r->head = next_head, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.head)); + + /* interrupt to FW */ + iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); + + return 0; +} + +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ + int rc; + + mutex_lock(&wil->wmi_mutex); + rc = __wmi_send(wil, cmdid, buf, len); + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +/*=== Event handlers ===*/ +static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct wmi_ready_event *evt = d; + u32 ver = le32_to_cpu(evt->sw_version); + + wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac); + + if (!is_valid_ether_addr(ndev->dev_addr)) { + memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); + memcpy(ndev->perm_addr, evt->mac, ETH_ALEN); + } + snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), + "%d", ver); +} + +static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, + int len) +{ + wil_dbg_WMI(wil, "WMI: FW ready\n"); + + set_bit(wil_status_fwready, &wil->status); + /* reuse wmi_ready for the firmware ready indication */ + complete(&wil->wmi_ready); +} + +static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct wmi_rx_mgmt_packet_event *data = d; + struct wiphy *wiphy = wil_to_wiphy(wil); + struct ieee80211_mgmt *rx_mgmt_frame = + (struct ieee80211_mgmt *)data->payload; + int ch_no = data->info.channel+1; + u32 freq = ieee80211_channel_to_frequency(ch_no, + IEEE80211_BAND_60GHZ); + struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); + /* TODO convert LE to CPU */ + s32 signal = 0; /* TODO */ + __le16 fc = rx_mgmt_frame->frame_control; + u32 d_len = le32_to_cpu(data->info.len); + u16 d_status = le16_to_cpu(data->info.status); + + wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n", + data->info.channel, data->info.mcs, data->info.snr); + wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, + le16_to_cpu(data->info.stype)); + wil_dbg_WMI(wil, "qid %d mid %d cid %d\n", + data->info.qid, data->info.mid, data->info.cid); + + if (!channel) { + wil_err(wil, "Frame on unsupported channel\n"); + return; + } + + if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { + struct cfg80211_bss *bss; + u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp); + u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info); + u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int); + const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; + size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, + u.beacon.variable); + wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap); + + bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid, + tsf, cap, bi, ie_buf, ie_len, + signal, GFP_KERNEL); + if (bss) { + wil_dbg_WMI(wil, "Added BSS %pM\n", + rx_mgmt_frame->bssid); + cfg80211_put_bss(bss); + } else { + wil_err(wil, "cfg80211_inform_bss() failed\n"); + } + } +} + +static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, + void *d, int len) +{ + if (wil->scan_request) { + struct wmi_scan_complete_event *data = d; + bool aborted = (data->status != 0); + + wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); + cfg80211_scan_done(wil->scan_request, aborted); + wil->scan_request = NULL; + } else { + wil_err(wil, "SCAN_COMPLETE while not scanning\n"); + } +} + +static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct wmi_connect_event *evt = d; + int ch; /* channel number */ + struct station_info sinfo; + u8 *assoc_req_ie, *assoc_resp_ie; + size_t assoc_req_ielen, assoc_resp_ielen; + /* capinfo(u16) + listen_interval(u16) + IEs */ + const size_t assoc_req_ie_offset = sizeof(u16) * 2; + /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ + const size_t assoc_resp_ie_offset = sizeof(u16) * 3; + + if (len < sizeof(*evt)) { + wil_err(wil, "Connect event too short : %d bytes\n", len); + return; + } + if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len + + evt->assoc_resp_len) { + wil_err(wil, + "Connect event corrupted : %d != %d + %d + %d + %d\n", + len, (int)sizeof(*evt), evt->beacon_ie_len, + evt->assoc_req_len, evt->assoc_resp_len); + return; + } + ch = evt->channel + 1; + wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n", + evt->bssid, ch, evt->cid); + wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, + evt->assoc_info, len - sizeof(*evt), true); + + /* figure out IE's */ + assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len + + assoc_req_ie_offset]; + assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset; + if (evt->assoc_req_len <= assoc_req_ie_offset) { + assoc_req_ie = NULL; + assoc_req_ielen = 0; + } + + assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len + + evt->assoc_req_len + + assoc_resp_ie_offset]; + assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset; + if (evt->assoc_resp_len <= assoc_resp_ie_offset) { + assoc_resp_ie = NULL; + assoc_resp_ielen = 0; + } + + if ((wdev->iftype == NL80211_IFTYPE_STATION) || + (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + if (wdev->sme_state != CFG80211_SME_CONNECTING) { + wil_err(wil, "Not in connecting state\n"); + return; + } + del_timer_sync(&wil->connect_timer); + cfg80211_connect_result(ndev, evt->bssid, + assoc_req_ie, assoc_req_ielen, + assoc_resp_ie, assoc_resp_ielen, + WLAN_STATUS_SUCCESS, GFP_KERNEL); + + } else if ((wdev->iftype == NL80211_IFTYPE_AP) || + (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { + memset(&sinfo, 0, sizeof(sinfo)); + + sinfo.generation = wil->sinfo_gen++; + + if (assoc_req_ie) { + sinfo.assoc_req_ies = assoc_req_ie; + sinfo.assoc_req_ies_len = assoc_req_ielen; + sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; + } + + cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); + } + set_bit(wil_status_fwconnected, &wil->status); + + /* FIXME FW can transmit only ucast frames to peer */ + /* FIXME real ring_id instead of hard coded 0 */ + memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); + + wil->pending_connect_cid = evt->cid; + queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker); +} + +static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct wmi_disconnect_event *evt = d; + + wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n", + evt->bssid, + evt->protocol_reason_status, evt->disconnect_reason); + + wil->sinfo_gen++; + + wil6210_disconnect(wil, evt->bssid); + clear_bit(wil_status_dontscan, &wil->status); +} + +static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct wmi_notify_req_done_event *evt = d; + + if (len < sizeof(*evt)) { + wil_err(wil, "Short NOTIFY event\n"); + return; + } + + wil->stats.tsf = le64_to_cpu(evt->tsf); + wil->stats.snr = le32_to_cpu(evt->snr_val); + wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs); + wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector); + wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector); + wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); + wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); + wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n" + "BF status 0x%08x SNR 0x%08x\n" + "Tx Tpt %d goodput %d Rx goodput %d\n" + "Sectors(rx:tx) my %d:%d peer %d:%d\n", + wil->stats.bf_mcs, wil->stats.tsf, evt->status, + wil->stats.snr, le32_to_cpu(evt->tx_tpt), + le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput), + wil->stats.my_rx_sector, wil->stats.my_tx_sector, + wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); +} + +/* + * Firmware reports EAPOL frame using WME event. + * Reconstruct Ethernet frame and deliver it via normal Rx + */ +static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wmi_eapol_rx_event *evt = d; + u16 eapol_len = le16_to_cpu(evt->eapol_len); + int sz = eapol_len + ETH_HLEN; + struct sk_buff *skb; + struct ethhdr *eth; + + wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len, + evt->src_mac); + + if (eapol_len > 196) { /* TODO: revisit size limit */ + wil_err(wil, "EAPOL too large\n"); + return; + } + + skb = alloc_skb(sz, GFP_KERNEL); + if (!skb) { + wil_err(wil, "Failed to allocate skb\n"); + return; + } + eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); + memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN); + memcpy(eth->h_source, evt->src_mac, ETH_ALEN); + eth->h_proto = cpu_to_be16(ETH_P_PAE); + memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len); + skb->protocol = eth_type_trans(skb, ndev); + if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + } else { + ndev->stats.rx_dropped++; + } +} + +static const struct { + int eventid; + void (*handler)(struct wil6210_priv *wil, int eventid, + void *data, int data_len); +} wmi_evt_handlers[] = { + {WMI_READY_EVENTID, wmi_evt_ready}, + {WMI_FW_READY_EVENTID, wmi_evt_fw_ready}, + {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt}, + {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, + {WMI_CONNECT_EVENTID, wmi_evt_connect}, + {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, + {WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify}, + {WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx}, +}; + +/* + * Run in IRQ context + * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev + * that will be eventually handled by the @wmi_event_worker in the thread + * context of thread "wil6210_wmi" + */ +void wmi_recv_cmd(struct wil6210_priv *wil) +{ + struct wil6210_mbox_ring_desc d_tail; + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; + struct pending_wmi_event *evt; + u8 *cmd; + void __iomem *src; + ulong flags; + + for (;;) { + u16 len; + + r->head = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.head)); + if (r->tail == r->head) + return; + + /* read cmd from tail */ + wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), + sizeof(struct wil6210_mbox_ring_desc)); + if (d_tail.sync == 0) { + wil_err(wil, "Mbox evt not owned by FW?\n"); + return; + } + + if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { + wil_err(wil, "Mbox evt at 0x%08x?\n", + le32_to_cpu(d_tail.addr)); + return; + } + + len = le16_to_cpu(hdr.len); + src = wmi_buffer(wil, d_tail.addr) + + sizeof(struct wil6210_mbox_hdr); + evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, + event.wmi) + len, 4), + GFP_KERNEL); + if (!evt) { + wil_err(wil, "kmalloc for WMI event (%d) failed\n", + len); + return; + } + evt->event.hdr = hdr; + cmd = (void *)&evt->event.wmi; + wil_memcpy_fromio_32(cmd, src, len); + /* mark entry as empty */ + iowrite32(0, wil->csr + HOSTADDR(r->tail) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* indicate */ + wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), + hdr.flags); + if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && + (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + wil_dbg_WMI(wil, "WMI event 0x%04x\n", + evt->event.wmi.id); + } + wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1, + &evt->event.hdr, sizeof(hdr) + len, true); + + /* advance tail */ + r->tail = r->base + ((r->tail - r->base + + sizeof(struct wil6210_mbox_ring_desc)) % r->size); + iowrite32(r->tail, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.tail)); + + /* add to the pending list */ + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + list_add_tail(&evt->list, &wil->pending_wmi_ev); + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + { + int q = queue_work(wil->wmi_wq, + &wil->wmi_event_worker); + wil_dbg_WMI(wil, "queue_work -> %d\n", q); + } + } +} + +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, + u16 reply_id, void *reply, u8 reply_size, int to_msec) +{ + int rc; + int remain; + + mutex_lock(&wil->wmi_mutex); + + rc = __wmi_send(wil, cmdid, buf, len); + if (rc) + goto out; + + wil->reply_id = reply_id; + wil->reply_buf = reply; + wil->reply_size = reply_size; + remain = wait_for_completion_timeout(&wil->wmi_ready, + msecs_to_jiffies(to_msec)); + if (0 == remain) { + wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n", + cmdid, reply_id, to_msec); + rc = -ETIME; + } else { + wil_dbg_WMI(wil, + "wmi_call(0x%04x->0x%04x) completed in %d msec\n", + cmdid, reply_id, + to_msec - jiffies_to_msecs(remain)); + } + wil->reply_id = 0; + wil->reply_buf = NULL; + wil->reply_size = 0; + out: + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +int wmi_echo(struct wil6210_priv *wil) +{ + struct wmi_echo_cmd cmd = { + .value = cpu_to_le32(0x12345678), + }; + + return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd), + WMI_ECHO_RSP_EVENTID, NULL, 0, 20); +} + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) +{ + struct wmi_set_mac_address_cmd cmd; + + memcpy(cmd.mac, addr, ETH_ALEN); + + wil_dbg_WMI(wil, "Set MAC %pM\n", addr); + + return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype) +{ + struct wmi_bcon_ctrl_cmd cmd = { + .bcon_interval = cpu_to_le16(bi), + .network_type = wmi_nettype, + .disable_sec_offload = 1, + }; + + if (!wil->secure_pcp) + cmd.disable_sec = 1; + + return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid) +{ + struct wmi_set_ssid_cmd cmd = { + .ssid_len = cpu_to_le32(ssid_len), + }; + + if (ssid_len > sizeof(cmd.ssid)) + return -EINVAL; + + memcpy(cmd.ssid, ssid, ssid_len); + + return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) +{ + int rc; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_set_ssid_cmd cmd; + } __packed reply; + int len; /* reply.cmd.ssid_len in CPU order */ + + rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID, + &reply, sizeof(reply), 20); + if (rc) + return rc; + + len = le32_to_cpu(reply.cmd.ssid_len); + if (len > sizeof(reply.cmd.ssid)) + return -EINVAL; + + *ssid_len = len; + memcpy(ssid, reply.cmd.ssid, len); + + return 0; +} + +int wmi_set_channel(struct wil6210_priv *wil, int channel) +{ + struct wmi_set_pcp_channel_cmd cmd = { + .channel = channel - 1, + }; + + return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_channel(struct wil6210_priv *wil, int *channel) +{ + int rc; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_set_pcp_channel_cmd cmd; + } __packed reply; + + rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0, + WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20); + if (rc) + return rc; + + if (reply.cmd.channel > 3) + return -EINVAL; + + *channel = reply.cmd.channel + 1; + + return 0; +} + +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) +{ + struct wmi_eapol_tx_cmd *cmd; + struct ethhdr *eth; + u16 eapol_len = skb->len - ETH_HLEN; + void *eapol = skb->data + ETH_HLEN; + uint i; + int rc; + + skb_set_mac_header(skb, 0); + eth = eth_hdr(skb); + wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) + goto found_dest; + } + + return -EINVAL; + + found_dest: + /* find out eapol data & len */ + cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL); + if (!cmd) + return -EINVAL; + + memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN); + cmd->eapol_len = cpu_to_le16(eapol_len); + memcpy(cmd->eapol, eapol, eapol_len); + rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len); + kfree(cmd); + + return rc; +} + +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr) +{ + struct wmi_delete_cipher_key_cmd cmd = { + .key_index = key_index, + }; + + if (mac_addr) + memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + + return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr, int key_len, const void *key) +{ + struct wmi_add_cipher_key_cmd cmd = { + .key_index = key_index, + .key_usage = WMI_KEY_USE_PAIRWISE, + .key_len = key_len, + }; + + if (!key || (key_len > sizeof(cmd.key))) + return -EINVAL; + + memcpy(cmd.key, key, key_len); + if (mac_addr) + memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + + return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) +{ + int rc; + u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len; + struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL); + if (!cmd) { + wil_err(wil, "kmalloc(%d) failed\n", len); + return -ENOMEM; + } + + cmd->mgmt_frm_type = type; + /* BUG: FW API define ieLen as u8. Will fix FW */ + cmd->ie_len = cpu_to_le16(ie_len); + memcpy(cmd->ie_info, ie, ie_len); + rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len); + kfree(cmd); + + return rc; +} + +void wmi_event_flush(struct wil6210_priv *wil) +{ + struct pending_wmi_event *evt, *t; + + wil_dbg_WMI(wil, "%s()\n", __func__); + + list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { + list_del(&evt->list); + kfree(evt); + } +} + +static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, + void *d, int len) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { + if (wmi_evt_handlers[i].eventid == id) { + wmi_evt_handlers[i].handler(wil, id, d, len); + return true; + } + } + + return false; +} + +static void wmi_event_handle(struct wil6210_priv *wil, + struct wil6210_mbox_hdr *hdr) +{ + u16 len = le16_to_cpu(hdr->len); + + if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && + (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); + void *evt_data = (void *)(&wmi[1]); + u16 id = le16_to_cpu(wmi->id); + /* check if someone waits for this event */ + if (wil->reply_id && wil->reply_id == id) { + if (wil->reply_buf) { + memcpy(wil->reply_buf, wmi, + min(len, wil->reply_size)); + } else { + wmi_evt_call_handler(wil, id, evt_data, + len - sizeof(*wmi)); + } + wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id); + complete(&wil->wmi_ready); + return; + } + /* unsolicited event */ + /* search for handler */ + if (!wmi_evt_call_handler(wil, id, evt_data, + len - sizeof(*wmi))) { + wil_err(wil, "Unhandled event 0x%04x\n", id); + } + } else { + wil_err(wil, "Unknown event type\n"); + print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1, + hdr, sizeof(*hdr) + len, true); + } +} + +/* + * Retrieve next WMI event from the pending list + */ +static struct list_head *next_wmi_ev(struct wil6210_priv *wil) +{ + ulong flags; + struct list_head *ret = NULL; + + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + + if (!list_empty(&wil->pending_wmi_ev)) { + ret = wil->pending_wmi_ev.next; + list_del(ret); + } + + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + + return ret; +} + +/* + * Handler for the WMI events + */ +void wmi_event_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + wmi_event_worker); + struct pending_wmi_event *evt; + struct list_head *lh; + + while ((lh = next_wmi_ev(wil)) != NULL) { + evt = list_entry(lh, struct pending_wmi_event, list); + wmi_event_handle(wil, &evt->event.hdr); + kfree(evt); + } +} + +void wmi_connect_worker(struct work_struct *work) +{ + int rc; + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + wmi_connect_worker); + + if (wil->pending_connect_cid < 0) { + wil_err(wil, "No connection pending\n"); + return; + } + + wil_dbg_WMI(wil, "Configure for connection CID %d\n", + wil->pending_connect_cid); + + rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, + wil->pending_connect_cid, 0); + wil->pending_connect_cid = -1; + if (rc == 0) + wil_link_on(wil); +} diff --git a/trunk/drivers/net/wireless/ath/wil6210/wmi.h b/trunk/drivers/net/wireless/ath/wil6210/wmi.h new file mode 100644 index 000000000000..3bbf87572b07 --- /dev/null +++ b/trunk/drivers/net/wireless/ath/wil6210/wmi.h @@ -0,0 +1,1116 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2006-2012 Wilocity . + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file contains the definitions of the WMI protocol specified in the + * Wireless Module Interface (WMI) for the Wilocity + * MARLON 60 Gigabit wireless solution. + * It includes definitions of all the commands and events. + * Commands are messages from the host to the WM. + * Events are messages from the WM to the host. + */ + +#ifndef __WILOCITY_WMI_H__ +#define __WILOCITY_WMI_H__ + +/* General */ + +#define WMI_MAC_LEN (6) +#define WMI_PROX_RANGE_NUM (3) + +/* List of Commands */ +enum wmi_command_id { + WMI_CONNECT_CMDID = 0x0001, + WMI_DISCONNECT_CMDID = 0x0003, + WMI_START_SCAN_CMDID = 0x0007, + WMI_SET_BSS_FILTER_CMDID = 0x0009, + WMI_SET_PROBED_SSID_CMDID = 0x000a, + WMI_SET_LISTEN_INT_CMDID = 0x000b, + WMI_BCON_CTRL_CMDID = 0x000f, + WMI_ADD_CIPHER_KEY_CMDID = 0x0016, + WMI_DELETE_CIPHER_KEY_CMDID = 0x0017, + WMI_SET_APPIE_CMDID = 0x003f, + WMI_GET_APPIE_CMDID = 0x0040, + WMI_SET_WSC_STATUS_CMDID = 0x0041, + WMI_PXMT_RANGE_CFG_CMDID = 0x0042, + WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x0043, + WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, + WMI_MEM_READ_CMDID = 0x0800, + WMI_MEM_WR_CMDID = 0x0801, + WMI_ECHO_CMDID = 0x0803, + WMI_DEEP_ECHO_CMDID = 0x0804, + WMI_CONFIG_MAC_CMDID = 0x0805, + WMI_CONFIG_PHY_DEBUG_CMDID = 0x0806, + WMI_ADD_STATION_CMDID = 0x0807, + WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x0808, + WMI_PHY_GET_STATISTICS_CMDID = 0x0809, + WMI_FS_TUNE_CMDID = 0x080a, + WMI_CORR_MEASURE_CMDID = 0x080b, + WMI_TEMP_SENSE_CMDID = 0x080e, + WMI_DC_CALIB_CMDID = 0x080f, + WMI_SEND_TONE_CMDID = 0x0810, + WMI_IQ_TX_CALIB_CMDID = 0x0811, + WMI_IQ_RX_CALIB_CMDID = 0x0812, + WMI_SET_UCODE_IDLE_CMDID = 0x0813, + WMI_SET_WORK_MODE_CMDID = 0x0815, + WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816, + WMI_MARLON_R_ACTIVATE_CMDID = 0x0817, + WMI_MARLON_R_READ_CMDID = 0x0818, + WMI_MARLON_R_WRITE_CMDID = 0x0819, + WMI_MARLON_R_TXRX_SEL_CMDID = 0x081a, + MAC_IO_STATIC_PARAMS_CMDID = 0x081b, + MAC_IO_DYNAMIC_PARAMS_CMDID = 0x081c, + WMI_SILENT_RSSI_CALIB_CMDID = 0x081d, + WMI_CFG_RX_CHAIN_CMDID = 0x0820, + WMI_VRING_CFG_CMDID = 0x0821, + WMI_RX_ON_CMDID = 0x0822, + WMI_VRING_BA_EN_CMDID = 0x0823, + WMI_VRING_BA_DIS_CMDID = 0x0824, + WMI_RCP_ADDBA_RESP_CMDID = 0x0825, + WMI_RCP_DELBA_CMDID = 0x0826, + WMI_SET_SSID_CMDID = 0x0827, + WMI_GET_SSID_CMDID = 0x0828, + WMI_SET_PCP_CHANNEL_CMDID = 0x0829, + WMI_GET_PCP_CHANNEL_CMDID = 0x082a, + WMI_SW_TX_REQ_CMDID = 0x082b, + WMI_RX_OFF_CMDID = 0x082c, + WMI_READ_MAC_RXQ_CMDID = 0x0830, + WMI_READ_MAC_TXQ_CMDID = 0x0831, + WMI_WRITE_MAC_RXQ_CMDID = 0x0832, + WMI_WRITE_MAC_TXQ_CMDID = 0x0833, + WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x0834, + WMI_MLME_PUSH_CMDID = 0x0835, + WMI_BEAMFORMING_MGMT_CMDID = 0x0836, + WMI_BF_TXSS_MGMT_CMDID = 0x0837, + WMI_BF_SM_MGMT_CMDID = 0x0838, + WMI_BF_RXSS_MGMT_CMDID = 0x0839, + WMI_SET_SECTORS_CMDID = 0x0849, + WMI_MAINTAIN_PAUSE_CMDID = 0x0850, + WMI_MAINTAIN_RESUME_CMDID = 0x0851, + WMI_RS_MGMT_CMDID = 0x0852, + WMI_RF_MGMT_CMDID = 0x0853, + /* Performance monitoring commands */ + WMI_BF_CTRL_CMDID = 0x0862, + WMI_NOTIFY_REQ_CMDID = 0x0863, + WMI_GET_STATUS_CMDID = 0x0864, + WMI_UNIT_TEST_CMDID = 0x0900, + WMI_HICCUP_CMDID = 0x0901, + WMI_FLASH_READ_CMDID = 0x0902, + WMI_FLASH_WRITE_CMDID = 0x0903, + WMI_SECURITY_UNIT_TEST_CMDID = 0x0904, + + WMI_SET_MAC_ADDRESS_CMDID = 0xf003, + WMI_ABORT_SCAN_CMDID = 0xf007, + WMI_SET_PMK_CMDID = 0xf028, + + WMI_SET_PROMISCUOUS_MODE_CMDID = 0xf041, + WMI_GET_PMK_CMDID = 0xf048, + WMI_SET_PASSPHRASE_CMDID = 0xf049, + WMI_SEND_ASSOC_RES_CMDID = 0xf04a, + WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xf04b, + WMI_EAPOL_TX_CMDID = 0xf04c, + WMI_MAC_ADDR_REQ_CMDID = 0xf04d, + WMI_FW_VER_CMDID = 0xf04e, +}; + +/* + * Commands data structures + */ + +/* + * Frame Types + */ +enum wmi_mgmt_frame_type { + WMI_FRAME_BEACON = 0, + WMI_FRAME_PROBE_REQ = 1, + WMI_FRAME_PROBE_RESP = 2, + WMI_FRAME_ASSOC_REQ = 3, + WMI_FRAME_ASSOC_RESP = 4, + WMI_NUM_MGMT_FRAME, +}; + +/* + * WMI_CONNECT_CMDID + */ +enum wmi_network_type { + WMI_NETTYPE_INFRA = 0x01, + WMI_NETTYPE_ADHOC = 0x02, + WMI_NETTYPE_ADHOC_CREATOR = 0x04, + WMI_NETTYPE_AP = 0x10, + WMI_NETTYPE_P2P = 0x20, + WMI_NETTYPE_WBE = 0x40, /* PCIE over 60g */ +}; + +enum wmi_dot11_auth_mode { + WMI_AUTH11_OPEN = 0x01, + WMI_AUTH11_SHARED = 0x02, + WMI_AUTH11_LEAP = 0x04, + WMI_AUTH11_WSC = 0x08, +}; + +enum wmi_auth_mode { + WMI_AUTH_NONE = 0x01, + WMI_AUTH_WPA = 0x02, + WMI_AUTH_WPA2 = 0x04, + WMI_AUTH_WPA_PSK = 0x08, + WMI_AUTH_WPA2_PSK = 0x10, + WMI_AUTH_WPA_CCKM = 0x20, + WMI_AUTH_WPA2_CCKM = 0x40, +}; + +enum wmi_crypto_type { + WMI_CRYPT_NONE = 0x01, + WMI_CRYPT_WEP = 0x02, + WMI_CRYPT_TKIP = 0x04, + WMI_CRYPT_AES = 0x08, + WMI_CRYPT_AES_GCMP = 0x20, +}; + + +enum wmi_connect_ctrl_flag_bits { + WMI_CONNECT_ASSOC_POLICY_USER = 0x0001, + WMI_CONNECT_SEND_REASSOC = 0x0002, + WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004, + WMI_CONNECT_PROFILE_MATCH_DONE = 0x0008, + WMI_CONNECT_IGNORE_AAC_BEACON = 0x0010, + WMI_CONNECT_CSA_FOLLOW_BSS = 0x0020, + WMI_CONNECT_DO_WPA_OFFLOAD = 0x0040, + WMI_CONNECT_DO_NOT_DEAUTH = 0x0080, +}; + +#define WMI_MAX_SSID_LEN (32) + +struct wmi_connect_cmd { + u8 network_type; + u8 dot11_auth_mode; + u8 auth_mode; + u8 pairwise_crypto_type; + u8 pairwise_crypto_len; + u8 group_crypto_type; + u8 group_crypto_len; + u8 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; + u8 channel; + u8 reserved0; + u8 bssid[WMI_MAC_LEN]; + __le32 ctrl_flags; + u8 dst_mac[WMI_MAC_LEN]; + u8 reserved1[2]; +} __packed; + + +/* + * WMI_RECONNECT_CMDID + */ +struct wmi_reconnect_cmd { + u8 channel; /* hint */ + u8 reserved; + u8 bssid[WMI_MAC_LEN]; /* mandatory if set */ +} __packed; + + +/* + * WMI_SET_PMK_CMDID + */ + +#define WMI_MIN_KEY_INDEX (0) +#define WMI_MAX_KEY_INDEX (3) +#define WMI_MAX_KEY_LEN (32) +#define WMI_PASSPHRASE_LEN (64) +#define WMI_PMK_LEN (32) + +struct wmi_set_pmk_cmd { + u8 pmk[WMI_PMK_LEN]; +} __packed; + + +/* + * WMI_SET_PASSPHRASE_CMDID + */ +struct wmi_set_passphrase_cmd { + u8 ssid[WMI_MAX_SSID_LEN]; + u8 passphrase[WMI_PASSPHRASE_LEN]; + u8 ssid_len; + u8 passphrase_len; +} __packed; + +/* + * WMI_ADD_CIPHER_KEY_CMDID + */ +enum wmi_key_usage { + WMI_KEY_USE_PAIRWISE = 0, + WMI_KEY_USE_GROUP = 1, + WMI_KEY_USE_TX = 2, /* default Tx Key - Static WEP only */ +}; + +struct wmi_add_cipher_key_cmd { + u8 key_index; + u8 key_type; + u8 key_usage; /* enum wmi_key_usage */ + u8 key_len; + u8 key_rsc[8]; /* key replay sequence counter */ + u8 key[WMI_MAX_KEY_LEN]; + u8 key_op_ctrl; /* Additional Key Control information */ + u8 mac[WMI_MAC_LEN]; +} __packed; + +/* + * WMI_DELETE_CIPHER_KEY_CMDID + */ +struct wmi_delete_cipher_key_cmd { + u8 key_index; + u8 mac[WMI_MAC_LEN]; +} __packed; + + +/* + * WMI_START_SCAN_CMDID + * + * Start L1 scan operation + * + * Returned events: + * - WMI_RX_MGMT_PACKET_EVENTID - for every probe resp. + * - WMI_SCAN_COMPLETE_EVENTID + */ +enum wmi_scan_type { + WMI_LONG_SCAN = 0, + WMI_SHORT_SCAN = 1, +}; + +struct wmi_start_scan_cmd { + u8 reserved[8]; + __le32 home_dwell_time; /* Max duration in the home channel(ms) */ + __le32 force_scan_interval; /* Time interval between scans (ms)*/ + u8 scan_type; /* wmi_scan_type */ + u8 num_channels; /* how many channels follow */ + struct { + u8 channel; + u8 reserved; + } channel_list[0]; /* channels ID's */ + /* 0 - 58320 MHz */ + /* 1 - 60480 MHz */ + /* 2 - 62640 MHz */ +} __packed; + +/* + * WMI_SET_PROBED_SSID_CMDID + */ +#define MAX_PROBED_SSID_INDEX (15) + +enum wmi_ssid_flag { + WMI_SSID_FLAG_DISABLE = 0, /* disables entry */ + WMI_SSID_FLAG_SPECIFIC = 1, /* probes specified ssid */ + WMI_SSID_FLAG_ANY = 2, /* probes for any ssid */ +}; + +struct wmi_probed_ssid_cmd { + u8 entry_index; /* 0 to MAX_PROBED_SSID_INDEX */ + u8 flag; /* enum wmi_ssid_flag */ + u8 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_APPIE_CMDID + * Add Application specified IE to a management frame + */ +struct wmi_set_appie_cmd { + u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ + u8 reserved; + __le16 ie_len; /* Length of the IE to be added to MGMT frame */ + u8 ie_info[0]; +} __packed; + +#define WMI_MAX_IE_LEN (1024) + +struct wmi_pxmt_range_cfg_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 range; +} __packed; + +struct wmi_pxmt_snr2_range_cfg_cmd { + s8 snr2range_arr[WMI_PROX_RANGE_NUM-1]; +} __packed; + +/* + * WMI_RF_MGMT_CMDID + */ +enum wmi_rf_mgmt_type { + WMI_RF_MGMT_W_DISABLE = 0, + WMI_RF_MGMT_W_ENABLE = 1, + WMI_RF_MGMT_GET_STATUS = 2, +}; + +struct wmi_rf_mgmt_cmd { + __le32 rf_mgmt_type; +} __packed; + +/* + * WMI_SET_SSID_CMDID + */ +struct wmi_set_ssid_cmd { + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_PCP_CHANNEL_CMDID + */ +struct wmi_set_pcp_channel_cmd { + u8 channel; + u8 reserved[3]; +} __packed; + +/* + * WMI_BCON_CTRL_CMDID + */ +struct wmi_bcon_ctrl_cmd { + __le16 bcon_interval; + __le16 frag_num; + __le64 ss_mask; + u8 network_type; + u8 reserved; + u8 disable_sec_offload; + u8 disable_sec; +} __packed; + +/* + * WMI_SW_TX_REQ_CMDID + */ +struct wmi_sw_tx_req_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 len; + u8 payload[0]; +} __packed; + +/* + * WMI_VRING_CFG_CMDID + */ + +struct wmi_sw_ring_cfg { + __le64 ring_mem_base; + __le16 ring_size; + __le16 max_mpdu_size; +} __packed; + +struct wmi_vring_cfg_schd { + __le16 priority; + __le16 timeslot_us; +} __packed; + +enum wmi_vring_cfg_encap_trans_type { + WMI_VRING_ENC_TYPE_802_3 = 0, + WMI_VRING_ENC_TYPE_NATIVE_WIFI = 1, +}; + +enum wmi_vring_cfg_ds_cfg { + WMI_VRING_DS_PBSS = 0, + WMI_VRING_DS_STATION = 1, + WMI_VRING_DS_AP = 2, + WMI_VRING_DS_ADDR4 = 3, +}; + +enum wmi_vring_cfg_nwifi_ds_trans_type { + WMI_NWIFI_TX_TRANS_MODE_NO = 0, + WMI_NWIFI_TX_TRANS_MODE_AP2PBSS = 1, + WMI_NWIFI_TX_TRANS_MODE_STA2PBSS = 2, +}; + +enum wmi_vring_cfg_schd_params_priority { + WMI_SCH_PRIO_REGULAR = 0, + WMI_SCH_PRIO_HIGH = 1, +}; + +struct wmi_vring_cfg { + struct wmi_sw_ring_cfg tx_sw_ring; + u8 ringid; /* 0-23 vrings */ + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 encap_trans_type; + u8 ds_cfg; /* 802.3 DS cfg */ + u8 nwifi_ds_trans_type; + + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_POS (0) + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_LEN (1) + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_MSK (0x1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_POS (1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_LEN (1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_MSK (0x2) + u8 mac_ctrl; + + #define VRING_CFG_TO_RESOLUTION_VALUE_POS (0) + #define VRING_CFG_TO_RESOLUTION_VALUE_LEN (6) + #define VRING_CFG_TO_RESOLUTION_VALUE_MSK (0x3F) + u8 to_resolution; + u8 agg_max_wsize; + struct wmi_vring_cfg_schd schd_params; +} __packed; + +enum wmi_vring_cfg_cmd_action { + WMI_VRING_CMD_ADD = 0, + WMI_VRING_CMD_MODIFY = 1, + WMI_VRING_CMD_DELETE = 2, +}; + +struct wmi_vring_cfg_cmd { + __le32 action; + struct wmi_vring_cfg vring_cfg; +} __packed; + +/* + * WMI_VRING_BA_EN_CMDID + */ +struct wmi_vring_ba_en_cmd { + u8 ringid; + u8 agg_max_wsize; + __le16 ba_timeout; +} __packed; + +/* + * WMI_VRING_BA_DIS_CMDID + */ +struct wmi_vring_ba_dis_cmd { + u8 ringid; + u8 reserved; + __le16 reason; +} __packed; + +/* + * WMI_NOTIFY_REQ_CMDID + */ +struct wmi_notify_req_cmd { + u8 cid; + u8 reserved[3]; + __le32 interval_usec; +} __packed; + +/* + * WMI_CFG_RX_CHAIN_CMDID + */ +enum wmi_sniffer_cfg_mode { + WMI_SNIFFER_OFF = 0, + WMI_SNIFFER_ON = 1, +}; + +enum wmi_sniffer_cfg_phy_info_mode { + WMI_SNIFFER_PHY_INFO_DISABLED = 0, + WMI_SNIFFER_PHY_INFO_ENABLED = 1, +}; + +enum wmi_sniffer_cfg_phy_support { + WMI_SNIFFER_CP = 0, + WMI_SNIFFER_DP = 1, + WMI_SNIFFER_BOTH_PHYS = 2, +}; + +struct wmi_sniffer_cfg { + __le32 mode; /* enum wmi_sniffer_cfg_mode */ + __le32 phy_info_mode; /* enum wmi_sniffer_cfg_phy_info_mode */ + __le32 phy_support; /* enum wmi_sniffer_cfg_phy_support */ + u8 channel; + u8 reserved[3]; +} __packed; + +enum wmi_cfg_rx_chain_cmd_action { + WMI_RX_CHAIN_ADD = 0, + WMI_RX_CHAIN_DEL = 1, +}; + +enum wmi_cfg_rx_chain_cmd_decap_trans_type { + WMI_DECAP_TYPE_802_3 = 0, + WMI_DECAP_TYPE_NATIVE_WIFI = 1, +}; + +enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type { + WMI_NWIFI_RX_TRANS_MODE_NO = 0, + WMI_NWIFI_RX_TRANS_MODE_PBSS2AP = 1, + WMI_NWIFI_RX_TRANS_MODE_PBSS2STA = 2, +}; + +struct wmi_cfg_rx_chain_cmd { + __le32 action; + struct wmi_sw_ring_cfg rx_sw_ring; + u8 mid; + u8 decap_trans_type; + + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0) + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1) + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1) + u8 l2_802_3_offload_ctrl; + + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_LEN (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_MSK (0x1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_POS (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_LEN (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_MSK (0x2) + u8 l2_nwifi_offload_ctrl; + + u8 vlan_id; + u8 nwifi_ds_trans_type; + + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_POS (0) + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_LEN (1) + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_MSK (0x1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS (1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_LEN (1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_MSK (0x2) + u8 l3_l4_ctrl; + + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_POS (0) + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_MSK (0x1) + #define RING_CTRL_OVERRIDE_WB_THRSH_POS (1) + #define RING_CTRL_OVERRIDE_WB_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_WB_THRSH_MSK (0x2) + #define RING_CTRL_OVERRIDE_ITR_THRSH_POS (2) + #define RING_CTRL_OVERRIDE_ITR_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_ITR_THRSH_MSK (0x4) + #define RING_CTRL_OVERRIDE_HOST_THRSH_POS (3) + #define RING_CTRL_OVERRIDE_HOST_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_HOST_THRSH_MSK (0x8) + u8 ring_ctrl; + + __le16 prefetch_thrsh; + __le16 wb_thrsh; + __le32 itr_value; + __le16 host_thrsh; + u8 reserved[2]; + struct wmi_sniffer_cfg sniffer_cfg; +} __packed; + +/* + * WMI_RCP_ADDBA_RESP_CMDID + */ +struct wmi_rcp_addba_resp_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + __le16 status_code; + __le16 ba_param_set; /* ieee80211_ba_parameterset field to send */ + __le16 ba_timeout; +} __packed; + +/* + * WMI_RCP_DELBA_CMDID + */ +struct wmi_rcp_delba_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 reserved; + __le16 reason; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_CMDID + */ +struct wmi_rcp_addba_req_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + /* ieee80211_ba_parameterset field as it received */ + __le16 ba_param_set; + __le16 ba_timeout; + /* ieee80211_ba_seqstrl field as it received */ + __le16 ba_seq_ctrl; +} __packed; + +/* + * WMI_SET_MAC_ADDRESS_CMDID + */ +struct wmi_set_mac_address_cmd { + u8 mac[WMI_MAC_LEN]; + u8 reserved[2]; +} __packed; + + +/* +* WMI_EAPOL_TX_CMDID +*/ +struct wmi_eapol_tx_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 eapol_len; + u8 eapol[0]; +} __packed; + +/* + * WMI_ECHO_CMDID + * + * Check FW is alive + * + * WMI_DEEP_ECHO_CMDID + * + * Check FW and ucode are alive + * + * Returned event: WMI_ECHO_RSP_EVENTID + * same event for both commands + */ +struct wmi_echo_cmd { + __le32 value; +} __packed; + +/* + * WMI Events + */ + +/* + * List of Events (target to host) + */ +enum wmi_event_id { + WMI_IMM_RSP_EVENTID = 0x0000, + WMI_READY_EVENTID = 0x1001, + WMI_CONNECT_EVENTID = 0x1002, + WMI_DISCONNECT_EVENTID = 0x1003, + WMI_SCAN_COMPLETE_EVENTID = 0x100a, + WMI_REPORT_STATISTICS_EVENTID = 0x100b, + WMI_RD_MEM_RSP_EVENTID = 0x1800, + WMI_FW_READY_EVENTID = 0x1801, + WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x0200, + WMI_ECHO_RSP_EVENTID = 0x1803, + WMI_CONFIG_MAC_DONE_EVENTID = 0x1805, + WMI_CONFIG_PHY_DEBUG_DONE_EVENTID = 0x1806, + WMI_ADD_STATION_DONE_EVENTID = 0x1807, + WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID = 0x1808, + WMI_PHY_GET_STATISTICS_EVENTID = 0x1809, + WMI_FS_TUNE_DONE_EVENTID = 0x180a, + WMI_CORR_MEASURE_DONE_EVENTID = 0x180b, + WMI_TEMP_SENSE_DONE_EVENTID = 0x180e, + WMI_DC_CALIB_DONE_EVENTID = 0x180f, + WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811, + WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812, + WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815, + WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816, + WMI_MARLON_R_ACTIVATE_DONE_EVENTID = 0x1817, + WMI_MARLON_R_READ_DONE_EVENTID = 0x1818, + WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819, + WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181a, + WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181d, + + WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820, + WMI_VRING_CFG_DONE_EVENTID = 0x1821, + WMI_RX_ON_DONE_EVENTID = 0x1822, + WMI_BA_STATUS_EVENTID = 0x1823, + WMI_RCP_ADDBA_REQ_EVENTID = 0x1824, + WMI_ADDBA_RESP_SENT_EVENTID = 0x1825, + WMI_DELBA_EVENTID = 0x1826, + WMI_GET_SSID_EVENTID = 0x1828, + WMI_GET_PCP_CHANNEL_EVENTID = 0x182a, + WMI_SW_TX_COMPLETE_EVENTID = 0x182b, + WMI_RX_OFF_DONE_EVENTID = 0x182c, + + WMI_READ_MAC_RXQ_EVENTID = 0x1830, + WMI_READ_MAC_TXQ_EVENTID = 0x1831, + WMI_WRITE_MAC_RXQ_EVENTID = 0x1832, + WMI_WRITE_MAC_TXQ_EVENTID = 0x1833, + WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834, + + WMI_BEAFORMING_MGMT_DONE_EVENTID = 0x1836, + WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837, + WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, + WMI_RS_MGMT_DONE_EVENTID = 0x1852, + WMI_RF_MGMT_STATUS_EVENTID = 0x1853, + WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, + WMI_RX_MGMT_PACKET_EVENTID = 0x1840, + + /* Performance monitoring events */ + WMI_DATA_PORT_OPEN_EVENTID = 0x1860, + WMI_WBE_LINKDOWN_EVENTID = 0x1861, + + WMI_BF_CTRL_DONE_EVENTID = 0x1862, + WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863, + WMI_GET_STATUS_DONE_EVENTID = 0x1864, + + WMI_UNIT_TEST_EVENTID = 0x1900, + WMI_FLASH_READ_DONE_EVENTID = 0x1902, + WMI_FLASH_WRITE_DONE_EVENTID = 0x1903, + + WMI_SET_CHANNEL_EVENTID = 0x9000, + WMI_ASSOC_REQ_EVENTID = 0x9001, + WMI_EAPOL_RX_EVENTID = 0x9002, + WMI_MAC_ADDR_RESP_EVENTID = 0x9003, + WMI_FW_VER_EVENTID = 0x9004, +}; + +/* + * Events data structures + */ + +/* + * WMI_RF_MGMT_STATUS_EVENTID + */ +enum wmi_rf_status { + WMI_RF_ENABLED = 0, + WMI_RF_DISABLED_HW = 1, + WMI_RF_DISABLED_SW = 2, + WMI_RF_DISABLED_HW_SW = 3, +}; + +struct wmi_rf_mgmt_status_event { + __le32 rf_status; +} __packed; + +/* + * WMI_GET_STATUS_DONE_EVENTID + */ +struct wmi_get_status_done_event { + __le32 is_associated; + u8 cid; + u8 reserved0[3]; + u8 bssid[WMI_MAC_LEN]; + u8 channel; + u8 reserved1; + u8 network_type; + u8 reserved2[3]; + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; + __le32 rf_status; + __le32 is_secured; +} __packed; + +/* + * WMI_FW_VER_EVENTID + */ +struct wmi_fw_ver_event { + u8 major; + u8 minor; + __le16 subminor; + __le16 build; +} __packed; + +/* +* WMI_MAC_ADDR_RESP_EVENTID +*/ +struct wmi_mac_addr_resp_event { + u8 mac[WMI_MAC_LEN]; + u8 auth_mode; + u8 crypt_mode; + __le32 offload_mode; +} __packed; + +/* +* WMI_EAPOL_RX_EVENTID +*/ +struct wmi_eapol_rx_event { + u8 src_mac[WMI_MAC_LEN]; + __le16 eapol_len; + u8 eapol[0]; +} __packed; + +/* +* WMI_READY_EVENTID +*/ +enum wmi_phy_capability { + WMI_11A_CAPABILITY = 1, + WMI_11G_CAPABILITY = 2, + WMI_11AG_CAPABILITY = 3, + WMI_11NA_CAPABILITY = 4, + WMI_11NG_CAPABILITY = 5, + WMI_11NAG_CAPABILITY = 6, + WMI_11AD_CAPABILITY = 7, + WMI_11N_CAPABILITY_OFFSET = WMI_11NA_CAPABILITY - WMI_11A_CAPABILITY, +}; + +struct wmi_ready_event { + __le32 sw_version; + __le32 abi_version; + u8 mac[WMI_MAC_LEN]; + u8 phy_capability; /* enum wmi_phy_capability */ + u8 reserved; +} __packed; + +/* + * WMI_NOTIFY_REQ_DONE_EVENTID + */ +struct wmi_notify_req_done_event { + __le32 status; + __le64 tsf; + __le32 snr_val; + __le32 tx_tpt; + __le32 tx_goodput; + __le32 rx_goodput; + __le16 bf_mcs; + __le16 my_rx_sector; + __le16 my_tx_sector; + __le16 other_rx_sector; + __le16 other_tx_sector; + __le16 range; +} __packed; + +/* + * WMI_CONNECT_EVENTID + */ +struct wmi_connect_event { + u8 channel; + u8 reserved0; + u8 bssid[WMI_MAC_LEN]; + __le16 listen_interval; + __le16 beacon_interval; + u8 network_type; + u8 reserved1[3]; + u8 beacon_ie_len; + u8 assoc_req_len; + u8 assoc_resp_len; + u8 cid; + u8 reserved2[3]; + u8 assoc_info[0]; +} __packed; + +/* + * WMI_DISCONNECT_EVENTID + */ +enum wmi_disconnect_reason { + WMI_DIS_REASON_NO_NETWORK_AVAIL = 1, + WMI_DIS_REASON_LOST_LINK = 2, /* bmiss */ + WMI_DIS_REASON_DISCONNECT_CMD = 3, + WMI_DIS_REASON_BSS_DISCONNECTED = 4, + WMI_DIS_REASON_AUTH_FAILED = 5, + WMI_DIS_REASON_ASSOC_FAILED = 6, + WMI_DIS_REASON_NO_RESOURCES_AVAIL = 7, + WMI_DIS_REASON_CSERV_DISCONNECT = 8, + WMI_DIS_REASON_INVALID_PROFILE = 10, + WMI_DIS_REASON_DOT11H_CHANNEL_SWITCH = 11, + WMI_DIS_REASON_PROFILE_MISMATCH = 12, + WMI_DIS_REASON_CONNECTION_EVICTED = 13, + WMI_DIS_REASON_IBSS_MERGE = 14, +}; + +struct wmi_disconnect_event { + __le16 protocol_reason_status; /* reason code, see 802.11 spec. */ + u8 bssid[WMI_MAC_LEN]; /* set if known */ + u8 disconnect_reason; /* see wmi_disconnect_reason_e */ + u8 assoc_resp_len; + u8 assoc_info[0]; +} __packed; + +/* + * WMI_SCAN_COMPLETE_EVENTID + */ +struct wmi_scan_complete_event { + __le32 status; +} __packed; + +/* + * WMI_BA_STATUS_EVENTID + */ +enum wmi_vring_ba_status { + WMI_BA_AGREED = 0, + WMI_BA_NON_AGREED = 1, +}; + +struct wmi_vring_ba_status_event { + __le16 status; + u8 reserved[2]; + u8 ringid; + u8 agg_wsize; + __le16 ba_timeout; +} __packed; + +/* + * WMI_DELBA_EVENTID + */ +struct wmi_delba_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 from_initiator; + __le16 reason; +} __packed; + +/* + * WMI_VRING_CFG_DONE_EVENTID + */ +enum wmi_vring_cfg_done_event_status { + WMI_VRING_CFG_SUCCESS = 0, + WMI_VRING_CFG_FAILURE = 1, +}; + +struct wmi_vring_cfg_done_event { + u8 ringid; + u8 status; + u8 reserved[2]; + __le32 tx_vring_tail_ptr; +} __packed; + +/* + * WMI_ADDBA_RESP_SENT_EVENTID + */ +enum wmi_rcp_addba_resp_sent_event_status { + WMI_ADDBA_SUCCESS = 0, + WMI_ADDBA_FAIL = 1, +}; + +struct wmi_rcp_addba_resp_sent_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 reserved; + __le16 status; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_EVENTID + */ +struct wmi_rcp_addba_req_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + __le16 ba_param_set; /* ieee80211_ba_parameterset as it received */ + __le16 ba_timeout; + __le16 ba_seq_ctrl; /* ieee80211_ba_seqstrl field as it received */ +} __packed; + +/* + * WMI_CFG_RX_CHAIN_DONE_EVENTID + */ +enum wmi_cfg_rx_chain_done_event_status { + WMI_CFG_RX_CHAIN_SUCCESS = 1, +}; + +struct wmi_cfg_rx_chain_done_event { + __le32 rx_ring_tail_ptr; /* Rx V-Ring Tail pointer */ + __le32 status; +} __packed; + +/* + * WMI_WBE_LINKDOWN_EVENTID + */ +enum wmi_wbe_link_down_event_reason { + WMI_WBE_REASON_USER_REQUEST = 0, + WMI_WBE_REASON_RX_DISASSOC = 1, + WMI_WBE_REASON_BAD_PHY_LINK = 2, +}; + +struct wmi_wbe_link_down_event { + u8 cid; + u8 reserved[3]; + __le32 reason; +} __packed; + +/* + * WMI_DATA_PORT_OPEN_EVENTID + */ +struct wmi_data_port_open_event { + u8 cid; + u8 reserved[3]; +} __packed; + +/* + * WMI_GET_PCP_CHANNEL_EVENTID + */ +struct wmi_get_pcp_channel_event { + u8 channel; + u8 reserved[3]; +} __packed; + +/* + * WMI_SW_TX_COMPLETE_EVENTID + */ +enum wmi_sw_tx_status { + WMI_TX_SW_STATUS_SUCCESS = 0, + WMI_TX_SW_STATUS_FAILED_NO_RESOURCES = 1, + WMI_TX_SW_STATUS_FAILED_TX = 2, +}; + +struct wmi_sw_tx_complete_event { + u8 status; /* enum wmi_sw_tx_status */ + u8 reserved[3]; +} __packed; + +/* + * WMI_GET_SSID_EVENTID + */ +struct wmi_get_ssid_event { + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_RX_MGMT_PACKET_EVENTID + */ +struct wmi_rx_mgmt_info { + u8 mcs; + s8 snr; + __le16 range; + __le16 stype; + __le16 status; + __le32 len; + u8 qid; + u8 mid; + u8 cid; + u8 channel; /* From Radio MNGR */ +} __packed; + +struct wmi_rx_mgmt_packet_event { + struct wmi_rx_mgmt_info info; + u8 payload[0]; +} __packed; + +/* + * WMI_ECHO_RSP_EVENTID + */ +struct wmi_echo_event { + __le32 echoed_value; +} __packed; + +#endif /* __WILOCITY_WMI_H__ */ diff --git a/trunk/drivers/net/wireless/b43/b43.h b/trunk/drivers/net/wireless/b43/b43.h index b298e5d68be2..10e288d470e7 100644 --- a/trunk/drivers/net/wireless/b43/b43.h +++ b/trunk/drivers/net/wireless/b43/b43.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "debugfs.h" @@ -722,6 +723,10 @@ enum b43_firmware_file_type { struct b43_request_fw_context { /* The device we are requesting the fw for. */ struct b43_wldev *dev; + /* a completion event structure needed if this call is asynchronous */ + struct completion fw_load_complete; + /* a pointer to the firmware object */ + const struct firmware *blob; /* The type of firmware to request. */ enum b43_firmware_file_type req_type; /* Error messages for each firmware type. */ diff --git a/trunk/drivers/net/wireless/b43/main.c b/trunk/drivers/net/wireless/b43/main.c index 16ab280359bd..806e34c19281 100644 --- a/trunk/drivers/net/wireless/b43/main.c +++ b/trunk/drivers/net/wireless/b43/main.c @@ -2088,11 +2088,18 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error) b43warn(wl, text); } +static void b43_fw_cb(const struct firmware *firmware, void *context) +{ + struct b43_request_fw_context *ctx = context; + + ctx->blob = firmware; + complete(&ctx->fw_load_complete); +} + int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, - struct b43_firmware_file *fw) + struct b43_firmware_file *fw, bool async) { - const struct firmware *blob; struct b43_fw_header *hdr; u32 size; int err; @@ -2131,11 +2138,31 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, B43_WARN_ON(1); return -ENOSYS; } - err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev); + if (async) { + /* do this part asynchronously */ + init_completion(&ctx->fw_load_complete); + err = request_firmware_nowait(THIS_MODULE, 1, ctx->fwname, + ctx->dev->dev->dev, GFP_KERNEL, + ctx, b43_fw_cb); + if (err < 0) { + pr_err("Unable to load firmware\n"); + return err; + } + /* stall here until fw ready */ + wait_for_completion(&ctx->fw_load_complete); + if (ctx->blob) + goto fw_ready; + /* On some ARM systems, the async request will fail, but the next sync + * request works. For this reason, we dall through here + */ + } + err = request_firmware(&ctx->blob, ctx->fwname, + ctx->dev->dev->dev); if (err == -ENOENT) { snprintf(ctx->errors[ctx->req_type], sizeof(ctx->errors[ctx->req_type]), - "Firmware file \"%s\" not found\n", ctx->fwname); + "Firmware file \"%s\" not found\n", + ctx->fwname); return err; } else if (err) { snprintf(ctx->errors[ctx->req_type], @@ -2144,14 +2171,15 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, ctx->fwname, err); return err; } - if (blob->size < sizeof(struct b43_fw_header)) +fw_ready: + if (ctx->blob->size < sizeof(struct b43_fw_header)) goto err_format; - hdr = (struct b43_fw_header *)(blob->data); + hdr = (struct b43_fw_header *)(ctx->blob->data); switch (hdr->type) { case B43_FW_TYPE_UCODE: case B43_FW_TYPE_PCM: size = be32_to_cpu(hdr->size); - if (size != blob->size - sizeof(struct b43_fw_header)) + if (size != ctx->blob->size - sizeof(struct b43_fw_header)) goto err_format; /* fallthrough */ case B43_FW_TYPE_IV: @@ -2162,7 +2190,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, goto err_format; } - fw->data = blob; + fw->data = ctx->blob; fw->filename = name; fw->type = ctx->req_type; @@ -2172,7 +2200,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, snprintf(ctx->errors[ctx->req_type], sizeof(ctx->errors[ctx->req_type]), "Firmware file \"%s\" format error.\n", ctx->fwname); - release_firmware(blob); + release_firmware(ctx->blob); return -EPROTO; } @@ -2223,7 +2251,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) goto err_no_ucode; } } - err = b43_do_request_fw(ctx, filename, &fw->ucode); + err = b43_do_request_fw(ctx, filename, &fw->ucode, true); if (err) goto err_load; @@ -2235,7 +2263,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) else goto err_no_pcm; fw->pcm_request_failed = false; - err = b43_do_request_fw(ctx, filename, &fw->pcm); + err = b43_do_request_fw(ctx, filename, &fw->pcm, false); if (err == -ENOENT) { /* We did not find a PCM file? Not fatal, but * core rev <= 10 must do without hwcrypto then. */ @@ -2296,7 +2324,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) default: goto err_no_initvals; } - err = b43_do_request_fw(ctx, filename, &fw->initvals); + err = b43_do_request_fw(ctx, filename, &fw->initvals, false); if (err) goto err_load; @@ -2355,7 +2383,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) default: goto err_no_initvals; } - err = b43_do_request_fw(ctx, filename, &fw->initvals_band); + err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false); if (err) goto err_load; diff --git a/trunk/drivers/net/wireless/b43/main.h b/trunk/drivers/net/wireless/b43/main.h index 8c684cd33529..abac25ee958d 100644 --- a/trunk/drivers/net/wireless/b43/main.h +++ b/trunk/drivers/net/wireless/b43/main.h @@ -137,9 +137,8 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on); struct b43_request_fw_context; -int b43_do_request_fw(struct b43_request_fw_context *ctx, - const char *name, - struct b43_firmware_file *fw); +int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, + struct b43_firmware_file *fw, bool async); void b43_do_release_fw(struct b43_firmware_file *fw); #endif /* B43_MAIN_H_ */ diff --git a/trunk/drivers/net/wireless/iwlegacy/3945-mac.c b/trunk/drivers/net/wireless/iwlegacy/3945-mac.c index d604b4036a76..3726cd6fcd75 100644 --- a/trunk/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/trunk/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3273,7 +3273,7 @@ il3945_store_measurement(struct device *d, struct device_attribute *attr, if (count) { char *p = buffer; - strncpy(buffer, buf, min(sizeof(buffer), count)); + strlcpy(buffer, buf, sizeof(buffer)); channel = simple_strtoul(p, NULL, 0); if (channel) params.channel = channel; diff --git a/trunk/drivers/net/wireless/iwlwifi/dvm/tx.c b/trunk/drivers/net/wireless/iwlwifi/dvm/tx.c index da21328ca8ed..a790599fe2c2 100644 --- a/trunk/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/trunk/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1151,13 +1151,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, next_reclaimed = ssn; } - if (tid != IWL_TID_NON_QOS) { - priv->tid_data[sta_id][tid].next_reclaimed = - next_reclaimed; - IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", - next_reclaimed); - } - iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); iwlagn_check_ratid_empty(priv, sta_id, tid); @@ -1208,11 +1201,28 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); + /* + * W/A for FW bug - the seq_ctl isn't updated when the + * queues are flushed. Fetch it from the packet itself + */ + if (!is_agg && status == TX_STATUS_FAIL_FIFO_FLUSHED) { + next_reclaimed = le16_to_cpu(hdr->seq_ctrl); + next_reclaimed = + SEQ_TO_SN(next_reclaimed + 0x10); + } + is_offchannel_skb = (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); freed++; } + if (tid != IWL_TID_NON_QOS) { + priv->tid_data[sta_id][tid].next_reclaimed = + next_reclaimed; + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", + next_reclaimed); + } + WARN_ON(!is_agg && freed != 1); /* diff --git a/trunk/drivers/net/wireless/iwlwifi/pcie/rx.c b/trunk/drivers/net/wireless/iwlwifi/pcie/rx.c index dad4c4aad91f..8389cd38338b 100644 --- a/trunk/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/trunk/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -1166,6 +1166,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); + return IRQ_HANDLED; none: /* re-enable interrupts here since we don't have anything to service. */ diff --git a/trunk/drivers/net/wireless/mwifiex/cfg80211.c b/trunk/drivers/net/wireless/mwifiex/cfg80211.c index a875499f8945..efe525be27dd 100644 --- a/trunk/drivers/net/wireless/mwifiex/cfg80211.c +++ b/trunk/drivers/net/wireless/mwifiex/cfg80211.c @@ -1709,7 +1709,7 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, NL80211_CHAN_NO_HT) config_bands |= BAND_GN; } else { - if (cfg80211_get_chandef_type(¶ms->chandef) != + if (cfg80211_get_chandef_type(¶ms->chandef) == NL80211_CHAN_NO_HT) config_bands = BAND_A; else diff --git a/trunk/drivers/net/wireless/mwifiex/sta_ioctl.c b/trunk/drivers/net/wireless/mwifiex/sta_ioctl.c index cb682561c438..60e88b58039d 100644 --- a/trunk/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/trunk/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -56,7 +56,6 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, */ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) { - bool cancel_flag = false; int status; struct cmd_ctrl_node *cmd_queued; @@ -70,14 +69,11 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) atomic_inc(&adapter->cmd_pending); /* Wait for completion */ - wait_event_interruptible(adapter->cmd_wait_q.wait, - *(cmd_queued->condition)); - if (!*(cmd_queued->condition)) - cancel_flag = true; - - if (cancel_flag) { - mwifiex_cancel_pending_ioctl(adapter); - dev_dbg(adapter->dev, "cmd cancel\n"); + status = wait_event_interruptible(adapter->cmd_wait_q.wait, + *(cmd_queued->condition)); + if (status) { + dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status); + return status; } status = adapter->cmd_wait_q.status; @@ -496,8 +492,11 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) return false; } - wait_event_interruptible(adapter->hs_activate_wait_q, - adapter->hs_activate_wait_q_woken); + if (wait_event_interruptible(adapter->hs_activate_wait_q, + adapter->hs_activate_wait_q_woken)) { + dev_err(adapter->dev, "hs_activate_wait_q terminated\n"); + return false; + } return true; } diff --git a/trunk/drivers/net/wireless/mwl8k.c b/trunk/drivers/net/wireless/mwl8k.c index f221b95b90b3..83564d36e801 100644 --- a/trunk/drivers/net/wireless/mwl8k.c +++ b/trunk/drivers/net/wireless/mwl8k.c @@ -4250,9 +4250,11 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, p->amsdu_enabled = 0; rc = mwl8k_post_cmd(hw, &cmd->header); + if (!rc) + rc = p->station_id; kfree(cmd); - return rc ? rc : p->station_id; + return rc; } static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, diff --git a/trunk/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/trunk/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c index 1d5d3604e3e0..246e5352f2e1 100644 --- a/trunk/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c +++ b/trunk/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c @@ -692,7 +692,7 @@ u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw) if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { rtl92c_phy_sw_chnl_callback(hw); RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, - "sw_chnl_inprogress false schdule workitem\n"); + "sw_chnl_inprogress false schedule workitem\n"); rtlphy->sw_chnl_inprogress = false; } else { RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, diff --git a/trunk/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/trunk/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c index 39cc7938eedf..3d8536bb0d2b 100644 --- a/trunk/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c +++ b/trunk/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c @@ -1106,7 +1106,7 @@ u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw) if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { rtl8723ae_phy_sw_chnl_callback(hw); RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, - "sw_chnl_inprogress false schdule workitem\n"); + "sw_chnl_inprogress false schedule workitem\n"); rtlphy->sw_chnl_inprogress = false; } else { RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, diff --git a/trunk/drivers/pci/hotplug/pciehp.h b/trunk/drivers/pci/hotplug/pciehp.h index 26ffd3e3fb74..2c113de94323 100644 --- a/trunk/drivers/pci/hotplug/pciehp.h +++ b/trunk/drivers/pci/hotplug/pciehp.h @@ -44,7 +44,6 @@ extern bool pciehp_poll_mode; extern int pciehp_poll_time; extern bool pciehp_debug; extern bool pciehp_force; -extern struct workqueue_struct *pciehp_wq; #define dbg(format, arg...) \ do { \ @@ -78,6 +77,7 @@ struct slot { struct hotplug_slot *hotplug_slot; struct delayed_work work; /* work for button event */ struct mutex lock; + struct workqueue_struct *wq; }; struct event_info { diff --git a/trunk/drivers/pci/hotplug/pciehp_core.c b/trunk/drivers/pci/hotplug/pciehp_core.c index 916bf4f53aba..939bd1d4b5b1 100644 --- a/trunk/drivers/pci/hotplug/pciehp_core.c +++ b/trunk/drivers/pci/hotplug/pciehp_core.c @@ -42,7 +42,6 @@ bool pciehp_debug; bool pciehp_poll_mode; int pciehp_poll_time; bool pciehp_force; -struct workqueue_struct *pciehp_wq; #define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Dan Zink , Greg Kroah-Hartman , Dely Sy " @@ -340,18 +339,13 @@ static int __init pcied_init(void) { int retval = 0; - pciehp_wq = alloc_workqueue("pciehp", 0, 0); - if (!pciehp_wq) - return -ENOMEM; - pciehp_firmware_init(); retval = pcie_port_service_register(&hpdriver_portdrv); dbg("pcie_port_service_register = %d\n", retval); info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - if (retval) { - destroy_workqueue(pciehp_wq); + if (retval) dbg("Failure to register service\n"); - } + return retval; } @@ -359,7 +353,6 @@ static void __exit pcied_cleanup(void) { dbg("unload_pciehpd()\n"); pcie_port_service_unregister(&hpdriver_portdrv); - destroy_workqueue(pciehp_wq); info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); } diff --git a/trunk/drivers/pci/hotplug/pciehp_ctrl.c b/trunk/drivers/pci/hotplug/pciehp_ctrl.c index 27f44295a657..38f018679175 100644 --- a/trunk/drivers/pci/hotplug/pciehp_ctrl.c +++ b/trunk/drivers/pci/hotplug/pciehp_ctrl.c @@ -49,7 +49,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type) info->p_slot = p_slot; INIT_WORK(&info->work, interrupt_event_handler); - queue_work(pciehp_wq, &info->work); + queue_work(p_slot->wq, &info->work); return 0; } @@ -344,7 +344,7 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) kfree(info); goto out; } - queue_work(pciehp_wq, &info->work); + queue_work(p_slot->wq, &info->work); out: mutex_unlock(&p_slot->lock); } @@ -377,7 +377,7 @@ static void handle_button_press_event(struct slot *p_slot) if (ATTN_LED(ctrl)) pciehp_set_attention_status(p_slot, 0); - queue_delayed_work(pciehp_wq, &p_slot->work, 5*HZ); + queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); break; case BLINKINGOFF_STATE: case BLINKINGON_STATE: @@ -439,7 +439,7 @@ static void handle_surprise_event(struct slot *p_slot) else p_slot->state = POWERON_STATE; - queue_work(pciehp_wq, &info->work); + queue_work(p_slot->wq, &info->work); } static void interrupt_event_handler(struct work_struct *work) diff --git a/trunk/drivers/pci/hotplug/pciehp_hpc.c b/trunk/drivers/pci/hotplug/pciehp_hpc.c index 13b2eaf7ba43..5127f3f41821 100644 --- a/trunk/drivers/pci/hotplug/pciehp_hpc.c +++ b/trunk/drivers/pci/hotplug/pciehp_hpc.c @@ -773,23 +773,32 @@ static void pcie_shutdown_notification(struct controller *ctrl) static int pcie_init_slot(struct controller *ctrl) { struct slot *slot; + char name[32]; slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; + snprintf(name, sizeof(name), "pciehp-%u", PSN(ctrl)); + slot->wq = alloc_workqueue(name, 0, 0); + if (!slot->wq) + goto abort; + slot->ctrl = ctrl; mutex_init(&slot->lock); INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); ctrl->slot = slot; return 0; +abort: + kfree(slot); + return -ENOMEM; } static void pcie_cleanup_slot(struct controller *ctrl) { struct slot *slot = ctrl->slot; cancel_delayed_work(&slot->work); - flush_workqueue(pciehp_wq); + destroy_workqueue(slot->wq); kfree(slot); } diff --git a/trunk/drivers/pci/hotplug/shpchp.h b/trunk/drivers/pci/hotplug/shpchp.h index ca64932e658b..b849f995075a 100644 --- a/trunk/drivers/pci/hotplug/shpchp.h +++ b/trunk/drivers/pci/hotplug/shpchp.h @@ -46,8 +46,6 @@ extern bool shpchp_poll_mode; extern int shpchp_poll_time; extern bool shpchp_debug; -extern struct workqueue_struct *shpchp_wq; -extern struct workqueue_struct *shpchp_ordered_wq; #define dbg(format, arg...) \ do { \ @@ -91,6 +89,7 @@ struct slot { struct list_head slot_list; struct delayed_work work; /* work for button event */ struct mutex lock; + struct workqueue_struct *wq; u8 hp_slot; }; diff --git a/trunk/drivers/pci/hotplug/shpchp_core.c b/trunk/drivers/pci/hotplug/shpchp_core.c index b6de307248e4..3100c52c837c 100644 --- a/trunk/drivers/pci/hotplug/shpchp_core.c +++ b/trunk/drivers/pci/hotplug/shpchp_core.c @@ -39,8 +39,6 @@ bool shpchp_debug; bool shpchp_poll_mode; int shpchp_poll_time; -struct workqueue_struct *shpchp_wq; -struct workqueue_struct *shpchp_ordered_wq; #define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Dan Zink , Greg Kroah-Hartman , Dely Sy " @@ -129,6 +127,14 @@ static int init_slots(struct controller *ctrl) slot->device = ctrl->slot_device_offset + i; slot->hpc_ops = ctrl->hpc_ops; slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i); + + snprintf(name, sizeof(name), "shpchp-%d", slot->number); + slot->wq = alloc_workqueue(name, 0, 0); + if (!slot->wq) { + retval = -ENOMEM; + goto error_info; + } + mutex_init(&slot->lock); INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work); @@ -148,7 +154,7 @@ static int init_slots(struct controller *ctrl) if (retval) { ctrl_err(ctrl, "pci_hp_register failed with error %d\n", retval); - goto error_info; + goto error_slotwq; } get_power_status(hotplug_slot, &info->power_status); @@ -160,6 +166,8 @@ static int init_slots(struct controller *ctrl) } return 0; +error_slotwq: + destroy_workqueue(slot->wq); error_info: kfree(info); error_hpslot: @@ -180,8 +188,7 @@ void cleanup_slots(struct controller *ctrl) slot = list_entry(tmp, struct slot, slot_list); list_del(&slot->slot_list); cancel_delayed_work(&slot->work); - flush_workqueue(shpchp_wq); - flush_workqueue(shpchp_ordered_wq); + destroy_workqueue(slot->wq); pci_hp_deregister(slot->hotplug_slot); } } @@ -364,25 +371,12 @@ static struct pci_driver shpc_driver = { static int __init shpcd_init(void) { - int retval = 0; - - shpchp_wq = alloc_ordered_workqueue("shpchp", 0); - if (!shpchp_wq) - return -ENOMEM; - - shpchp_ordered_wq = alloc_ordered_workqueue("shpchp_ordered", 0); - if (!shpchp_ordered_wq) { - destroy_workqueue(shpchp_wq); - return -ENOMEM; - } + int retval; retval = pci_register_driver(&shpc_driver); dbg("%s: pci_register_driver = %d\n", __func__, retval); info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - if (retval) { - destroy_workqueue(shpchp_ordered_wq); - destroy_workqueue(shpchp_wq); - } + return retval; } @@ -390,8 +384,6 @@ static void __exit shpcd_cleanup(void) { dbg("unload_shpchpd()\n"); pci_unregister_driver(&shpc_driver); - destroy_workqueue(shpchp_ordered_wq); - destroy_workqueue(shpchp_wq); info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); } diff --git a/trunk/drivers/pci/hotplug/shpchp_ctrl.c b/trunk/drivers/pci/hotplug/shpchp_ctrl.c index f9b5a52e4115..58499277903a 100644 --- a/trunk/drivers/pci/hotplug/shpchp_ctrl.c +++ b/trunk/drivers/pci/hotplug/shpchp_ctrl.c @@ -51,7 +51,7 @@ static int queue_interrupt_event(struct slot *p_slot, u32 event_type) info->p_slot = p_slot; INIT_WORK(&info->work, interrupt_event_handler); - queue_work(shpchp_wq, &info->work); + queue_work(p_slot->wq, &info->work); return 0; } @@ -453,7 +453,7 @@ void shpchp_queue_pushbutton_work(struct work_struct *work) kfree(info); goto out; } - queue_work(shpchp_ordered_wq, &info->work); + queue_work(p_slot->wq, &info->work); out: mutex_unlock(&p_slot->lock); } @@ -501,7 +501,7 @@ static void handle_button_press_event(struct slot *p_slot) p_slot->hpc_ops->green_led_blink(p_slot); p_slot->hpc_ops->set_attention_status(p_slot, 0); - queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ); + queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); break; case BLINKINGOFF_STATE: case BLINKINGON_STATE: diff --git a/trunk/drivers/pci/iov.c b/trunk/drivers/pci/iov.c index bafd2bbcaf65..c18e5bf444fa 100644 --- a/trunk/drivers/pci/iov.c +++ b/trunk/drivers/pci/iov.c @@ -739,7 +739,7 @@ EXPORT_SYMBOL_GPL(pci_num_vf); /** * pci_sriov_set_totalvfs -- reduce the TotalVFs available * @dev: the PCI PF device - * numvfs: number that should be used for TotalVFs supported + * @numvfs: number that should be used for TotalVFs supported * * Should be called from PF driver's probe routine with * device's mutex held. diff --git a/trunk/drivers/pci/pcie/Kconfig b/trunk/drivers/pci/pcie/Kconfig index 6c8bc5809787..fde4a32a0295 100644 --- a/trunk/drivers/pci/pcie/Kconfig +++ b/trunk/drivers/pci/pcie/Kconfig @@ -82,4 +82,4 @@ endchoice config PCIE_PME def_bool y - depends on PCIEPORTBUS && PM_RUNTIME && EXPERIMENTAL && ACPI + depends on PCIEPORTBUS && PM_RUNTIME && ACPI diff --git a/trunk/drivers/pci/pcie/aer/aerdrv_core.c b/trunk/drivers/pci/pcie/aer/aerdrv_core.c index 421bbc5fee32..564d97f94b6c 100644 --- a/trunk/drivers/pci/pcie/aer/aerdrv_core.c +++ b/trunk/drivers/pci/pcie/aer/aerdrv_core.c @@ -630,6 +630,7 @@ static void aer_recover_work_func(struct work_struct *work) continue; } do_recovery(pdev, entry.severity); + pci_dev_put(pdev); } } #endif diff --git a/trunk/drivers/pci/pcie/aspm.c b/trunk/drivers/pci/pcie/aspm.c index b52630b8eada..8474b6a4fc9b 100644 --- a/trunk/drivers/pci/pcie/aspm.c +++ b/trunk/drivers/pci/pcie/aspm.c @@ -771,6 +771,9 @@ void pcie_clear_aspm(struct pci_bus *bus) { struct pci_dev *child; + if (aspm_force) + return; + /* * Clear any ASPM setup that the firmware has carried out on this bus */ diff --git a/trunk/drivers/platform/x86/acer-wmi.c b/trunk/drivers/platform/x86/acer-wmi.c index 06f4eb7ab87e..afed7018a2b5 100644 --- a/trunk/drivers/platform/x86/acer-wmi.c +++ b/trunk/drivers/platform/x86/acer-wmi.c @@ -125,8 +125,11 @@ static const struct key_entry acer_wmi_keymap[] = { {KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} }, {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */ {KE_IGNORE, 0x81, {KEY_SLEEP} }, - {KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad On/Off */ + {KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad Toggle */ + {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, + {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} }, {KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} }, + {KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} }, {KE_END, 0} }; @@ -147,6 +150,7 @@ struct event_return_value { #define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ #define ACER_WMID3_GDS_WIMAX (1<<7) /* WiMAX */ #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ +#define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */ struct lm_input_params { u8 function_num; /* Function Number */ @@ -875,7 +879,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out) struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) }; struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; - u32 tmp; + u32 tmp = 0; acpi_status status; status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result); @@ -884,14 +888,14 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out) return status; obj = (union acpi_object *) result.pointer; - if (obj && obj->type == ACPI_TYPE_BUFFER && - (obj->buffer.length == sizeof(u32) || - obj->buffer.length == sizeof(u64))) { - tmp = *((u32 *) obj->buffer.pointer); - } else if (obj->type == ACPI_TYPE_INTEGER) { - tmp = (u32) obj->integer.value; - } else { - tmp = 0; + if (obj) { + if (obj->type == ACPI_TYPE_BUFFER && + (obj->buffer.length == sizeof(u32) || + obj->buffer.length == sizeof(u64))) { + tmp = *((u32 *) obj->buffer.pointer); + } else if (obj->type == ACPI_TYPE_INTEGER) { + tmp = (u32) obj->integer.value; + } } if (out) @@ -1193,12 +1197,14 @@ static acpi_status WMID_set_capabilities(void) return status; obj = (union acpi_object *) out.pointer; - if (obj && obj->type == ACPI_TYPE_BUFFER && - (obj->buffer.length == sizeof(u32) || - obj->buffer.length == sizeof(u64))) { - devices = *((u32 *) obj->buffer.pointer); - } else if (obj->type == ACPI_TYPE_INTEGER) { - devices = (u32) obj->integer.value; + if (obj) { + if (obj->type == ACPI_TYPE_BUFFER && + (obj->buffer.length == sizeof(u32) || + obj->buffer.length == sizeof(u64))) { + devices = *((u32 *) obj->buffer.pointer); + } else if (obj->type == ACPI_TYPE_INTEGER) { + devices = (u32) obj->integer.value; + } } else { kfree(out.pointer); return AE_ERROR; @@ -1676,6 +1682,7 @@ static void acer_wmi_notify(u32 value, void *context) acpi_status status; u16 device_state; const struct key_entry *key; + u32 scancode; status = wmi_get_event_data(value, &response); if (status != AE_OK) { @@ -1712,6 +1719,7 @@ static void acer_wmi_notify(u32 value, void *context) pr_warn("Unknown key number - 0x%x\n", return_value.key_num); } else { + scancode = return_value.key_num; switch (key->keycode) { case KEY_WLAN: case KEY_BLUETOOTH: @@ -1725,9 +1733,11 @@ static void acer_wmi_notify(u32 value, void *context) rfkill_set_sw_state(bluetooth_rfkill, !(device_state & ACER_WMID3_GDS_BLUETOOTH)); break; + case KEY_TOUCHPAD_TOGGLE: + scancode = (device_state & ACER_WMID3_GDS_TOUCHPAD) ? + KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF; } - sparse_keymap_report_entry(acer_wmi_input_dev, key, - 1, true); + sparse_keymap_report_event(acer_wmi_input_dev, scancode, 1, true); } break; case WMID_ACCEL_EVENT: @@ -1946,12 +1956,14 @@ static u32 get_wmid_devices(void) return 0; obj = (union acpi_object *) out.pointer; - if (obj && obj->type == ACPI_TYPE_BUFFER && - (obj->buffer.length == sizeof(u32) || - obj->buffer.length == sizeof(u64))) { - devices = *((u32 *) obj->buffer.pointer); - } else if (obj->type == ACPI_TYPE_INTEGER) { - devices = (u32) obj->integer.value; + if (obj) { + if (obj->type == ACPI_TYPE_BUFFER && + (obj->buffer.length == sizeof(u32) || + obj->buffer.length == sizeof(u64))) { + devices = *((u32 *) obj->buffer.pointer); + } else if (obj->type == ACPI_TYPE_INTEGER) { + devices = (u32) obj->integer.value; + } } kfree(out.pointer); diff --git a/trunk/drivers/platform/x86/asus-laptop.c b/trunk/drivers/platform/x86/asus-laptop.c index ec1d3bc2dbe2..fcde4e528819 100644 --- a/trunk/drivers/platform/x86/asus-laptop.c +++ b/trunk/drivers/platform/x86/asus-laptop.c @@ -860,8 +860,10 @@ static ssize_t show_infos(struct device *dev, /* * The HWRS method return informations about the hardware. * 0x80 bit is for WLAN, 0x100 for Bluetooth. + * 0x40 for WWAN, 0x10 for WIMAX. * The significance of others is yet to be found. - * If we don't find the method, we assume the device are present. + * We don't currently use this for device detection, and it + * takes several seconds to run on some systems. */ rv = acpi_evaluate_integer(asus->handle, "HWRS", NULL, &temp); if (!ACPI_FAILURE(rv)) @@ -1682,7 +1684,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *model = NULL; - unsigned long long bsts_result, hwrs_result; + unsigned long long bsts_result; char *string = NULL; acpi_status status; @@ -1741,20 +1743,9 @@ static int asus_laptop_get_info(struct asus_laptop *asus) return -ENOMEM; } - if (*string) + if (string) pr_notice(" %s model detected\n", string); - /* - * The HWRS method return informations about the hardware. - * 0x80 bit is for WLAN, 0x100 for Bluetooth, - * 0x40 for WWAN, 0x10 for WIMAX. - * The significance of others is yet to be found. - */ - status = - acpi_evaluate_integer(asus->handle, "HWRS", NULL, &hwrs_result); - if (!ACPI_FAILURE(status)) - pr_notice(" HWRS returned %x", (int)hwrs_result); - if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL)) asus->have_rsts = true; diff --git a/trunk/drivers/platform/x86/samsung-laptop.c b/trunk/drivers/platform/x86/samsung-laptop.c index dd90d15f5210..71623a2ff3e8 100644 --- a/trunk/drivers/platform/x86/samsung-laptop.c +++ b/trunk/drivers/platform/x86/samsung-laptop.c @@ -1523,6 +1523,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { }, .driver_data = &samsung_broken_acpi_video, }, + { + .callback = samsung_dmi_matched, + .ident = "N250P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), + DMI_MATCH(DMI_BOARD_NAME, "N250P"), + }, + .driver_data = &samsung_broken_acpi_video, + }, { }, }; MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); diff --git a/trunk/drivers/platform/x86/sony-laptop.c b/trunk/drivers/platform/x86/sony-laptop.c index daaddec68def..b8ad71f7863f 100644 --- a/trunk/drivers/platform/x86/sony-laptop.c +++ b/trunk/drivers/platform/x86/sony-laptop.c @@ -786,28 +786,29 @@ static int sony_nc_int_call(acpi_handle handle, char *name, int *value, static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value, void *buffer, size_t buflen) { + int ret = 0; size_t len = len; union acpi_object *object = __call_snc_method(handle, name, value); if (!object) return -EINVAL; - if (object->type == ACPI_TYPE_BUFFER) + if (object->type == ACPI_TYPE_BUFFER) { len = MIN(buflen, object->buffer.length); + memcpy(buffer, object->buffer.pointer, len); - else if (object->type == ACPI_TYPE_INTEGER) + } else if (object->type == ACPI_TYPE_INTEGER) { len = MIN(buflen, sizeof(object->integer.value)); + memcpy(buffer, &object->integer.value, len); - else { + } else { pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n", ACPI_TYPE_BUFFER, object->type); - kfree(object); - return -EINVAL; + ret = -EINVAL; } - memcpy(buffer, object->buffer.pointer, len); kfree(object); - return 0; + return ret; } struct sony_nc_handles { diff --git a/trunk/drivers/regulator/core.c b/trunk/drivers/regulator/core.c index 0f65b246cc0c..278584302f2d 100644 --- a/trunk/drivers/regulator/core.c +++ b/trunk/drivers/regulator/core.c @@ -1885,9 +1885,15 @@ int regulator_can_change_voltage(struct regulator *regulator) struct regulator_dev *rdev = regulator->rdev; if (rdev->constraints && - rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE && - (rdev->desc->n_voltages - rdev->desc->linear_min_sel) > 1) - return 1; + (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + if (rdev->desc->n_voltages - rdev->desc->linear_min_sel > 1) + return 1; + + if (rdev->desc->continuous_voltage_range && + rdev->constraints->min_uV && rdev->constraints->max_uV && + rdev->constraints->min_uV != rdev->constraints->max_uV) + return 1; + } return 0; } @@ -3315,7 +3321,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) * @config: runtime configuration for regulator * * Called by regulator drivers to register a regulator. - * Returns 0 on success. + * Returns a valid pointer to struct regulator_dev on success + * or an ERR_PTR() on error. */ struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, diff --git a/trunk/drivers/regulator/max8997.c b/trunk/drivers/regulator/max8997.c index df0eafb0dc7e..02be7fcae32f 100644 --- a/trunk/drivers/regulator/max8997.c +++ b/trunk/drivers/regulator/max8997.c @@ -71,26 +71,26 @@ struct voltage_map_desc { int step; }; -/* Voltage maps in mV */ +/* Voltage maps in uV */ static const struct voltage_map_desc ldo_voltage_map_desc = { - .min = 800, .max = 3950, .step = 50, + .min = 800000, .max = 3950000, .step = 50000, }; /* LDO1 ~ 18, 21 all */ static const struct voltage_map_desc buck1245_voltage_map_desc = { - .min = 650, .max = 2225, .step = 25, + .min = 650000, .max = 2225000, .step = 25000, }; /* Buck1, 2, 4, 5 */ static const struct voltage_map_desc buck37_voltage_map_desc = { - .min = 750, .max = 3900, .step = 50, + .min = 750000, .max = 3900000, .step = 50000, }; /* Buck3, 7 */ -/* current map in mA */ +/* current map in uA */ static const struct voltage_map_desc charger_current_map_desc = { - .min = 200, .max = 950, .step = 50, + .min = 200000, .max = 950000, .step = 50000, }; static const struct voltage_map_desc topoff_current_map_desc = { - .min = 50, .max = 200, .step = 10, + .min = 50000, .max = 200000, .step = 10000, }; static const struct voltage_map_desc *reg_voltage_map[] = { @@ -194,7 +194,7 @@ static int max8997_list_voltage(struct regulator_dev *rdev, if (val > desc->max) return -EINVAL; - return val * 1000; + return val; } static int max8997_get_enable_register(struct regulator_dev *rdev, @@ -485,7 +485,6 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev, { struct max8997_data *max8997 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8997->iodev->i2c; - int min_vol = min_uV / 1000, max_vol = max_uV / 1000; const struct voltage_map_desc *desc; int rid = rdev_get_id(rdev); int i, reg, shift, mask, ret; @@ -509,7 +508,7 @@ static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev, desc = reg_voltage_map[rid]; - i = max8997_get_voltage_proper_val(desc, min_vol, max_vol); + i = max8997_get_voltage_proper_val(desc, min_uV, max_uV); if (i < 0) return i; @@ -557,7 +556,7 @@ static int max8997_set_voltage_ldobuck_time_sel(struct regulator_dev *rdev, case MAX8997_BUCK4: case MAX8997_BUCK5: return DIV_ROUND_UP(desc->step * (new_selector - old_selector), - max8997->ramp_delay); + max8997->ramp_delay * 1000); } return 0; @@ -656,7 +655,6 @@ static int max8997_set_voltage_buck(struct regulator_dev *rdev, const struct voltage_map_desc *desc; int new_val, new_idx, damage, tmp_val, tmp_idx, tmp_dmg; bool gpio_dvs_mode = false; - int min_vol = min_uV / 1000, max_vol = max_uV / 1000; if (rid < MAX8997_BUCK1 || rid > MAX8997_BUCK7) return -EINVAL; @@ -681,7 +679,7 @@ static int max8997_set_voltage_buck(struct regulator_dev *rdev, selector); desc = reg_voltage_map[rid]; - new_val = max8997_get_voltage_proper_val(desc, min_vol, max_vol); + new_val = max8997_get_voltage_proper_val(desc, min_uV, max_uV); if (new_val < 0) return new_val; @@ -1123,8 +1121,8 @@ static int max8997_pmic_probe(struct platform_device *pdev) max8997->buck1_vol[i] = ret = max8997_get_voltage_proper_val( &buck1245_voltage_map_desc, - pdata->buck1_voltage[i] / 1000, - pdata->buck1_voltage[i] / 1000 + + pdata->buck1_voltage[i], + pdata->buck1_voltage[i] + buck1245_voltage_map_desc.step); if (ret < 0) goto err_out; @@ -1132,8 +1130,8 @@ static int max8997_pmic_probe(struct platform_device *pdev) max8997->buck2_vol[i] = ret = max8997_get_voltage_proper_val( &buck1245_voltage_map_desc, - pdata->buck2_voltage[i] / 1000, - pdata->buck2_voltage[i] / 1000 + + pdata->buck2_voltage[i], + pdata->buck2_voltage[i] + buck1245_voltage_map_desc.step); if (ret < 0) goto err_out; @@ -1141,8 +1139,8 @@ static int max8997_pmic_probe(struct platform_device *pdev) max8997->buck5_vol[i] = ret = max8997_get_voltage_proper_val( &buck1245_voltage_map_desc, - pdata->buck5_voltage[i] / 1000, - pdata->buck5_voltage[i] / 1000 + + pdata->buck5_voltage[i], + pdata->buck5_voltage[i] + buck1245_voltage_map_desc.step); if (ret < 0) goto err_out; diff --git a/trunk/drivers/regulator/max8998.c b/trunk/drivers/regulator/max8998.c index b821d08eb64a..1f0df4046b86 100644 --- a/trunk/drivers/regulator/max8998.c +++ b/trunk/drivers/regulator/max8998.c @@ -51,39 +51,39 @@ struct voltage_map_desc { int step; }; -/* Voltage maps */ +/* Voltage maps in uV*/ static const struct voltage_map_desc ldo23_voltage_map_desc = { - .min = 800, .step = 50, .max = 1300, + .min = 800000, .step = 50000, .max = 1300000, }; static const struct voltage_map_desc ldo456711_voltage_map_desc = { - .min = 1600, .step = 100, .max = 3600, + .min = 1600000, .step = 100000, .max = 3600000, }; static const struct voltage_map_desc ldo8_voltage_map_desc = { - .min = 3000, .step = 100, .max = 3600, + .min = 3000000, .step = 100000, .max = 3600000, }; static const struct voltage_map_desc ldo9_voltage_map_desc = { - .min = 2800, .step = 100, .max = 3100, + .min = 2800000, .step = 100000, .max = 3100000, }; static const struct voltage_map_desc ldo10_voltage_map_desc = { - .min = 950, .step = 50, .max = 1300, + .min = 95000, .step = 50000, .max = 1300000, }; static const struct voltage_map_desc ldo1213_voltage_map_desc = { - .min = 800, .step = 100, .max = 3300, + .min = 800000, .step = 100000, .max = 3300000, }; static const struct voltage_map_desc ldo1415_voltage_map_desc = { - .min = 1200, .step = 100, .max = 3300, + .min = 1200000, .step = 100000, .max = 3300000, }; static const struct voltage_map_desc ldo1617_voltage_map_desc = { - .min = 1600, .step = 100, .max = 3600, + .min = 1600000, .step = 100000, .max = 3600000, }; static const struct voltage_map_desc buck12_voltage_map_desc = { - .min = 750, .step = 25, .max = 1525, + .min = 750000, .step = 25000, .max = 1525000, }; static const struct voltage_map_desc buck3_voltage_map_desc = { - .min = 1600, .step = 100, .max = 3600, + .min = 1600000, .step = 100000, .max = 3600000, }; static const struct voltage_map_desc buck4_voltage_map_desc = { - .min = 800, .step = 100, .max = 2300, + .min = 800000, .step = 100000, .max = 2300000, }; static const struct voltage_map_desc *ldo_voltage_map[] = { @@ -445,9 +445,9 @@ static int max8998_set_voltage_buck_time_sel(struct regulator_dev *rdev, if (max8998->iodev->type == TYPE_MAX8998 && !(val & MAX8998_ENRAMP)) return 0; - difference = (new_selector - old_selector) * desc->step; + difference = (new_selector - old_selector) * desc->step / 1000; if (difference > 0) - return difference / ((val & 0x0f) + 1); + return DIV_ROUND_UP(difference, (val & 0x0f) + 1); return 0; } @@ -702,7 +702,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck1_voltage1 / 1000)) + < pdata->buck1_voltage1) i++; max8998->buck1_vol[0] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE1, i); @@ -713,7 +713,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck1_voltage2 / 1000)) + < pdata->buck1_voltage2) i++; max8998->buck1_vol[1] = i; @@ -725,7 +725,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck1_voltage3 / 1000)) + < pdata->buck1_voltage3) i++; max8998->buck1_vol[2] = i; @@ -737,7 +737,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck1_voltage4 / 1000)) + < pdata->buck1_voltage4) i++; max8998->buck1_vol[3] = i; @@ -763,7 +763,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck2_voltage1 / 1000)) + < pdata->buck2_voltage1) i++; max8998->buck2_vol[0] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE1, i); @@ -774,7 +774,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) i = 0; while (buck12_voltage_map_desc.min + buck12_voltage_map_desc.step*i - < (pdata->buck2_voltage2 / 1000)) + < pdata->buck2_voltage2) i++; max8998->buck2_vol[1] = i; ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE2, i); @@ -792,8 +792,8 @@ static int max8998_pmic_probe(struct platform_device *pdev) int count = (desc->max - desc->min) / desc->step + 1; regulators[index].n_voltages = count; - regulators[index].min_uV = desc->min * 1000; - regulators[index].uV_step = desc->step * 1000; + regulators[index].min_uV = desc->min; + regulators[index].uV_step = desc->step; } config.dev = max8998->dev; diff --git a/trunk/drivers/regulator/s5m8767.c b/trunk/drivers/regulator/s5m8767.c index 9f991f2c525a..33b65c9ad5d5 100644 --- a/trunk/drivers/regulator/s5m8767.c +++ b/trunk/drivers/regulator/s5m8767.c @@ -214,7 +214,7 @@ static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); int ret, reg; int mask = 0xc0, enable_ctrl; - u8 val; + unsigned int val; ret = s5m8767_get_register(rdev, ®, &enable_ctrl); if (ret == -EINVAL) @@ -306,7 +306,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev) struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); int reg, mask, ret; int reg_id = rdev_get_id(rdev); - u8 val; + unsigned int val; ret = s5m8767_get_voltage_register(rdev, ®); if (ret) diff --git a/trunk/drivers/rtc/rtc-da9055.c b/trunk/drivers/rtc/rtc-da9055.c index 96bafc5c3bf8..8f0dcfedb83c 100644 --- a/trunk/drivers/rtc/rtc-da9055.c +++ b/trunk/drivers/rtc/rtc-da9055.c @@ -227,7 +227,7 @@ static const struct rtc_class_ops da9055_rtc_ops = { .alarm_irq_enable = da9055_rtc_alarm_irq_enable, }; -static int __init da9055_rtc_device_init(struct da9055 *da9055, +static int da9055_rtc_device_init(struct da9055 *da9055, struct da9055_pdata *pdata) { int ret; diff --git a/trunk/drivers/s390/block/dasd_diag.c b/trunk/drivers/s390/block/dasd_diag.c index 9bd5da36f99e..704488d0f819 100644 --- a/trunk/drivers/s390/block/dasd_diag.c +++ b/trunk/drivers/s390/block/dasd_diag.c @@ -248,7 +248,7 @@ static void dasd_ext_handler(struct ext_code ext_code, default: return; } - kstat_cpu(smp_processor_id()).irqs[EXTINT_DSD]++; + inc_irq_stat(IRQEXT_DSD); if (!ip) { /* no intparm: unsolicited interrupt */ DBF_EVENT(DBF_NOTICE, "%s", "caught unsolicited " "interrupt"); diff --git a/trunk/drivers/s390/block/dasd_eckd.c b/trunk/drivers/s390/block/dasd_eckd.c index 806fe912d6e7..e37bc1620d14 100644 --- a/trunk/drivers/s390/block/dasd_eckd.c +++ b/trunk/drivers/s390/block/dasd_eckd.c @@ -4274,7 +4274,7 @@ static struct ccw_driver dasd_eckd_driver = { .thaw = dasd_generic_restore_device, .restore = dasd_generic_restore_device, .uc_handler = dasd_generic_uc_handler, - .int_class = IOINT_DAS, + .int_class = IRQIO_DAS, }; /* diff --git a/trunk/drivers/s390/block/dasd_fba.c b/trunk/drivers/s390/block/dasd_fba.c index eb748507c7fa..414698584344 100644 --- a/trunk/drivers/s390/block/dasd_fba.c +++ b/trunk/drivers/s390/block/dasd_fba.c @@ -78,7 +78,7 @@ static struct ccw_driver dasd_fba_driver = { .freeze = dasd_generic_pm_freeze, .thaw = dasd_generic_restore_device, .restore = dasd_generic_restore_device, - .int_class = IOINT_DAS, + .int_class = IRQIO_DAS, }; static void diff --git a/trunk/drivers/s390/char/con3215.c b/trunk/drivers/s390/char/con3215.c index 40084501c31b..33b7141a182f 100644 --- a/trunk/drivers/s390/char/con3215.c +++ b/trunk/drivers/s390/char/con3215.c @@ -44,6 +44,7 @@ #define RAW3215_NR_CCWS 3 #define RAW3215_TIMEOUT HZ/10 /* time for delayed output */ +#define RAW3215_FIXED 1 /* 3215 console device is not be freed */ #define RAW3215_WORKING 4 /* set if a request is being worked on */ #define RAW3215_THROTTLED 8 /* set if reading is disabled */ #define RAW3215_STOPPED 16 /* set if writing is disabled */ @@ -630,7 +631,8 @@ static void raw3215_shutdown(struct raw3215_info *raw) DECLARE_WAITQUEUE(wait, current); unsigned long flags; - if (!(raw->port.flags & ASYNC_INITIALIZED)) + if (!(raw->port.flags & ASYNC_INITIALIZED) || + (raw->flags & RAW3215_FIXED)) return; /* Wait for outstanding requests, then free irq */ spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); @@ -805,7 +807,7 @@ static struct ccw_driver raw3215_ccw_driver = { .freeze = &raw3215_pm_stop, .thaw = &raw3215_pm_start, .restore = &raw3215_pm_start, - .int_class = IOINT_C15, + .int_class = IRQIO_C15, }; #ifdef CONFIG_TN3215_CONSOLE @@ -927,6 +929,8 @@ static int __init con3215_init(void) dev_set_drvdata(&cdev->dev, raw); cdev->handler = raw3215_irq; + raw->flags |= RAW3215_FIXED; + /* Request the console irq */ if (raw3215_startup(raw) != 0) { raw3215_free_info(raw); diff --git a/trunk/drivers/s390/char/raw3270.c b/trunk/drivers/s390/char/raw3270.c index f3b8bb84faf2..9a6c140c5f07 100644 --- a/trunk/drivers/s390/char/raw3270.c +++ b/trunk/drivers/s390/char/raw3270.c @@ -1396,7 +1396,7 @@ static struct ccw_driver raw3270_ccw_driver = { .freeze = &raw3270_pm_stop, .thaw = &raw3270_pm_start, .restore = &raw3270_pm_start, - .int_class = IOINT_C70, + .int_class = IRQIO_C70, }; static int diff --git a/trunk/drivers/s390/char/sclp.c b/trunk/drivers/s390/char/sclp.c index 4fa21f7e2308..12c16a65dd25 100644 --- a/trunk/drivers/s390/char/sclp.c +++ b/trunk/drivers/s390/char/sclp.c @@ -400,7 +400,7 @@ static void sclp_interrupt_handler(struct ext_code ext_code, u32 finished_sccb; u32 evbuf_pending; - kstat_cpu(smp_processor_id()).irqs[EXTINT_SCP]++; + inc_irq_stat(IRQEXT_SCP); spin_lock(&sclp_lock); finished_sccb = param32 & 0xfffffff8; evbuf_pending = param32 & 0x3; @@ -813,7 +813,7 @@ static void sclp_check_handler(struct ext_code ext_code, { u32 finished_sccb; - kstat_cpu(smp_processor_id()).irqs[EXTINT_SCP]++; + inc_irq_stat(IRQEXT_SCP); finished_sccb = param32 & 0xfffffff8; /* Is this the interrupt we are waiting for? */ if (finished_sccb == 0) diff --git a/trunk/drivers/s390/char/tape_34xx.c b/trunk/drivers/s390/char/tape_34xx.c index 6ae929c024ae..9aa79702b370 100644 --- a/trunk/drivers/s390/char/tape_34xx.c +++ b/trunk/drivers/s390/char/tape_34xx.c @@ -1193,7 +1193,7 @@ static struct ccw_driver tape_34xx_driver = { .set_online = tape_34xx_online, .set_offline = tape_generic_offline, .freeze = tape_generic_pm_suspend, - .int_class = IOINT_TAP, + .int_class = IRQIO_TAP, }; static int diff --git a/trunk/drivers/s390/char/tape_3590.c b/trunk/drivers/s390/char/tape_3590.c index 1b0eb49f739c..327cb19ad0b0 100644 --- a/trunk/drivers/s390/char/tape_3590.c +++ b/trunk/drivers/s390/char/tape_3590.c @@ -1656,7 +1656,7 @@ static struct ccw_driver tape_3590_driver = { .set_offline = tape_generic_offline, .set_online = tape_3590_online, .freeze = tape_generic_pm_suspend, - .int_class = IOINT_TAP, + .int_class = IRQIO_TAP, }; /* diff --git a/trunk/drivers/s390/char/vmur.c b/trunk/drivers/s390/char/vmur.c index 73bef0bd394c..483f72ba030d 100644 --- a/trunk/drivers/s390/char/vmur.c +++ b/trunk/drivers/s390/char/vmur.c @@ -74,7 +74,7 @@ static struct ccw_driver ur_driver = { .set_online = ur_set_online, .set_offline = ur_set_offline, .freeze = ur_pm_suspend, - .int_class = IOINT_VMR, + .int_class = IRQIO_VMR, }; static DEFINE_MUTEX(vmur_mutex); diff --git a/trunk/drivers/s390/cio/chsc.c b/trunk/drivers/s390/cio/chsc.c index 68e80e2734a4..10729bbceced 100644 --- a/trunk/drivers/s390/cio/chsc.c +++ b/trunk/drivers/s390/cio/chsc.c @@ -283,7 +283,7 @@ struct chsc_sei_nt2_area { u8 ccdf[PAGE_SIZE - 24 - 56]; /* content-code dependent field */ } __packed; -#define CHSC_SEI_NT0 0ULL +#define CHSC_SEI_NT0 (1ULL << 63) #define CHSC_SEI_NT2 (1ULL << 61) struct chsc_sei { @@ -291,7 +291,8 @@ struct chsc_sei { u32 reserved1; u64 ntsm; /* notification type mask */ struct chsc_header response; - u32 reserved2; + u32 :24; + u8 nt; union { struct chsc_sei_nt0_area nt0_area; struct chsc_sei_nt2_area nt2_area; @@ -496,17 +497,17 @@ static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm) css_schedule_eval_all(); } - switch (sei->ntsm) { - case CHSC_SEI_NT0: + switch (sei->nt) { + case 0: chsc_process_sei_nt0(&sei->u.nt0_area); - return 1; - case CHSC_SEI_NT2: + break; + case 2: chsc_process_sei_nt2(&sei->u.nt2_area); - return 1; + break; default: - CIO_CRW_EVENT(2, "chsc: unhandled nt (nt=%08Lx)\n", - sei->ntsm); - return 0; + CIO_CRW_EVENT(2, "chsc: unhandled nt=%d\n", + sei->nt); + break; } } else { CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", @@ -537,15 +538,7 @@ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) sei = sei_page; CIO_TRACE_EVENT(2, "prcss"); - - /* - * The ntsm does not allow to select NT0 and NT2 together. We need to - * first check for NT2, than additionally for NT0... - */ -#ifdef CONFIG_PCI - if (!__chsc_process_crw(sei, CHSC_SEI_NT2)) -#endif - __chsc_process_crw(sei, CHSC_SEI_NT0); + __chsc_process_crw(sei, CHSC_SEI_NT0 | CHSC_SEI_NT2); } void chsc_chp_online(struct chp_id chpid) diff --git a/trunk/drivers/s390/cio/chsc_sch.c b/trunk/drivers/s390/cio/chsc_sch.c index 8f9a1a384496..facdf809113f 100644 --- a/trunk/drivers/s390/cio/chsc_sch.c +++ b/trunk/drivers/s390/cio/chsc_sch.c @@ -58,7 +58,7 @@ static void chsc_subchannel_irq(struct subchannel *sch) CHSC_LOG(4, "irb"); CHSC_LOG_HEX(4, irb, sizeof(*irb)); - kstat_cpu(smp_processor_id()).irqs[IOINT_CSC]++; + inc_irq_stat(IRQIO_CSC); /* Copy irb to provided request and set done. */ if (!request) { diff --git a/trunk/drivers/s390/cio/cio.c b/trunk/drivers/s390/cio/cio.c index 8e927b9f285f..c8faf6230b0f 100644 --- a/trunk/drivers/s390/cio/cio.c +++ b/trunk/drivers/s390/cio/cio.c @@ -611,7 +611,7 @@ void __irq_entry do_IRQ(struct pt_regs *regs) tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id; irb = (struct irb *)&S390_lowcore.irb; do { - kstat_cpu(smp_processor_id()).irqs[IO_INTERRUPT]++; + kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); if (tpi_info->adapter_IO) { do_adapter_IO(tpi_info->isc); continue; @@ -619,7 +619,7 @@ void __irq_entry do_IRQ(struct pt_regs *regs) sch = (struct subchannel *)(unsigned long)tpi_info->intparm; if (!sch) { /* Clear pending interrupt condition. */ - kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++; + inc_irq_stat(IRQIO_CIO); tsch(tpi_info->schid, irb); continue; } @@ -633,9 +633,9 @@ void __irq_entry do_IRQ(struct pt_regs *regs) if (sch->driver && sch->driver->irq) sch->driver->irq(sch); else - kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++; + inc_irq_stat(IRQIO_CIO); } else - kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++; + inc_irq_stat(IRQIO_CIO); spin_unlock(sch->lock); /* * Are more interrupts pending? @@ -678,7 +678,7 @@ static void cio_tsch(struct subchannel *sch) if (sch->driver && sch->driver->irq) sch->driver->irq(sch); else - kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++; + inc_irq_stat(IRQIO_CIO); if (!irq_context) { irq_exit(); _local_bh_enable(); diff --git a/trunk/drivers/s390/cio/device.c b/trunk/drivers/s390/cio/device.c index 6995cff44636..7cd5c6812ac7 100644 --- a/trunk/drivers/s390/cio/device.c +++ b/trunk/drivers/s390/cio/device.c @@ -758,7 +758,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, struct ccw_device *cdev) { cdev->private->cdev = cdev; - cdev->private->int_class = IOINT_CIO; + cdev->private->int_class = IRQIO_CIO; atomic_set(&cdev->private->onoff, 0); cdev->dev.parent = &sch->dev; cdev->dev.release = ccw_device_release; @@ -1023,7 +1023,7 @@ static void io_subchannel_irq(struct subchannel *sch) if (cdev) dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); else - kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++; + inc_irq_stat(IRQIO_CIO); } void io_subchannel_init_config(struct subchannel *sch) @@ -1634,7 +1634,7 @@ ccw_device_probe_console(void) memset(&console_private, 0, sizeof(struct ccw_device_private)); console_cdev.private = &console_private; console_private.cdev = &console_cdev; - console_private.int_class = IOINT_CIO; + console_private.int_class = IRQIO_CIO; ret = ccw_device_console_enable(&console_cdev, sch); if (ret) { cio_release_console(); @@ -1715,13 +1715,13 @@ ccw_device_probe (struct device *dev) if (cdrv->int_class != 0) cdev->private->int_class = cdrv->int_class; else - cdev->private->int_class = IOINT_CIO; + cdev->private->int_class = IRQIO_CIO; ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV; if (ret) { cdev->drv = NULL; - cdev->private->int_class = IOINT_CIO; + cdev->private->int_class = IRQIO_CIO; return ret; } @@ -1755,7 +1755,7 @@ ccw_device_remove (struct device *dev) } ccw_device_set_timeout(cdev, 0); cdev->drv = NULL; - cdev->private->int_class = IOINT_CIO; + cdev->private->int_class = IRQIO_CIO; return 0; } diff --git a/trunk/drivers/s390/cio/device.h b/trunk/drivers/s390/cio/device.h index 2e575cff9845..7d4ecb65db00 100644 --- a/trunk/drivers/s390/cio/device.h +++ b/trunk/drivers/s390/cio/device.h @@ -61,11 +61,10 @@ dev_fsm_event(struct ccw_device *cdev, enum dev_event dev_event) if (dev_event == DEV_EVENT_INTERRUPT) { if (state == DEV_STATE_ONLINE) - kstat_cpu(smp_processor_id()). - irqs[cdev->private->int_class]++; + inc_irq_stat(cdev->private->int_class); else if (state != DEV_STATE_CMFCHANGE && state != DEV_STATE_CMFUPDATE) - kstat_cpu(smp_processor_id()).irqs[IOINT_CIO]++; + inc_irq_stat(IRQIO_CIO); } dev_jumptable[state][dev_event](cdev, dev_event); } diff --git a/trunk/drivers/s390/cio/eadm_sch.c b/trunk/drivers/s390/cio/eadm_sch.c index 6c9673400464..d9eddcba7e88 100644 --- a/trunk/drivers/s390/cio/eadm_sch.c +++ b/trunk/drivers/s390/cio/eadm_sch.c @@ -139,7 +139,7 @@ static void eadm_subchannel_irq(struct subchannel *sch) EADM_LOG(6, "irq"); EADM_LOG_HEX(6, irb, sizeof(*irb)); - kstat_cpu(smp_processor_id()).irqs[IOINT_ADM]++; + inc_irq_stat(IRQIO_ADM); if ((scsw->stctl & (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) && scsw->eswf == 1 && irb->esw.eadm.erw.r) diff --git a/trunk/drivers/s390/cio/qdio_thinint.c b/trunk/drivers/s390/cio/qdio_thinint.c index bdb394b066fc..bde5255200dc 100644 --- a/trunk/drivers/s390/cio/qdio_thinint.c +++ b/trunk/drivers/s390/cio/qdio_thinint.c @@ -182,7 +182,7 @@ static void tiqdio_thinint_handler(void *alsi, void *data) struct qdio_q *q; last_ai_time = S390_lowcore.int_clock; - kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++; + inc_irq_stat(IRQIO_QAI); /* protect tiq_list entries, only changed in activate or shutdown */ rcu_read_lock(); diff --git a/trunk/drivers/s390/crypto/ap_bus.c b/trunk/drivers/s390/crypto/ap_bus.c index 7b865a7300e6..b8b340ac5332 100644 --- a/trunk/drivers/s390/crypto/ap_bus.c +++ b/trunk/drivers/s390/crypto/ap_bus.c @@ -1272,7 +1272,7 @@ static int ap_probe_device_type(struct ap_device *ap_dev) static void ap_interrupt_handler(void *unused1, void *unused2) { - kstat_cpu(smp_processor_id()).irqs[IOINT_APB]++; + inc_irq_stat(IRQIO_APB); tasklet_schedule(&ap_tasklet); } diff --git a/trunk/drivers/s390/kvm/kvm_virtio.c b/trunk/drivers/s390/kvm/kvm_virtio.c index 7dabef624da3..8491111aec12 100644 --- a/trunk/drivers/s390/kvm/kvm_virtio.c +++ b/trunk/drivers/s390/kvm/kvm_virtio.c @@ -392,7 +392,7 @@ static void kvm_extint_handler(struct ext_code ext_code, if ((ext_code.subcode & 0xff00) != VIRTIO_SUBCODE_64) return; - kstat_cpu(smp_processor_id()).irqs[EXTINT_VRT]++; + inc_irq_stat(IRQEXT_VRT); /* The LSB might be overloaded, we have to mask it */ vq = (struct virtqueue *)(param64 & ~1UL); diff --git a/trunk/drivers/s390/net/claw.c b/trunk/drivers/s390/net/claw.c index 5c70a6599578..83bc9c5fa0c1 100644 --- a/trunk/drivers/s390/net/claw.c +++ b/trunk/drivers/s390/net/claw.c @@ -282,7 +282,7 @@ static struct ccw_driver claw_ccw_driver = { .ids = claw_ids, .probe = ccwgroup_probe_ccwdev, .remove = ccwgroup_remove_ccwdev, - .int_class = IOINT_CLW, + .int_class = IRQIO_CLW, }; static ssize_t claw_driver_group_store(struct device_driver *ddrv, diff --git a/trunk/drivers/s390/net/ctcm_main.c b/trunk/drivers/s390/net/ctcm_main.c index 817b68925ddd..676f12049a36 100644 --- a/trunk/drivers/s390/net/ctcm_main.c +++ b/trunk/drivers/s390/net/ctcm_main.c @@ -1755,7 +1755,7 @@ static struct ccw_driver ctcm_ccw_driver = { .ids = ctcm_ids, .probe = ccwgroup_probe_ccwdev, .remove = ccwgroup_remove_ccwdev, - .int_class = IOINT_CTC, + .int_class = IRQIO_CTC, }; static struct ccwgroup_driver ctcm_group_driver = { diff --git a/trunk/drivers/s390/net/lcs.c b/trunk/drivers/s390/net/lcs.c index 2ca0f1dd7a00..c645dc9e98af 100644 --- a/trunk/drivers/s390/net/lcs.c +++ b/trunk/drivers/s390/net/lcs.c @@ -2384,7 +2384,7 @@ static struct ccw_driver lcs_ccw_driver = { .ids = lcs_ids, .probe = ccwgroup_probe_ccwdev, .remove = ccwgroup_remove_ccwdev, - .int_class = IOINT_LCS, + .int_class = IRQIO_LCS, }; /** diff --git a/trunk/drivers/sh/clk/cpg.c b/trunk/drivers/sh/clk/cpg.c index 5aedcdf4ac5c..1ebe67cd1833 100644 --- a/trunk/drivers/sh/clk/cpg.c +++ b/trunk/drivers/sh/clk/cpg.c @@ -126,6 +126,12 @@ static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) static int sh_clk_div_enable(struct clk *clk) { + if (clk->div_mask == SH_CLK_DIV6_MSK) { + int ret = sh_clk_div_set_rate(clk, clk->rate); + if (ret < 0) + return ret; + } + sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk); return 0; } diff --git a/trunk/drivers/staging/comedi/Kconfig b/trunk/drivers/staging/comedi/Kconfig index 7de2a10213bd..36eec320569c 100644 --- a/trunk/drivers/staging/comedi/Kconfig +++ b/trunk/drivers/staging/comedi/Kconfig @@ -444,6 +444,7 @@ config COMEDI_ADQ12B config COMEDI_NI_AT_A2150 tristate "NI AT-A2150 ISA card support" + select COMEDI_FC depends on VIRT_TO_BUS ---help--- Enable support for National Instruments AT-A2150 cards diff --git a/trunk/drivers/staging/comedi/comedi_fops.c b/trunk/drivers/staging/comedi/comedi_fops.c index b7bba1790a20..9b038e4a7e71 100644 --- a/trunk/drivers/staging/comedi/comedi_fops.c +++ b/trunk/drivers/staging/comedi/comedi_fops.c @@ -1549,6 +1549,9 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, if (cmd == COMEDI_DEVCONFIG) { rc = do_devconfig_ioctl(dev, (struct comedi_devconfig __user *)arg); + if (rc == 0) + /* Evade comedi_auto_unconfig(). */ + dev_file_info->hardware_device = NULL; goto done; } diff --git a/trunk/drivers/staging/comedi/drivers/comedi_test.c b/trunk/drivers/staging/comedi/drivers/comedi_test.c index fb3d09323ba1..01de996239f1 100644 --- a/trunk/drivers/staging/comedi/drivers/comedi_test.c +++ b/trunk/drivers/staging/comedi/drivers/comedi_test.c @@ -345,7 +345,7 @@ static int waveform_ai_cancel(struct comedi_device *dev, struct waveform_private *devpriv = dev->private; devpriv->timer_running = 0; - del_timer(&devpriv->timer); + del_timer_sync(&devpriv->timer); return 0; } diff --git a/trunk/drivers/staging/comedi/drivers/ni_pcimio.c b/trunk/drivers/staging/comedi/drivers/ni_pcimio.c index aaac0b2cc9eb..fd1662b4175d 100644 --- a/trunk/drivers/staging/comedi/drivers/ni_pcimio.c +++ b/trunk/drivers/staging/comedi/drivers/ni_pcimio.c @@ -963,7 +963,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -982,7 +982,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -1001,7 +1001,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -1037,7 +1037,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 32, .caldac = {caldac_none}, .has_8255 = 0, @@ -1056,7 +1056,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_625x_ao, .reg_type = ni_reg_625x, .ao_unipolar = 0, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 32, .caldac = {caldac_none}, .has_8255 = 0, @@ -1092,7 +1092,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_628x_ao, .reg_type = ni_reg_628x, .ao_unipolar = 1, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -1111,7 +1111,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_628x_ao, .reg_type = ni_reg_628x, .ao_unipolar = 1, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 8, .caldac = {caldac_none}, .has_8255 = 0, @@ -1147,7 +1147,7 @@ static const struct ni_board_struct ni_boards[] = { .ao_range_table = &range_ni_M_628x_ao, .reg_type = ni_reg_628x, .ao_unipolar = 1, - .ao_speed = 357, + .ao_speed = 350, .num_p0_dio_channels = 32, .caldac = {caldac_none}, .has_8255 = 0, diff --git a/trunk/drivers/staging/fwserial/Kconfig b/trunk/drivers/staging/fwserial/Kconfig index 580406cb1808..b2f8331e4acf 100644 --- a/trunk/drivers/staging/fwserial/Kconfig +++ b/trunk/drivers/staging/fwserial/Kconfig @@ -3,7 +3,9 @@ config FIREWIRE_SERIAL depends on FIREWIRE help This enables TTY over IEEE 1394, providing high-speed serial - connectivity to cabled peers. + connectivity to cabled peers. This driver implements a + ad-hoc transport protocol and is currently limited to + Linux-to-Linux communication. To compile this driver as a module, say M here: the module will be called firewire-serial. diff --git a/trunk/drivers/staging/fwserial/TODO b/trunk/drivers/staging/fwserial/TODO index 726900548eae..8dae8fb25223 100644 --- a/trunk/drivers/staging/fwserial/TODO +++ b/trunk/drivers/staging/fwserial/TODO @@ -1,5 +1,5 @@ -TODOs ------ +TODOs prior to this driver moving out of staging +------------------------------------------------ 1. Implement retries for RCODE_BUSY, RCODE_NO_ACK and RCODE_SEND_ERROR - I/O is handled asynchronously which presents some issues when error conditions occur. @@ -11,17 +11,9 @@ TODOs -- Issues with firewire stack -- 1. This driver uses the same unregistered vendor id that the firewire core does (0xd00d1e). Perhaps this could be exposed as a define in - firewire-constants.h? -2. MAX_ASYNC_PAYLOAD needs to be publicly exposed by core/ohci - - otherwise how will this driver know the max size of address window to - open for one packet write? + firewire.h? 3. Maybe device_max_receive() and link_speed_to_max_payload() should be taken up by the firewire core? -4. To avoid dropping rx data while still limiting the maximum buffering, - the size of the AR context must be known. How to expose this to drivers? -5. Explore if bigger AR context will reduce RCODE_BUSY responses - (or auto-grow to certain max size -- but this would require major surgery - as the current AR is contiguously mapped) -- Issues with TTY core -- 1. Hack for alternate device name scheme diff --git a/trunk/drivers/staging/fwserial/fwserial.c b/trunk/drivers/staging/fwserial/fwserial.c index 61ee29083b26..d03a7f57e8d4 100644 --- a/trunk/drivers/staging/fwserial/fwserial.c +++ b/trunk/drivers/staging/fwserial/fwserial.c @@ -179,7 +179,7 @@ static void dump_profile(struct seq_file *m, struct stats *stats) /* Returns the max receive packet size for the given card */ static inline int device_max_receive(struct fw_device *fw_device) { - return 1 << (clamp_t(int, fw_device->max_rec, 8U, 13U) + 1); + return 1 << (clamp_t(int, fw_device->max_rec, 8U, 11U) + 1); } static void fwtty_log_tx_error(struct fwtty_port *port, int rcode) diff --git a/trunk/drivers/staging/fwserial/fwserial.h b/trunk/drivers/staging/fwserial/fwserial.h index 8b572edf9563..caa1c1ea82d5 100644 --- a/trunk/drivers/staging/fwserial/fwserial.h +++ b/trunk/drivers/staging/fwserial/fwserial.h @@ -374,10 +374,10 @@ static inline void fwtty_bind_console(struct fwtty_port *port, */ static inline int link_speed_to_max_payload(unsigned speed) { - static const int max_async[] = { 307, 614, 1229, 2458, 4916, 9832, }; - BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_3200); + static const int max_async[] = { 307, 614, 1229, 2458, }; + BUILD_BUG_ON(ARRAY_SIZE(max_async) - 1 != SCODE_800); - speed = clamp(speed, (unsigned) SCODE_100, (unsigned) SCODE_3200); + speed = clamp(speed, (unsigned) SCODE_100, (unsigned) SCODE_800); if (limit_bw) return max_async[speed]; else diff --git a/trunk/drivers/staging/iio/adc/mxs-lradc.c b/trunk/drivers/staging/iio/adc/mxs-lradc.c index fb31b457a56a..c5ceb9d90ea8 100644 --- a/trunk/drivers/staging/iio/adc/mxs-lradc.c +++ b/trunk/drivers/staging/iio/adc/mxs-lradc.c @@ -239,7 +239,7 @@ static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p) struct mxs_lradc *lradc = iio_priv(iio); const uint32_t chan_value = LRADC_CH_ACCUMULATE | ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); - int i, j = 0; + unsigned int i, j = 0; for_each_set_bit(i, iio->active_scan_mask, iio->masklength) { lradc->buffer[j] = readl(lradc->base + LRADC_CH(j)); diff --git a/trunk/drivers/staging/iio/gyro/Kconfig b/trunk/drivers/staging/iio/gyro/Kconfig index ea295b25308c..87979a0d03a9 100644 --- a/trunk/drivers/staging/iio/gyro/Kconfig +++ b/trunk/drivers/staging/iio/gyro/Kconfig @@ -27,8 +27,8 @@ config ADIS16130 config ADIS16260 tristate "Analog Devices ADIS16260 Digital Gyroscope Sensor SPI driver" depends on SPI - select IIO_TRIGGER if IIO_BUFFER - select IIO_SW_RING if IIO_BUFFER + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER help Say yes here to build support for Analog Devices ADIS16260 ADIS16265 ADIS16250 ADIS16255 and ADIS16251 programmable digital gyroscope sensors. diff --git a/trunk/drivers/staging/iio/gyro/adis16080_core.c b/trunk/drivers/staging/iio/gyro/adis16080_core.c index 3525a68d6a75..41d7350d030f 100644 --- a/trunk/drivers/staging/iio/gyro/adis16080_core.c +++ b/trunk/drivers/staging/iio/gyro/adis16080_core.c @@ -69,7 +69,7 @@ static int adis16080_spi_read(struct iio_dev *indio_dev, ret = spi_read(st->us, st->buf, 2); if (ret == 0) - *val = ((st->buf[0] & 0xF) << 8) | st->buf[1]; + *val = sign_extend32(((st->buf[0] & 0xF) << 8) | st->buf[1], 11); mutex_unlock(&st->buf_lock); return ret; diff --git a/trunk/drivers/staging/imx-drm/imx-drm-core.c b/trunk/drivers/staging/imx-drm/imx-drm-core.c index ecf0f44bc70e..cec19f1cf56c 100644 --- a/trunk/drivers/staging/imx-drm/imx-drm-core.c +++ b/trunk/drivers/staging/imx-drm/imx-drm-core.c @@ -584,7 +584,6 @@ int imx_drm_add_encoder(struct drm_encoder *encoder, ret = imx_drm_encoder_register(imx_drm_encoder); if (ret) { - kfree(imx_drm_encoder); ret = -ENOMEM; goto err_register; } diff --git a/trunk/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/trunk/drivers/staging/imx-drm/ipu-v3/ipu-common.c index 677e665ca86d..f7059cddd7fd 100644 --- a/trunk/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/trunk/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -1104,7 +1104,9 @@ static int ipu_probe(struct platform_device *pdev) if (ret) goto out_failed_irq; - ipu_reset(ipu); + ret = ipu_reset(ipu); + if (ret) + goto out_failed_reset; /* Set MCU_T to divide MCU access window into 2 */ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), @@ -1129,6 +1131,7 @@ static int ipu_probe(struct platform_device *pdev) ipu_submodules_exit(ipu); failed_submodules_init: ipu_irq_exit(ipu); +out_failed_reset: out_failed_irq: clk_disable_unprepare(ipu->clk); failed_clk_get: diff --git a/trunk/drivers/staging/imx-drm/ipuv3-crtc.c b/trunk/drivers/staging/imx-drm/ipuv3-crtc.c index 1892006526b5..4b3a019409b5 100644 --- a/trunk/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/trunk/drivers/staging/imx-drm/ipuv3-crtc.c @@ -452,7 +452,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc, int ret; ipu_crtc->ipu_ch = ipu_idmac_get(ipu, pdata->dma[0]); - if (IS_ERR_OR_NULL(ipu_crtc->ipu_ch)) { + if (IS_ERR(ipu_crtc->ipu_ch)) { ret = PTR_ERR(ipu_crtc->ipu_ch); goto err_out; } @@ -472,7 +472,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc, if (pdata->dp >= 0) { ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp); if (IS_ERR(ipu_crtc->dp)) { - ret = PTR_ERR(ipu_crtc->ipu_ch); + ret = PTR_ERR(ipu_crtc->dp); goto err_out; } } @@ -548,6 +548,8 @@ static int ipu_drm_probe(struct platform_device *pdev) ipu_crtc->dev = &pdev->dev; ret = ipu_crtc_init(ipu_crtc, pdata); + if (ret) + return ret; platform_set_drvdata(pdev, ipu_crtc); diff --git a/trunk/drivers/staging/omapdrm/Makefile b/trunk/drivers/staging/omapdrm/Makefile index 1ca0e0016de4..d85e058f2845 100644 --- a/trunk/drivers/staging/omapdrm/Makefile +++ b/trunk/drivers/staging/omapdrm/Makefile @@ -5,6 +5,7 @@ ccflags-y := -Iinclude/drm -Werror omapdrm-y := omap_drv.o \ + omap_irq.o \ omap_debugfs.o \ omap_crtc.o \ omap_plane.o \ diff --git a/trunk/drivers/staging/omapdrm/TODO b/trunk/drivers/staging/omapdrm/TODO index 938c7888ca31..abeeb00aaa12 100644 --- a/trunk/drivers/staging/omapdrm/TODO +++ b/trunk/drivers/staging/omapdrm/TODO @@ -17,9 +17,6 @@ TODO . Revisit GEM sync object infrastructure.. TTM has some framework for this already. Possibly this could be refactored out and made more common? There should be some way to do this with less wheel-reinvention. -. Review DSS vs KMS mismatches. The omap_dss_device is sort of part encoder, - part connector. Which results in a bit of duct tape to fwd calls from - encoder to connector. Possibly this could be done a bit better. . Solve PM sequencing on resume. DMM/TILER must be reloaded before any access is made from any component in the system. Which means on suspend CRTC's should be disabled, and on resume the LUT should be reprogrammed diff --git a/trunk/drivers/staging/omapdrm/omap_connector.c b/trunk/drivers/staging/omapdrm/omap_connector.c index 91edb3f96972..4cc9ee733c5f 100644 --- a/trunk/drivers/staging/omapdrm/omap_connector.c +++ b/trunk/drivers/staging/omapdrm/omap_connector.c @@ -31,9 +31,10 @@ struct omap_connector { struct drm_connector base; struct omap_dss_device *dssdev; + struct drm_encoder *encoder; }; -static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode, +void copy_timings_omap_to_drm(struct drm_display_mode *mode, struct omap_video_timings *timings) { mode->clock = timings->pixel_clock; @@ -64,7 +65,7 @@ static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode, mode->flags |= DRM_MODE_FLAG_NVSYNC; } -static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings, +void copy_timings_drm_to_omap(struct omap_video_timings *timings, struct drm_display_mode *mode) { timings->pixel_clock = mode->clock; @@ -96,48 +97,7 @@ static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings, timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; } -static void omap_connector_dpms(struct drm_connector *connector, int mode) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev = omap_connector->dssdev; - int old_dpms; - - DBG("%s: %d", dssdev->name, mode); - - old_dpms = connector->dpms; - - /* from off to on, do from crtc to connector */ - if (mode < old_dpms) - drm_helper_connector_dpms(connector, mode); - - if (mode == DRM_MODE_DPMS_ON) { - /* store resume info for suspended displays */ - switch (dssdev->state) { - case OMAP_DSS_DISPLAY_SUSPENDED: - dssdev->activate_after_resume = true; - break; - case OMAP_DSS_DISPLAY_DISABLED: { - int ret = dssdev->driver->enable(dssdev); - if (ret) { - DBG("%s: failed to enable: %d", - dssdev->name, ret); - dssdev->driver->disable(dssdev); - } - break; - } - default: - break; - } - } else { - /* TODO */ - } - - /* from on to off, do from connector to crtc */ - if (mode > old_dpms) - drm_helper_connector_dpms(connector, mode); -} - -enum drm_connector_status omap_connector_detect( +static enum drm_connector_status omap_connector_detect( struct drm_connector *connector, bool force) { struct omap_connector *omap_connector = to_omap_connector(connector); @@ -164,8 +124,6 @@ static void omap_connector_destroy(struct drm_connector *connector) struct omap_connector *omap_connector = to_omap_connector(connector); struct omap_dss_device *dssdev = omap_connector->dssdev; - dssdev->driver->disable(dssdev); - DBG("%s", omap_connector->dssdev->name); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -261,36 +219,12 @@ static int omap_connector_mode_valid(struct drm_connector *connector, struct drm_encoder *omap_connector_attached_encoder( struct drm_connector *connector) { - int i; struct omap_connector *omap_connector = to_omap_connector(connector); - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - struct drm_mode_object *obj; - - if (connector->encoder_ids[i] == 0) - break; - - obj = drm_mode_object_find(connector->dev, - connector->encoder_ids[i], - DRM_MODE_OBJECT_ENCODER); - - if (obj) { - struct drm_encoder *encoder = obj_to_encoder(obj); - struct omap_overlay_manager *mgr = - omap_encoder_get_manager(encoder); - DBG("%s: found %s", omap_connector->dssdev->name, - mgr->name); - return encoder; - } - } - - DBG("%s: no encoder", omap_connector->dssdev->name); - - return NULL; + return omap_connector->encoder; } static const struct drm_connector_funcs omap_connector_funcs = { - .dpms = omap_connector_dpms, + .dpms = drm_helper_connector_dpms, .detect = omap_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = omap_connector_destroy, @@ -302,34 +236,6 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { .best_encoder = omap_connector_attached_encoder, }; -/* called from encoder when mode is set, to propagate settings to the dssdev */ -void omap_connector_mode_set(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct drm_device *dev = connector->dev; - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev = omap_connector->dssdev; - struct omap_dss_driver *dssdrv = dssdev->driver; - struct omap_video_timings timings = {0}; - - copy_timings_drm_to_omap(&timings, mode); - - DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", - omap_connector->dssdev->name, - mode->base.id, mode->name, mode->vrefresh, mode->clock, - mode->hdisplay, mode->hsync_start, - mode->hsync_end, mode->htotal, - mode->vdisplay, mode->vsync_start, - mode->vsync_end, mode->vtotal, mode->type, mode->flags); - - if (dssdrv->check_timings(dssdev, &timings)) { - dev_err(dev->dev, "could not set timings\n"); - return; - } - - dssdrv->set_timings(dssdev, &timings); -} - /* flush an area of the framebuffer (in case of manual update display that * is not automatically flushed) */ @@ -344,7 +250,8 @@ void omap_connector_flush(struct drm_connector *connector, /* initialize connector */ struct drm_connector *omap_connector_init(struct drm_device *dev, - int connector_type, struct omap_dss_device *dssdev) + int connector_type, struct omap_dss_device *dssdev, + struct drm_encoder *encoder) { struct drm_connector *connector = NULL; struct omap_connector *omap_connector; @@ -360,6 +267,8 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, } omap_connector->dssdev = dssdev; + omap_connector->encoder = encoder; + connector = &omap_connector->base; drm_connector_init(dev, connector, &omap_connector_funcs, diff --git a/trunk/drivers/staging/omapdrm/omap_crtc.c b/trunk/drivers/staging/omapdrm/omap_crtc.c index d87bd84257bd..5c6ed6040eff 100644 --- a/trunk/drivers/staging/omapdrm/omap_crtc.c +++ b/trunk/drivers/staging/omapdrm/omap_crtc.c @@ -28,19 +28,131 @@ struct omap_crtc { struct drm_crtc base; struct drm_plane *plane; + const char *name; - int id; + int pipe; + enum omap_channel channel; + struct omap_overlay_manager_info info; + + /* + * Temporary: eventually this will go away, but it is needed + * for now to keep the output's happy. (They only need + * mgr->id.) Eventually this will be replaced w/ something + * more common-panel-framework-y + */ + struct omap_overlay_manager mgr; + + struct omap_video_timings timings; + bool enabled; + bool full_update; + + struct omap_drm_apply apply; + + struct omap_drm_irq apply_irq; + struct omap_drm_irq error_irq; + + /* list of in-progress apply's: */ + struct list_head pending_applies; + + /* list of queued apply's: */ + struct list_head queued_applies; + + /* for handling queued and in-progress applies: */ + struct work_struct apply_work; /* if there is a pending flip, these will be non-null: */ struct drm_pending_vblank_event *event; struct drm_framebuffer *old_fb; + + /* for handling page flips without caring about what + * the callback is called from. Possibly we should just + * make omap_gem always call the cb from the worker so + * we don't have to care about this.. + * + * XXX maybe fold into apply_work?? + */ + struct work_struct page_flip_work; +}; + +/* + * Manager-ops, callbacks from output when they need to configure + * the upstream part of the video pipe. + * + * Most of these we can ignore until we add support for command-mode + * panels.. for video-mode the crtc-helpers already do an adequate + * job of sequencing the setup of the video pipe in the proper order + */ + +/* we can probably ignore these until we support command-mode panels: */ +static void omap_crtc_start_update(struct omap_overlay_manager *mgr) +{ +} + +static int omap_crtc_enable(struct omap_overlay_manager *mgr) +{ + return 0; +} + +static void omap_crtc_disable(struct omap_overlay_manager *mgr) +{ +} + +static void omap_crtc_set_timings(struct omap_overlay_manager *mgr, + const struct omap_video_timings *timings) +{ + struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr); + DBG("%s", omap_crtc->name); + omap_crtc->timings = *timings; + omap_crtc->full_update = true; +} + +static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr, + const struct dss_lcd_mgr_config *config) +{ + struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr); + DBG("%s", omap_crtc->name); + dispc_mgr_set_lcd_config(omap_crtc->channel, config); +} + +static int omap_crtc_register_framedone_handler( + struct omap_overlay_manager *mgr, + void (*handler)(void *), void *data) +{ + return 0; +} + +static void omap_crtc_unregister_framedone_handler( + struct omap_overlay_manager *mgr, + void (*handler)(void *), void *data) +{ +} + +static const struct dss_mgr_ops mgr_ops = { + .start_update = omap_crtc_start_update, + .enable = omap_crtc_enable, + .disable = omap_crtc_disable, + .set_timings = omap_crtc_set_timings, + .set_lcd_config = omap_crtc_set_lcd_config, + .register_framedone_handler = omap_crtc_register_framedone_handler, + .unregister_framedone_handler = omap_crtc_unregister_framedone_handler, }; +/* + * CRTC funcs: + */ + static void omap_crtc_destroy(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + + DBG("%s", omap_crtc->name); + + WARN_ON(omap_crtc->apply_irq.registered); + omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); + omap_crtc->plane->funcs->destroy(omap_crtc->plane); drm_crtc_cleanup(crtc); + kfree(omap_crtc); } @@ -48,14 +160,25 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) { struct omap_drm_private *priv = crtc->dev->dev_private; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + bool enabled = (mode == DRM_MODE_DPMS_ON); int i; - WARN_ON(omap_plane_dpms(omap_crtc->plane, mode)); + DBG("%s: %d", omap_crtc->name, mode); + + if (enabled != omap_crtc->enabled) { + omap_crtc->enabled = enabled; + omap_crtc->full_update = true; + omap_crtc_apply(crtc, &omap_crtc->apply); - for (i = 0; i < priv->num_planes; i++) { - struct drm_plane *plane = priv->planes[i]; - if (plane->crtc == crtc) - WARN_ON(omap_plane_dpms(plane, mode)); + /* also enable our private plane: */ + WARN_ON(omap_plane_dpms(omap_crtc->plane, mode)); + + /* and any attached overlay planes: */ + for (i = 0; i < priv->num_planes; i++) { + struct drm_plane *plane = priv->planes[i]; + if (plane->crtc == crtc) + WARN_ON(omap_plane_dpms(plane, mode)); + } } } @@ -73,12 +196,26 @@ static int omap_crtc_mode_set(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_plane *plane = omap_crtc->plane; - return omap_plane_mode_set(plane, crtc, crtc->fb, + mode = adjusted_mode; + + DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", + omap_crtc->name, mode->base.id, mode->name, + mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, + mode->type, mode->flags); + + copy_timings_drm_to_omap(&omap_crtc->timings, mode); + omap_crtc->full_update = true; + + return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb, 0, 0, mode->hdisplay, mode->vdisplay, x << 16, y << 16, - mode->hdisplay << 16, mode->vdisplay << 16); + mode->hdisplay << 16, mode->vdisplay << 16, + NULL, NULL); } static void omap_crtc_prepare(struct drm_crtc *crtc) @@ -102,10 +239,11 @@ static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_plane *plane = omap_crtc->plane; struct drm_display_mode *mode = &crtc->mode; - return plane->funcs->update_plane(plane, crtc, crtc->fb, + return omap_plane_mode_set(plane, crtc, crtc->fb, 0, 0, mode->hdisplay, mode->vdisplay, x << 16, y << 16, - mode->hdisplay << 16, mode->vdisplay << 16); + mode->hdisplay << 16, mode->vdisplay << 16, + NULL, NULL); } static void omap_crtc_load_lut(struct drm_crtc *crtc) @@ -114,63 +252,54 @@ static void omap_crtc_load_lut(struct drm_crtc *crtc) static void vblank_cb(void *arg) { - static uint32_t sequence; struct drm_crtc *crtc = arg; struct drm_device *dev = crtc->dev; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_pending_vblank_event *event = omap_crtc->event; unsigned long flags; - struct timeval now; - WARN_ON(!event); + spin_lock_irqsave(&dev->event_lock, flags); + + /* wakeup userspace */ + if (omap_crtc->event) + drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event); omap_crtc->event = NULL; + omap_crtc->old_fb = NULL; - /* wakeup userspace */ - if (event) { - do_gettimeofday(&now); - - spin_lock_irqsave(&dev->event_lock, flags); - /* TODO: we can't yet use the vblank time accounting, - * because omapdss lower layer is the one that knows - * the irq # and registers the handler, which more or - * less defeats how drm_irq works.. for now just fake - * the sequence number and use gettimeofday.. - * - event->event.sequence = drm_vblank_count_and_time( - dev, omap_crtc->id, &now); - */ - event->event.sequence = sequence++; - event->event.tv_sec = now.tv_sec; - event->event.tv_usec = now.tv_usec; - list_add_tail(&event->base.link, - &event->base.file_priv->event_list); - wake_up_interruptible(&event->base.file_priv->event_wait); - spin_unlock_irqrestore(&dev->event_lock, flags); - } + spin_unlock_irqrestore(&dev->event_lock, flags); } -static void page_flip_cb(void *arg) +static void page_flip_worker(struct work_struct *work) { - struct drm_crtc *crtc = arg; - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_framebuffer *old_fb = omap_crtc->old_fb; + struct omap_crtc *omap_crtc = + container_of(work, struct omap_crtc, page_flip_work); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_device *dev = crtc->dev; + struct drm_display_mode *mode = &crtc->mode; struct drm_gem_object *bo; - omap_crtc->old_fb = NULL; - - omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb); - - /* really we'd like to setup the callback atomically w/ setting the - * new scanout buffer to avoid getting stuck waiting an extra vblank - * cycle.. for now go for correctness and later figure out speed.. - */ - omap_plane_on_endwin(omap_crtc->plane, vblank_cb, crtc); + mutex_lock(&dev->mode_config.mutex); + omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb, + 0, 0, mode->hdisplay, mode->vdisplay, + crtc->x << 16, crtc->y << 16, + mode->hdisplay << 16, mode->vdisplay << 16, + vblank_cb, crtc); + mutex_unlock(&dev->mode_config.mutex); bo = omap_framebuffer_bo(crtc->fb, 0); drm_gem_object_unreference_unlocked(bo); } +static void page_flip_cb(void *arg) +{ + struct drm_crtc *crtc = arg; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct omap_drm_private *priv = crtc->dev->dev_private; + + /* avoid assumptions about what ctxt we are called from: */ + queue_work(priv->wq, &omap_crtc->page_flip_work); +} + static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event) @@ -179,14 +308,14 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_gem_object *bo; - DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id); + DBG("%d -> %d (event=%p)", crtc->fb ? crtc->fb->base.id : -1, + fb->base.id, event); - if (omap_crtc->event) { + if (omap_crtc->old_fb) { dev_err(dev->dev, "already a pending flip\n"); return -EINVAL; } - omap_crtc->old_fb = crtc->fb; omap_crtc->event = event; crtc->fb = fb; @@ -234,14 +363,244 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { .load_lut = omap_crtc_load_lut, }; +const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + return &omap_crtc->timings; +} + +enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + return omap_crtc->channel; +} + +static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_crtc *omap_crtc = + container_of(irq, struct omap_crtc, error_irq); + struct drm_crtc *crtc = &omap_crtc->base; + DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus); + /* avoid getting in a flood, unregister the irq until next vblank */ + omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); +} + +static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_crtc *omap_crtc = + container_of(irq, struct omap_crtc, apply_irq); + struct drm_crtc *crtc = &omap_crtc->base; + + if (!omap_crtc->error_irq.registered) + omap_irq_register(crtc->dev, &omap_crtc->error_irq); + + if (!dispc_mgr_go_busy(omap_crtc->channel)) { + struct omap_drm_private *priv = + crtc->dev->dev_private; + DBG("%s: apply done", omap_crtc->name); + omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq); + queue_work(priv->wq, &omap_crtc->apply_work); + } +} + +static void apply_worker(struct work_struct *work) +{ + struct omap_crtc *omap_crtc = + container_of(work, struct omap_crtc, apply_work); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_device *dev = crtc->dev; + struct omap_drm_apply *apply, *n; + bool need_apply; + + /* + * Synchronize everything on mode_config.mutex, to keep + * the callbacks and list modification all serialized + * with respect to modesetting ioctls from userspace. + */ + mutex_lock(&dev->mode_config.mutex); + dispc_runtime_get(); + + /* + * If we are still pending a previous update, wait.. when the + * pending update completes, we get kicked again. + */ + if (omap_crtc->apply_irq.registered) + goto out; + + /* finish up previous apply's: */ + list_for_each_entry_safe(apply, n, + &omap_crtc->pending_applies, pending_node) { + apply->post_apply(apply); + list_del(&apply->pending_node); + } + + need_apply = !list_empty(&omap_crtc->queued_applies); + + /* then handle the next round of of queued apply's: */ + list_for_each_entry_safe(apply, n, + &omap_crtc->queued_applies, queued_node) { + apply->pre_apply(apply); + list_del(&apply->queued_node); + apply->queued = false; + list_add_tail(&apply->pending_node, + &omap_crtc->pending_applies); + } + + if (need_apply) { + enum omap_channel channel = omap_crtc->channel; + + DBG("%s: GO", omap_crtc->name); + + if (dispc_mgr_is_enabled(channel)) { + omap_irq_register(dev, &omap_crtc->apply_irq); + dispc_mgr_go(channel); + } else { + struct omap_drm_private *priv = dev->dev_private; + queue_work(priv->wq, &omap_crtc->apply_work); + } + } + +out: + dispc_runtime_put(); + mutex_unlock(&dev->mode_config.mutex); +} + +int omap_crtc_apply(struct drm_crtc *crtc, + struct omap_drm_apply *apply) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + struct drm_device *dev = crtc->dev; + + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + + /* no need to queue it again if it is already queued: */ + if (apply->queued) + return 0; + + apply->queued = true; + list_add_tail(&apply->queued_node, &omap_crtc->queued_applies); + + /* + * If there are no currently pending updates, then go ahead and + * kick the worker immediately, otherwise it will run again when + * the current update finishes. + */ + if (list_empty(&omap_crtc->pending_applies)) { + struct omap_drm_private *priv = crtc->dev->dev_private; + queue_work(priv->wq, &omap_crtc->apply_work); + } + + return 0; +} + +/* called only from apply */ +static void set_enabled(struct drm_crtc *crtc, bool enable) +{ + struct drm_device *dev = crtc->dev; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + enum omap_channel channel = omap_crtc->channel; + struct omap_irq_wait *wait = NULL; + + if (dispc_mgr_is_enabled(channel) == enable) + return; + + /* ignore sync-lost irqs during enable/disable */ + omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); + + if (dispc_mgr_get_framedone_irq(channel)) { + if (!enable) { + wait = omap_irq_wait_init(dev, + dispc_mgr_get_framedone_irq(channel), 1); + } + } else { + /* + * When we disable digit output, we need to wait until fields + * are done. Otherwise the DSS is still working, and turning + * off the clocks prevents DSS from going to OFF mode. And when + * enabling, we need to wait for the extra sync losts + */ + wait = omap_irq_wait_init(dev, + dispc_mgr_get_vsync_irq(channel), 2); + } + + dispc_mgr_enable(channel, enable); + + if (wait) { + int ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); + if (ret) { + dev_err(dev->dev, "%s: timeout waiting for %s\n", + omap_crtc->name, enable ? "enable" : "disable"); + } + } + + omap_irq_register(crtc->dev, &omap_crtc->error_irq); +} + +static void omap_crtc_pre_apply(struct omap_drm_apply *apply) +{ + struct omap_crtc *omap_crtc = + container_of(apply, struct omap_crtc, apply); + struct drm_crtc *crtc = &omap_crtc->base; + struct drm_encoder *encoder = NULL; + + DBG("%s: enabled=%d, full=%d", omap_crtc->name, + omap_crtc->enabled, omap_crtc->full_update); + + if (omap_crtc->full_update) { + struct omap_drm_private *priv = crtc->dev->dev_private; + int i; + for (i = 0; i < priv->num_encoders; i++) { + if (priv->encoders[i]->crtc == crtc) { + encoder = priv->encoders[i]; + break; + } + } + } + + if (!omap_crtc->enabled) { + set_enabled(&omap_crtc->base, false); + if (encoder) + omap_encoder_set_enabled(encoder, false); + } else { + if (encoder) { + omap_encoder_set_enabled(encoder, false); + omap_encoder_update(encoder, &omap_crtc->mgr, + &omap_crtc->timings); + omap_encoder_set_enabled(encoder, true); + omap_crtc->full_update = false; + } + + dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info); + dispc_mgr_set_timings(omap_crtc->channel, + &omap_crtc->timings); + set_enabled(&omap_crtc->base, true); + } + + omap_crtc->full_update = false; +} + +static void omap_crtc_post_apply(struct omap_drm_apply *apply) +{ + /* nothing needed for post-apply */ +} + +static const char *channel_names[] = { + [OMAP_DSS_CHANNEL_LCD] = "lcd", + [OMAP_DSS_CHANNEL_DIGIT] = "tv", + [OMAP_DSS_CHANNEL_LCD2] = "lcd2", +}; + /* initialize crtc */ struct drm_crtc *omap_crtc_init(struct drm_device *dev, - struct omap_overlay *ovl, int id) + struct drm_plane *plane, enum omap_channel channel, int id) { struct drm_crtc *crtc = NULL; - struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); + struct omap_crtc *omap_crtc; + struct omap_overlay_manager_info *info; + + DBG("%s", channel_names[channel]); - DBG("%s", ovl->name); + omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); if (!omap_crtc) { dev_err(dev->dev, "could not allocate CRTC\n"); @@ -250,10 +609,40 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, crtc = &omap_crtc->base; - omap_crtc->plane = omap_plane_init(dev, ovl, (1 << id), true); + INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker); + INIT_WORK(&omap_crtc->apply_work, apply_worker); + + INIT_LIST_HEAD(&omap_crtc->pending_applies); + INIT_LIST_HEAD(&omap_crtc->queued_applies); + + omap_crtc->apply.pre_apply = omap_crtc_pre_apply; + omap_crtc->apply.post_apply = omap_crtc_post_apply; + + omap_crtc->apply_irq.irqmask = pipe2vbl(id); + omap_crtc->apply_irq.irq = omap_crtc_apply_irq; + + omap_crtc->error_irq.irqmask = + dispc_mgr_get_sync_lost_irq(channel); + omap_crtc->error_irq.irq = omap_crtc_error_irq; + omap_irq_register(dev, &omap_crtc->error_irq); + + omap_crtc->channel = channel; + omap_crtc->plane = plane; omap_crtc->plane->crtc = crtc; - omap_crtc->name = ovl->name; - omap_crtc->id = id; + omap_crtc->name = channel_names[channel]; + omap_crtc->pipe = id; + + /* temporary: */ + omap_crtc->mgr.id = channel; + + dss_install_mgr_ops(&mgr_ops); + + /* TODO: fix hard-coded setup.. add properties! */ + info = &omap_crtc->info; + info->default_color = 0x00000000; + info->trans_key = 0x00000000; + info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; + info->trans_enabled = false; drm_crtc_init(dev, crtc, &omap_crtc_funcs); drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); diff --git a/trunk/drivers/staging/omapdrm/omap_drv.c b/trunk/drivers/staging/omapdrm/omap_drv.c index 84943e5ba1d6..ae5ecc2efbc7 100644 --- a/trunk/drivers/staging/omapdrm/omap_drv.c +++ b/trunk/drivers/staging/omapdrm/omap_drv.c @@ -74,320 +74,99 @@ static int get_connector_type(struct omap_dss_device *dssdev) } } -#if 0 /* enable when dss2 supports hotplug */ -static int omap_drm_notifier(struct notifier_block *nb, - unsigned long evt, void *arg) -{ - switch (evt) { - case OMAP_DSS_SIZE_CHANGE: - case OMAP_DSS_HOTPLUG_CONNECT: - case OMAP_DSS_HOTPLUG_DISCONNECT: { - struct drm_device *dev = drm_device; - DBG("hotplug event: evt=%d, dev=%p", evt, dev); - if (dev) - drm_sysfs_hotplug_event(dev); - - return NOTIFY_OK; - } - default: /* don't care about other events for now */ - return NOTIFY_DONE; - } -} -#endif - -static void dump_video_chains(void) -{ - int i; - - DBG("dumping video chains: "); - for (i = 0; i < omap_dss_get_num_overlays(); i++) { - struct omap_overlay *ovl = omap_dss_get_overlay(i); - struct omap_overlay_manager *mgr = ovl->manager; - struct omap_dss_device *dssdev = mgr ? - mgr->get_device(mgr) : NULL; - if (dssdev) { - DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name, - dssdev->name); - } else if (mgr) { - DBG("%d: %s -> %s", i, ovl->name, mgr->name); - } else { - DBG("%d: %s", i, ovl->name); - } - } -} - -/* create encoders for each manager */ -static int create_encoder(struct drm_device *dev, - struct omap_overlay_manager *mgr) -{ - struct omap_drm_private *priv = dev->dev_private; - struct drm_encoder *encoder = omap_encoder_init(dev, mgr); - - if (!encoder) { - dev_err(dev->dev, "could not create encoder: %s\n", - mgr->name); - return -ENOMEM; - } - - BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders)); - - priv->encoders[priv->num_encoders++] = encoder; - - return 0; -} - -/* create connectors for each display device */ -static int create_connector(struct drm_device *dev, - struct omap_dss_device *dssdev) +static int omap_modeset_init(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - static struct notifier_block *notifier; - struct drm_connector *connector; - int j; - - if (!dssdev->driver) { - dev_warn(dev->dev, "%s has no driver.. skipping it\n", - dssdev->name); - return 0; - } + struct omap_dss_device *dssdev = NULL; + int num_ovls = dss_feat_get_num_ovls(); + int id; - if (!(dssdev->driver->get_timings || - dssdev->driver->read_edid)) { - dev_warn(dev->dev, "%s driver does not support " - "get_timings or read_edid.. skipping it!\n", - dssdev->name); - return 0; - } + drm_mode_config_init(dev); - connector = omap_connector_init(dev, - get_connector_type(dssdev), dssdev); + omap_drm_irq_install(dev); - if (!connector) { - dev_err(dev->dev, "could not create connector: %s\n", - dssdev->name); - return -ENOMEM; - } - - BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors)); + /* + * Create private planes and CRTCs for the last NUM_CRTCs overlay + * plus manager: + */ + for (id = 0; id < min(num_crtc, num_ovls); id++) { + struct drm_plane *plane; + struct drm_crtc *crtc; - priv->connectors[priv->num_connectors++] = connector; + plane = omap_plane_init(dev, id, true); + crtc = omap_crtc_init(dev, plane, pipe2chan(id), id); -#if 0 /* enable when dss2 supports hotplug */ - notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL); - notifier->notifier_call = omap_drm_notifier; - omap_dss_add_notify(dssdev, notifier); -#else - notifier = NULL; -#endif + BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs)); + priv->crtcs[id] = crtc; + priv->num_crtcs++; - for (j = 0; j < priv->num_encoders; j++) { - struct omap_overlay_manager *mgr = - omap_encoder_get_manager(priv->encoders[j]); - if (mgr->get_device(mgr) == dssdev) { - drm_mode_connector_attach_encoder(connector, - priv->encoders[j]); - } + priv->planes[id] = plane; + priv->num_planes++; } - return 0; -} - -/* create up to max_overlays CRTCs mapping to overlays.. by default, - * connect the overlays to different managers/encoders, giving priority - * to encoders connected to connectors with a detected connection - */ -static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl, - int *j, unsigned int connected_connectors) -{ - struct omap_drm_private *priv = dev->dev_private; - struct omap_overlay_manager *mgr = NULL; - struct drm_crtc *crtc; - - /* find next best connector, ones with detected connection first + /* + * Create normal planes for the remaining overlays: */ - while (*j < priv->num_connectors && !mgr) { - if (connected_connectors & (1 << *j)) { - struct drm_encoder *encoder = - omap_connector_attached_encoder( - priv->connectors[*j]); - if (encoder) - mgr = omap_encoder_get_manager(encoder); + for (; id < num_ovls; id++) { + struct drm_plane *plane = omap_plane_init(dev, id, false); - } - (*j)++; + BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); + priv->planes[priv->num_planes++] = plane; } - /* if we couldn't find another connected connector, lets start - * looking at the unconnected connectors: - * - * note: it might not be immediately apparent, but thanks to - * the !mgr check in both this loop and the one above, the only - * way to enter this loop is with *j == priv->num_connectors, - * so idx can never go negative. - */ - while (*j < 2 * priv->num_connectors && !mgr) { - int idx = *j - priv->num_connectors; - if (!(connected_connectors & (1 << idx))) { - struct drm_encoder *encoder = - omap_connector_attached_encoder( - priv->connectors[idx]); - if (encoder) - mgr = omap_encoder_get_manager(encoder); + for_each_dss_dev(dssdev) { + struct drm_connector *connector; + struct drm_encoder *encoder; + if (!dssdev->driver) { + dev_warn(dev->dev, "%s has no driver.. skipping it\n", + dssdev->name); + return 0; } - (*j)++; - } - - crtc = omap_crtc_init(dev, ovl, priv->num_crtcs); - - if (!crtc) { - dev_err(dev->dev, "could not create CRTC: %s\n", - ovl->name); - return -ENOMEM; - } - BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs)); - - priv->crtcs[priv->num_crtcs++] = crtc; - - return 0; -} - -static int create_plane(struct drm_device *dev, struct omap_overlay *ovl, - unsigned int possible_crtcs) -{ - struct omap_drm_private *priv = dev->dev_private; - struct drm_plane *plane = - omap_plane_init(dev, ovl, possible_crtcs, false); - - if (!plane) { - dev_err(dev->dev, "could not create plane: %s\n", - ovl->name); - return -ENOMEM; - } - - BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)); - - priv->planes[priv->num_planes++] = plane; - - return 0; -} - -static int match_dev_name(struct omap_dss_device *dssdev, void *data) -{ - return !strcmp(dssdev->name, data); -} - -static unsigned int detect_connectors(struct drm_device *dev) -{ - struct omap_drm_private *priv = dev->dev_private; - unsigned int connected_connectors = 0; - int i; - - for (i = 0; i < priv->num_connectors; i++) { - struct drm_connector *connector = priv->connectors[i]; - if (omap_connector_detect(connector, true) == - connector_status_connected) { - connected_connectors |= (1 << i); + if (!(dssdev->driver->get_timings || + dssdev->driver->read_edid)) { + dev_warn(dev->dev, "%s driver does not support " + "get_timings or read_edid.. skipping it!\n", + dssdev->name); + return 0; } - } - - return connected_connectors; -} -static int omap_modeset_init(struct drm_device *dev) -{ - const struct omap_drm_platform_data *pdata = dev->dev->platform_data; - struct omap_kms_platform_data *kms_pdata = NULL; - struct omap_drm_private *priv = dev->dev_private; - struct omap_dss_device *dssdev = NULL; - int i, j; - unsigned int connected_connectors = 0; + encoder = omap_encoder_init(dev, dssdev); - drm_mode_config_init(dev); - - if (pdata && pdata->kms_pdata) { - kms_pdata = pdata->kms_pdata; - - /* if platform data is provided by the board file, use it to - * control which overlays, managers, and devices we own. - */ - for (i = 0; i < kms_pdata->mgr_cnt; i++) { - struct omap_overlay_manager *mgr = - omap_dss_get_overlay_manager( - kms_pdata->mgr_ids[i]); - create_encoder(dev, mgr); - } - - for (i = 0; i < kms_pdata->dev_cnt; i++) { - struct omap_dss_device *dssdev = - omap_dss_find_device( - (void *)kms_pdata->dev_names[i], - match_dev_name); - if (!dssdev) { - dev_warn(dev->dev, "no such dssdev: %s\n", - kms_pdata->dev_names[i]); - continue; - } - create_connector(dev, dssdev); + if (!encoder) { + dev_err(dev->dev, "could not create encoder: %s\n", + dssdev->name); + return -ENOMEM; } - connected_connectors = detect_connectors(dev); + connector = omap_connector_init(dev, + get_connector_type(dssdev), dssdev, encoder); - j = 0; - for (i = 0; i < kms_pdata->ovl_cnt; i++) { - struct omap_overlay *ovl = - omap_dss_get_overlay(kms_pdata->ovl_ids[i]); - create_crtc(dev, ovl, &j, connected_connectors); + if (!connector) { + dev_err(dev->dev, "could not create connector: %s\n", + dssdev->name); + return -ENOMEM; } - for (i = 0; i < kms_pdata->pln_cnt; i++) { - struct omap_overlay *ovl = - omap_dss_get_overlay(kms_pdata->pln_ids[i]); - create_plane(dev, ovl, (1 << priv->num_crtcs) - 1); - } - } else { - /* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try - * to make educated guesses about everything else - */ - int max_overlays = min(omap_dss_get_num_overlays(), num_crtc); + BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders)); + BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors)); - for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) - create_encoder(dev, omap_dss_get_overlay_manager(i)); - - for_each_dss_dev(dssdev) { - create_connector(dev, dssdev); - } + priv->encoders[priv->num_encoders++] = encoder; + priv->connectors[priv->num_connectors++] = connector; - connected_connectors = detect_connectors(dev); + drm_mode_connector_attach_encoder(connector, encoder); - j = 0; - for (i = 0; i < max_overlays; i++) { - create_crtc(dev, omap_dss_get_overlay(i), - &j, connected_connectors); - } - - /* use any remaining overlays as drm planes */ - for (; i < omap_dss_get_num_overlays(); i++) { - struct omap_overlay *ovl = omap_dss_get_overlay(i); - create_plane(dev, ovl, (1 << priv->num_crtcs) - 1); + /* figure out which crtc's we can connect the encoder to: */ + encoder->possible_crtcs = 0; + for (id = 0; id < priv->num_crtcs; id++) { + enum omap_dss_output_id supported_outputs = + dss_feat_get_supported_outputs(pipe2chan(id)); + if (supported_outputs & dssdev->output->id) + encoder->possible_crtcs |= (1 << id); } } - /* for now keep the mapping of CRTCs and encoders static.. */ - for (i = 0; i < priv->num_encoders; i++) { - struct drm_encoder *encoder = priv->encoders[i]; - struct omap_overlay_manager *mgr = - omap_encoder_get_manager(encoder); - - encoder->possible_crtcs = (1 << priv->num_crtcs) - 1; - - DBG("%s: possible_crtcs=%08x", mgr->name, - encoder->possible_crtcs); - } - - dump_video_chains(); - dev->mode_config.min_width = 32; dev->mode_config.min_height = 32; @@ -450,7 +229,7 @@ static int ioctl_gem_new(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_omap_gem_new *args = data; - DBG("%p:%p: size=0x%08x, flags=%08x", dev, file_priv, + VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv, args->size.bytes, args->flags); return omap_gem_new_handle(dev, file_priv, args->size, args->flags, &args->handle); @@ -510,7 +289,7 @@ static int ioctl_gem_info(struct drm_device *dev, void *data, struct drm_gem_object *obj; int ret = 0; - DBG("%p:%p: handle=%d", dev, file_priv, args->handle); + VERB("%p:%p: handle=%d", dev, file_priv, args->handle); obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (!obj) @@ -565,14 +344,6 @@ static int dev_load(struct drm_device *dev, unsigned long flags) dev->dev_private = priv; - ret = omapdss_compat_init(); - if (ret) { - dev_err(dev->dev, "coult not init omapdss\n"); - dev->dev_private = NULL; - kfree(priv); - return ret; - } - priv->wq = alloc_ordered_workqueue("omapdrm", 0); INIT_LIST_HEAD(&priv->obj_list); @@ -584,10 +355,13 @@ static int dev_load(struct drm_device *dev, unsigned long flags) dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret); dev->dev_private = NULL; kfree(priv); - omapdss_compat_uninit(); return ret; } + ret = drm_vblank_init(dev, priv->num_crtcs); + if (ret) + dev_warn(dev->dev, "could not init vblank\n"); + priv->fbdev = omap_fbdev_init(dev); if (!priv->fbdev) { dev_warn(dev->dev, "omap_fbdev_init failed\n"); @@ -596,10 +370,6 @@ static int dev_load(struct drm_device *dev, unsigned long flags) drm_kms_helper_poll_init(dev); - ret = drm_vblank_init(dev, priv->num_crtcs); - if (ret) - dev_warn(dev->dev, "could not init vblank\n"); - return 0; } @@ -609,8 +379,9 @@ static int dev_unload(struct drm_device *dev) DBG("unload: dev=%p", dev); - drm_vblank_cleanup(dev); drm_kms_helper_poll_fini(dev); + drm_vblank_cleanup(dev); + omap_drm_irq_uninstall(dev); omap_fbdev_free(dev); omap_modeset_free(dev); @@ -619,8 +390,6 @@ static int dev_unload(struct drm_device *dev) flush_workqueue(priv->wq); destroy_workqueue(priv->wq); - omapdss_compat_uninit(); - kfree(dev->dev_private); dev->dev_private = NULL; @@ -680,7 +449,9 @@ static void dev_lastclose(struct drm_device *dev) } } + mutex_lock(&dev->mode_config.mutex); ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); + mutex_unlock(&dev->mode_config.mutex); if (ret) DBG("failed to restore crtc mode"); } @@ -695,60 +466,6 @@ static void dev_postclose(struct drm_device *dev, struct drm_file *file) DBG("postclose: dev=%p, file=%p", dev, file); } -/** - * enable_vblank - enable vblank interrupt events - * @dev: DRM device - * @crtc: which irq to enable - * - * Enable vblank interrupts for @crtc. If the device doesn't have - * a hardware vblank counter, this routine should be a no-op, since - * interrupts will have to stay on to keep the count accurate. - * - * RETURNS - * Zero on success, appropriate errno if the given @crtc's vblank - * interrupt cannot be enabled. - */ -static int dev_enable_vblank(struct drm_device *dev, int crtc) -{ - DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc); - return 0; -} - -/** - * disable_vblank - disable vblank interrupt events - * @dev: DRM device - * @crtc: which irq to enable - * - * Disable vblank interrupts for @crtc. If the device doesn't have - * a hardware vblank counter, this routine should be a no-op, since - * interrupts will have to stay on to keep the count accurate. - */ -static void dev_disable_vblank(struct drm_device *dev, int crtc) -{ - DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc); -} - -static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS) -{ - return IRQ_HANDLED; -} - -static void dev_irq_preinstall(struct drm_device *dev) -{ - DBG("irq_preinstall: dev=%p", dev); -} - -static int dev_irq_postinstall(struct drm_device *dev) -{ - DBG("irq_postinstall: dev=%p", dev); - return 0; -} - -static void dev_irq_uninstall(struct drm_device *dev) -{ - DBG("irq_uninstall: dev=%p", dev); -} - static const struct vm_operations_struct omap_gem_vm_ops = { .fault = omap_gem_fault, .open = drm_gem_vm_open, @@ -778,12 +495,12 @@ static struct drm_driver omap_drm_driver = { .preclose = dev_preclose, .postclose = dev_postclose, .get_vblank_counter = drm_vblank_count, - .enable_vblank = dev_enable_vblank, - .disable_vblank = dev_disable_vblank, - .irq_preinstall = dev_irq_preinstall, - .irq_postinstall = dev_irq_postinstall, - .irq_uninstall = dev_irq_uninstall, - .irq_handler = dev_irq_handler, + .enable_vblank = omap_irq_enable_vblank, + .disable_vblank = omap_irq_disable_vblank, + .irq_preinstall = omap_irq_preinstall, + .irq_postinstall = omap_irq_postinstall, + .irq_uninstall = omap_irq_uninstall, + .irq_handler = omap_irq_handler, #ifdef CONFIG_DEBUG_FS .debugfs_init = omap_debugfs_init, .debugfs_cleanup = omap_debugfs_cleanup, diff --git a/trunk/drivers/staging/omapdrm/omap_drv.h b/trunk/drivers/staging/omapdrm/omap_drv.h index 1d4aea53b75d..cd1f22b0b124 100644 --- a/trunk/drivers/staging/omapdrm/omap_drv.h +++ b/trunk/drivers/staging/omapdrm/omap_drv.h @@ -28,6 +28,7 @@ #include #include "omap_drm.h" + #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */ @@ -39,6 +40,51 @@ */ #define MAX_MAPPERS 2 +/* parameters which describe (unrotated) coordinates of scanout within a fb: */ +struct omap_drm_window { + uint32_t rotation; + int32_t crtc_x, crtc_y; /* signed because can be offscreen */ + uint32_t crtc_w, crtc_h; + uint32_t src_x, src_y; + uint32_t src_w, src_h; +}; + +/* Once GO bit is set, we can't make further updates to shadowed registers + * until the GO bit is cleared. So various parts in the kms code that need + * to update shadowed registers queue up a pair of callbacks, pre_apply + * which is called before setting GO bit, and post_apply that is called + * after GO bit is cleared. The crtc manages the queuing, and everyone + * else goes thru omap_crtc_apply() using these callbacks so that the + * code which has to deal w/ GO bit state is centralized. + */ +struct omap_drm_apply { + struct list_head pending_node, queued_node; + bool queued; + void (*pre_apply)(struct omap_drm_apply *apply); + void (*post_apply)(struct omap_drm_apply *apply); +}; + +/* For transiently registering for different DSS irqs that various parts + * of the KMS code need during setup/configuration. We these are not + * necessarily the same as what drm_vblank_get/put() are requesting, and + * the hysteresis in drm_vblank_put() is not necessarily desirable for + * internal housekeeping related irq usage. + */ +struct omap_drm_irq { + struct list_head node; + uint32_t irqmask; + bool registered; + void (*irq)(struct omap_drm_irq *irq, uint32_t irqstatus); +}; + +/* For KMS code that needs to wait for a certain # of IRQs: + */ +struct omap_irq_wait; +struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, + uint32_t irqmask, int count); +int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, + unsigned long timeout); + struct omap_drm_private { uint32_t omaprev; @@ -58,6 +104,7 @@ struct omap_drm_private { struct workqueue_struct *wq; + /* list of GEM objects: */ struct list_head obj_list; bool has_dmm; @@ -65,6 +112,11 @@ struct omap_drm_private { /* properties: */ struct drm_property *rotation_prop; struct drm_property *zorder_prop; + + /* irq handling: */ + struct list_head irq_list; /* list of omap_drm_irq */ + uint32_t vblank_mask; /* irq bits set for userspace vblank */ + struct omap_drm_irq error_handler; }; /* this should probably be in drm-core to standardize amongst drivers */ @@ -75,15 +127,6 @@ struct omap_drm_private { #define DRM_REFLECT_X 4 #define DRM_REFLECT_Y 5 -/* parameters which describe (unrotated) coordinates of scanout within a fb: */ -struct omap_drm_window { - uint32_t rotation; - int32_t crtc_x, crtc_y; /* signed because can be offscreen */ - uint32_t crtc_w, crtc_h; - uint32_t src_x, src_y; - uint32_t src_w, src_h; -}; - #ifdef CONFIG_DEBUG_FS int omap_debugfs_init(struct drm_minor *minor); void omap_debugfs_cleanup(struct drm_minor *minor); @@ -92,23 +135,36 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m); void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); #endif +int omap_irq_enable_vblank(struct drm_device *dev, int crtc); +void omap_irq_disable_vblank(struct drm_device *dev, int crtc); +irqreturn_t omap_irq_handler(DRM_IRQ_ARGS); +void omap_irq_preinstall(struct drm_device *dev); +int omap_irq_postinstall(struct drm_device *dev); +void omap_irq_uninstall(struct drm_device *dev); +void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq); +void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq); +int omap_drm_irq_uninstall(struct drm_device *dev); +int omap_drm_irq_install(struct drm_device *dev); + struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev); void omap_fbdev_free(struct drm_device *dev); +const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc); +enum omap_channel omap_crtc_channel(struct drm_crtc *crtc); +int omap_crtc_apply(struct drm_crtc *crtc, + struct omap_drm_apply *apply); struct drm_crtc *omap_crtc_init(struct drm_device *dev, - struct omap_overlay *ovl, int id); + struct drm_plane *plane, enum omap_channel channel, int id); struct drm_plane *omap_plane_init(struct drm_device *dev, - struct omap_overlay *ovl, unsigned int possible_crtcs, - bool priv); + int plane_id, bool private_plane); int omap_plane_dpms(struct drm_plane *plane, int mode); int omap_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h); -void omap_plane_on_endwin(struct drm_plane *plane, + uint32_t src_w, uint32_t src_h, void (*fxn)(void *), void *arg); void omap_plane_install_properties(struct drm_plane *plane, struct drm_mode_object *obj); @@ -116,21 +172,25 @@ int omap_plane_set_property(struct drm_plane *plane, struct drm_property *property, uint64_t val); struct drm_encoder *omap_encoder_init(struct drm_device *dev, - struct omap_overlay_manager *mgr); -struct omap_overlay_manager *omap_encoder_get_manager( + struct omap_dss_device *dssdev); +int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled); +int omap_encoder_update(struct drm_encoder *encoder, + struct omap_overlay_manager *mgr, + struct omap_video_timings *timings); + +struct drm_connector *omap_connector_init(struct drm_device *dev, + int connector_type, struct omap_dss_device *dssdev, struct drm_encoder *encoder); struct drm_encoder *omap_connector_attached_encoder( struct drm_connector *connector); -enum drm_connector_status omap_connector_detect( - struct drm_connector *connector, bool force); - -struct drm_connector *omap_connector_init(struct drm_device *dev, - int connector_type, struct omap_dss_device *dssdev); -void omap_connector_mode_set(struct drm_connector *connector, - struct drm_display_mode *mode); void omap_connector_flush(struct drm_connector *connector, int x, int y, int w, int h); +void copy_timings_omap_to_drm(struct drm_display_mode *mode, + struct omap_video_timings *timings); +void copy_timings_drm_to_omap(struct omap_video_timings *timings, + struct drm_display_mode *mode); + uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats, uint32_t max_formats, enum omap_color_mode supported_modes); struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, @@ -207,6 +267,40 @@ static inline int align_pitch(int pitch, int width, int bpp) return ALIGN(pitch, 8 * bytespp); } +static inline enum omap_channel pipe2chan(int pipe) +{ + int num_mgrs = dss_feat_get_num_mgrs(); + + /* + * We usually don't want to create a CRTC for each manager, + * at least not until we have a way to expose private planes + * to userspace. Otherwise there would not be enough video + * pipes left for drm planes. The higher #'d managers tend + * to have more features so start in reverse order. + */ + return num_mgrs - pipe - 1; +} + +/* map crtc to vblank mask */ +static inline uint32_t pipe2vbl(int crtc) +{ + enum omap_channel channel = pipe2chan(crtc); + return dispc_mgr_get_vsync_irq(channel); +} + +static inline int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct omap_drm_private *priv = dev->dev_private; + int i; + + for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++) + if (priv->crtcs[i] == crtc) + return i; + + BUG(); /* bogus CRTC ptr */ + return -1; +} + /* should these be made into common util helpers? */ diff --git a/trunk/drivers/staging/omapdrm/omap_encoder.c b/trunk/drivers/staging/omapdrm/omap_encoder.c index 5341d5e3e317..e053160d2db3 100644 --- a/trunk/drivers/staging/omapdrm/omap_encoder.c +++ b/trunk/drivers/staging/omapdrm/omap_encoder.c @@ -22,37 +22,56 @@ #include "drm_crtc.h" #include "drm_crtc_helper.h" +#include + + /* * encoder funcs */ #define to_omap_encoder(x) container_of(x, struct omap_encoder, base) +/* The encoder and connector both map to same dssdev.. the encoder + * handles the 'active' parts, ie. anything the modifies the state + * of the hw, and the connector handles the 'read-only' parts, like + * detecting connection and reading edid. + */ struct omap_encoder { struct drm_encoder base; - struct omap_overlay_manager *mgr; + struct omap_dss_device *dssdev; }; static void omap_encoder_destroy(struct drm_encoder *encoder) { struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - DBG("%s", omap_encoder->mgr->name); drm_encoder_cleanup(encoder); kfree(omap_encoder); } +static const struct drm_encoder_funcs omap_encoder_funcs = { + .destroy = omap_encoder_destroy, +}; + +/* + * The CRTC drm_crtc_helper_set_mode() doesn't really give us the right + * order.. the easiest way to work around this for now is to make all + * the encoder-helper's no-op's and have the omap_crtc code take care + * of the sequencing and call us in the right points. + * + * Eventually to handle connecting CRTCs to different encoders properly, + * either the CRTC helpers need to change or we need to replace + * drm_crtc_helper_set_mode(), but lets wait until atomic-modeset for + * that. + */ + static void omap_encoder_dpms(struct drm_encoder *encoder, int mode) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - DBG("%s: %d", omap_encoder->mgr->name, mode); } static bool omap_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - DBG("%s", omap_encoder->mgr->name); return true; } @@ -60,47 +79,16 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct omap_drm_private *priv = dev->dev_private; - int i; - - mode = adjusted_mode; - - DBG("%s: set mode: %dx%d", omap_encoder->mgr->name, - mode->hdisplay, mode->vdisplay); - - for (i = 0; i < priv->num_connectors; i++) { - struct drm_connector *connector = priv->connectors[i]; - if (connector->encoder == encoder) - omap_connector_mode_set(connector, mode); - - } } static void omap_encoder_prepare(struct drm_encoder *encoder) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - DBG("%s", omap_encoder->mgr->name); - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); } static void omap_encoder_commit(struct drm_encoder *encoder) { - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - DBG("%s", omap_encoder->mgr->name); - omap_encoder->mgr->apply(omap_encoder->mgr); - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); } -static const struct drm_encoder_funcs omap_encoder_funcs = { - .destroy = omap_encoder_destroy, -}; - static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = { .dpms = omap_encoder_dpms, .mode_fixup = omap_encoder_mode_fixup, @@ -109,23 +97,54 @@ static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = { .commit = omap_encoder_commit, }; -struct omap_overlay_manager *omap_encoder_get_manager( - struct drm_encoder *encoder) +/* + * Instead of relying on the helpers for modeset, the omap_crtc code + * calls these functions in the proper sequence. + */ + +int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled) { struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - return omap_encoder->mgr; + struct omap_dss_device *dssdev = omap_encoder->dssdev; + struct omap_dss_driver *dssdrv = dssdev->driver; + + if (enabled) { + return dssdrv->enable(dssdev); + } else { + dssdrv->disable(dssdev); + return 0; + } +} + +int omap_encoder_update(struct drm_encoder *encoder, + struct omap_overlay_manager *mgr, + struct omap_video_timings *timings) +{ + struct drm_device *dev = encoder->dev; + struct omap_encoder *omap_encoder = to_omap_encoder(encoder); + struct omap_dss_device *dssdev = omap_encoder->dssdev; + struct omap_dss_driver *dssdrv = dssdev->driver; + int ret; + + dssdev->output->manager = mgr; + + ret = dssdrv->check_timings(dssdev, timings); + if (ret) { + dev_err(dev->dev, "could not set timings: %d\n", ret); + return ret; + } + + dssdrv->set_timings(dssdev, timings); + + return 0; } /* initialize encoder */ struct drm_encoder *omap_encoder_init(struct drm_device *dev, - struct omap_overlay_manager *mgr) + struct omap_dss_device *dssdev) { struct drm_encoder *encoder = NULL; struct omap_encoder *omap_encoder; - struct omap_overlay_manager_info info; - int ret; - - DBG("%s", mgr->name); omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL); if (!omap_encoder) { @@ -133,33 +152,14 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev, goto fail; } - omap_encoder->mgr = mgr; + omap_encoder->dssdev = dssdev; + encoder = &omap_encoder->base; drm_encoder_init(dev, encoder, &omap_encoder_funcs, DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs); - mgr->get_manager_info(mgr, &info); - - /* TODO: fix hard-coded setup.. */ - info.default_color = 0x00000000; - info.trans_key = 0x00000000; - info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; - info.trans_enabled = false; - - ret = mgr->set_manager_info(mgr, &info); - if (ret) { - dev_err(dev->dev, "could not set manager info\n"); - goto fail; - } - - ret = mgr->apply(mgr); - if (ret) { - dev_err(dev->dev, "could not apply\n"); - goto fail; - } - return encoder; fail: diff --git a/trunk/drivers/staging/omapdrm/omap_gem_dmabuf.c b/trunk/drivers/staging/omapdrm/omap_gem_dmabuf.c index ea3840038250..b6c5b5c6c8c5 100644 --- a/trunk/drivers/staging/omapdrm/omap_gem_dmabuf.c +++ b/trunk/drivers/staging/omapdrm/omap_gem_dmabuf.c @@ -194,7 +194,7 @@ struct dma_buf_ops omap_dmabuf_ops = { struct dma_buf *omap_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) { - return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, 0600); + return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags); } struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, diff --git a/trunk/drivers/staging/omapdrm/omap_irq.c b/trunk/drivers/staging/omapdrm/omap_irq.c new file mode 100644 index 000000000000..2629ba7be6c8 --- /dev/null +++ b/trunk/drivers/staging/omapdrm/omap_irq.c @@ -0,0 +1,322 @@ +/* + * drivers/staging/omapdrm/omap_irq.c + * + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "omap_drv.h" + +static DEFINE_SPINLOCK(list_lock); + +static void omap_irq_error_handler(struct omap_drm_irq *irq, + uint32_t irqstatus) +{ + DRM_ERROR("errors: %08x\n", irqstatus); +} + +/* call with list_lock and dispc runtime held */ +static void omap_irq_update(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + struct omap_drm_irq *irq; + uint32_t irqmask = priv->vblank_mask; + + BUG_ON(!spin_is_locked(&list_lock)); + + list_for_each_entry(irq, &priv->irq_list, node) + irqmask |= irq->irqmask; + + DBG("irqmask=%08x", irqmask); + + dispc_write_irqenable(irqmask); + dispc_read_irqenable(); /* flush posted write */ +} + +void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) +{ + struct omap_drm_private *priv = dev->dev_private; + unsigned long flags; + + dispc_runtime_get(); + spin_lock_irqsave(&list_lock, flags); + + if (!WARN_ON(irq->registered)) { + irq->registered = true; + list_add(&irq->node, &priv->irq_list); + omap_irq_update(dev); + } + + spin_unlock_irqrestore(&list_lock, flags); + dispc_runtime_put(); +} + +void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) +{ + unsigned long flags; + + dispc_runtime_get(); + spin_lock_irqsave(&list_lock, flags); + + if (!WARN_ON(!irq->registered)) { + irq->registered = false; + list_del(&irq->node); + omap_irq_update(dev); + } + + spin_unlock_irqrestore(&list_lock, flags); + dispc_runtime_put(); +} + +struct omap_irq_wait { + struct omap_drm_irq irq; + int count; +}; + +static DECLARE_WAIT_QUEUE_HEAD(wait_event); + +static void wait_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_irq_wait *wait = + container_of(irq, struct omap_irq_wait, irq); + wait->count--; + wake_up_all(&wait_event); +} + +struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, + uint32_t irqmask, int count) +{ + struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL); + wait->irq.irq = wait_irq; + wait->irq.irqmask = irqmask; + wait->count = count; + omap_irq_register(dev, &wait->irq); + return wait; +} + +int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, + unsigned long timeout) +{ + int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout); + omap_irq_unregister(dev, &wait->irq); + kfree(wait); + if (ret == 0) + return -1; + return 0; +} + +/** + * enable_vblank - enable vblank interrupt events + * @dev: DRM device + * @crtc: which irq to enable + * + * Enable vblank interrupts for @crtc. If the device doesn't have + * a hardware vblank counter, this routine should be a no-op, since + * interrupts will have to stay on to keep the count accurate. + * + * RETURNS + * Zero on success, appropriate errno if the given @crtc's vblank + * interrupt cannot be enabled. + */ +int omap_irq_enable_vblank(struct drm_device *dev, int crtc) +{ + struct omap_drm_private *priv = dev->dev_private; + unsigned long flags; + + DBG("dev=%p, crtc=%d", dev, crtc); + + dispc_runtime_get(); + spin_lock_irqsave(&list_lock, flags); + priv->vblank_mask |= pipe2vbl(crtc); + omap_irq_update(dev); + spin_unlock_irqrestore(&list_lock, flags); + dispc_runtime_put(); + + return 0; +} + +/** + * disable_vblank - disable vblank interrupt events + * @dev: DRM device + * @crtc: which irq to enable + * + * Disable vblank interrupts for @crtc. If the device doesn't have + * a hardware vblank counter, this routine should be a no-op, since + * interrupts will have to stay on to keep the count accurate. + */ +void omap_irq_disable_vblank(struct drm_device *dev, int crtc) +{ + struct omap_drm_private *priv = dev->dev_private; + unsigned long flags; + + DBG("dev=%p, crtc=%d", dev, crtc); + + dispc_runtime_get(); + spin_lock_irqsave(&list_lock, flags); + priv->vblank_mask &= ~pipe2vbl(crtc); + omap_irq_update(dev); + spin_unlock_irqrestore(&list_lock, flags); + dispc_runtime_put(); +} + +irqreturn_t omap_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + struct omap_drm_private *priv = dev->dev_private; + struct omap_drm_irq *handler, *n; + unsigned long flags; + unsigned int id; + u32 irqstatus; + + irqstatus = dispc_read_irqstatus(); + dispc_clear_irqstatus(irqstatus); + dispc_read_irqstatus(); /* flush posted write */ + + VERB("irqs: %08x", irqstatus); + + for (id = 0; id < priv->num_crtcs; id++) + if (irqstatus & pipe2vbl(id)) + drm_handle_vblank(dev, id); + + spin_lock_irqsave(&list_lock, flags); + list_for_each_entry_safe(handler, n, &priv->irq_list, node) { + if (handler->irqmask & irqstatus) { + spin_unlock_irqrestore(&list_lock, flags); + handler->irq(handler, handler->irqmask & irqstatus); + spin_lock_irqsave(&list_lock, flags); + } + } + spin_unlock_irqrestore(&list_lock, flags); + + return IRQ_HANDLED; +} + +void omap_irq_preinstall(struct drm_device *dev) +{ + DBG("dev=%p", dev); + dispc_runtime_get(); + dispc_clear_irqstatus(0xffffffff); + dispc_runtime_put(); +} + +int omap_irq_postinstall(struct drm_device *dev) +{ + struct omap_drm_private *priv = dev->dev_private; + struct omap_drm_irq *error_handler = &priv->error_handler; + + DBG("dev=%p", dev); + + INIT_LIST_HEAD(&priv->irq_list); + + error_handler->irq = omap_irq_error_handler; + error_handler->irqmask = DISPC_IRQ_OCP_ERR; + + /* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think + * we just need to ignore it while enabling tv-out + */ + error_handler->irqmask &= ~DISPC_IRQ_SYNC_LOST_DIGIT; + + omap_irq_register(dev, error_handler); + + return 0; +} + +void omap_irq_uninstall(struct drm_device *dev) +{ + DBG("dev=%p", dev); + // TODO prolly need to call drm_irq_uninstall() somewhere too +} + +/* + * We need a special version, instead of just using drm_irq_install(), + * because we need to register the irq via omapdss. Once omapdss and + * omapdrm are merged together we can assign the dispc hwmod data to + * ourselves and drop these and just use drm_irq_{install,uninstall}() + */ + +int omap_drm_irq_install(struct drm_device *dev) +{ + int ret; + + mutex_lock(&dev->struct_mutex); + + if (dev->irq_enabled) { + mutex_unlock(&dev->struct_mutex); + return -EBUSY; + } + dev->irq_enabled = 1; + mutex_unlock(&dev->struct_mutex); + + /* Before installing handler */ + if (dev->driver->irq_preinstall) + dev->driver->irq_preinstall(dev); + + ret = dispc_request_irq(dev->driver->irq_handler, dev); + + if (ret < 0) { + mutex_lock(&dev->struct_mutex); + dev->irq_enabled = 0; + mutex_unlock(&dev->struct_mutex); + return ret; + } + + /* After installing handler */ + if (dev->driver->irq_postinstall) + ret = dev->driver->irq_postinstall(dev); + + if (ret < 0) { + mutex_lock(&dev->struct_mutex); + dev->irq_enabled = 0; + mutex_unlock(&dev->struct_mutex); + dispc_free_irq(dev); + } + + return ret; +} + +int omap_drm_irq_uninstall(struct drm_device *dev) +{ + unsigned long irqflags; + int irq_enabled, i; + + mutex_lock(&dev->struct_mutex); + irq_enabled = dev->irq_enabled; + dev->irq_enabled = 0; + mutex_unlock(&dev->struct_mutex); + + /* + * Wake up any waiters so they don't hang. + */ + if (dev->num_crtcs) { + spin_lock_irqsave(&dev->vbl_lock, irqflags); + for (i = 0; i < dev->num_crtcs; i++) { + DRM_WAKEUP(&dev->vbl_queue[i]); + dev->vblank_enabled[i] = 0; + dev->last_vblank[i] = + dev->driver->get_vblank_counter(dev, i); + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + } + + if (!irq_enabled) + return -EINVAL; + + if (dev->driver->irq_uninstall) + dev->driver->irq_uninstall(dev); + + dispc_free_irq(dev); + + return 0; +} diff --git a/trunk/drivers/staging/omapdrm/omap_plane.c b/trunk/drivers/staging/omapdrm/omap_plane.c index 2a8e5bab49c9..bb989d7f026d 100644 --- a/trunk/drivers/staging/omapdrm/omap_plane.c +++ b/trunk/drivers/staging/omapdrm/omap_plane.c @@ -41,12 +41,14 @@ struct callback { struct omap_plane { struct drm_plane base; - struct omap_overlay *ovl; + int id; /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */ + const char *name; struct omap_overlay_info info; + struct omap_drm_apply apply; /* position/orientation of scanout within the fb: */ struct omap_drm_window win; - + bool enabled; /* last fb that we pinned: */ struct drm_framebuffer *pinned_fb; @@ -54,189 +56,15 @@ struct omap_plane { uint32_t nformats; uint32_t formats[32]; - /* for synchronizing access to unpins fifo */ - struct mutex unpin_mutex; + struct omap_drm_irq error_irq; - /* set of bo's pending unpin until next END_WIN irq */ + /* set of bo's pending unpin until next post_apply() */ DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *); - int num_unpins, pending_num_unpins; - - /* for deferred unpin when we need to wait for scanout complete irq */ - struct work_struct work; - - /* callback on next endwin irq */ - struct callback endwin; -}; -/* map from ovl->id to the irq we are interested in for scanout-done */ -static const uint32_t id2irq[] = { - [OMAP_DSS_GFX] = DISPC_IRQ_GFX_END_WIN, - [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_END_WIN, - [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_END_WIN, - [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_END_WIN, + // XXX maybe get rid of this and handle vblank in crtc too? + struct callback apply_done_cb; }; -static void dispc_isr(void *arg, uint32_t mask) -{ - struct drm_plane *plane = arg; - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_drm_private *priv = plane->dev->dev_private; - - omap_dispc_unregister_isr(dispc_isr, plane, - id2irq[omap_plane->ovl->id]); - - queue_work(priv->wq, &omap_plane->work); -} - -static void unpin_worker(struct work_struct *work) -{ - struct omap_plane *omap_plane = - container_of(work, struct omap_plane, work); - struct callback endwin; - - mutex_lock(&omap_plane->unpin_mutex); - DBG("unpinning %d of %d", omap_plane->num_unpins, - omap_plane->num_unpins + omap_plane->pending_num_unpins); - while (omap_plane->num_unpins > 0) { - struct drm_gem_object *bo = NULL; - int ret = kfifo_get(&omap_plane->unpin_fifo, &bo); - WARN_ON(!ret); - omap_gem_put_paddr(bo); - drm_gem_object_unreference_unlocked(bo); - omap_plane->num_unpins--; - } - endwin = omap_plane->endwin; - omap_plane->endwin.fxn = NULL; - mutex_unlock(&omap_plane->unpin_mutex); - - if (endwin.fxn) - endwin.fxn(endwin.arg); -} - -static void install_irq(struct drm_plane *plane) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - int ret; - - ret = omap_dispc_register_isr(dispc_isr, plane, id2irq[ovl->id]); - - /* - * omapdss has upper limit on # of registered irq handlers, - * which we shouldn't hit.. but if we do the limit should - * be raised or bad things happen: - */ - WARN_ON(ret == -EBUSY); -} - -/* push changes down to dss2 */ -static int commit(struct drm_plane *plane) -{ - struct drm_device *dev = plane->dev; - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - struct omap_overlay_info *info = &omap_plane->info; - int ret; - - DBG("%s", ovl->name); - DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, - info->out_height, info->screen_width); - DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, - info->paddr, info->p_uv_addr); - - /* NOTE: do we want to do this at all here, or just wait - * for dpms(ON) since other CRTC's may not have their mode - * set yet, so fb dimensions may still change.. - */ - ret = ovl->set_overlay_info(ovl, info); - if (ret) { - dev_err(dev->dev, "could not set overlay info\n"); - return ret; - } - - mutex_lock(&omap_plane->unpin_mutex); - omap_plane->num_unpins += omap_plane->pending_num_unpins; - omap_plane->pending_num_unpins = 0; - mutex_unlock(&omap_plane->unpin_mutex); - - /* our encoder doesn't necessarily get a commit() after this, in - * particular in the dpms() and mode_set_base() cases, so force the - * manager to update: - * - * could this be in the encoder somehow? - */ - if (ovl->manager) { - ret = ovl->manager->apply(ovl->manager); - if (ret) { - dev_err(dev->dev, "could not apply settings\n"); - return ret; - } - - /* - * NOTE: really this should be atomic w/ mgr->apply() but - * omapdss does not expose such an API - */ - if (omap_plane->num_unpins > 0) - install_irq(plane); - - } else { - struct omap_drm_private *priv = dev->dev_private; - queue_work(priv->wq, &omap_plane->work); - } - - - if (ovl->is_enabled(ovl)) { - omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, - info->out_width, info->out_height); - } - - return 0; -} - -/* when CRTC that we are attached to has potentially changed, this checks - * if we are attached to proper manager, and if necessary updates. - */ -static void update_manager(struct drm_plane *plane) -{ - struct omap_drm_private *priv = plane->dev->dev_private; - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - struct omap_overlay_manager *mgr = NULL; - int i; - - if (plane->crtc) { - for (i = 0; i < priv->num_encoders; i++) { - struct drm_encoder *encoder = priv->encoders[i]; - if (encoder->crtc == plane->crtc) { - mgr = omap_encoder_get_manager(encoder); - break; - } - } - } - - if (ovl->manager != mgr) { - bool enabled = ovl->is_enabled(ovl); - - /* don't switch things around with enabled overlays: */ - if (enabled) - omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); - - if (ovl->manager) { - DBG("disconnecting %s from %s", ovl->name, - ovl->manager->name); - ovl->unset_manager(ovl); - } - - if (mgr) { - DBG("connecting %s to %s", ovl->name, mgr->name); - ovl->set_manager(ovl, mgr); - } - - if (enabled && mgr) - omap_plane_dpms(plane, DRM_MODE_DPMS_ON); - } -} - static void unpin(void *arg, struct drm_gem_object *bo) { struct drm_plane *plane = arg; @@ -244,7 +72,6 @@ static void unpin(void *arg, struct drm_gem_object *bo) if (kfifo_put(&omap_plane->unpin_fifo, (const struct drm_gem_object **)&bo)) { - omap_plane->pending_num_unpins++; /* also hold a ref so it isn't free'd while pinned */ drm_gem_object_reference(bo); } else { @@ -264,13 +91,19 @@ static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) DBG("%p -> %p", pinned_fb, fb); - mutex_lock(&omap_plane->unpin_mutex); + if (fb) + drm_framebuffer_reference(fb); + ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin); - mutex_unlock(&omap_plane->unpin_mutex); + + if (pinned_fb) + drm_framebuffer_unreference(pinned_fb); if (ret) { dev_err(plane->dev->dev, "could not swap %p -> %p\n", omap_plane->pinned_fb, fb); + if (fb) + drm_framebuffer_unreference(fb); omap_plane->pinned_fb = NULL; return ret; } @@ -281,31 +114,90 @@ static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb) return 0; } -/* update parameters that are dependent on the framebuffer dimensions and - * position within the fb that this plane scans out from. This is called - * when framebuffer or x,y base may have changed. - */ -static void update_scanout(struct drm_plane *plane) +static void omap_plane_pre_apply(struct omap_drm_apply *apply) { - struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay_info *info = &omap_plane->info; + struct omap_plane *omap_plane = + container_of(apply, struct omap_plane, apply); struct omap_drm_window *win = &omap_plane->win; + struct drm_plane *plane = &omap_plane->base; + struct drm_device *dev = plane->dev; + struct omap_overlay_info *info = &omap_plane->info; + struct drm_crtc *crtc = plane->crtc; + enum omap_channel channel; + bool enabled = omap_plane->enabled && crtc; + bool ilace, replication; int ret; - ret = update_pin(plane, plane->fb); - if (ret) { - dev_err(plane->dev->dev, - "could not pin fb: %d\n", ret); - omap_plane_dpms(plane, DRM_MODE_DPMS_OFF); + DBG("%s, enabled=%d", omap_plane->name, enabled); + + /* if fb has changed, pin new fb: */ + update_pin(plane, enabled ? plane->fb : NULL); + + if (!enabled) { + dispc_ovl_enable(omap_plane->id, false); return; } + channel = omap_crtc_channel(crtc); + + /* update scanout: */ omap_framebuffer_update_scanout(plane->fb, win, info); - DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name, - win->src_x, win->src_y, - (u32)info->paddr, (u32)info->p_uv_addr, + DBG("%dx%d -> %dx%d (%d)", info->width, info->height, + info->out_width, info->out_height, info->screen_width); + DBG("%d,%d %08x %08x", info->pos_x, info->pos_y, + info->paddr, info->p_uv_addr); + + /* TODO: */ + ilace = false; + replication = false; + + /* and finally, update omapdss: */ + ret = dispc_ovl_setup(omap_plane->id, info, + replication, omap_crtc_timings(crtc), false); + if (ret) { + dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret); + return; + } + + dispc_ovl_enable(omap_plane->id, true); + dispc_ovl_set_channel_out(omap_plane->id, channel); +} + +static void omap_plane_post_apply(struct omap_drm_apply *apply) +{ + struct omap_plane *omap_plane = + container_of(apply, struct omap_plane, apply); + struct drm_plane *plane = &omap_plane->base; + struct omap_overlay_info *info = &omap_plane->info; + struct drm_gem_object *bo = NULL; + struct callback cb; + + cb = omap_plane->apply_done_cb; + omap_plane->apply_done_cb.fxn = NULL; + + while (kfifo_get(&omap_plane->unpin_fifo, &bo)) { + omap_gem_put_paddr(bo); + drm_gem_object_unreference_unlocked(bo); + } + + if (cb.fxn) + cb.fxn(cb.arg); + + if (omap_plane->enabled) { + omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, + info->out_width, info->out_height); + } +} + +static int apply(struct drm_plane *plane) +{ + if (plane->crtc) { + struct omap_plane *omap_plane = to_omap_plane(plane); + return omap_crtc_apply(plane->crtc, &omap_plane->apply); + } + return 0; } int omap_plane_mode_set(struct drm_plane *plane, @@ -313,7 +205,8 @@ int omap_plane_mode_set(struct drm_plane *plane, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) + uint32_t src_w, uint32_t src_h, + void (*fxn)(void *), void *arg) { struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_drm_window *win = &omap_plane->win; @@ -329,17 +222,20 @@ int omap_plane_mode_set(struct drm_plane *plane, win->src_w = src_w >> 16; win->src_h = src_h >> 16; - /* note: this is done after this fxn returns.. but if we need - * to do a commit/update_scanout, etc before this returns we - * need the current value. - */ + if (fxn) { + /* omap_crtc should ensure that a new page flip + * isn't permitted while there is one pending: + */ + BUG_ON(omap_plane->apply_done_cb.fxn); + + omap_plane->apply_done_cb.fxn = fxn; + omap_plane->apply_done_cb.arg = arg; + } + plane->fb = fb; plane->crtc = crtc; - update_scanout(plane); - update_manager(plane); - - return 0; + return apply(plane); } static int omap_plane_update(struct drm_plane *plane, @@ -349,9 +245,12 @@ static int omap_plane_update(struct drm_plane *plane, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { - omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); - return omap_plane_dpms(plane, DRM_MODE_DPMS_ON); + struct omap_plane *omap_plane = to_omap_plane(plane); + omap_plane->enabled = true; + return omap_plane_mode_set(plane, crtc, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h, + NULL, NULL); } static int omap_plane_disable(struct drm_plane *plane) @@ -364,48 +263,32 @@ static int omap_plane_disable(struct drm_plane *plane) static void omap_plane_destroy(struct drm_plane *plane) { struct omap_plane *omap_plane = to_omap_plane(plane); - DBG("%s", omap_plane->ovl->name); + + DBG("%s", omap_plane->name); + + omap_irq_unregister(plane->dev, &omap_plane->error_irq); + omap_plane_disable(plane); drm_plane_cleanup(plane); - WARN_ON(omap_plane->pending_num_unpins + omap_plane->num_unpins > 0); + + WARN_ON(!kfifo_is_empty(&omap_plane->unpin_fifo)); kfifo_free(&omap_plane->unpin_fifo); + kfree(omap_plane); } int omap_plane_dpms(struct drm_plane *plane, int mode) { struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_overlay *ovl = omap_plane->ovl; - int r; + bool enabled = (mode == DRM_MODE_DPMS_ON); + int ret = 0; - DBG("%s: %d", omap_plane->ovl->name, mode); - - if (mode == DRM_MODE_DPMS_ON) { - update_scanout(plane); - r = commit(plane); - if (!r) - r = ovl->enable(ovl); - } else { - struct omap_drm_private *priv = plane->dev->dev_private; - r = ovl->disable(ovl); - update_pin(plane, NULL); - queue_work(priv->wq, &omap_plane->work); + if (enabled != omap_plane->enabled) { + omap_plane->enabled = enabled; + ret = apply(plane); } - return r; -} - -void omap_plane_on_endwin(struct drm_plane *plane, - void (*fxn)(void *), void *arg) -{ - struct omap_plane *omap_plane = to_omap_plane(plane); - - mutex_lock(&omap_plane->unpin_mutex); - omap_plane->endwin.fxn = fxn; - omap_plane->endwin.arg = arg; - mutex_unlock(&omap_plane->unpin_mutex); - - install_irq(plane); + return ret; } /* helper to install properties which are common to planes and crtcs */ @@ -454,25 +337,13 @@ int omap_plane_set_property(struct drm_plane *plane, int ret = -EINVAL; if (property == priv->rotation_prop) { - struct omap_overlay *ovl = omap_plane->ovl; - - DBG("%s: rotation: %02x", ovl->name, (uint32_t)val); + DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val); omap_plane->win.rotation = val; - - if (ovl->is_enabled(ovl)) - ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON); - else - ret = 0; + ret = apply(plane); } else if (property == priv->zorder_prop) { - struct omap_overlay *ovl = omap_plane->ovl; - - DBG("%s: zorder: %d", ovl->name, (uint32_t)val); + DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); omap_plane->info.zorder = val; - - if (ovl->is_enabled(ovl)) - ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON); - else - ret = 0; + ret = apply(plane); } return ret; @@ -485,20 +356,38 @@ static const struct drm_plane_funcs omap_plane_funcs = { .set_property = omap_plane_set_property, }; +static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +{ + struct omap_plane *omap_plane = + container_of(irq, struct omap_plane, error_irq); + DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus); +} + +static const char *plane_names[] = { + [OMAP_DSS_GFX] = "gfx", + [OMAP_DSS_VIDEO1] = "vid1", + [OMAP_DSS_VIDEO2] = "vid2", + [OMAP_DSS_VIDEO3] = "vid3", +}; + +static const uint32_t error_irqs[] = { + [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, +}; + /* initialize plane */ struct drm_plane *omap_plane_init(struct drm_device *dev, - struct omap_overlay *ovl, unsigned int possible_crtcs, - bool priv) + int id, bool private_plane) { + struct omap_drm_private *priv = dev->dev_private; struct drm_plane *plane = NULL; struct omap_plane *omap_plane; + struct omap_overlay_info *info; int ret; - DBG("%s: possible_crtcs=%08x, priv=%d", ovl->name, - possible_crtcs, priv); - - /* friendly reminder to update table for future hw: */ - WARN_ON(ovl->id >= ARRAY_SIZE(id2irq)); + DBG("%s: priv=%d", plane_names[id], private_plane); omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); if (!omap_plane) { @@ -506,47 +395,50 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, goto fail; } - mutex_init(&omap_plane->unpin_mutex); - ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL); if (ret) { dev_err(dev->dev, "could not allocate unpin FIFO\n"); goto fail; } - INIT_WORK(&omap_plane->work, unpin_worker); - omap_plane->nformats = omap_framebuffer_get_formats( omap_plane->formats, ARRAY_SIZE(omap_plane->formats), - ovl->supported_modes); - omap_plane->ovl = ovl; + dss_feat_get_supported_color_modes(id)); + omap_plane->id = id; + omap_plane->name = plane_names[id]; + plane = &omap_plane->base; - drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs, - omap_plane->formats, omap_plane->nformats, priv); + omap_plane->apply.pre_apply = omap_plane_pre_apply; + omap_plane->apply.post_apply = omap_plane_post_apply; + + omap_plane->error_irq.irqmask = error_irqs[id]; + omap_plane->error_irq.irq = omap_plane_error_irq; + omap_irq_register(dev, &omap_plane->error_irq); + + drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs, + omap_plane->formats, omap_plane->nformats, private_plane); omap_plane_install_properties(plane, &plane->base); /* get our starting configuration, set defaults for parameters * we don't currently use, etc: */ - ovl->get_overlay_info(ovl, &omap_plane->info); - omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA; - omap_plane->info.rotation = OMAP_DSS_ROT_0; - omap_plane->info.global_alpha = 0xff; - omap_plane->info.mirror = 0; + info = &omap_plane->info; + info->rotation_type = OMAP_DSS_ROT_DMA; + info->rotation = OMAP_DSS_ROT_0; + info->global_alpha = 0xff; + info->mirror = 0; /* Set defaults depending on whether we are a CRTC or overlay * layer. * TODO add ioctl to give userspace an API to change this.. this * will come in a subsequent patch. */ - if (priv) + if (private_plane) omap_plane->info.zorder = 0; else - omap_plane->info.zorder = ovl->id; - - update_manager(plane); + omap_plane->info.zorder = id; return plane; diff --git a/trunk/drivers/staging/rtl8187se/r8180_core.c b/trunk/drivers/staging/rtl8187se/r8180_core.c index ae38475854b5..d10d75e8a33f 100644 --- a/trunk/drivers/staging/rtl8187se/r8180_core.c +++ b/trunk/drivers/staging/rtl8187se/r8180_core.c @@ -937,7 +937,8 @@ short alloc_rx_desc_ring(struct net_device *dev, u16 bufsize, int count) dma_tmp = pci_map_single(pdev, buf, bufsize * sizeof(u8), PCI_DMA_FROMDEVICE); - + if (pci_dma_mapping_error(pdev, dma_tmp)) + return -1; if (-1 == buffer_add(&(priv->rxbuffer), buf, dma_tmp, &(priv->rxbufferhead))) { DMESGE("Unable to allocate mem RX buf"); diff --git a/trunk/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/trunk/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c index 808aab6fa5ef..a9d78e9651c6 100644 --- a/trunk/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c +++ b/trunk/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c @@ -1183,6 +1183,8 @@ void rtl8192_tx_fill_desc(struct net_device *dev, struct tx_desc *pdesc, pTxFwInfo->TxRate, cb_desc); + if (pci_dma_mapping_error(priv->pdev, mapping)) + RT_TRACE(COMP_ERR, "DMA Mapping error\n");; if (cb_desc->bAMPDUEnable) { pTxFwInfo->AllowAggregation = 1; pTxFwInfo->RxMF = cb_desc->ampdu_factor; @@ -1280,6 +1282,8 @@ void rtl8192_tx_fill_cmd_desc(struct net_device *dev, dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pdev, mapping)) + RT_TRACE(COMP_ERR, "DMA Mapping error\n");; memset(entry, 0, 12); entry->LINIP = cb_desc->bLastIniPkt; entry->FirstSeg = 1; diff --git a/trunk/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/trunk/drivers/staging/rtl8192e/rtl8192e/rtl_core.c index 1a70f324552f..4ebf99b30975 100644 --- a/trunk/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +++ b/trunk/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -2104,7 +2104,10 @@ static short rtl8192_alloc_rx_desc_ring(struct net_device *dev) skb_tail_pointer_rsl(skb), priv->rxbuffersize, PCI_DMA_FROMDEVICE); - + if (pci_dma_mapping_error(priv->pdev, *mapping)) { + dev_kfree_skb_any(skb); + return -1; + } entry->BufferAddress = cpu_to_le32(*mapping); entry->Length = priv->rxbuffersize; @@ -2397,7 +2400,11 @@ static void rtl8192_rx_normal(struct net_device *dev) skb_tail_pointer_rsl(skb), priv->rxbuffersize, PCI_DMA_FROMDEVICE); - + if (pci_dma_mapping_error(priv->pdev, + *((dma_addr_t *)skb->cb))) { + dev_kfree_skb_any(skb); + return; + } } done: pdesc->BufferAddress = cpu_to_le32(*((dma_addr_t *)skb->cb)); diff --git a/trunk/drivers/staging/rtl8712/usb_intf.c b/trunk/drivers/staging/rtl8712/usb_intf.c index 6b73843e580a..a96cd06d69dd 100644 --- a/trunk/drivers/staging/rtl8712/usb_intf.c +++ b/trunk/drivers/staging/rtl8712/usb_intf.c @@ -63,6 +63,8 @@ static struct usb_device_id rtl871x_usb_id_tbl[] = { {USB_DEVICE(0x0B05, 0x1791)}, /* 11n mode disable */ /* Belkin */ {USB_DEVICE(0x050D, 0x945A)}, + /* ISY IWL - Belkin clone */ + {USB_DEVICE(0x050D, 0x11F1)}, /* Corega */ {USB_DEVICE(0x07AA, 0x0047)}, /* D-Link */ diff --git a/trunk/drivers/staging/sb105x/Kconfig b/trunk/drivers/staging/sb105x/Kconfig index ac87c5e38dee..1facad625554 100644 --- a/trunk/drivers/staging/sb105x/Kconfig +++ b/trunk/drivers/staging/sb105x/Kconfig @@ -2,6 +2,7 @@ config SB105X tristate "SystemBase PCI Multiport UART" select SERIAL_CORE depends on PCI + depends on X86 help A driver for the SystemBase Multi-2/PCI serial card diff --git a/trunk/drivers/staging/sb105x/sb_pci_mp.c b/trunk/drivers/staging/sb105x/sb_pci_mp.c index edb2a85b9d52..9464f3874346 100644 --- a/trunk/drivers/staging/sb105x/sb_pci_mp.c +++ b/trunk/drivers/staging/sb105x/sb_pci_mp.c @@ -3054,6 +3054,7 @@ static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd) sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16); } break; +#ifdef CONFIG_PARPORT_PC case PCI_DEVICE_ID_MP2S1P : sbdev->nr_ports = 2; @@ -3073,6 +3074,7 @@ static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd) /* add PC compatible parallel port */ parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); break; +#endif } ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name); diff --git a/trunk/drivers/staging/speakup/synth.c b/trunk/drivers/staging/speakup/synth.c index df9533798095..7616f058a00b 100644 --- a/trunk/drivers/staging/speakup/synth.c +++ b/trunk/drivers/staging/speakup/synth.c @@ -342,7 +342,7 @@ int synth_init(char *synth_name) mutex_lock(&spk_mutex); /* First, check if we already have it loaded. */ - for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++) + for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++) if (strcmp(synths[i]->name, synth_name) == 0) synth = synths[i]; @@ -423,7 +423,7 @@ int synth_add(struct spk_synth *in_synth) int i; int status = 0; mutex_lock(&spk_mutex); - for (i = 0; synths[i] != NULL && i < MAXSYNTHS; i++) + for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++) /* synth_remove() is responsible for rotating the array down */ if (in_synth == synths[i]) { mutex_unlock(&spk_mutex); diff --git a/trunk/drivers/staging/tidspbridge/core/_tiomap.h b/trunk/drivers/staging/tidspbridge/core/_tiomap.h index 543a127c7d4d..b783bfa59b1c 100644 --- a/trunk/drivers/staging/tidspbridge/core/_tiomap.h +++ b/trunk/drivers/staging/tidspbridge/core/_tiomap.h @@ -31,7 +31,7 @@ * driver should read or write to PRM/CM registers directly; they * should rely on OMAP core code to do this. */ -#include +#include #include #include #include diff --git a/trunk/drivers/staging/tidspbridge/core/dsp-clock.c b/trunk/drivers/staging/tidspbridge/core/dsp-clock.c index b647207928b1..2f084e181d39 100644 --- a/trunk/drivers/staging/tidspbridge/core/dsp-clock.c +++ b/trunk/drivers/staging/tidspbridge/core/dsp-clock.c @@ -121,9 +121,13 @@ void dsp_clk_exit(void) for (i = 0; i < DM_TIMER_CLOCKS; i++) omap_dm_timer_free(timer[i]); + clk_unprepare(iva2_clk); clk_put(iva2_clk); + clk_unprepare(ssi.sst_fck); clk_put(ssi.sst_fck); + clk_unprepare(ssi.ssr_fck); clk_put(ssi.ssr_fck); + clk_unprepare(ssi.ick); clk_put(ssi.ick); } @@ -145,14 +149,21 @@ void dsp_clk_init(void) iva2_clk = clk_get(&dspbridge_device.dev, "iva2_ck"); if (IS_ERR(iva2_clk)) dev_err(bridge, "failed to get iva2 clock %p\n", iva2_clk); + else + clk_prepare(iva2_clk); ssi.sst_fck = clk_get(&dspbridge_device.dev, "ssi_sst_fck"); ssi.ssr_fck = clk_get(&dspbridge_device.dev, "ssi_ssr_fck"); ssi.ick = clk_get(&dspbridge_device.dev, "ssi_ick"); - if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick)) + if (IS_ERR(ssi.sst_fck) || IS_ERR(ssi.ssr_fck) || IS_ERR(ssi.ick)) { dev_err(bridge, "failed to get ssi: sst %p, ssr %p, ick %p\n", ssi.sst_fck, ssi.ssr_fck, ssi.ick); + } else { + clk_prepare(ssi.sst_fck); + clk_prepare(ssi.ssr_fck); + clk_prepare(ssi.ick); + } } /** diff --git a/trunk/drivers/staging/tidspbridge/core/wdt.c b/trunk/drivers/staging/tidspbridge/core/wdt.c index 1dce36fb828f..7ff0e6c98039 100644 --- a/trunk/drivers/staging/tidspbridge/core/wdt.c +++ b/trunk/drivers/staging/tidspbridge/core/wdt.c @@ -63,11 +63,15 @@ int dsp_wdt_init(void) dsp_wdt.fclk = clk_get(NULL, "wdt3_fck"); if (!IS_ERR(dsp_wdt.fclk)) { + clk_prepare(dsp_wdt.fclk); + dsp_wdt.iclk = clk_get(NULL, "wdt3_ick"); if (IS_ERR(dsp_wdt.iclk)) { clk_put(dsp_wdt.fclk); dsp_wdt.fclk = NULL; ret = -EFAULT; + } else { + clk_prepare(dsp_wdt.iclk); } } else ret = -EFAULT; @@ -95,10 +99,14 @@ void dsp_wdt_exit(void) free_irq(INT_34XX_WDT3_IRQ, &dsp_wdt); tasklet_kill(&dsp_wdt.wdt3_tasklet); - if (dsp_wdt.fclk) + if (dsp_wdt.fclk) { + clk_unprepare(dsp_wdt.fclk); clk_put(dsp_wdt.fclk); - if (dsp_wdt.iclk) + } + if (dsp_wdt.iclk) { + clk_unprepare(dsp_wdt.iclk); clk_put(dsp_wdt.iclk); + } dsp_wdt.fclk = NULL; dsp_wdt.iclk = NULL; diff --git a/trunk/drivers/staging/vme/devices/vme_pio2_core.c b/trunk/drivers/staging/vme/devices/vme_pio2_core.c index 0331178ca3b3..bf73ba26e88a 100644 --- a/trunk/drivers/staging/vme/devices/vme_pio2_core.c +++ b/trunk/drivers/staging/vme/devices/vme_pio2_core.c @@ -162,11 +162,9 @@ static struct vme_driver pio2_driver = { static int __init pio2_init(void) { - int retval = 0; - if (bus_num == 0) { pr_err("No cards, skipping registration\n"); - goto err_nocard; + return -ENODEV; } if (bus_num > PIO2_CARDS_MAX) { @@ -176,15 +174,7 @@ static int __init pio2_init(void) } /* Register the PIO2 driver */ - retval = vme_register_driver(&pio2_driver, bus_num); - if (retval != 0) - goto err_reg; - - return retval; - -err_reg: -err_nocard: - return retval; + return vme_register_driver(&pio2_driver, bus_num); } static int pio2_match(struct vme_dev *vdev) diff --git a/trunk/drivers/staging/vt6656/bssdb.h b/trunk/drivers/staging/vt6656/bssdb.h index 6b2ec390e775..806cbf72fb59 100644 --- a/trunk/drivers/staging/vt6656/bssdb.h +++ b/trunk/drivers/staging/vt6656/bssdb.h @@ -90,7 +90,6 @@ typedef struct tagSRSNCapObject { } SRSNCapObject, *PSRSNCapObject; // BSS info(AP) -#pragma pack(1) typedef struct tagKnownBSS { // BSS info BOOL bActive; diff --git a/trunk/drivers/staging/vt6656/int.h b/trunk/drivers/staging/vt6656/int.h index 5d8faf9f96ec..e0d2b07ba608 100644 --- a/trunk/drivers/staging/vt6656/int.h +++ b/trunk/drivers/staging/vt6656/int.h @@ -34,7 +34,6 @@ #include "device.h" /*--------------------- Export Definitions -------------------------*/ -#pragma pack(1) typedef struct tagSINTData { BYTE byTSR0; BYTE byPkt0; diff --git a/trunk/drivers/staging/vt6656/iocmd.h b/trunk/drivers/staging/vt6656/iocmd.h index 22710cef751d..ae6e2d237b20 100644 --- a/trunk/drivers/staging/vt6656/iocmd.h +++ b/trunk/drivers/staging/vt6656/iocmd.h @@ -95,13 +95,12 @@ typedef enum tagWZONETYPE { // Ioctl interface structure // Command structure // -#pragma pack(1) typedef struct tagSCmdRequest { u8 name[16]; void *data; u16 wResult; u16 wCmdCode; -} SCmdRequest, *PSCmdRequest; +} __packed SCmdRequest, *PSCmdRequest; // // Scan @@ -111,7 +110,7 @@ typedef struct tagSCmdScan { u8 ssid[SSID_MAXLEN + 2]; -} SCmdScan, *PSCmdScan; +} __packed SCmdScan, *PSCmdScan; // // BSS Join @@ -126,7 +125,7 @@ typedef struct tagSCmdBSSJoin { BOOL bPSEnable; BOOL bShareKeyAuth; -} SCmdBSSJoin, *PSCmdBSSJoin; +} __packed SCmdBSSJoin, *PSCmdBSSJoin; // // Zonetype Setting @@ -137,7 +136,7 @@ typedef struct tagSCmdZoneTypeSet { BOOL bWrite; WZONETYPE ZoneType; -} SCmdZoneTypeSet, *PSCmdZoneTypeSet; +} __packed SCmdZoneTypeSet, *PSCmdZoneTypeSet; typedef struct tagSWPAResult { char ifname[100]; @@ -145,7 +144,7 @@ typedef struct tagSWPAResult { u8 key_mgmt; u8 eap_type; BOOL authenticated; -} SWPAResult, *PSWPAResult; +} __packed SWPAResult, *PSWPAResult; typedef struct tagSCmdStartAP { @@ -157,7 +156,7 @@ typedef struct tagSCmdStartAP { BOOL bShareKeyAuth; u8 byBasicRate; -} SCmdStartAP, *PSCmdStartAP; +} __packed SCmdStartAP, *PSCmdStartAP; typedef struct tagSCmdSetWEP { @@ -167,7 +166,7 @@ typedef struct tagSCmdSetWEP { BOOL bWepKeyAvailable[WEP_NKEYS]; u32 auWepKeyLength[WEP_NKEYS]; -} SCmdSetWEP, *PSCmdSetWEP; +} __packed SCmdSetWEP, *PSCmdSetWEP; typedef struct tagSBSSIDItem { @@ -180,14 +179,14 @@ typedef struct tagSBSSIDItem { BOOL bWEPOn; u32 uRSSI; -} SBSSIDItem; +} __packed SBSSIDItem; typedef struct tagSBSSIDList { u32 uItem; SBSSIDItem sBSSIDList[0]; -} SBSSIDList, *PSBSSIDList; +} __packed SBSSIDList, *PSBSSIDList; typedef struct tagSNodeItem { @@ -208,7 +207,7 @@ typedef struct tagSNodeItem { u32 uTxAttempts; u16 wFailureRatio; -} SNodeItem; +} __packed SNodeItem; typedef struct tagSNodeList { @@ -216,7 +215,7 @@ typedef struct tagSNodeList { u32 uItem; SNodeItem sNodeList[0]; -} SNodeList, *PSNodeList; +} __packed SNodeList, *PSNodeList; typedef struct tagSCmdLinkStatus { @@ -229,7 +228,7 @@ typedef struct tagSCmdLinkStatus { u32 uChannel; u32 uLinkRate; -} SCmdLinkStatus, *PSCmdLinkStatus; +} __packed SCmdLinkStatus, *PSCmdLinkStatus; // // 802.11 counter @@ -247,7 +246,7 @@ typedef struct tagSDot11MIBCount { u32 ReceivedFragmentCount; u32 MulticastReceivedFrameCount; u32 FCSErrorCount; -} SDot11MIBCount, *PSDot11MIBCount; +} __packed SDot11MIBCount, *PSDot11MIBCount; @@ -355,13 +354,13 @@ typedef struct tagSStatMIBCount { u32 ullTxBroadcastBytes[2]; u32 ullTxMulticastBytes[2]; u32 ullTxDirectedBytes[2]; -} SStatMIBCount, *PSStatMIBCount; +} __packed SStatMIBCount, *PSStatMIBCount; typedef struct tagSCmdValue { u32 dwValue; -} SCmdValue, *PSCmdValue; +} __packed SCmdValue, *PSCmdValue; // // hostapd & viawget ioctl related @@ -431,7 +430,7 @@ struct viawget_hostapd_param { u8 ssid[32]; } scan_req; } u; -}; +} __packed; /*--------------------- Export Classes ----------------------------*/ diff --git a/trunk/drivers/staging/vt6656/iowpa.h b/trunk/drivers/staging/vt6656/iowpa.h index 959c8868f6e2..2522ddec718d 100644 --- a/trunk/drivers/staging/vt6656/iowpa.h +++ b/trunk/drivers/staging/vt6656/iowpa.h @@ -67,12 +67,11 @@ enum { -#pragma pack(1) typedef struct viawget_wpa_header { u8 type; u16 req_ie_len; u16 resp_ie_len; -} viawget_wpa_header; +} __packed viawget_wpa_header; struct viawget_wpa_param { u32 cmd; @@ -113,9 +112,8 @@ struct viawget_wpa_param { u8 *buf; } scan_results; } u; -}; +} __packed; -#pragma pack(1) struct viawget_scan_result { u8 bssid[6]; u8 ssid[32]; @@ -130,7 +128,7 @@ struct viawget_scan_result { int noise; int level; int maxrate; -}; +} __packed; /*--------------------- Export Classes ----------------------------*/ diff --git a/trunk/drivers/staging/wlan-ng/cfg80211.c b/trunk/drivers/staging/wlan-ng/cfg80211.c index 18c06a59c091..1d31eab19d16 100644 --- a/trunk/drivers/staging/wlan-ng/cfg80211.c +++ b/trunk/drivers/staging/wlan-ng/cfg80211.c @@ -638,8 +638,8 @@ int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev) } -int prism2_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, - int mbm) +int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, int mbm) { struct prism2_wiphy_private *priv = wiphy_priv(wiphy); wlandevice_t *wlandev = priv->wlandev; @@ -665,7 +665,8 @@ int prism2_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, return err; } -int prism2_get_tx_power(struct wiphy *wiphy, int *dbm) +int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + int *dbm) { struct prism2_wiphy_private *priv = wiphy_priv(wiphy); wlandevice_t *wlandev = priv->wlandev; diff --git a/trunk/drivers/staging/wlan-ng/prism2mgmt.c b/trunk/drivers/staging/wlan-ng/prism2mgmt.c index 4efa9bc0fcf0..89bfd858bb28 100644 --- a/trunk/drivers/staging/wlan-ng/prism2mgmt.c +++ b/trunk/drivers/staging/wlan-ng/prism2mgmt.c @@ -406,7 +406,7 @@ int prism2mgmt_scan_results(wlandevice_t *wlandev, void *msgp) /* SSID */ req->ssid.status = P80211ENUM_msgitem_status_data_ok; req->ssid.data.len = le16_to_cpu(item->ssid.len); - req->ssid.data.len = min_t(u16, req->ssid.data.len, WLAN_BSSID_LEN); + req->ssid.data.len = min_t(u16, req->ssid.data.len, WLAN_SSID_MAXLEN); memcpy(req->ssid.data.data, item->ssid.data, req->ssid.data.len); /* supported rates */ diff --git a/trunk/drivers/staging/zram/zram_drv.c b/trunk/drivers/staging/zram/zram_drv.c index fb4a7c94aed3..f2a73bd739fb 100644 --- a/trunk/drivers/staging/zram/zram_drv.c +++ b/trunk/drivers/staging/zram/zram_drv.c @@ -265,7 +265,7 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, int offset) { - int ret; + int ret = 0; size_t clen; unsigned long handle; struct page *page; @@ -286,10 +286,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } ret = zram_decompress_page(zram, uncmem, index); - if (ret) { - kfree(uncmem); + if (ret) goto out; - } } /* @@ -302,16 +300,18 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, user_mem = kmap_atomic(page); - if (is_partial_io(bvec)) + if (is_partial_io(bvec)) { memcpy(uncmem + offset, user_mem + bvec->bv_offset, bvec->bv_len); - else + kunmap_atomic(user_mem); + user_mem = NULL; + } else { uncmem = user_mem; + } if (page_zero_filled(uncmem)) { - kunmap_atomic(user_mem); - if (is_partial_io(bvec)) - kfree(uncmem); + if (!is_partial_io(bvec)) + kunmap_atomic(user_mem); zram_stat_inc(&zram->stats.pages_zero); zram_set_flag(zram, index, ZRAM_ZERO); ret = 0; @@ -321,9 +321,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, zram->compress_workmem); - kunmap_atomic(user_mem); - if (is_partial_io(bvec)) - kfree(uncmem); + if (!is_partial_io(bvec)) { + kunmap_atomic(user_mem); + user_mem = NULL; + uncmem = NULL; + } if (unlikely(ret != LZO_E_OK)) { pr_err("Compression failed! err=%d\n", ret); @@ -332,8 +334,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if (unlikely(clen > max_zpage_size)) { zram_stat_inc(&zram->stats.bad_compress); - src = uncmem; clen = PAGE_SIZE; + src = NULL; + if (is_partial_io(bvec)) + src = uncmem; } handle = zs_malloc(zram->mem_pool, clen); @@ -345,7 +349,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, } cmem = zs_map_object(zram->mem_pool, handle, ZS_MM_WO); + if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) + src = kmap_atomic(page); memcpy(cmem, src, clen); + if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) + kunmap_atomic(src); zs_unmap_object(zram->mem_pool, handle); @@ -358,9 +366,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, if (clen <= PAGE_SIZE / 2) zram_stat_inc(&zram->stats.good_compress); - return 0; - out: + if (is_partial_io(bvec)) + kfree(uncmem); + if (ret) zram_stat64_inc(zram, &zram->stats.failed_writes); return ret; diff --git a/trunk/drivers/target/iscsi/iscsi_target_erl2.c b/trunk/drivers/target/iscsi/iscsi_target_erl2.c index 9ac4c151eae4..ba6091bf93fc 100644 --- a/trunk/drivers/target/iscsi/iscsi_target_erl2.c +++ b/trunk/drivers/target/iscsi/iscsi_target_erl2.c @@ -372,7 +372,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) * made generic here. */ if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd && - iscsi_sna_gte(cmd->stat_sn, conn->sess->exp_cmd_sn)) { + iscsi_sna_gte(cmd->cmd_sn, conn->sess->exp_cmd_sn)) { list_del(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); iscsit_free_cmd(cmd); diff --git a/trunk/drivers/target/target_core_alua.c b/trunk/drivers/target/target_core_alua.c index 85140f7dde1e..7d4ec02e29a9 100644 --- a/trunk/drivers/target/target_core_alua.c +++ b/trunk/drivers/target/target_core_alua.c @@ -212,7 +212,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd) struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem, *l_tg_pt_gp_mem; unsigned char *buf; unsigned char *ptr; - sense_reason_t rc; + sense_reason_t rc = TCM_NO_SENSE; u32 len = 4; /* Skip over RESERVED area in header */ int alua_access_state, primary = 0; u16 tg_pt_id, rtpi; diff --git a/trunk/drivers/target/target_core_pr.c b/trunk/drivers/target/target_core_pr.c index e35dbf85841f..8e0290b38e43 100644 --- a/trunk/drivers/target/target_core_pr.c +++ b/trunk/drivers/target/target_core_pr.c @@ -2053,7 +2053,7 @@ core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key, /* Used for APTPL metadata w/ UNREGISTER */ unsigned char *pr_aptpl_buf = NULL; unsigned char isid_buf[PR_REG_ISID_LEN], *isid_ptr = NULL; - sense_reason_t ret; + sense_reason_t ret = TCM_NO_SENSE; int pr_holder = 0, type; if (!se_sess || !se_lun) { diff --git a/trunk/drivers/target/target_core_transport.c b/trunk/drivers/target/target_core_transport.c index c23c76ccef65..bd587b70661a 100644 --- a/trunk/drivers/target/target_core_transport.c +++ b/trunk/drivers/target/target_core_transport.c @@ -541,9 +541,6 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd) void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) { - if (!(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) - transport_lun_remove_cmd(cmd); - if (transport_cmd_check_stop_to_fabric(cmd)) return; if (remove) @@ -1396,6 +1393,8 @@ static void target_complete_tmr_failure(struct work_struct *work) se_cmd->se_tmr_req->response = TMR_LUN_DOES_NOT_EXIST; se_cmd->se_tfo->queue_tm_rsp(se_cmd); + + transport_cmd_check_stop_to_fabric(se_cmd); } /** @@ -1688,6 +1687,7 @@ void target_execute_cmd(struct se_cmd *cmd) } cmd->t_state = TRANSPORT_PROCESSING; + cmd->transport_state |= CMD_T_ACTIVE; spin_unlock_irq(&cmd->t_state_lock); if (!target_handle_task_attr(cmd)) @@ -2597,6 +2597,16 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, * SENSE KEY values from include/scsi/scsi.h */ switch (reason) { + case TCM_NO_SENSE: + /* CURRENT ERROR */ + buffer[0] = 0x70; + buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; + /* Not Ready */ + buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY; + /* NO ADDITIONAL SENSE INFORMATION */ + buffer[SPC_ASC_KEY_OFFSET] = 0; + buffer[SPC_ASCQ_KEY_OFFSET] = 0; + break; case TCM_NON_EXISTENT_LUN: /* CURRENT ERROR */ buffer[0] = 0x70; @@ -2743,7 +2753,7 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, /* ILLEGAL REQUEST */ buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; /* LOGICAL UNIT COMMUNICATION FAILURE */ - buffer[SPC_ASC_KEY_OFFSET] = 0x80; + buffer[SPC_ASC_KEY_OFFSET] = 0x08; break; } /* @@ -2804,6 +2814,8 @@ void transport_send_task_abort(struct se_cmd *cmd) } cmd->scsi_status = SAM_STAT_TASK_ABORTED; + transport_lun_remove_cmd(cmd); + pr_debug("Setting SAM_STAT_TASK_ABORTED status for CDB: 0x%02x," " ITT: 0x%08x\n", cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd)); diff --git a/trunk/drivers/target/tcm_fc/tfc_sess.c b/trunk/drivers/target/tcm_fc/tfc_sess.c index 12d6fa21e5e1..6659dd36e806 100644 --- a/trunk/drivers/target/tcm_fc/tfc_sess.c +++ b/trunk/drivers/target/tcm_fc/tfc_sess.c @@ -355,11 +355,11 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len, tport = ft_tport_create(rdata->local_port); if (!tport) - return 0; /* not a target for this local port */ + goto not_target; /* not a target for this local port */ acl = ft_acl_get(tport->tpg, rdata); if (!acl) - return 0; + goto not_target; /* no target for this remote */ if (!rspp) goto fill; @@ -396,12 +396,18 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len, /* * OR in our service parameters with other provider (initiator), if any. - * TBD XXX - indicate RETRY capability? */ fill: fcp_parm = ntohl(spp->spp_params); + fcp_parm &= ~FCP_SPPF_RETRY; spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN); return FC_SPP_RESP_ACK; + +not_target: + fcp_parm = ntohl(spp->spp_params); + fcp_parm &= ~FCP_SPPF_TARG_FCN; + spp->spp_params = htonl(fcp_parm); + return 0; } /** diff --git a/trunk/drivers/tty/pty.c b/trunk/drivers/tty/pty.c index be6a373601b7..79ff3a5e925d 100644 --- a/trunk/drivers/tty/pty.c +++ b/trunk/drivers/tty/pty.c @@ -441,6 +441,8 @@ static int pty_bsd_ioctl(struct tty_struct *tty, return pty_get_pktmode(tty, (int __user *)arg); case TIOCSIG: /* Send signal to other side of pty */ return pty_signal(tty, (int) arg); + case TIOCGPTN: /* TTY returns ENOTTY, but glibc expects EINVAL here */ + return -EINVAL; } return -ENOIOCTLCMD; } diff --git a/trunk/drivers/tty/serial/8250/8250.c b/trunk/drivers/tty/serial/8250/8250.c index d085e3a8ec06..f9320437a649 100644 --- a/trunk/drivers/tty/serial/8250/8250.c +++ b/trunk/drivers/tty/serial/8250/8250.c @@ -300,6 +300,12 @@ static const struct serial8250_config uart_config[] = { UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00, .flags = UART_CAP_FIFO, }, + [PORT_BRCM_TRUMANAGE] = { + .name = "TruManage", + .fifo_size = 1, + .tx_loadsz = 1024, + .flags = UART_CAP_HFIFO, + }, [PORT_8250_CIR] = { .name = "CIR port" } @@ -1490,6 +1496,11 @@ void serial8250_tx_chars(struct uart_8250_port *up) port->icount.tx++; if (uart_circ_empty(xmit)) break; + if (up->capabilities & UART_CAP_HFIFO) { + if ((serial_port_in(port, UART_LSR) & BOTH_EMPTY) != + BOTH_EMPTY) + break; + } } while (--count > 0); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) diff --git a/trunk/drivers/tty/serial/8250/8250.h b/trunk/drivers/tty/serial/8250/8250.h index 3b4ea84898c2..12caa1292b75 100644 --- a/trunk/drivers/tty/serial/8250/8250.h +++ b/trunk/drivers/tty/serial/8250/8250.h @@ -40,6 +40,7 @@ struct serial8250_config { #define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */ #define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ #define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */ +#define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */ #define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ diff --git a/trunk/drivers/tty/serial/8250/8250_dw.c b/trunk/drivers/tty/serial/8250/8250_dw.c index 1d0dba2d562d..096d2ef48b32 100644 --- a/trunk/drivers/tty/serial/8250/8250_dw.c +++ b/trunk/drivers/tty/serial/8250/8250_dw.c @@ -79,7 +79,7 @@ static int dw8250_handle_irq(struct uart_port *p) } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* Clear the USR and write the LCR again. */ (void)p->serial_in(p, UART_USR); - p->serial_out(p, d->last_lcr, UART_LCR); + p->serial_out(p, UART_LCR, d->last_lcr); return 1; } diff --git a/trunk/drivers/tty/serial/8250/8250_pci.c b/trunk/drivers/tty/serial/8250/8250_pci.c index 26b9dc012ed0..a27a98e1b066 100644 --- a/trunk/drivers/tty/serial/8250/8250_pci.c +++ b/trunk/drivers/tty/serial/8250/8250_pci.c @@ -1085,6 +1085,18 @@ pci_omegapci_setup(struct serial_private *priv, return setup_port(priv, port, 2, idx * 8, 0); } +static int +pci_brcm_trumanage_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + int ret = pci_default_setup(priv, board, port, idx); + + port->port.type = PORT_BRCM_TRUMANAGE; + port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); + return ret; +} + static int skip_tx_en_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) @@ -1301,9 +1313,10 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_VENDOR_ID_AGESTAR 0x5372 #define PCI_DEVICE_ID_AGESTAR_9375 0x6872 #define PCI_VENDOR_ID_ASIX 0x9710 -#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0019 #define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 #define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 +#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 +#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ @@ -1953,6 +1966,17 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_xr17v35x_setup, }, + /* + * Broadcom TruManage (NetXtreme) + */ + { + .vendor = PCI_VENDOR_ID_BROADCOM, + .device = PCI_DEVICE_ID_BROADCOM_TRUMANAGE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_brcm_trumanage_setup, + }, + /* * Default "match everything" terminator entry */ @@ -2148,6 +2172,7 @@ enum pci_board_num_t { pbn_ce4100_1_115200, pbn_omegapci, pbn_NETMOS9900_2s_115200, + pbn_brcm_trumanage, }; /* @@ -2246,7 +2271,7 @@ static struct pciserial_board pci_boards[] = { [pbn_b0_8_1152000_200] = { .flags = FL_BASE0, - .num_ports = 2, + .num_ports = 8, .base_baud = 1152000, .uart_offset = 0x200, }, @@ -2892,6 +2917,12 @@ static struct pciserial_board pci_boards[] = { .num_ports = 2, .base_baud = 115200, }, + [pbn_brcm_trumanage] = { + .flags = FL_BASE0, + .num_ports = 1, + .reg_shift = 2, + .base_baud = 115200, + }, }; static const struct pci_device_id blacklist[] = { @@ -4470,6 +4501,13 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_omegapci }, + /* + * Broadcom TruManage + */ + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_brcm_trumanage }, + /* * AgeStar as-prs2-009 */ diff --git a/trunk/drivers/tty/serial/ifx6x60.c b/trunk/drivers/tty/serial/ifx6x60.c index 675d94ab0aff..8cb6d8d66a13 100644 --- a/trunk/drivers/tty/serial/ifx6x60.c +++ b/trunk/drivers/tty/serial/ifx6x60.c @@ -637,6 +637,7 @@ static void ifx_port_shutdown(struct tty_port *port) clear_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); mrdy_set_low(ifx_dev); + del_timer(&ifx_dev->spi_timer); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); tasklet_kill(&ifx_dev->io_work_tasklet); } @@ -810,7 +811,8 @@ static void ifx_spi_io(unsigned long data) ifx_dev->spi_xfer.cs_change = 0; ifx_dev->spi_xfer.speed_hz = ifx_dev->spi_dev->max_speed_hz; /* ifx_dev->spi_xfer.speed_hz = 390625; */ - ifx_dev->spi_xfer.bits_per_word = spi_bpw; + ifx_dev->spi_xfer.bits_per_word = + ifx_dev->spi_dev->bits_per_word; ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer; ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer; diff --git a/trunk/drivers/tty/serial/mxs-auart.c b/trunk/drivers/tty/serial/mxs-auart.c index 6db23b035efe..e55615eb34ad 100644 --- a/trunk/drivers/tty/serial/mxs-auart.c +++ b/trunk/drivers/tty/serial/mxs-auart.c @@ -253,7 +253,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s) struct circ_buf *xmit = &s->port.state->xmit; if (auart_dma_enabled(s)) { - int i = 0; + u32 i = 0; int size; void *buffer = s->tx_dma_buf; @@ -412,10 +412,12 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) u32 ctrl = readl(u->membase + AUART_CTRL2); - ctrl &= ~AUART_CTRL2_RTSEN; + ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS); if (mctrl & TIOCM_RTS) { if (tty_port_cts_enabled(&u->state->port)) ctrl |= AUART_CTRL2_RTSEN; + else + ctrl |= AUART_CTRL2_RTS; } s->ctrl = mctrl; diff --git a/trunk/drivers/tty/serial/samsung.c b/trunk/drivers/tty/serial/samsung.c index 12e5249d053e..e514b3a4dc57 100644 --- a/trunk/drivers/tty/serial/samsung.c +++ b/trunk/drivers/tty/serial/samsung.c @@ -1006,7 +1006,6 @@ static void s3c24xx_serial_resetport(struct uart_port *port, ucon &= ucon_mask; wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); /* reset both fifos */ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); diff --git a/trunk/drivers/tty/serial/vt8500_serial.c b/trunk/drivers/tty/serial/vt8500_serial.c index 8fd181436a6b..d5ed9f613005 100644 --- a/trunk/drivers/tty/serial/vt8500_serial.c +++ b/trunk/drivers/tty/serial/vt8500_serial.c @@ -604,7 +604,7 @@ static int vt8500_serial_probe(struct platform_device *pdev) vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0); - if (vt8500_port->clk) { + if (!IS_ERR(vt8500_port->clk)) { vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk); } else { /* use the default of 24Mhz if not specified and warn */ diff --git a/trunk/drivers/usb/Kconfig b/trunk/drivers/usb/Kconfig index 4c90b510d016..640ae6c6d2d2 100644 --- a/trunk/drivers/usb/Kconfig +++ b/trunk/drivers/usb/Kconfig @@ -37,6 +37,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_W90X900 default y if ARCH_AT91 default y if ARCH_MXC + default y if ARCH_MXS default y if ARCH_OMAP3 default y if ARCH_CNS3XXX default y if ARCH_VT8500 diff --git a/trunk/drivers/usb/chipidea/host.c b/trunk/drivers/usb/chipidea/host.c index caecad9213f5..8e9d31277c43 100644 --- a/trunk/drivers/usb/chipidea/host.c +++ b/trunk/drivers/usb/chipidea/host.c @@ -70,6 +70,9 @@ static int host_start(struct ci13xxx *ci) else ci->hcd = hcd; + if (ci->platdata->flags & CI13XXX_DISABLE_STREAMING) + hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); + return ret; } diff --git a/trunk/drivers/usb/class/cdc-acm.c b/trunk/drivers/usb/class/cdc-acm.c index 8d809a811e16..2d92cce260d7 100644 --- a/trunk/drivers/usb/class/cdc-acm.c +++ b/trunk/drivers/usb/class/cdc-acm.c @@ -1602,6 +1602,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */ .driver_info = NO_UNION_NORMAL, }, + { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */ + .driver_info = NO_UNION_NORMAL, + }, { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, diff --git a/trunk/drivers/usb/core/hub.c b/trunk/drivers/usb/core/hub.c index a815fd2cc5e7..957ed2c41482 100644 --- a/trunk/drivers/usb/core/hub.c +++ b/trunk/drivers/usb/core/hub.c @@ -877,6 +877,60 @@ static int hub_hub_status(struct usb_hub *hub, return ret; } +static int hub_set_port_link_state(struct usb_hub *hub, int port1, + unsigned int link_status) +{ + return set_port_feature(hub->hdev, + port1 | (link_status << 3), + USB_PORT_FEAT_LINK_STATE); +} + +/* + * If USB 3.0 ports are placed into the Disabled state, they will no longer + * detect any device connects or disconnects. This is generally not what the + * USB core wants, since it expects a disabled port to produce a port status + * change event when a new device connects. + * + * Instead, set the link state to Disabled, wait for the link to settle into + * that state, clear any change bits, and then put the port into the RxDetect + * state. + */ +static int hub_usb3_port_disable(struct usb_hub *hub, int port1) +{ + int ret; + int total_time; + u16 portchange, portstatus; + + if (!hub_is_superspeed(hub->hdev)) + return -EINVAL; + + ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED); + if (ret) { + dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", + port1, ret); + return ret; + } + + /* Wait for the link to enter the disabled state. */ + for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { + ret = hub_port_status(hub, port1, &portstatus, &portchange); + if (ret < 0) + return ret; + + if ((portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_SS_DISABLED) + break; + if (total_time >= HUB_DEBOUNCE_TIMEOUT) + break; + msleep(HUB_DEBOUNCE_STEP); + } + if (total_time >= HUB_DEBOUNCE_TIMEOUT) + dev_warn(hub->intfdev, "Could not disable port %d after %d ms\n", + port1, total_time); + + return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT); +} + static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) { struct usb_device *hdev = hub->hdev; @@ -885,8 +939,13 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) if (hub->ports[port1 - 1]->child && set_state) usb_set_device_state(hub->ports[port1 - 1]->child, USB_STATE_NOTATTACHED); - if (!hub->error && !hub_is_superspeed(hub->hdev)) - ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); + if (!hub->error) { + if (hub_is_superspeed(hub->hdev)) + ret = hub_usb3_port_disable(hub, port1); + else + ret = clear_port_feature(hdev, port1, + USB_PORT_FEAT_ENABLE); + } if (ret) dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", port1, ret); @@ -2440,7 +2499,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub) #define HUB_SHORT_RESET_TIME 10 #define HUB_BH_RESET_TIME 50 #define HUB_LONG_RESET_TIME 200 -#define HUB_RESET_TIMEOUT 500 +#define HUB_RESET_TIMEOUT 800 static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm); @@ -2475,6 +2534,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (ret < 0) return ret; + /* The port state is unknown until the reset completes. */ + if ((portstatus & USB_PORT_STAT_RESET)) + goto delay; + /* * Some buggy devices require a warm reset to be issued even * when the port appears not to be connected. @@ -2520,11 +2583,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if ((portchange & USB_PORT_STAT_C_CONNECTION)) return -ENOTCONN; - /* if we`ve finished resetting, then break out of - * the loop - */ - if (!(portstatus & USB_PORT_STAT_RESET) && - (portstatus & USB_PORT_STAT_ENABLE)) { + if ((portstatus & USB_PORT_STAT_ENABLE)) { if (hub_is_wusb(hub)) udev->speed = USB_SPEED_WIRELESS; else if (hub_is_superspeed(hub->hdev)) @@ -2538,10 +2597,15 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, return 0; } } else { - if (portchange & USB_PORT_STAT_C_BH_RESET) - return 0; + if (!(portstatus & USB_PORT_STAT_CONNECTION) || + hub_port_warm_reset_required(hub, + portstatus)) + return -ENOTCONN; + + return 0; } +delay: /* switch to the long delay after two short delay failures */ if (delay_time >= 2 * HUB_SHORT_RESET_TIME) delay = HUB_LONG_RESET_TIME; @@ -2565,14 +2629,11 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1, msleep(10 + 40); update_devnum(udev, 0); hcd = bus_to_hcd(udev->bus); - if (hcd->driver->reset_device) { - *status = hcd->driver->reset_device(hcd, udev); - if (*status < 0) { - dev_err(&udev->dev, "Cannot reset " - "HCD device state\n"); - break; - } - } + /* The xHC may think the device is already reset, + * so ignore the status. + */ + if (hcd->driver->reset_device) + hcd->driver->reset_device(hcd, udev); } /* FALL THROUGH */ case -ENOTCONN: @@ -2580,16 +2641,16 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1, clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_RESET); /* FIXME need disconnect() for NOTATTACHED device */ - if (warm) { + if (hub_is_superspeed(hub->hdev)) { clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_BH_PORT_RESET); clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_PORT_LINK_STATE); - } else { + } + if (!warm) usb_set_device_state(udev, *status ? USB_STATE_NOTATTACHED : USB_STATE_DEFAULT); - } break; } } @@ -2939,7 +3000,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) static int finish_port_resume(struct usb_device *udev) { int status = 0; - u16 devstatus; + u16 devstatus = 0; /* caller owns the udev device lock */ dev_dbg(&udev->dev, "%s\n", @@ -2984,7 +3045,13 @@ static int finish_port_resume(struct usb_device *udev) if (status) { dev_dbg(&udev->dev, "gone after usb resume? status %d\n", status); - } else if (udev->actconfig) { + /* + * There are a few quirky devices which violate the standard + * by claiming to have remote wakeup enabled after a reset, + * which crash if the feature is cleared, hence check for + * udev->reset_resume + */ + } else if (udev->actconfig && !udev->reset_resume) { le16_to_cpus(&devstatus); if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { status = usb_control_msg(udev, @@ -4638,9 +4705,14 @@ static void hub_events(void) * SS.Inactive state. */ if (hub_port_warm_reset_required(hub, portstatus)) { + int status; + dev_dbg(hub_dev, "warm reset port %d\n", i); - hub_port_reset(hub, i, NULL, + status = hub_port_reset(hub, i, NULL, HUB_BH_RESET_TIME, true); + if (status < 0) + hub_port_disable(hub, i, 1); + connect_change = 0; } if (connect_change) diff --git a/trunk/drivers/usb/core/quirks.c b/trunk/drivers/usb/core/quirks.c index fdefd9c7f7af..3113c1d71442 100644 --- a/trunk/drivers/usb/core/quirks.c +++ b/trunk/drivers/usb/core/quirks.c @@ -43,6 +43,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Microsoft LifeCam-VX700 v2.0 */ + { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Quickcam Fusion */ { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/trunk/drivers/usb/dwc3/debugfs.c b/trunk/drivers/usb/dwc3/debugfs.c index 92604b4f9712..5945aadaa1c9 100644 --- a/trunk/drivers/usb/dwc3/debugfs.c +++ b/trunk/drivers/usb/dwc3/debugfs.c @@ -56,7 +56,7 @@ #define dump_register(nm) \ { \ .name = __stringify(nm), \ - .offset = DWC3_ ##nm, \ + .offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \ } static const struct debugfs_reg32 dwc3_regs[] = { diff --git a/trunk/drivers/usb/dwc3/gadget.c b/trunk/drivers/usb/dwc3/gadget.c index 2e43b332aae8..2fdd767f8fe8 100644 --- a/trunk/drivers/usb/dwc3/gadget.c +++ b/trunk/drivers/usb/dwc3/gadget.c @@ -1605,6 +1605,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) if (epnum == 0 || epnum == 1) { dep->endpoint.maxpacket = 512; + dep->endpoint.maxburst = 1; dep->endpoint.ops = &dwc3_gadget_ep0_ops; if (!epnum) dwc->gadget.ep0 = &dep->endpoint; diff --git a/trunk/drivers/usb/gadget/amd5536udc.c b/trunk/drivers/usb/gadget/amd5536udc.c index fc0ec5e0d58e..d9f6b9372491 100644 --- a/trunk/drivers/usb/gadget/amd5536udc.c +++ b/trunk/drivers/usb/gadget/amd5536udc.c @@ -3231,7 +3231,7 @@ static int udc_pci_probe( } if (!pdev->irq) { - dev_err(&dev->pdev->dev, "irq not set\n"); + dev_err(&pdev->dev, "irq not set\n"); kfree(dev); dev = NULL; retval = -ENODEV; @@ -3250,7 +3250,7 @@ static int udc_pci_probe( dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR); if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) { - dev_dbg(&dev->pdev->dev, "request_irq(%d) fail\n", pdev->irq); + dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq); kfree(dev); dev = NULL; retval = -EBUSY; diff --git a/trunk/drivers/usb/gadget/dummy_hcd.c b/trunk/drivers/usb/gadget/dummy_hcd.c index 95d584dbed13..8cf0c0f6fa1f 100644 --- a/trunk/drivers/usb/gadget/dummy_hcd.c +++ b/trunk/drivers/usb/gadget/dummy_hcd.c @@ -130,10 +130,7 @@ static const char ep0name[] = "ep0"; static const char *const ep_name[] = { ep0name, /* everyone has ep0 */ - /* act like a net2280: high speed, six configurable endpoints */ - "ep-a", "ep-b", "ep-c", "ep-d", "ep-e", "ep-f", - - /* or like pxa250: fifteen fixed function endpoints */ + /* act like a pxa250: fifteen fixed function endpoints */ "ep1in-bulk", "ep2out-bulk", "ep3in-iso", "ep4out-iso", "ep5in-int", "ep6in-bulk", "ep7out-bulk", "ep8in-iso", "ep9out-iso", "ep10in-int", "ep11in-bulk", "ep12out-bulk", "ep13in-iso", "ep14out-iso", @@ -141,6 +138,10 @@ static const char *const ep_name[] = { /* or like sa1100: two fixed function endpoints */ "ep1out-bulk", "ep2in-bulk", + + /* and now some generic EPs so we have enough in multi config */ + "ep3out", "ep4in", "ep5out", "ep6out", "ep7in", "ep8out", "ep9in", + "ep10out", "ep11out", "ep12in", "ep13out", "ep14in", "ep15out", }; #define DUMMY_ENDPOINTS ARRAY_SIZE(ep_name) diff --git a/trunk/drivers/usb/gadget/f_fs.c b/trunk/drivers/usb/gadget/f_fs.c index 4a6961c517f2..8c2f25121149 100644 --- a/trunk/drivers/usb/gadget/f_fs.c +++ b/trunk/drivers/usb/gadget/f_fs.c @@ -1153,15 +1153,15 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) pr_err("%s: unmapped value: %lu\n", opts, value); return -EINVAL; } - } - else if (!memcmp(opts, "gid", 3)) + } else if (!memcmp(opts, "gid", 3)) { data->perms.gid = make_kgid(current_user_ns(), value); if (!gid_valid(data->perms.gid)) { pr_err("%s: unmapped value: %lu\n", opts, value); return -EINVAL; } - else + } else { goto invalid; + } break; default: diff --git a/trunk/drivers/usb/gadget/fsl_mxc_udc.c b/trunk/drivers/usb/gadget/fsl_mxc_udc.c index 1b0f086426bd..d3bd7b095ba3 100644 --- a/trunk/drivers/usb/gadget/fsl_mxc_udc.c +++ b/trunk/drivers/usb/gadget/fsl_mxc_udc.c @@ -18,14 +18,13 @@ #include #include -#include - static struct clk *mxc_ahb_clk; static struct clk *mxc_per_clk; static struct clk *mxc_ipg_clk; /* workaround ENGcm09152 for i.MX35 */ -#define USBPHYCTRL_OTGBASE_OFFSET 0x608 +#define MX35_USBPHYCTRL_OFFSET 0x600 +#define USBPHYCTRL_OTGBASE_OFFSET 0x8 #define USBPHYCTRL_EVDO (1 << 23) int fsl_udc_clk_init(struct platform_device *pdev) @@ -59,7 +58,7 @@ int fsl_udc_clk_init(struct platform_device *pdev) clk_prepare_enable(mxc_per_clk); /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */ - if (!cpu_is_mx51()) { + if (!strcmp(pdev->id_entry->name, "imx-udc-mx27")) { freq = clk_get_rate(mxc_per_clk); if (pdata->phy_mode != FSL_USB2_PHY_ULPI && (freq < 59999000 || freq > 60001000)) { @@ -79,27 +78,40 @@ int fsl_udc_clk_init(struct platform_device *pdev) return ret; } -void fsl_udc_clk_finalize(struct platform_device *pdev) +int fsl_udc_clk_finalize(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - if (cpu_is_mx35()) { - unsigned int v; + int ret = 0; - /* workaround ENGcm09152 for i.MX35 */ - if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) { - v = readl(MX35_IO_ADDRESS(MX35_USB_BASE_ADDR + - USBPHYCTRL_OTGBASE_OFFSET)); - writel(v | USBPHYCTRL_EVDO, - MX35_IO_ADDRESS(MX35_USB_BASE_ADDR + - USBPHYCTRL_OTGBASE_OFFSET)); + /* workaround ENGcm09152 for i.MX35 */ + if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) { + unsigned int v; + struct resource *res = platform_get_resource + (pdev, IORESOURCE_MEM, 0); + void __iomem *phy_regs = ioremap(res->start + + MX35_USBPHYCTRL_OFFSET, 512); + if (!phy_regs) { + dev_err(&pdev->dev, "ioremap for phy address fails\n"); + ret = -EINVAL; + goto ioremap_err; } + + v = readl(phy_regs + USBPHYCTRL_OTGBASE_OFFSET); + writel(v | USBPHYCTRL_EVDO, + phy_regs + USBPHYCTRL_OTGBASE_OFFSET); + + iounmap(phy_regs); } + +ioremap_err: /* ULPI transceivers don't need usbpll */ if (pdata->phy_mode == FSL_USB2_PHY_ULPI) { clk_disable_unprepare(mxc_per_clk); mxc_per_clk = NULL; } + + return ret; } void fsl_udc_clk_release(void) diff --git a/trunk/drivers/usb/gadget/fsl_udc_core.c b/trunk/drivers/usb/gadget/fsl_udc_core.c index c19f7f13790b..667275cb7bad 100644 --- a/trunk/drivers/usb/gadget/fsl_udc_core.c +++ b/trunk/drivers/usb/gadget/fsl_udc_core.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -2438,11 +2439,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) unsigned int i; u32 dccparams; - if (strcmp(pdev->name, driver_name)) { - VDBG("Wrong device"); - return -ENODEV; - } - udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); if (udc_controller == NULL) { ERR("malloc udc failed\n"); @@ -2547,7 +2543,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev) dr_controller_setup(udc_controller); } - fsl_udc_clk_finalize(pdev); + ret = fsl_udc_clk_finalize(pdev); + if (ret) + goto err_free_irq; /* Setup gadget structure */ udc_controller->gadget.ops = &fsl_gadget_ops; @@ -2756,22 +2754,32 @@ static int fsl_udc_otg_resume(struct device *dev) return fsl_udc_resume(NULL); } - /*------------------------------------------------------------------------- Register entry point for the peripheral controller driver --------------------------------------------------------------------------*/ - +static const struct platform_device_id fsl_udc_devtype[] = { + { + .name = "imx-udc-mx27", + }, { + .name = "imx-udc-mx51", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, fsl_udc_devtype); static struct platform_driver udc_driver = { - .remove = __exit_p(fsl_udc_remove), + .remove = __exit_p(fsl_udc_remove), + /* Just for FSL i.mx SoC currently */ + .id_table = fsl_udc_devtype, /* these suspend and resume are not usb suspend and resume */ - .suspend = fsl_udc_suspend, - .resume = fsl_udc_resume, - .driver = { - .name = (char *)driver_name, - .owner = THIS_MODULE, - /* udc suspend/resume called from OTG driver */ - .suspend = fsl_udc_otg_suspend, - .resume = fsl_udc_otg_resume, + .suspend = fsl_udc_suspend, + .resume = fsl_udc_resume, + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, + /* udc suspend/resume called from OTG driver */ + .suspend = fsl_udc_otg_suspend, + .resume = fsl_udc_otg_resume, }, }; diff --git a/trunk/drivers/usb/gadget/fsl_usb2_udc.h b/trunk/drivers/usb/gadget/fsl_usb2_udc.h index f61a967f7082..c6703bb07b23 100644 --- a/trunk/drivers/usb/gadget/fsl_usb2_udc.h +++ b/trunk/drivers/usb/gadget/fsl_usb2_udc.h @@ -592,15 +592,16 @@ static inline struct ep_queue_head *get_qh_by_ep(struct fsl_ep *ep) struct platform_device; #ifdef CONFIG_ARCH_MXC int fsl_udc_clk_init(struct platform_device *pdev); -void fsl_udc_clk_finalize(struct platform_device *pdev); +int fsl_udc_clk_finalize(struct platform_device *pdev); void fsl_udc_clk_release(void); #else static inline int fsl_udc_clk_init(struct platform_device *pdev) { return 0; } -static inline void fsl_udc_clk_finalize(struct platform_device *pdev) +static inline int fsl_udc_clk_finalize(struct platform_device *pdev) { + return 0; } static inline void fsl_udc_clk_release(void) { diff --git a/trunk/drivers/usb/gadget/mv_udc_core.c b/trunk/drivers/usb/gadget/mv_udc_core.c index 379aac7b82fc..6e8b1272ebce 100644 --- a/trunk/drivers/usb/gadget/mv_udc_core.c +++ b/trunk/drivers/usb/gadget/mv_udc_core.c @@ -1012,7 +1012,7 @@ static void udc_clock_enable(struct mv_udc *udc) unsigned int i; for (i = 0; i < udc->clknum; i++) - clk_enable(udc->clk[i]); + clk_prepare_enable(udc->clk[i]); } static void udc_clock_disable(struct mv_udc *udc) @@ -1020,7 +1020,7 @@ static void udc_clock_disable(struct mv_udc *udc) unsigned int i; for (i = 0; i < udc->clknum; i++) - clk_disable(udc->clk[i]); + clk_disable_unprepare(udc->clk[i]); } static void udc_stop(struct mv_udc *udc) diff --git a/trunk/drivers/usb/gadget/s3c-hsotg.c b/trunk/drivers/usb/gadget/s3c-hsotg.c index 141971d9051e..439c3f972f8c 100644 --- a/trunk/drivers/usb/gadget/s3c-hsotg.c +++ b/trunk/drivers/usb/gadget/s3c-hsotg.c @@ -3477,12 +3477,11 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg) /** * s3c_hsotg_release - release callback for hsotg device * @dev: Device to for which release is called + * + * Nothing to do as the resource is allocated using devm_ API. */ static void s3c_hsotg_release(struct device *dev) { - struct s3c_hsotg *hsotg = dev_get_drvdata(dev); - - kfree(hsotg); } /** diff --git a/trunk/drivers/usb/gadget/tcm_usb_gadget.c b/trunk/drivers/usb/gadget/tcm_usb_gadget.c index 4f7f76f00c74..7cacd6ae818e 100644 --- a/trunk/drivers/usb/gadget/tcm_usb_gadget.c +++ b/trunk/drivers/usb/gadget/tcm_usb_gadget.c @@ -1794,9 +1794,10 @@ static int tcm_usbg_drop_nexus(struct usbg_tpg *tpg) tpg->tpg_nexus = NULL; kfree(tv_nexus); + ret = 0; out: mutex_unlock(&tpg->tpg_mutex); - return 0; + return ret; } static ssize_t tcm_usbg_tpg_store_nexus( diff --git a/trunk/drivers/usb/gadget/u_serial.c b/trunk/drivers/usb/gadget/u_serial.c index d0f95482f40e..598dcc1212f0 100644 --- a/trunk/drivers/usb/gadget/u_serial.c +++ b/trunk/drivers/usb/gadget/u_serial.c @@ -887,7 +887,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", port->port_num, tty, file); - wake_up_interruptible(&port->port.close_wait); + wake_up(&port->port.close_wait); exit: spin_unlock_irq(&port->port_lock); } diff --git a/trunk/drivers/usb/host/Kconfig b/trunk/drivers/usb/host/Kconfig index d6bb128ce21e..3a21c5d683c0 100644 --- a/trunk/drivers/usb/host/Kconfig +++ b/trunk/drivers/usb/host/Kconfig @@ -148,7 +148,7 @@ config USB_EHCI_FSL Variation of ARC USB block used in some Freescale chips. config USB_EHCI_MXC - bool "Support for Freescale i.MX on-chip EHCI USB controller" + tristate "Support for Freescale i.MX on-chip EHCI USB controller" depends on USB_EHCI_HCD && ARCH_MXC select USB_EHCI_ROOT_HUB_TT ---help--- diff --git a/trunk/drivers/usb/host/Makefile b/trunk/drivers/usb/host/Makefile index 1eb4c3006e9e..001fbff2fdef 100644 --- a/trunk/drivers/usb/host/Makefile +++ b/trunk/drivers/usb/host/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o +obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o diff --git a/trunk/drivers/usb/host/ehci-fsl.c b/trunk/drivers/usb/host/ehci-fsl.c index fd9b5424b860..d81d2fcbff18 100644 --- a/trunk/drivers/usb/host/ehci-fsl.c +++ b/trunk/drivers/usb/host/ehci-fsl.c @@ -230,7 +230,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, switch (phy_mode) { case FSL_USB2_PHY_ULPI: - if (pdata->controller_ver) { + if (pdata->have_sysif_regs && pdata->controller_ver) { /* controller version 1.6 or above */ setbits32(non_ehci + FSL_SOC_USB_CTRL, ULPI_PHY_CLK_SEL); @@ -251,7 +251,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, portsc |= PORT_PTS_PTW; /* fall through */ case FSL_USB2_PHY_UTMI: - if (pdata->controller_ver) { + if (pdata->have_sysif_regs && pdata->controller_ver) { /* controller version 1.6 or above */ setbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN); mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI PHY CLK to @@ -267,7 +267,8 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, break; } - if (pdata->controller_ver && (phy_mode == FSL_USB2_PHY_ULPI)) { + if (pdata->have_sysif_regs && pdata->controller_ver && + (phy_mode == FSL_USB2_PHY_ULPI)) { /* check PHY_CLK_VALID to get phy clk valid */ if (!spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) & PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0)) { @@ -278,7 +279,7 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); - if (phy_mode != FSL_USB2_PHY_ULPI) + if (phy_mode != FSL_USB2_PHY_ULPI && pdata->have_sysif_regs) setbits32(non_ehci + FSL_SOC_USB_CTRL, USB_CTRL_USB_EN); return 0; diff --git a/trunk/drivers/usb/host/ehci-hcd.c b/trunk/drivers/usb/host/ehci-hcd.c index c97503bb0b0e..09537b2f1002 100644 --- a/trunk/drivers/usb/host/ehci-hcd.c +++ b/trunk/drivers/usb/host/ehci-hcd.c @@ -74,10 +74,6 @@ static const char hcd_name [] = "ehci_hcd"; #undef VERBOSE_DEBUG #undef EHCI_URB_TRACE -#ifdef DEBUG -#define EHCI_STATS -#endif - /* magic numbers that can affect system performance */ #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ @@ -1250,11 +1246,6 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_fsl_driver #endif -#ifdef CONFIG_USB_EHCI_MXC -#include "ehci-mxc.c" -#define PLATFORM_DRIVER ehci_mxc_driver -#endif - #ifdef CONFIG_USB_EHCI_SH #include "ehci-sh.c" #define PLATFORM_DRIVER ehci_hcd_sh_driver @@ -1352,7 +1343,8 @@ MODULE_LICENSE ("GPL"); #if !IS_ENABLED(CONFIG_USB_EHCI_PCI) && \ !IS_ENABLED(CONFIG_USB_EHCI_HCD_PLATFORM) && \ - !defined(CONFIG_USB_CHIPIDEA_HOST) && \ + !IS_ENABLED(CONFIG_USB_CHIPIDEA_HOST) && \ + !IS_ENABLED(CONFIG_USB_EHCI_MXC) && \ !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ diff --git a/trunk/drivers/usb/host/ehci-mv.c b/trunk/drivers/usb/host/ehci-mv.c index f7bfc0b898b9..6c56297ea16b 100644 --- a/trunk/drivers/usb/host/ehci-mv.c +++ b/trunk/drivers/usb/host/ehci-mv.c @@ -43,7 +43,7 @@ static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv) unsigned int i; for (i = 0; i < ehci_mv->clknum; i++) - clk_enable(ehci_mv->clk[i]); + clk_prepare_enable(ehci_mv->clk[i]); } static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) @@ -51,7 +51,7 @@ static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) unsigned int i; for (i = 0; i < ehci_mv->clknum; i++) - clk_disable(ehci_mv->clk[i]); + clk_disable_unprepare(ehci_mv->clk[i]); } static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) diff --git a/trunk/drivers/usb/host/ehci-mxc.c b/trunk/drivers/usb/host/ehci-mxc.c index ec7f5d2c90de..dedb80bb8d40 100644 --- a/trunk/drivers/usb/host/ehci-mxc.c +++ b/trunk/drivers/usb/host/ehci-mxc.c @@ -17,75 +17,38 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include +#include +#include #include #include #include #include #include #include +#include +#include #include #include +#include "ehci.h" + +#define DRIVER_DESC "Freescale On-Chip EHCI Host driver" + +static const char hcd_name[] = "ehci-mxc"; + #define ULPI_VIEWPORT_OFFSET 0x170 struct ehci_mxc_priv { struct clk *usbclk, *ahbclk, *phyclk; - struct usb_hcd *hcd; }; -/* called during probe() after chip reset completes */ -static int ehci_mxc_setup(struct usb_hcd *hcd) -{ - hcd->has_tt = 1; - - return ehci_setup(hcd); -} +static struct hc_driver __read_mostly ehci_mxc_hc_driver; -static const struct hc_driver ehci_mxc_hc_driver = { - .description = hcd_name, - .product_desc = "Freescale On-Chip EHCI Host Controller", - .hcd_priv_size = sizeof(struct ehci_hcd), - - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .reset = ehci_mxc_setup, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, - - /* - * scheduling support - */ - .get_frame_number = ehci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +static const struct ehci_driver_overrides ehci_mxc_overrides __initdata = { + .extra_priv_size = sizeof(struct ehci_mxc_priv), }; static int ehci_mxc_drv_probe(struct platform_device *pdev) @@ -112,12 +75,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) if (!hcd) return -ENOMEM; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto err_alloc; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "Found HC with no register addr. Check setup!\n"); @@ -135,6 +92,10 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) goto err_alloc; } + hcd->has_tt = 1; + ehci = hcd_to_ehci(hcd); + priv = (struct ehci_mxc_priv *) ehci->priv; + /* enable clocks */ priv->usbclk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(priv->usbclk)) { @@ -169,8 +130,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) mdelay(10); } - ehci = hcd_to_ehci(hcd); - /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + @@ -198,8 +157,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) } } - priv->hcd = hcd; - platform_set_drvdata(pdev, priv); + platform_set_drvdata(pdev, hcd); ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) @@ -244,8 +202,11 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) { struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data; - struct ehci_mxc_priv *priv = platform_get_drvdata(pdev); - struct usb_hcd *hcd = priv->hcd; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv; + + usb_remove_hcd(hcd); if (pdata && pdata->exit) pdata->exit(pdev); @@ -253,23 +214,20 @@ static int __exit ehci_mxc_drv_remove(struct platform_device *pdev) if (pdata->otg) usb_phy_shutdown(pdata->otg); - usb_remove_hcd(hcd); - usb_put_hcd(hcd); - platform_set_drvdata(pdev, NULL); - clk_disable_unprepare(priv->usbclk); clk_disable_unprepare(priv->ahbclk); if (priv->phyclk) clk_disable_unprepare(priv->phyclk); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); return 0; } static void ehci_mxc_drv_shutdown(struct platform_device *pdev) { - struct ehci_mxc_priv *priv = platform_get_drvdata(pdev); - struct usb_hcd *hcd = priv->hcd; + struct usb_hcd *hcd = platform_get_drvdata(pdev); if (hcd->driver->shutdown) hcd->driver->shutdown(hcd); @@ -279,9 +237,31 @@ MODULE_ALIAS("platform:mxc-ehci"); static struct platform_driver ehci_mxc_driver = { .probe = ehci_mxc_drv_probe, - .remove = __exit_p(ehci_mxc_drv_remove), + .remove = ehci_mxc_drv_remove, .shutdown = ehci_mxc_drv_shutdown, .driver = { .name = "mxc-ehci", }, }; + +static int __init ehci_mxc_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides); + return platform_driver_register(&ehci_mxc_driver); +} +module_init(ehci_mxc_init); + +static void __exit ehci_mxc_cleanup(void) +{ + platform_driver_unregister(&ehci_mxc_driver); +} +module_exit(ehci_mxc_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Sascha Hauer"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/usb/host/ehci-pci.c b/trunk/drivers/usb/host/ehci-pci.c index dabb20494826..170b9399e09f 100644 --- a/trunk/drivers/usb/host/ehci-pci.c +++ b/trunk/drivers/usb/host/ehci-pci.c @@ -200,6 +200,26 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; } + /* optional debug port, normally in the first BAR */ + temp = pci_find_capability(pdev, PCI_CAP_ID_DBG); + if (temp) { + pci_read_config_dword(pdev, temp, &temp); + temp >>= 16; + if (((temp >> 13) & 7) == 1) { + u32 hcs_params = ehci_readl(ehci, + &ehci->caps->hcs_params); + + temp &= 0x1fff; + ehci->debug = hcd->regs + temp; + temp = ehci_readl(ehci, &ehci->debug->control); + ehci_info(ehci, "debug port %d%s\n", + HCS_DEBUG_PORT(hcs_params), + (temp & DBGP_ENABLED) ? " IN USE" : ""); + if (!(temp & DBGP_ENABLED)) + ehci->debug = NULL; + } + } + retval = ehci_setup(hcd); if (retval) return retval; @@ -228,25 +248,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; } - /* optional debug port, normally in the first BAR */ - temp = pci_find_capability(pdev, 0x0a); - if (temp) { - pci_read_config_dword(pdev, temp, &temp); - temp >>= 16; - if ((temp & (3 << 13)) == (1 << 13)) { - temp &= 0x1fff; - ehci->debug = hcd->regs + temp; - temp = ehci_readl(ehci, &ehci->debug->control); - ehci_info(ehci, "debug port %d%s\n", - HCS_DEBUG_PORT(ehci->hcs_params), - (temp & DBGP_ENABLED) - ? " IN USE" - : ""); - if (!(temp & DBGP_ENABLED)) - ehci->debug = NULL; - } - } - /* at least the Genesys GL880S needs fixup here */ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); temp &= 0x0f; diff --git a/trunk/drivers/usb/host/ehci.h b/trunk/drivers/usb/host/ehci.h index 9dadc7118d68..36c3a8210595 100644 --- a/trunk/drivers/usb/host/ehci.h +++ b/trunk/drivers/usb/host/ehci.h @@ -38,6 +38,10 @@ typedef __u16 __bitwise __hc16; #endif /* statistics can be kept for tuning/monitoring */ +#ifdef DEBUG +#define EHCI_STATS +#endif + struct ehci_stats { /* irq usage */ unsigned long normal; @@ -221,6 +225,9 @@ struct ehci_hcd { /* one per controller */ #ifdef DEBUG struct dentry *debug_dir; #endif + + /* platform-specific data -- must come last */ + unsigned long priv[0] __aligned(sizeof(s64)); }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ diff --git a/trunk/drivers/usb/host/fsl-mph-dr-of.c b/trunk/drivers/usb/host/fsl-mph-dr-of.c index 5105127c1d4b..11e0b79ff9d5 100644 --- a/trunk/drivers/usb/host/fsl-mph-dr-of.c +++ b/trunk/drivers/usb/host/fsl-mph-dr-of.c @@ -142,6 +142,9 @@ static int usb_get_ver_info(struct device_node *np) return ver; } + if (of_device_is_compatible(np, "fsl,mpc5121-usb2-dr")) + return FSL_USB_VER_OLD; + if (of_device_is_compatible(np, "fsl-usb2-mph")) { if (of_device_is_compatible(np, "fsl-usb2-mph-v1.6")) ver = FSL_USB_VER_1_6; diff --git a/trunk/drivers/usb/host/imx21-hcd.c b/trunk/drivers/usb/host/imx21-hcd.c index bd6a7447ccc9..f0ebe8e7c58b 100644 --- a/trunk/drivers/usb/host/imx21-hcd.c +++ b/trunk/drivers/usb/host/imx21-hcd.c @@ -58,6 +58,7 @@ #include #include #include +#include #include "imx21-hcd.h" diff --git a/trunk/drivers/usb/host/ohci-tmio.c b/trunk/drivers/usb/host/ohci-tmio.c index d370245a4ee2..5e3a6deb62b1 100644 --- a/trunk/drivers/usb/host/ohci-tmio.c +++ b/trunk/drivers/usb/host/ohci-tmio.c @@ -128,7 +128,8 @@ static void tmio_start_hc(struct platform_device *dev) tmio_iowrite8(2, tmio->ccr + CCR_INTC); dev_info(&dev->dev, "revision %d @ 0x%08llx, irq %d\n", - tmio_ioread8(tmio->ccr + CCR_REVID), hcd->rsrc_start, hcd->irq); + tmio_ioread8(tmio->ccr + CCR_REVID), + (u64) hcd->rsrc_start, hcd->irq); } static int ohci_tmio_start(struct usb_hcd *hcd) diff --git a/trunk/drivers/usb/host/uhci-hcd.c b/trunk/drivers/usb/host/uhci-hcd.c index 4b9e9aba2665..4f64d24eebc8 100644 --- a/trunk/drivers/usb/host/uhci-hcd.c +++ b/trunk/drivers/usb/host/uhci-hcd.c @@ -447,6 +447,10 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd) return IRQ_NONE; uhci_writew(uhci, status, USBSTS); /* Clear it */ + spin_lock(&uhci->lock); + if (unlikely(!uhci->is_initialized)) /* not yet configured */ + goto done; + if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) { if (status & USBSTS_HSE) dev_err(uhci_dev(uhci), "host system error, " @@ -455,7 +459,6 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd) dev_err(uhci_dev(uhci), "host controller process " "error, something bad happened!\n"); if (status & USBSTS_HCH) { - spin_lock(&uhci->lock); if (uhci->rh_state >= UHCI_RH_RUNNING) { dev_err(uhci_dev(uhci), "host controller halted, " @@ -473,15 +476,15 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd) * pending unlinks */ mod_timer(&hcd->rh_timer, jiffies); } - spin_unlock(&uhci->lock); } } - if (status & USBSTS_RD) + if (status & USBSTS_RD) { + spin_unlock(&uhci->lock); usb_hcd_poll_rh_status(hcd); - else { - spin_lock(&uhci->lock); + } else { uhci_scan_schedule(uhci); + done: spin_unlock(&uhci->lock); } @@ -662,9 +665,9 @@ static int uhci_start(struct usb_hcd *hcd) */ mb(); + spin_lock_irq(&uhci->lock); configure_hc(uhci); uhci->is_initialized = 1; - spin_lock_irq(&uhci->lock); start_rh(uhci); spin_unlock_irq(&uhci->lock); return 0; diff --git a/trunk/drivers/usb/host/xhci-hub.c b/trunk/drivers/usb/host/xhci-hub.c index a686cf4905bb..68914429482f 100644 --- a/trunk/drivers/usb/host/xhci-hub.c +++ b/trunk/drivers/usb/host/xhci-hub.c @@ -761,12 +761,39 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_LINK_STATE: temp = xhci_readl(xhci, port_array[wIndex]); + + /* Disable port */ + if (link_state == USB_SS_PORT_LS_SS_DISABLED) { + xhci_dbg(xhci, "Disable port %d\n", wIndex); + temp = xhci_port_state_to_neutral(temp); + /* + * Clear all change bits, so that we get a new + * connection event. + */ + temp |= PORT_CSC | PORT_PEC | PORT_WRC | + PORT_OCC | PORT_RC | PORT_PLC | + PORT_CEC; + xhci_writel(xhci, temp | PORT_PE, + port_array[wIndex]); + temp = xhci_readl(xhci, port_array[wIndex]); + break; + } + + /* Put link in RxDetect (enable port) */ + if (link_state == USB_SS_PORT_LS_RX_DETECT) { + xhci_dbg(xhci, "Enable port %d\n", wIndex); + xhci_set_link_state(xhci, port_array, wIndex, + link_state); + temp = xhci_readl(xhci, port_array[wIndex]); + break; + } + /* Software should not attempt to set - * port link state above '5' (Rx.Detect) and the port + * port link state above '3' (U3) and the port * must be enabled. */ if ((temp & PORT_PE) == 0 || - (link_state > USB_SS_PORT_LS_RX_DETECT)) { + (link_state > USB_SS_PORT_LS_U3)) { xhci_warn(xhci, "Cannot set link state.\n"); goto error; } @@ -957,6 +984,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) int max_ports; __le32 __iomem **port_array; struct xhci_bus_state *bus_state; + bool reset_change = false; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -988,6 +1016,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) buf[(i + 1) / 8] |= 1 << (i + 1) % 8; status = 1; } + if ((temp & PORT_RC)) + reset_change = true; + } + if (!status && !reset_change) { + xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); } spin_unlock_irqrestore(&xhci->lock, flags); return status ? retval : 0; diff --git a/trunk/drivers/usb/host/xhci-mem.c b/trunk/drivers/usb/host/xhci-mem.c index fb51c7085ad0..35616ffbe3ae 100644 --- a/trunk/drivers/usb/host/xhci-mem.c +++ b/trunk/drivers/usb/host/xhci-mem.c @@ -1250,6 +1250,8 @@ static unsigned int xhci_microframes_to_exponent(struct usb_device *udev, static unsigned int xhci_parse_microframe_interval(struct usb_device *udev, struct usb_host_endpoint *ep) { + if (ep->desc.bInterval == 0) + return 0; return xhci_microframes_to_exponent(udev, ep, ep->desc.bInterval, 0, 15); } diff --git a/trunk/drivers/usb/host/xhci-ring.c b/trunk/drivers/usb/host/xhci-ring.c index cbb44b7b9d65..59fb5c677dbe 100644 --- a/trunk/drivers/usb/host/xhci-ring.c +++ b/trunk/drivers/usb/host/xhci-ring.c @@ -1725,6 +1725,15 @@ static void handle_port_status(struct xhci_hcd *xhci, if (bogus_port_status) return; + /* + * xHCI port-status-change events occur when the "or" of all the + * status-change bits in the portsc register changes from 0 to 1. + * New status changes won't cause an event if any other change + * bits are still set. When an event occurs, switch over to + * polling to avoid losing status changes. + */ + xhci_dbg(xhci, "%s: starting port polling.\n", __func__); + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); spin_unlock(&xhci->lock); /* Pass this up to the core */ usb_hcd_poll_rh_status(hcd); diff --git a/trunk/drivers/usb/host/xhci.c b/trunk/drivers/usb/host/xhci.c index 5c72c431bab1..f1f01a834ba7 100644 --- a/trunk/drivers/usb/host/xhci.c +++ b/trunk/drivers/usb/host/xhci.c @@ -884,6 +884,11 @@ int xhci_suspend(struct xhci_hcd *xhci) xhci->shared_hcd->state != HC_STATE_SUSPENDED) return -EINVAL; + /* Don't poll the roothubs on bus suspend. */ + xhci_dbg(xhci, "%s: stopping port polling.\n", __func__); + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + del_timer_sync(&hcd->rh_timer); + spin_lock_irq(&xhci->lock); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); @@ -1069,6 +1074,11 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) if (xhci->quirks & XHCI_COMP_MODE_QUIRK) compliance_mode_recovery_timer_init(xhci); + /* Re-enable port polling. */ + xhci_dbg(xhci, "%s: starting port polling.\n", __func__); + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); + usb_hcd_poll_rh_status(hcd); + return retval; } #endif /* CONFIG_PM */ diff --git a/trunk/drivers/usb/misc/usbtest.c b/trunk/drivers/usb/misc/usbtest.c index 7667b12f2ff5..268148de9714 100644 --- a/trunk/drivers/usb/misc/usbtest.c +++ b/trunk/drivers/usb/misc/usbtest.c @@ -2179,7 +2179,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) if (dev->out_pipe == 0 || !param->length || param->sglen < 4) break; retval = 0; - dev_info(&intf->dev, "TEST 17: unlink from %d queues of " + dev_info(&intf->dev, "TEST 24: unlink from %d queues of " "%d %d-byte writes\n", param->iterations, param->sglen, param->length); for (i = param->iterations; retval == 0 && i > 0; --i) { diff --git a/trunk/drivers/usb/musb/cppi_dma.c b/trunk/drivers/usb/musb/cppi_dma.c index 0968dd7a859d..f522000e8f06 100644 --- a/trunk/drivers/usb/musb/cppi_dma.c +++ b/trunk/drivers/usb/musb/cppi_dma.c @@ -105,7 +105,7 @@ static void cppi_reset_tx(struct cppi_tx_stateram __iomem *tx, u32 ptr) musb_writel(&tx->tx_complete, 0, ptr); } -static void __init cppi_pool_init(struct cppi *cppi, struct cppi_channel *c) +static void cppi_pool_init(struct cppi *cppi, struct cppi_channel *c) { int j; @@ -150,7 +150,7 @@ static void cppi_pool_free(struct cppi_channel *c) c->last_processed = NULL; } -static int __init cppi_controller_start(struct dma_controller *c) +static int cppi_controller_start(struct dma_controller *c) { struct cppi *controller; void __iomem *tibase; diff --git a/trunk/drivers/usb/musb/musb_core.c b/trunk/drivers/usb/musb/musb_core.c index f1c6c5470b92..fd3486745e64 100644 --- a/trunk/drivers/usb/musb/musb_core.c +++ b/trunk/drivers/usb/musb/musb_core.c @@ -2298,10 +2298,7 @@ static int __init musb_init(void) if (usb_disabled()) return 0; - pr_info("%s: version " MUSB_VERSION ", " - "?dma?" - ", " - "otg (peripheral+host)", + pr_info("%s: version " MUSB_VERSION ", ?dma?, otg (peripheral+host)\n", musb_driver_name); return platform_driver_register(&musb_driver); } diff --git a/trunk/drivers/usb/musb/musb_dsps.c b/trunk/drivers/usb/musb/musb_dsps.c index e6f2ae8368bb..f7d764de6fda 100644 --- a/trunk/drivers/usb/musb/musb_dsps.c +++ b/trunk/drivers/usb/musb/musb_dsps.c @@ -134,6 +134,11 @@ static const resource_size_t dsps_control_module_phys[] = { DSPS_AM33XX_CONTROL_MODULE_PHYS_1, }; +#define USBPHY_CM_PWRDN (1 << 0) +#define USBPHY_OTG_PWRDN (1 << 1) +#define USBPHY_OTGVDET_EN (1 << 19) +#define USBPHY_OTGSESSEND_EN (1 << 20) + /** * musb_dsps_phy_control - phy on/off * @glue: struct dsps_glue * diff --git a/trunk/drivers/usb/otg/Kconfig b/trunk/drivers/usb/otg/Kconfig index 6223062d5d1b..37962c99ff1e 100644 --- a/trunk/drivers/usb/otg/Kconfig +++ b/trunk/drivers/usb/otg/Kconfig @@ -110,7 +110,7 @@ config AB8500_USB config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" - depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 && USB_SUSPEND + depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_SUSPEND select USB_OTG select USB_OTG_UTILS help diff --git a/trunk/drivers/usb/otg/mv_otg.c b/trunk/drivers/usb/otg/mv_otg.c index 1dd57504186d..eace975991a8 100644 --- a/trunk/drivers/usb/otg/mv_otg.c +++ b/trunk/drivers/usb/otg/mv_otg.c @@ -240,7 +240,7 @@ static void otg_clock_enable(struct mv_otg *mvotg) unsigned int i; for (i = 0; i < mvotg->clknum; i++) - clk_enable(mvotg->clk[i]); + clk_prepare_enable(mvotg->clk[i]); } static void otg_clock_disable(struct mv_otg *mvotg) @@ -248,7 +248,7 @@ static void otg_clock_disable(struct mv_otg *mvotg) unsigned int i; for (i = 0; i < mvotg->clknum; i++) - clk_disable(mvotg->clk[i]); + clk_disable_unprepare(mvotg->clk[i]); } static int mv_otg_enable_internal(struct mv_otg *mvotg) diff --git a/trunk/drivers/usb/renesas_usbhs/mod_gadget.c b/trunk/drivers/usb/renesas_usbhs/mod_gadget.c index dd41f61893ef..f2985cd88021 100644 --- a/trunk/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/trunk/drivers/usb/renesas_usbhs/mod_gadget.c @@ -545,15 +545,6 @@ static int usbhsg_pipe_disable(struct usbhsg_uep *uep) return 0; } -static void usbhsg_uep_init(struct usbhsg_gpriv *gpriv) -{ - int i; - struct usbhsg_uep *uep; - - usbhsg_for_each_uep_with_dcp(uep, gpriv, i) - uep->pipe = NULL; -} - /* * * usb_ep_ops @@ -610,7 +601,12 @@ static int usbhsg_ep_disable(struct usb_ep *ep) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); - return usbhsg_pipe_disable(uep); + usbhsg_pipe_disable(uep); + + uep->pipe->mod_private = NULL; + uep->pipe = NULL; + + return 0; } static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep, @@ -761,9 +757,8 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) usbhs_pipe_init(priv, usbhsg_dma_map_ctrl); usbhs_fifo_init(priv); - usbhsg_uep_init(gpriv); - /* dcp init */ + /* dcp init instead of usbhsg_ep_enable() */ dcp->pipe = usbhs_dcp_malloc(priv); dcp->pipe->mod_private = dcp; usbhs_pipe_config_update(dcp->pipe, 0, 0, 64); @@ -825,7 +820,7 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) usbhs_sys_set_test_mode(priv, 0); usbhs_sys_function_ctrl(priv, 0); - usbhsg_pipe_disable(dcp); + usbhsg_ep_disable(&dcp->ep); dev_dbg(dev, "stop gadget\n"); @@ -998,6 +993,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) */ usbhsg_for_each_uep_with_dcp(uep, gpriv, i) { uep->gpriv = gpriv; + uep->pipe = NULL; snprintf(uep->ep_name, EP_NAME_SIZE, "ep%d", i); uep->ep.name = uep->ep_name; diff --git a/trunk/drivers/usb/renesas_usbhs/mod_host.c b/trunk/drivers/usb/renesas_usbhs/mod_host.c index 3d3cd6ca2689..b86815421c8d 100644 --- a/trunk/drivers/usb/renesas_usbhs/mod_host.c +++ b/trunk/drivers/usb/renesas_usbhs/mod_host.c @@ -661,9 +661,10 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) status = -ESHUTDOWN; urb->actual_length = pkt->actual; - usbhsh_ureq_free(hpriv, ureq); usbhsh_endpoint_sequence_save(hpriv, urb, pkt); + usbhsh_ureq_free(hpriv, ureq); + usbhsh_pipe_detach(hpriv, usbhsh_ep_to_uep(urb->ep)); usb_hcd_unlink_urb_from_ep(hcd, urb); diff --git a/trunk/drivers/usb/serial/ftdi_sio.c b/trunk/drivers/usb/serial/ftdi_sio.c index 0a373b3ae96a..ba68835d06a6 100644 --- a/trunk/drivers/usb/serial/ftdi_sio.c +++ b/trunk/drivers/usb/serial/ftdi_sio.c @@ -875,6 +875,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, + /* Crucible Devices */ + { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/trunk/drivers/usb/serial/ftdi_sio_ids.h b/trunk/drivers/usb/serial/ftdi_sio_ids.h index 049b6e715fa4..fa5d56038276 100644 --- a/trunk/drivers/usb/serial/ftdi_sio_ids.h +++ b/trunk/drivers/usb/serial/ftdi_sio_ids.h @@ -1259,3 +1259,9 @@ * ATI command output: Cinterion MC55i */ #define FTDI_CINTERION_MC55I_PID 0xA951 + +/* + * Product: Comet Caller ID decoder + * Manufacturer: Crucible Technologies + */ +#define FTDI_CT_COMET_PID 0x8e08 diff --git a/trunk/drivers/usb/serial/io_ti.c b/trunk/drivers/usb/serial/io_ti.c index 58184f3de686..82afc4d6a327 100644 --- a/trunk/drivers/usb/serial/io_ti.c +++ b/trunk/drivers/usb/serial/io_ti.c @@ -530,6 +530,9 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, wait_queue_t wait; unsigned long flags; + if (!tty) + return; + if (!timeout) timeout = (HZ * EDGE_CLOSING_WAIT)/100; diff --git a/trunk/drivers/usb/serial/option.c b/trunk/drivers/usb/serial/option.c index e6f87b76c715..0d9dac9e7f93 100644 --- a/trunk/drivers/usb/serial/option.c +++ b/trunk/drivers/usb/serial/option.c @@ -288,6 +288,7 @@ static void option_instat_callback(struct urb *urb); #define ALCATEL_VENDOR_ID 0x1bbb #define ALCATEL_PRODUCT_X060S_X200 0x0000 #define ALCATEL_PRODUCT_X220_X500D 0x0017 +#define ALCATEL_PRODUCT_L100V 0x011e #define PIRELLI_VENDOR_ID 0x1266 #define PIRELLI_PRODUCT_C100_1 0x1002 @@ -429,9 +430,12 @@ static void option_instat_callback(struct urb *urb); #define MEDIATEK_VENDOR_ID 0x0e8d #define MEDIATEK_PRODUCT_DC_1COM 0x00a0 #define MEDIATEK_PRODUCT_DC_4COM 0x00a5 +#define MEDIATEK_PRODUCT_DC_4COM2 0x00a7 #define MEDIATEK_PRODUCT_DC_5COM 0x00a4 #define MEDIATEK_PRODUCT_7208_1COM 0x7101 #define MEDIATEK_PRODUCT_7208_2COM 0x7102 +#define MEDIATEK_PRODUCT_7103_2COM 0x7103 +#define MEDIATEK_PRODUCT_7106_2COM 0x7106 #define MEDIATEK_PRODUCT_FP_1COM 0x0003 #define MEDIATEK_PRODUCT_FP_2COM 0x0023 #define MEDIATEK_PRODUCT_FPDC_1COM 0x0043 @@ -441,6 +445,14 @@ static void option_instat_callback(struct urb *urb); #define CELLIENT_VENDOR_ID 0x2692 #define CELLIENT_PRODUCT_MEN200 0x9005 +/* Hyundai Petatel Inc. products */ +#define PETATEL_VENDOR_ID 0x1ff4 +#define PETATEL_PRODUCT_NP10T 0x600e + +/* TP-LINK Incorporated products */ +#define TPLINK_VENDOR_ID 0x2357 +#define TPLINK_PRODUCT_MA180 0x0201 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -922,8 +934,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0254, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0257, 0xff, 0xff, 0xff), /* ZTE MF821 */ .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff), /* ONDA MT8205 */ + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff), /* ZTE MF880 */ + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0317, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, @@ -1190,6 +1204,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&alcatel_x200_blacklist }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D) }, + { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L100V), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) }, { USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14), @@ -1294,7 +1310,14 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FP_2COM, 0x0a, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_1COM, 0x0a, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_2COM, 0x0a, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7103_2COM, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7106_2COM, 0x02, 0x02, 0x01) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x02, 0x01) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM2, 0xff, 0x00, 0x00) }, { USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) }, + { USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T) }, + { USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/trunk/drivers/vfio/pci/vfio_pci_rdwr.c b/trunk/drivers/vfio/pci/vfio_pci_rdwr.c index 4362d9e7baa3..f72323ef618f 100644 --- a/trunk/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/trunk/drivers/vfio/pci/vfio_pci_rdwr.c @@ -240,17 +240,17 @@ ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, filled = 1; } else { /* Drop writes, fill reads with FF */ + filled = min((size_t)(x_end - pos), count); if (!iswrite) { char val = 0xFF; size_t i; - for (i = 0; i < x_end - pos; i++) { + for (i = 0; i < filled; i++) { if (put_user(val, buf + i)) goto out; } } - filled = x_end - pos; } count -= filled; diff --git a/trunk/drivers/video/imxfb.c b/trunk/drivers/video/imxfb.c index 12526787a7c7..0abf2bf20836 100644 --- a/trunk/drivers/video/imxfb.c +++ b/trunk/drivers/video/imxfb.c @@ -139,6 +139,7 @@ struct imxfb_info { struct clk *clk_ahb; struct clk *clk_per; enum imxfb_type devtype; + bool enabled; /* * These are the addresses we mapped @@ -536,6 +537,10 @@ static void imxfb_exit_backlight(struct imxfb_info *fbi) static void imxfb_enable_controller(struct imxfb_info *fbi) { + + if (fbi->enabled) + return; + pr_debug("Enabling LCD controller\n"); writel(fbi->screen_dma, fbi->regs + LCDC_SSA); @@ -556,6 +561,7 @@ static void imxfb_enable_controller(struct imxfb_info *fbi) clk_prepare_enable(fbi->clk_ipg); clk_prepare_enable(fbi->clk_ahb); clk_prepare_enable(fbi->clk_per); + fbi->enabled = true; if (fbi->backlight_power) fbi->backlight_power(1); @@ -565,6 +571,9 @@ static void imxfb_enable_controller(struct imxfb_info *fbi) static void imxfb_disable_controller(struct imxfb_info *fbi) { + if (!fbi->enabled) + return; + pr_debug("Disabling LCD controller\n"); if (fbi->backlight_power) @@ -575,6 +584,7 @@ static void imxfb_disable_controller(struct imxfb_info *fbi) clk_disable_unprepare(fbi->clk_per); clk_disable_unprepare(fbi->clk_ipg); clk_disable_unprepare(fbi->clk_ahb); + fbi->enabled = false; writel(0, fbi->regs + LCDC_RMCR); } @@ -729,6 +739,8 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev) memset(fbi, 0, sizeof(struct imxfb_info)); + fbi->devtype = pdev->id_entry->driver_data; + strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id)); info->fix.type = FB_TYPE_PACKED_PIXELS; @@ -789,7 +801,6 @@ static int __init imxfb_probe(struct platform_device *pdev) return -ENOMEM; fbi = info->par; - fbi->devtype = pdev->id_entry->driver_data; if (!fb_mode) fb_mode = pdata->mode[0].mode.name; diff --git a/trunk/drivers/video/ssd1307fb.c b/trunk/drivers/video/ssd1307fb.c index 4d99dd7a6831..395cb6a8d8f3 100644 --- a/trunk/drivers/video/ssd1307fb.c +++ b/trunk/drivers/video/ssd1307fb.c @@ -145,8 +145,8 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par) u32 page_length = SSD1307FB_WIDTH * i; u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8; u8 byte = *(vmem + index); - u8 bit = byte & (1 << (7 - (j % 8))); - bit = bit >> (7 - (j % 8)); + u8 bit = byte & (1 << (j % 8)); + bit = bit >> (j % 8); buf |= bit << k; } ssd1307fb_write_data(par->client, buf); diff --git a/trunk/drivers/xen/cpu_hotplug.c b/trunk/drivers/xen/cpu_hotplug.c index 4dcfced107f5..084041d42c9a 100644 --- a/trunk/drivers/xen/cpu_hotplug.c +++ b/trunk/drivers/xen/cpu_hotplug.c @@ -25,10 +25,10 @@ static void disable_hotplug_cpu(int cpu) static int vcpu_online(unsigned int cpu) { int err; - char dir[32], state[32]; + char dir[16], state[16]; sprintf(dir, "cpu/%u", cpu); - err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state); + err = xenbus_scanf(XBT_NIL, dir, "availability", "%15s", state); if (err != 1) { if (!xen_initial_domain()) printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); diff --git a/trunk/drivers/xen/gntdev.c b/trunk/drivers/xen/gntdev.c index 2e22df2f7a3f..3c8803feba26 100644 --- a/trunk/drivers/xen/gntdev.c +++ b/trunk/drivers/xen/gntdev.c @@ -56,10 +56,15 @@ MODULE_PARM_DESC(limit, "Maximum number of grants that may be mapped by " static atomic_t pages_mapped = ATOMIC_INIT(0); static int use_ptemod; +#define populate_freeable_maps use_ptemod struct gntdev_priv { + /* maps with visible offsets in the file descriptor */ struct list_head maps; - /* lock protects maps from concurrent changes */ + /* maps that are not visible; will be freed on munmap. + * Only populated if populate_freeable_maps == 1 */ + struct list_head freeable_maps; + /* lock protects maps and freeable_maps */ spinlock_t lock; struct mm_struct *mm; struct mmu_notifier mn; @@ -193,7 +198,7 @@ static struct grant_map *gntdev_find_map_index(struct gntdev_priv *priv, return NULL; } -static void gntdev_put_map(struct grant_map *map) +static void gntdev_put_map(struct gntdev_priv *priv, struct grant_map *map) { if (!map) return; @@ -208,6 +213,12 @@ static void gntdev_put_map(struct grant_map *map) evtchn_put(map->notify.event); } + if (populate_freeable_maps && priv) { + spin_lock(&priv->lock); + list_del(&map->next); + spin_unlock(&priv->lock); + } + if (map->pages && !use_ptemod) unmap_grant_pages(map, 0, map->count); gntdev_free_map(map); @@ -301,17 +312,10 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages) if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { int pgno = (map->notify.addr >> PAGE_SHIFT); - if (pgno >= offset && pgno < offset + pages && use_ptemod) { - void __user *tmp = (void __user *) - map->vma->vm_start + map->notify.addr; - err = copy_to_user(tmp, &err, 1); - if (err) - return -EFAULT; - map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; - } else if (pgno >= offset && pgno < offset + pages) { - uint8_t *tmp = kmap(map->pages[pgno]); + if (pgno >= offset && pgno < offset + pages) { + /* No need for kmap, pages are in lowmem */ + uint8_t *tmp = pfn_to_kaddr(page_to_pfn(map->pages[pgno])); tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; - kunmap(map->pages[pgno]); map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; } } @@ -376,11 +380,24 @@ static void gntdev_vma_open(struct vm_area_struct *vma) static void gntdev_vma_close(struct vm_area_struct *vma) { struct grant_map *map = vma->vm_private_data; + struct file *file = vma->vm_file; + struct gntdev_priv *priv = file->private_data; pr_debug("gntdev_vma_close %p\n", vma); - map->vma = NULL; + if (use_ptemod) { + /* It is possible that an mmu notifier could be running + * concurrently, so take priv->lock to ensure that the vma won't + * vanishing during the unmap_grant_pages call, since we will + * spin here until that completes. Such a concurrent call will + * not do any unmapping, since that has been done prior to + * closing the vma, but it may still iterate the unmap_ops list. + */ + spin_lock(&priv->lock); + map->vma = NULL; + spin_unlock(&priv->lock); + } vma->vm_private_data = NULL; - gntdev_put_map(map); + gntdev_put_map(priv, map); } static struct vm_operations_struct gntdev_vmops = { @@ -390,33 +407,43 @@ static struct vm_operations_struct gntdev_vmops = { /* ------------------------------------------------------------------ */ +static void unmap_if_in_range(struct grant_map *map, + unsigned long start, unsigned long end) +{ + unsigned long mstart, mend; + int err; + + if (!map->vma) + return; + if (map->vma->vm_start >= end) + return; + if (map->vma->vm_end <= start) + return; + mstart = max(start, map->vma->vm_start); + mend = min(end, map->vma->vm_end); + pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", + map->index, map->count, + map->vma->vm_start, map->vma->vm_end, + start, end, mstart, mend); + err = unmap_grant_pages(map, + (mstart - map->vma->vm_start) >> PAGE_SHIFT, + (mend - mstart) >> PAGE_SHIFT); + WARN_ON(err); +} + static void mn_invl_range_start(struct mmu_notifier *mn, struct mm_struct *mm, unsigned long start, unsigned long end) { struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); struct grant_map *map; - unsigned long mstart, mend; - int err; spin_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { - if (!map->vma) - continue; - if (map->vma->vm_start >= end) - continue; - if (map->vma->vm_end <= start) - continue; - mstart = max(start, map->vma->vm_start); - mend = min(end, map->vma->vm_end); - pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", - map->index, map->count, - map->vma->vm_start, map->vma->vm_end, - start, end, mstart, mend); - err = unmap_grant_pages(map, - (mstart - map->vma->vm_start) >> PAGE_SHIFT, - (mend - mstart) >> PAGE_SHIFT); - WARN_ON(err); + unmap_if_in_range(map, start, end); + } + list_for_each_entry(map, &priv->freeable_maps, next) { + unmap_if_in_range(map, start, end); } spin_unlock(&priv->lock); } @@ -445,6 +472,15 @@ static void mn_release(struct mmu_notifier *mn, err = unmap_grant_pages(map, /* offset */ 0, map->count); WARN_ON(err); } + list_for_each_entry(map, &priv->freeable_maps, next) { + if (!map->vma) + continue; + pr_debug("map %d+%d (%lx %lx)\n", + map->index, map->count, + map->vma->vm_start, map->vma->vm_end); + err = unmap_grant_pages(map, /* offset */ 0, map->count); + WARN_ON(err); + } spin_unlock(&priv->lock); } @@ -466,6 +502,7 @@ static int gntdev_open(struct inode *inode, struct file *flip) return -ENOMEM; INIT_LIST_HEAD(&priv->maps); + INIT_LIST_HEAD(&priv->freeable_maps); spin_lock_init(&priv->lock); if (use_ptemod) { @@ -500,8 +537,9 @@ static int gntdev_release(struct inode *inode, struct file *flip) while (!list_empty(&priv->maps)) { map = list_entry(priv->maps.next, struct grant_map, next); list_del(&map->next); - gntdev_put_map(map); + gntdev_put_map(NULL /* already removed */, map); } + WARN_ON(!list_empty(&priv->freeable_maps)); if (use_ptemod) mmu_notifier_unregister(&priv->mn, priv->mm); @@ -529,14 +567,14 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv, if (unlikely(atomic_add_return(op.count, &pages_mapped) > limit)) { pr_debug("can't map: over limit\n"); - gntdev_put_map(map); + gntdev_put_map(NULL, map); return err; } if (copy_from_user(map->grants, &u->refs, sizeof(map->grants[0]) * op.count) != 0) { - gntdev_put_map(map); - return err; + gntdev_put_map(NULL, map); + return -EFAULT; } spin_lock(&priv->lock); @@ -565,11 +603,13 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv, map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count); if (map) { list_del(&map->next); + if (populate_freeable_maps) + list_add_tail(&map->next, &priv->freeable_maps); err = 0; } spin_unlock(&priv->lock); if (map) - gntdev_put_map(map); + gntdev_put_map(priv, map); return err; } @@ -579,25 +619,31 @@ static long gntdev_ioctl_get_offset_for_vaddr(struct gntdev_priv *priv, struct ioctl_gntdev_get_offset_for_vaddr op; struct vm_area_struct *vma; struct grant_map *map; + int rv = -EINVAL; if (copy_from_user(&op, u, sizeof(op)) != 0) return -EFAULT; pr_debug("priv %p, offset for vaddr %lx\n", priv, (unsigned long)op.vaddr); + down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, op.vaddr); if (!vma || vma->vm_ops != &gntdev_vmops) - return -EINVAL; + goto out_unlock; map = vma->vm_private_data; if (!map) - return -EINVAL; + goto out_unlock; op.offset = map->index << PAGE_SHIFT; op.count = map->count; + rv = 0; - if (copy_to_user(u, &op, sizeof(op)) != 0) + out_unlock: + up_read(¤t->mm->mmap_sem); + + if (rv == 0 && copy_to_user(u, &op, sizeof(op)) != 0) return -EFAULT; - return 0; + return rv; } static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) @@ -778,7 +824,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) out_put_map: if (use_ptemod) map->vma = NULL; - gntdev_put_map(map); + gntdev_put_map(priv, map); return err; } diff --git a/trunk/drivers/xen/grant-table.c b/trunk/drivers/xen/grant-table.c index 7038de53652b..157c0ccda3ef 100644 --- a/trunk/drivers/xen/grant-table.c +++ b/trunk/drivers/xen/grant-table.c @@ -56,10 +56,6 @@ /* External tools reserve first few grant table entries. */ #define NR_RESERVED_ENTRIES 8 #define GNTTAB_LIST_END 0xffffffff -#define GREFS_PER_GRANT_FRAME \ -(grant_table_version == 1 ? \ -(PAGE_SIZE / sizeof(struct grant_entry_v1)) : \ -(PAGE_SIZE / sizeof(union grant_entry_v2))) static grant_ref_t **gnttab_list; static unsigned int nr_grant_frames; @@ -154,6 +150,7 @@ static struct gnttab_ops *gnttab_interface; static grant_status_t *grstatus; static int grant_table_version; +static int grefs_per_grant_frame; static struct gnttab_free_callback *gnttab_free_callback_list; @@ -767,12 +764,14 @@ static int grow_gnttab_list(unsigned int more_frames) unsigned int new_nr_grant_frames, extra_entries, i; unsigned int nr_glist_frames, new_nr_glist_frames; + BUG_ON(grefs_per_grant_frame == 0); + new_nr_grant_frames = nr_grant_frames + more_frames; - extra_entries = more_frames * GREFS_PER_GRANT_FRAME; + extra_entries = more_frames * grefs_per_grant_frame; - nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; + nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP; new_nr_glist_frames = - (new_nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; + (new_nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP; for (i = nr_glist_frames; i < new_nr_glist_frames; i++) { gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC); if (!gnttab_list[i]) @@ -780,12 +779,12 @@ static int grow_gnttab_list(unsigned int more_frames) } - for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames; - i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++) + for (i = grefs_per_grant_frame * nr_grant_frames; + i < grefs_per_grant_frame * new_nr_grant_frames - 1; i++) gnttab_entry(i) = i + 1; gnttab_entry(i) = gnttab_free_head; - gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames; + gnttab_free_head = grefs_per_grant_frame * nr_grant_frames; gnttab_free_count += extra_entries; nr_grant_frames = new_nr_grant_frames; @@ -957,7 +956,8 @@ EXPORT_SYMBOL_GPL(gnttab_unmap_refs); static unsigned nr_status_frames(unsigned nr_grant_frames) { - return (nr_grant_frames * GREFS_PER_GRANT_FRAME + SPP - 1) / SPP; + BUG_ON(grefs_per_grant_frame == 0); + return (nr_grant_frames * grefs_per_grant_frame + SPP - 1) / SPP; } static int gnttab_map_frames_v1(xen_pfn_t *frames, unsigned int nr_gframes) @@ -1115,6 +1115,7 @@ static void gnttab_request_version(void) rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1); if (rc == 0 && gsv.version == 2) { grant_table_version = 2; + grefs_per_grant_frame = PAGE_SIZE / sizeof(union grant_entry_v2); gnttab_interface = &gnttab_v2_ops; } else if (grant_table_version == 2) { /* @@ -1127,17 +1128,17 @@ static void gnttab_request_version(void) panic("we need grant tables version 2, but only version 1 is available"); } else { grant_table_version = 1; + grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1); gnttab_interface = &gnttab_v1_ops; } printk(KERN_INFO "Grant tables using version %d layout.\n", grant_table_version); } -int gnttab_resume(void) +static int gnttab_setup(void) { unsigned int max_nr_gframes; - gnttab_request_version(); max_nr_gframes = gnttab_max_grant_frames(); if (max_nr_gframes < nr_grant_frames) return -ENOSYS; @@ -1160,6 +1161,12 @@ int gnttab_resume(void) return 0; } +int gnttab_resume(void) +{ + gnttab_request_version(); + return gnttab_setup(); +} + int gnttab_suspend(void) { gnttab_interface->unmap_frames(); @@ -1171,9 +1178,10 @@ static int gnttab_expand(unsigned int req_entries) int rc; unsigned int cur, extra; + BUG_ON(grefs_per_grant_frame == 0); cur = nr_grant_frames; - extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) / - GREFS_PER_GRANT_FRAME); + extra = ((req_entries + (grefs_per_grant_frame-1)) / + grefs_per_grant_frame); if (cur + extra > gnttab_max_grant_frames()) return -ENOSPC; @@ -1191,21 +1199,23 @@ int gnttab_init(void) unsigned int nr_init_grefs; int ret; + gnttab_request_version(); nr_grant_frames = 1; boot_max_nr_grant_frames = __max_nr_grant_frames(); /* Determine the maximum number of frames required for the * grant reference free list on the current hypervisor. */ + BUG_ON(grefs_per_grant_frame == 0); max_nr_glist_frames = (boot_max_nr_grant_frames * - GREFS_PER_GRANT_FRAME / RPP); + grefs_per_grant_frame / RPP); gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *), GFP_KERNEL); if (gnttab_list == NULL) return -ENOMEM; - nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; + nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP; for (i = 0; i < nr_glist_frames; i++) { gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL); if (gnttab_list[i] == NULL) { @@ -1214,12 +1224,12 @@ int gnttab_init(void) } } - if (gnttab_resume() < 0) { + if (gnttab_setup() < 0) { ret = -ENODEV; goto ini_nomem; } - nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME; + nr_init_grefs = nr_grant_frames * grefs_per_grant_frame; for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++) gnttab_entry(i) = i + 1; diff --git a/trunk/drivers/xen/privcmd.c b/trunk/drivers/xen/privcmd.c index 0bbbccbb1f12..ca2b00e9d558 100644 --- a/trunk/drivers/xen/privcmd.c +++ b/trunk/drivers/xen/privcmd.c @@ -199,9 +199,6 @@ static long privcmd_ioctl_mmap(void __user *udata) LIST_HEAD(pagelist); struct mmap_mfn_state state; - if (!xen_initial_domain()) - return -EPERM; - /* We only support privcmd_ioctl_mmap_batch for auto translated. */ if (xen_feature(XENFEAT_auto_translated_physmap)) return -ENOSYS; @@ -261,11 +258,12 @@ struct mmap_batch_state { * -ENOENT if at least 1 -ENOENT has happened. */ int global_error; - /* An array for individual errors */ - int *err; + int version; /* User-space mfn array to store errors in the second pass for V1. */ xen_pfn_t __user *user_mfn; + /* User-space int array to store errors in the second pass for V2. */ + int __user *user_err; }; /* auto translated dom0 note: if domU being created is PV, then mfn is @@ -288,7 +286,19 @@ static int mmap_batch_fn(void *data, void *state) &cur_page); /* Store error code for second pass. */ - *(st->err++) = ret; + if (st->version == 1) { + if (ret < 0) { + /* + * V1 encodes the error codes in the 32bit top nibble of the + * mfn (with its known limitations vis-a-vis 64 bit callers). + */ + *mfnp |= (ret == -ENOENT) ? + PRIVCMD_MMAPBATCH_PAGED_ERROR : + PRIVCMD_MMAPBATCH_MFN_ERROR; + } + } else { /* st->version == 2 */ + *((int *) mfnp) = ret; + } /* And see if it affects the global_error. */ if (ret < 0) { @@ -305,20 +315,25 @@ static int mmap_batch_fn(void *data, void *state) return 0; } -static int mmap_return_errors_v1(void *data, void *state) +static int mmap_return_errors(void *data, void *state) { - xen_pfn_t *mfnp = data; struct mmap_batch_state *st = state; - int err = *(st->err++); - /* - * V1 encodes the error codes in the 32bit top nibble of the - * mfn (with its known limitations vis-a-vis 64 bit callers). - */ - *mfnp |= (err == -ENOENT) ? - PRIVCMD_MMAPBATCH_PAGED_ERROR : - PRIVCMD_MMAPBATCH_MFN_ERROR; - return __put_user(*mfnp, st->user_mfn++); + if (st->version == 1) { + xen_pfn_t mfnp = *((xen_pfn_t *) data); + if (mfnp & PRIVCMD_MMAPBATCH_MFN_ERROR) + return __put_user(mfnp, st->user_mfn++); + else + st->user_mfn++; + } else { /* st->version == 2 */ + int err = *((int *) data); + if (err) + return __put_user(err, st->user_err++); + else + st->user_err++; + } + + return 0; } /* Allocate pfns that are then mapped with gmfns from foreign domid. Update @@ -357,12 +372,8 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) struct vm_area_struct *vma; unsigned long nr_pages; LIST_HEAD(pagelist); - int *err_array = NULL; struct mmap_batch_state state; - if (!xen_initial_domain()) - return -EPERM; - switch (version) { case 1: if (copy_from_user(&m, udata, sizeof(struct privcmd_mmapbatch))) @@ -396,10 +407,12 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) goto out; } - err_array = kcalloc(m.num, sizeof(int), GFP_KERNEL); - if (err_array == NULL) { - ret = -ENOMEM; - goto out; + if (version == 2) { + /* Zero error array now to only copy back actual errors. */ + if (clear_user(m.err, sizeof(int) * m.num)) { + ret = -EFAULT; + goto out; + } } down_write(&mm->mmap_sem); @@ -427,7 +440,7 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) state.va = m.addr; state.index = 0; state.global_error = 0; - state.err = err_array; + state.version = version; /* mmap_batch_fn guarantees ret == 0 */ BUG_ON(traverse_pages(m.num, sizeof(xen_pfn_t), @@ -435,21 +448,14 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) up_write(&mm->mmap_sem); - if (version == 1) { - if (state.global_error) { - /* Write back errors in second pass. */ - state.user_mfn = (xen_pfn_t *)m.arr; - state.err = err_array; - ret = traverse_pages(m.num, sizeof(xen_pfn_t), - &pagelist, mmap_return_errors_v1, &state); - } else - ret = 0; - - } else if (version == 2) { - ret = __copy_to_user(m.err, err_array, m.num * sizeof(int)); - if (ret) - ret = -EFAULT; - } + if (state.global_error) { + /* Write back errors in second pass. */ + state.user_mfn = (xen_pfn_t *)m.arr; + state.user_err = m.err; + ret = traverse_pages(m.num, sizeof(xen_pfn_t), + &pagelist, mmap_return_errors, &state); + } else + ret = 0; /* If we have not had any EFAULT-like global errors then set the global * error to -ENOENT if necessary. */ @@ -457,7 +463,6 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version) ret = -ENOENT; out: - kfree(err_array); free_page_list(&pagelist); return ret; diff --git a/trunk/drivers/xen/xen-pciback/pciback.h b/trunk/drivers/xen/xen-pciback/pciback.h index a7def010eba3..f72af87640e0 100644 --- a/trunk/drivers/xen/xen-pciback/pciback.h +++ b/trunk/drivers/xen/xen-pciback/pciback.h @@ -124,7 +124,7 @@ static inline int xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, static inline void xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, struct pci_dev *dev) { - if (xen_pcibk_backend && xen_pcibk_backend->free) + if (xen_pcibk_backend && xen_pcibk_backend->release) return xen_pcibk_backend->release(pdev, dev); } diff --git a/trunk/fs/Kconfig b/trunk/fs/Kconfig index cfe512fd1caf..780725a463b1 100644 --- a/trunk/fs/Kconfig +++ b/trunk/fs/Kconfig @@ -68,16 +68,6 @@ source "fs/quota/Kconfig" source "fs/autofs4/Kconfig" source "fs/fuse/Kconfig" -config CUSE - tristate "Character device in Userspace support" - depends on FUSE_FS - help - This FUSE extension allows character devices to be - implemented in userspace. - - If you want to develop or use userspace character device - based on CUSE, answer Y or M. - config GENERIC_ACL bool select FS_POSIX_ACL diff --git a/trunk/fs/btrfs/extent-tree.c b/trunk/fs/btrfs/extent-tree.c index 521e9d4424f6..a8b8adc05070 100644 --- a/trunk/fs/btrfs/extent-tree.c +++ b/trunk/fs/btrfs/extent-tree.c @@ -3997,7 +3997,7 @@ static int reserve_metadata_bytes(struct btrfs_root *root, * We make the other tasks wait for the flush only when we can flush * all things. */ - if (ret && flush == BTRFS_RESERVE_FLUSH_ALL) { + if (ret && flush != BTRFS_RESERVE_NO_FLUSH) { flushing = true; space_info->flush = 1; } @@ -5560,7 +5560,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, int empty_cluster = 2 * 1024 * 1024; struct btrfs_space_info *space_info; int loop = 0; - int index = 0; + int index = __get_raid_index(data); int alloc_type = (data & BTRFS_BLOCK_GROUP_DATA) ? RESERVE_ALLOC_NO_ACCOUNT : RESERVE_ALLOC; bool found_uncached_bg = false; @@ -6788,11 +6788,13 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans, &wc->flags[level]); if (ret < 0) { btrfs_tree_unlock_rw(eb, path->locks[level]); + path->locks[level] = 0; return ret; } BUG_ON(wc->refs[level] == 0); if (wc->refs[level] == 1) { btrfs_tree_unlock_rw(eb, path->locks[level]); + path->locks[level] = 0; return 1; } } diff --git a/trunk/fs/btrfs/extent_map.c b/trunk/fs/btrfs/extent_map.c index f169d6b11d7f..2e8cae63d247 100644 --- a/trunk/fs/btrfs/extent_map.c +++ b/trunk/fs/btrfs/extent_map.c @@ -171,6 +171,10 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next) if (test_bit(EXTENT_FLAG_COMPRESSED, &prev->flags)) return 0; + if (test_bit(EXTENT_FLAG_LOGGING, &prev->flags) || + test_bit(EXTENT_FLAG_LOGGING, &next->flags)) + return 0; + if (extent_map_end(prev) == next->start && prev->flags == next->flags && prev->bdev == next->bdev && @@ -255,7 +259,8 @@ int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len, if (!em) goto out; - list_move(&em->list, &tree->modified_extents); + if (!test_bit(EXTENT_FLAG_LOGGING, &em->flags)) + list_move(&em->list, &tree->modified_extents); em->generation = gen; clear_bit(EXTENT_FLAG_PINNED, &em->flags); em->mod_start = em->start; @@ -280,6 +285,12 @@ int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len, } +void clear_em_logging(struct extent_map_tree *tree, struct extent_map *em) +{ + clear_bit(EXTENT_FLAG_LOGGING, &em->flags); + try_merge_map(tree, em); +} + /** * add_extent_mapping - add new extent map to the extent tree * @tree: tree to insert new map in diff --git a/trunk/fs/btrfs/extent_map.h b/trunk/fs/btrfs/extent_map.h index 922943ce29e8..c6598c89cff8 100644 --- a/trunk/fs/btrfs/extent_map.h +++ b/trunk/fs/btrfs/extent_map.h @@ -69,6 +69,7 @@ void free_extent_map(struct extent_map *em); int __init extent_map_init(void); void extent_map_exit(void); int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len, u64 gen); +void clear_em_logging(struct extent_map_tree *tree, struct extent_map *em); struct extent_map *search_extent_mapping(struct extent_map_tree *tree, u64 start, u64 len); #endif diff --git a/trunk/fs/btrfs/file-item.c b/trunk/fs/btrfs/file-item.c index bd38cef42358..94aa53b38721 100644 --- a/trunk/fs/btrfs/file-item.c +++ b/trunk/fs/btrfs/file-item.c @@ -460,8 +460,8 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode, if (!contig) offset = page_offset(bvec->bv_page) + bvec->bv_offset; - if (!contig && (offset >= ordered->file_offset + ordered->len || - offset < ordered->file_offset)) { + if (offset >= ordered->file_offset + ordered->len || + offset < ordered->file_offset) { unsigned long bytes_left; sums->len = this_sum_bytes; this_sum_bytes = 0; diff --git a/trunk/fs/btrfs/file.c b/trunk/fs/btrfs/file.c index 77061bf43edb..f76b1fd160d4 100644 --- a/trunk/fs/btrfs/file.c +++ b/trunk/fs/btrfs/file.c @@ -2241,6 +2241,7 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence) if (lockend <= lockstart) lockend = lockstart + root->sectorsize; + lockend--; len = lockend - lockstart + 1; len = max_t(u64, len, root->sectorsize); @@ -2307,9 +2308,12 @@ static int find_desired_extent(struct inode *inode, loff_t *offset, int whence) } } - *offset = start; - free_extent_map(em); - break; + if (!test_bit(EXTENT_FLAG_PREALLOC, + &em->flags)) { + *offset = start; + free_extent_map(em); + break; + } } } diff --git a/trunk/fs/btrfs/free-space-cache.c b/trunk/fs/btrfs/free-space-cache.c index 59ea2e4349c9..0be7a8742a43 100644 --- a/trunk/fs/btrfs/free-space-cache.c +++ b/trunk/fs/btrfs/free-space-cache.c @@ -1862,11 +1862,13 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group, { struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl; struct btrfs_free_space *info; - int ret = 0; + int ret; + bool re_search = false; spin_lock(&ctl->tree_lock); again: + ret = 0; if (!bytes) goto out_lock; @@ -1879,17 +1881,17 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group, info = tree_search_offset(ctl, offset_to_bitmap(ctl, offset), 1, 0); if (!info) { - /* the tree logging code might be calling us before we - * have fully loaded the free space rbtree for this - * block group. So it is possible the entry won't - * be in the rbtree yet at all. The caching code - * will make sure not to put it in the rbtree if - * the logging code has pinned it. + /* + * If we found a partial bit of our free space in a + * bitmap but then couldn't find the other part this may + * be a problem, so WARN about it. */ + WARN_ON(re_search); goto out_lock; } } + re_search = false; if (!info->bitmap) { unlink_free_space(ctl, info); if (offset == info->offset) { @@ -1935,8 +1937,10 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group, } ret = remove_from_bitmap(ctl, info, &offset, &bytes); - if (ret == -EAGAIN) + if (ret == -EAGAIN) { + re_search = true; goto again; + } BUG_ON(ret); /* logic error */ out_lock: spin_unlock(&ctl->tree_lock); diff --git a/trunk/fs/btrfs/inode.c b/trunk/fs/btrfs/inode.c index 16d9e8e191e6..cc93b23ca352 100644 --- a/trunk/fs/btrfs/inode.c +++ b/trunk/fs/btrfs/inode.c @@ -88,7 +88,7 @@ static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = { [S_IFLNK >> S_SHIFT] = BTRFS_FT_SYMLINK, }; -static int btrfs_setsize(struct inode *inode, loff_t newsize); +static int btrfs_setsize(struct inode *inode, struct iattr *attr); static int btrfs_truncate(struct inode *inode); static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent); static noinline int cow_file_range(struct inode *inode, @@ -2478,6 +2478,18 @@ int btrfs_orphan_cleanup(struct btrfs_root *root) continue; } nr_truncate++; + + /* 1 for the orphan item deletion. */ + trans = btrfs_start_transaction(root, 1); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } + ret = btrfs_orphan_add(trans, inode); + btrfs_end_transaction(trans, root); + if (ret) + goto out; + ret = btrfs_truncate(inode); } else { nr_unlink++; @@ -3665,6 +3677,7 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size) block_end - cur_offset, 0); if (IS_ERR(em)) { err = PTR_ERR(em); + em = NULL; break; } last_byte = min(extent_map_end(em), block_end); @@ -3748,16 +3761,27 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size) return err; } -static int btrfs_setsize(struct inode *inode, loff_t newsize) +static int btrfs_setsize(struct inode *inode, struct iattr *attr) { struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_trans_handle *trans; loff_t oldsize = i_size_read(inode); + loff_t newsize = attr->ia_size; + int mask = attr->ia_valid; int ret; if (newsize == oldsize) return 0; + /* + * The regular truncate() case without ATTR_CTIME and ATTR_MTIME is a + * special case where we need to update the times despite not having + * these flags set. For all other operations the VFS set these flags + * explicitly if it wants a timestamp update. + */ + if (newsize != oldsize && (!(mask & (ATTR_CTIME | ATTR_MTIME)))) + inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb); + if (newsize > oldsize) { truncate_pagecache(inode, oldsize, newsize); ret = btrfs_cont_expand(inode, oldsize, newsize); @@ -3783,9 +3807,34 @@ static int btrfs_setsize(struct inode *inode, loff_t newsize) set_bit(BTRFS_INODE_ORDERED_DATA_CLOSE, &BTRFS_I(inode)->runtime_flags); + /* + * 1 for the orphan item we're going to add + * 1 for the orphan item deletion. + */ + trans = btrfs_start_transaction(root, 2); + if (IS_ERR(trans)) + return PTR_ERR(trans); + + /* + * We need to do this in case we fail at _any_ point during the + * actual truncate. Once we do the truncate_setsize we could + * invalidate pages which forces any outstanding ordered io to + * be instantly completed which will give us extents that need + * to be truncated. If we fail to get an orphan inode down we + * could have left over extents that were never meant to live, + * so we need to garuntee from this point on that everything + * will be consistent. + */ + ret = btrfs_orphan_add(trans, inode); + btrfs_end_transaction(trans, root); + if (ret) + return ret; + /* we don't support swapfiles, so vmtruncate shouldn't fail */ truncate_setsize(inode, newsize); ret = btrfs_truncate(inode); + if (ret && inode->i_nlink) + btrfs_orphan_del(NULL, inode); } return ret; @@ -3805,7 +3854,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) return err; if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) { - err = btrfs_setsize(inode, attr->ia_size); + err = btrfs_setsize(inode, attr); if (err) return err; } @@ -5572,10 +5621,13 @@ struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *pag return em; if (em) { /* - * if our em maps to a hole, there might - * actually be delalloc bytes behind it + * if our em maps to + * - a hole or + * - a pre-alloc extent, + * there might actually be delalloc bytes behind it. */ - if (em->block_start != EXTENT_MAP_HOLE) + if (em->block_start != EXTENT_MAP_HOLE && + !test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) return em; else hole_em = em; @@ -5657,6 +5709,8 @@ struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *pag */ em->block_start = hole_em->block_start; em->block_len = hole_len; + if (test_bit(EXTENT_FLAG_PREALLOC, &hole_em->flags)) + set_bit(EXTENT_FLAG_PREALLOC, &em->flags); } else { em->start = range_start; em->len = found; @@ -6915,11 +6969,9 @@ static int btrfs_truncate(struct inode *inode) /* * 1 for the truncate slack space - * 1 for the orphan item we're going to add - * 1 for the orphan item deletion * 1 for updating the inode. */ - trans = btrfs_start_transaction(root, 4); + trans = btrfs_start_transaction(root, 2); if (IS_ERR(trans)) { err = PTR_ERR(trans); goto out; @@ -6930,12 +6982,6 @@ static int btrfs_truncate(struct inode *inode) min_size); BUG_ON(ret); - ret = btrfs_orphan_add(trans, inode); - if (ret) { - btrfs_end_transaction(trans, root); - goto out; - } - /* * setattr is responsible for setting the ordered_data_close flag, * but that is only tested during the last file release. That @@ -7004,12 +7050,6 @@ static int btrfs_truncate(struct inode *inode) ret = btrfs_orphan_del(trans, inode); if (ret) err = ret; - } else if (ret && inode->i_nlink > 0) { - /* - * Failed to do the truncate, remove us from the in memory - * orphan list. - */ - ret = btrfs_orphan_del(NULL, inode); } if (trans) { @@ -7531,41 +7571,61 @@ void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work) */ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput) { - struct list_head *head = &root->fs_info->delalloc_inodes; struct btrfs_inode *binode; struct inode *inode; struct btrfs_delalloc_work *work, *next; struct list_head works; + struct list_head splice; int ret = 0; if (root->fs_info->sb->s_flags & MS_RDONLY) return -EROFS; INIT_LIST_HEAD(&works); - + INIT_LIST_HEAD(&splice); +again: spin_lock(&root->fs_info->delalloc_lock); - while (!list_empty(head)) { - binode = list_entry(head->next, struct btrfs_inode, + list_splice_init(&root->fs_info->delalloc_inodes, &splice); + while (!list_empty(&splice)) { + binode = list_entry(splice.next, struct btrfs_inode, delalloc_inodes); + + list_del_init(&binode->delalloc_inodes); + inode = igrab(&binode->vfs_inode); if (!inode) - list_del_init(&binode->delalloc_inodes); + continue; + + list_add_tail(&binode->delalloc_inodes, + &root->fs_info->delalloc_inodes); spin_unlock(&root->fs_info->delalloc_lock); - if (inode) { - work = btrfs_alloc_delalloc_work(inode, 0, delay_iput); - if (!work) { - ret = -ENOMEM; - goto out; - } - list_add_tail(&work->list, &works); - btrfs_queue_worker(&root->fs_info->flush_workers, - &work->work); + + work = btrfs_alloc_delalloc_work(inode, 0, delay_iput); + if (unlikely(!work)) { + ret = -ENOMEM; + goto out; } + list_add_tail(&work->list, &works); + btrfs_queue_worker(&root->fs_info->flush_workers, + &work->work); + cond_resched(); spin_lock(&root->fs_info->delalloc_lock); } spin_unlock(&root->fs_info->delalloc_lock); + list_for_each_entry_safe(work, next, &works, list) { + list_del_init(&work->list); + btrfs_wait_and_free_delalloc_work(work); + } + + spin_lock(&root->fs_info->delalloc_lock); + if (!list_empty(&root->fs_info->delalloc_inodes)) { + spin_unlock(&root->fs_info->delalloc_lock); + goto again; + } + spin_unlock(&root->fs_info->delalloc_lock); + /* the filemap_flush will queue IO into the worker threads, but * we have to make sure the IO is actually started and that * ordered extents get created before we return @@ -7578,11 +7638,18 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root, int delay_iput) atomic_read(&root->fs_info->async_delalloc_pages) == 0)); } atomic_dec(&root->fs_info->async_submit_draining); + return 0; out: list_for_each_entry_safe(work, next, &works, list) { list_del_init(&work->list); btrfs_wait_and_free_delalloc_work(work); } + + if (!list_empty_careful(&splice)) { + spin_lock(&root->fs_info->delalloc_lock); + list_splice_tail(&splice, &root->fs_info->delalloc_inodes); + spin_unlock(&root->fs_info->delalloc_lock); + } return ret; } diff --git a/trunk/fs/btrfs/ioctl.c b/trunk/fs/btrfs/ioctl.c index 4b4516770f05..5b22d45d3c6a 100644 --- a/trunk/fs/btrfs/ioctl.c +++ b/trunk/fs/btrfs/ioctl.c @@ -1339,7 +1339,8 @@ static noinline int btrfs_ioctl_resize(struct file *file, if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running, 1)) { pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); - return -EINPROGRESS; + mnt_drop_write_file(file); + return -EINVAL; } mutex_lock(&root->fs_info->volume_mutex); @@ -1362,6 +1363,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, printk(KERN_INFO "btrfs: resizing devid %llu\n", (unsigned long long)devid); } + device = btrfs_find_device(root->fs_info, devid, NULL, NULL); if (!device) { printk(KERN_INFO "btrfs: resizer unable to find device %llu\n", @@ -1369,9 +1371,10 @@ static noinline int btrfs_ioctl_resize(struct file *file, ret = -EINVAL; goto out_free; } - if (device->fs_devices && device->fs_devices->seeding) { + + if (!device->writeable) { printk(KERN_INFO "btrfs: resizer unable to apply on " - "seeding device %llu\n", + "readonly device %llu\n", (unsigned long long)devid); ret = -EINVAL; goto out_free; @@ -1443,8 +1446,8 @@ static noinline int btrfs_ioctl_resize(struct file *file, kfree(vol_args); out: mutex_unlock(&root->fs_info->volume_mutex); - mnt_drop_write_file(file); atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); + mnt_drop_write_file(file); return ret; } @@ -2095,13 +2098,13 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, err = inode_permission(inode, MAY_WRITE | MAY_EXEC); if (err) goto out_dput; - - /* check if subvolume may be deleted by a non-root user */ - err = btrfs_may_delete(dir, dentry, 1); - if (err) - goto out_dput; } + /* check if subvolume may be deleted by a user */ + err = btrfs_may_delete(dir, dentry, 1); + if (err) + goto out_dput; + if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) { err = -EINVAL; goto out_dput; @@ -2183,19 +2186,20 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp) struct btrfs_ioctl_defrag_range_args *range; int ret; - if (btrfs_root_readonly(root)) - return -EROFS; + ret = mnt_want_write_file(file); + if (ret) + return ret; if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running, 1)) { pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); - return -EINPROGRESS; + mnt_drop_write_file(file); + return -EINVAL; } - ret = mnt_want_write_file(file); - if (ret) { - atomic_set(&root->fs_info->mutually_exclusive_operation_running, - 0); - return ret; + + if (btrfs_root_readonly(root)) { + ret = -EROFS; + goto out; } switch (inode->i_mode & S_IFMT) { @@ -2247,8 +2251,8 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp) ret = -EINVAL; } out: - mnt_drop_write_file(file); atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); + mnt_drop_write_file(file); return ret; } @@ -2263,7 +2267,7 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg) if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running, 1)) { pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); - return -EINPROGRESS; + return -EINVAL; } mutex_lock(&root->fs_info->volume_mutex); @@ -2300,7 +2304,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) 1)) { pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); mnt_drop_write_file(file); - return -EINPROGRESS; + return -EINVAL; } mutex_lock(&root->fs_info->volume_mutex); @@ -2316,8 +2320,8 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) kfree(vol_args); out: mutex_unlock(&root->fs_info->volume_mutex); - mnt_drop_write_file(file); atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); + mnt_drop_write_file(file); return ret; } @@ -3437,8 +3441,8 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_ioctl_balance_args *bargs; struct btrfs_balance_control *bctl; + bool need_unlock; /* for mut. excl. ops lock */ int ret; - int need_to_clear_lock = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -3447,14 +3451,61 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) if (ret) return ret; - mutex_lock(&fs_info->volume_mutex); +again: + if (!atomic_xchg(&fs_info->mutually_exclusive_operation_running, 1)) { + mutex_lock(&fs_info->volume_mutex); + mutex_lock(&fs_info->balance_mutex); + need_unlock = true; + goto locked; + } + + /* + * mut. excl. ops lock is locked. Three possibilites: + * (1) some other op is running + * (2) balance is running + * (3) balance is paused -- special case (think resume) + */ mutex_lock(&fs_info->balance_mutex); + if (fs_info->balance_ctl) { + /* this is either (2) or (3) */ + if (!atomic_read(&fs_info->balance_running)) { + mutex_unlock(&fs_info->balance_mutex); + if (!mutex_trylock(&fs_info->volume_mutex)) + goto again; + mutex_lock(&fs_info->balance_mutex); + + if (fs_info->balance_ctl && + !atomic_read(&fs_info->balance_running)) { + /* this is (3) */ + need_unlock = false; + goto locked; + } + + mutex_unlock(&fs_info->balance_mutex); + mutex_unlock(&fs_info->volume_mutex); + goto again; + } else { + /* this is (2) */ + mutex_unlock(&fs_info->balance_mutex); + ret = -EINPROGRESS; + goto out; + } + } else { + /* this is (1) */ + mutex_unlock(&fs_info->balance_mutex); + pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); + ret = -EINVAL; + goto out; + } + +locked: + BUG_ON(!atomic_read(&fs_info->mutually_exclusive_operation_running)); if (arg) { bargs = memdup_user(arg, sizeof(*bargs)); if (IS_ERR(bargs)) { ret = PTR_ERR(bargs); - goto out; + goto out_unlock; } if (bargs->flags & BTRFS_BALANCE_RESUME) { @@ -3474,13 +3525,10 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) bargs = NULL; } - if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running, - 1)) { - pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); + if (fs_info->balance_ctl) { ret = -EINPROGRESS; goto out_bargs; } - need_to_clear_lock = 1; bctl = kzalloc(sizeof(*bctl), GFP_NOFS); if (!bctl) { @@ -3501,11 +3549,17 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) } do_balance: - ret = btrfs_balance(bctl, bargs); /* - * bctl is freed in __cancel_balance or in free_fs_info if - * restriper was paused all the way until unmount + * Ownership of bctl and mutually_exclusive_operation_running + * goes to to btrfs_balance. bctl is freed in __cancel_balance, + * or, if restriper was paused all the way until unmount, in + * free_fs_info. mutually_exclusive_operation_running is + * cleared in __cancel_balance. */ + need_unlock = false; + + ret = btrfs_balance(bctl, bargs); + if (arg) { if (copy_to_user(arg, bargs, sizeof(*bargs))) ret = -EFAULT; @@ -3513,12 +3567,12 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg) out_bargs: kfree(bargs); -out: - if (need_to_clear_lock) - atomic_set(&root->fs_info->mutually_exclusive_operation_running, - 0); +out_unlock: mutex_unlock(&fs_info->balance_mutex); mutex_unlock(&fs_info->volume_mutex); + if (need_unlock) + atomic_set(&fs_info->mutually_exclusive_operation_running, 0); +out: mnt_drop_write_file(file); return ret; } @@ -3698,6 +3752,11 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg) goto drop_write; } + if (!sa->qgroupid) { + ret = -EINVAL; + goto out; + } + trans = btrfs_join_transaction(root); if (IS_ERR(trans)) { ret = PTR_ERR(trans); diff --git a/trunk/fs/btrfs/qgroup.c b/trunk/fs/btrfs/qgroup.c index fe9d02c45f8e..a5c856234323 100644 --- a/trunk/fs/btrfs/qgroup.c +++ b/trunk/fs/btrfs/qgroup.c @@ -379,6 +379,13 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info) ret = add_relation_rb(fs_info, found_key.objectid, found_key.offset); + if (ret == -ENOENT) { + printk(KERN_WARNING + "btrfs: orphan qgroup relation 0x%llx->0x%llx\n", + (unsigned long long)found_key.objectid, + (unsigned long long)found_key.offset); + ret = 0; /* ignore the error */ + } if (ret) goto out; next2: @@ -956,17 +963,28 @@ int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 qgroupid) { struct btrfs_root *quota_root; + struct btrfs_qgroup *qgroup; int ret = 0; quota_root = fs_info->quota_root; if (!quota_root) return -EINVAL; + /* check if there are no relations to this qgroup */ + spin_lock(&fs_info->qgroup_lock); + qgroup = find_qgroup_rb(fs_info, qgroupid); + if (qgroup) { + if (!list_empty(&qgroup->groups) || !list_empty(&qgroup->members)) { + spin_unlock(&fs_info->qgroup_lock); + return -EBUSY; + } + } + spin_unlock(&fs_info->qgroup_lock); + ret = del_qgroup_item(trans, quota_root, qgroupid); spin_lock(&fs_info->qgroup_lock); del_qgroup_rb(quota_root->fs_info, qgroupid); - spin_unlock(&fs_info->qgroup_lock); return ret; diff --git a/trunk/fs/btrfs/send.c b/trunk/fs/btrfs/send.c index 54454542ad40..321b7fb4e441 100644 --- a/trunk/fs/btrfs/send.c +++ b/trunk/fs/btrfs/send.c @@ -1814,8 +1814,10 @@ static int name_cache_insert(struct send_ctx *sctx, (unsigned long)nce->ino); if (!nce_head) { nce_head = kmalloc(sizeof(*nce_head), GFP_NOFS); - if (!nce_head) + if (!nce_head) { + kfree(nce); return -ENOMEM; + } INIT_LIST_HEAD(nce_head); ret = radix_tree_insert(&sctx->name_cache, nce->ino, nce_head); diff --git a/trunk/fs/btrfs/super.c b/trunk/fs/btrfs/super.c index 99545df1b86c..d8982e9601d3 100644 --- a/trunk/fs/btrfs/super.c +++ b/trunk/fs/btrfs/super.c @@ -267,7 +267,7 @@ void __btrfs_abort_transaction(struct btrfs_trans_handle *trans, function, line, errstr); return; } - trans->transaction->aborted = errno; + ACCESS_ONCE(trans->transaction->aborted) = errno; __btrfs_std_error(root->fs_info, function, line, errno, NULL); } /* diff --git a/trunk/fs/btrfs/transaction.c b/trunk/fs/btrfs/transaction.c index 87fac9a21ea5..f15494699f3b 100644 --- a/trunk/fs/btrfs/transaction.c +++ b/trunk/fs/btrfs/transaction.c @@ -1468,7 +1468,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, goto cleanup_transaction; } - if (cur_trans->aborted) { + /* Stop the commit early if ->aborted is set */ + if (unlikely(ACCESS_ONCE(cur_trans->aborted))) { ret = cur_trans->aborted; goto cleanup_transaction; } @@ -1574,6 +1575,11 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, wait_event(cur_trans->writer_wait, atomic_read(&cur_trans->num_writers) == 1); + /* ->aborted might be set after the previous check, so check it */ + if (unlikely(ACCESS_ONCE(cur_trans->aborted))) { + ret = cur_trans->aborted; + goto cleanup_transaction; + } /* * the reloc mutex makes sure that we stop * the balancing code from coming in and moving @@ -1657,6 +1663,17 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, goto cleanup_transaction; } + /* + * The tasks which save the space cache and inode cache may also + * update ->aborted, check it. + */ + if (unlikely(ACCESS_ONCE(cur_trans->aborted))) { + ret = cur_trans->aborted; + mutex_unlock(&root->fs_info->tree_log_mutex); + mutex_unlock(&root->fs_info->reloc_mutex); + goto cleanup_transaction; + } + btrfs_prepare_extent_commit(trans, root); cur_trans = root->fs_info->running_transaction; diff --git a/trunk/fs/btrfs/tree-log.c b/trunk/fs/btrfs/tree-log.c index 83186c7e45d4..9027bb1e7466 100644 --- a/trunk/fs/btrfs/tree-log.c +++ b/trunk/fs/btrfs/tree-log.c @@ -3357,6 +3357,11 @@ static int log_one_extent(struct btrfs_trans_handle *trans, if (skip_csum) return 0; + if (em->compress_type) { + csum_offset = 0; + csum_len = block_len; + } + /* block start is already adjusted for the file extent offset. */ ret = btrfs_lookup_csums_range(log->fs_info->csum_root, em->block_start + csum_offset, @@ -3410,13 +3415,13 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, em = list_entry(extents.next, struct extent_map, list); list_del_init(&em->list); - clear_bit(EXTENT_FLAG_LOGGING, &em->flags); /* * If we had an error we just need to delete everybody from our * private list. */ if (ret) { + clear_em_logging(tree, em); free_extent_map(em); continue; } @@ -3424,8 +3429,9 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans, write_unlock(&tree->lock); ret = log_one_extent(trans, inode, root, em, path); - free_extent_map(em); write_lock(&tree->lock); + clear_em_logging(tree, em); + free_extent_map(em); } WARN_ON(!list_empty(&extents)); write_unlock(&tree->lock); diff --git a/trunk/fs/btrfs/volumes.c b/trunk/fs/btrfs/volumes.c index 5cce6aa74012..15f6efdf6463 100644 --- a/trunk/fs/btrfs/volumes.c +++ b/trunk/fs/btrfs/volumes.c @@ -1431,7 +1431,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) } } else { ret = btrfs_get_bdev_and_sb(device_path, - FMODE_READ | FMODE_EXCL, + FMODE_WRITE | FMODE_EXCL, root->fs_info->bdev_holder, 0, &bdev, &bh); if (ret) @@ -2614,7 +2614,14 @@ static int chunk_usage_filter(struct btrfs_fs_info *fs_info, u64 chunk_offset, cache = btrfs_lookup_block_group(fs_info, chunk_offset); chunk_used = btrfs_block_group_used(&cache->item); - user_thresh = div_factor_fine(cache->key.offset, bargs->usage); + if (bargs->usage == 0) + user_thresh = 0; + else if (bargs->usage > 100) + user_thresh = cache->key.offset; + else + user_thresh = div_factor_fine(cache->key.offset, + bargs->usage); + if (chunk_used < user_thresh) ret = 0; @@ -2959,6 +2966,8 @@ static void __cancel_balance(struct btrfs_fs_info *fs_info) unset_balance_control(fs_info); ret = del_balance_item(fs_info->tree_root); BUG_ON(ret); + + atomic_set(&fs_info->mutually_exclusive_operation_running, 0); } void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, @@ -3138,8 +3147,10 @@ int btrfs_balance(struct btrfs_balance_control *bctl, out: if (bctl->flags & BTRFS_BALANCE_RESUME) __cancel_balance(fs_info); - else + else { kfree(bctl); + atomic_set(&fs_info->mutually_exclusive_operation_running, 0); + } return ret; } @@ -3156,7 +3167,6 @@ static int balance_kthread(void *data) ret = btrfs_balance(fs_info->balance_ctl, NULL); } - atomic_set(&fs_info->mutually_exclusive_operation_running, 0); mutex_unlock(&fs_info->balance_mutex); mutex_unlock(&fs_info->volume_mutex); @@ -3179,7 +3189,6 @@ int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info) return 0; } - WARN_ON(atomic_xchg(&fs_info->mutually_exclusive_operation_running, 1)); tsk = kthread_run(balance_kthread, fs_info, "btrfs-balance"); if (IS_ERR(tsk)) return PTR_ERR(tsk); @@ -3233,6 +3242,8 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info) btrfs_balance_sys(leaf, item, &disk_bargs); btrfs_disk_balance_args_to_cpu(&bctl->sys, &disk_bargs); + WARN_ON(atomic_xchg(&fs_info->mutually_exclusive_operation_running, 1)); + mutex_lock(&fs_info->volume_mutex); mutex_lock(&fs_info->balance_mutex); @@ -3496,7 +3507,7 @@ struct btrfs_raid_attr btrfs_raid_array[BTRFS_NR_RAID_TYPES] = { { 1, 1, 2, 2, 2, 2 /* raid1 */ }, { 1, 2, 1, 1, 1, 2 /* dup */ }, { 1, 1, 0, 2, 1, 1 /* raid0 */ }, - { 1, 1, 0, 1, 1, 1 /* single */ }, + { 1, 1, 1, 1, 1, 1 /* single */ }, }; static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, diff --git a/trunk/fs/buffer.c b/trunk/fs/buffer.c index c017a2dfb909..7a75c3e0fd58 100644 --- a/trunk/fs/buffer.c +++ b/trunk/fs/buffer.c @@ -2935,6 +2935,7 @@ static void guard_bh_eod(int rw, struct bio *bio, struct buffer_head *bh) void *kaddr = kmap_atomic(bh->b_page); memset(kaddr + bh_offset(bh) + bytes, 0, bh->b_size - bytes); kunmap_atomic(kaddr); + flush_dcache_page(bh->b_page); } } diff --git a/trunk/fs/cifs/cifs_dfs_ref.c b/trunk/fs/cifs/cifs_dfs_ref.c index ce5cbd717bfc..210fce2df308 100644 --- a/trunk/fs/cifs/cifs_dfs_ref.c +++ b/trunk/fs/cifs/cifs_dfs_ref.c @@ -226,6 +226,8 @@ char *cifs_compose_mount_options(const char *sb_mountdata, compose_mount_options_err: kfree(mountdata); mountdata = ERR_PTR(rc); + kfree(*devname); + *devname = NULL; goto compose_mount_options_out; } diff --git a/trunk/fs/cifs/connect.c b/trunk/fs/cifs/connect.c index 17c3643e5950..12b3da39733b 100644 --- a/trunk/fs/cifs/connect.c +++ b/trunk/fs/cifs/connect.c @@ -1917,7 +1917,7 @@ srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs) } case AF_INET6: { struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; - struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)&rhs; + struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs; return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr); } default: diff --git a/trunk/fs/debugfs/inode.c b/trunk/fs/debugfs/inode.c index 153bb1e42e63..a5f12b7e228d 100644 --- a/trunk/fs/debugfs/inode.c +++ b/trunk/fs/debugfs/inode.c @@ -176,7 +176,7 @@ static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts) opts->uid = uid; break; case Opt_gid: - if (match_octal(&args[0], &option)) + if (match_int(&args[0], &option)) return -EINVAL; gid = make_kgid(current_user_ns(), option); if (!gid_valid(gid)) diff --git a/trunk/fs/exec.c b/trunk/fs/exec.c index 18c45cac368f..20df02c1cc70 100644 --- a/trunk/fs/exec.c +++ b/trunk/fs/exec.c @@ -434,8 +434,9 @@ static int count(struct user_arg_ptr argv, int max) if (IS_ERR(p)) return -EFAULT; - if (i++ >= max) + if (i >= max) return -E2BIG; + ++i; if (fatal_signal_pending(current)) return -ERESTARTNOHAND; diff --git a/trunk/fs/f2fs/acl.c b/trunk/fs/f2fs/acl.c index e95b94945d5f..137af4255da6 100644 --- a/trunk/fs/f2fs/acl.c +++ b/trunk/fs/f2fs/acl.c @@ -191,15 +191,14 @@ struct posix_acl *f2fs_get_acl(struct inode *inode, int type) retval = f2fs_getxattr(inode, name_index, "", value, retval); } - if (retval < 0) { - if (retval == -ENODATA) - acl = NULL; - else - acl = ERR_PTR(retval); - } else { + if (retval > 0) acl = f2fs_acl_from_disk(value, retval); - } + else if (retval == -ENODATA) + acl = NULL; + else + acl = ERR_PTR(retval); kfree(value); + if (!IS_ERR(acl)) set_cached_acl(inode, type, acl); diff --git a/trunk/fs/f2fs/checkpoint.c b/trunk/fs/f2fs/checkpoint.c index 6ef36c37e2be..ff3c8439af87 100644 --- a/trunk/fs/f2fs/checkpoint.c +++ b/trunk/fs/f2fs/checkpoint.c @@ -214,7 +214,6 @@ void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) goto retry; } new->ino = ino; - INIT_LIST_HEAD(&new->list); /* add new_oentry into list which is sorted by inode number */ if (orphan) { @@ -772,7 +771,7 @@ void init_orphan_info(struct f2fs_sb_info *sbi) sbi->n_orphans = 0; } -int create_checkpoint_caches(void) +int __init create_checkpoint_caches(void) { orphan_entry_slab = f2fs_kmem_cache_create("f2fs_orphan_entry", sizeof(struct orphan_inode_entry), NULL); diff --git a/trunk/fs/f2fs/data.c b/trunk/fs/f2fs/data.c index 3aa5ce7cab83..7bd22a201125 100644 --- a/trunk/fs/f2fs/data.c +++ b/trunk/fs/f2fs/data.c @@ -547,6 +547,15 @@ static int f2fs_write_data_page(struct page *page, #define MAX_DESIRED_PAGES_WP 4096 +static int __f2fs_writepage(struct page *page, struct writeback_control *wbc, + void *data) +{ + struct address_space *mapping = data; + int ret = mapping->a_ops->writepage(page, wbc); + mapping_set_error(mapping, ret); + return ret; +} + static int f2fs_write_data_pages(struct address_space *mapping, struct writeback_control *wbc) { @@ -563,7 +572,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, if (!S_ISDIR(inode->i_mode)) mutex_lock(&sbi->writepages); - ret = generic_writepages(mapping, wbc); + ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); if (!S_ISDIR(inode->i_mode)) mutex_unlock(&sbi->writepages); f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL)); @@ -689,6 +698,11 @@ static int f2fs_set_data_page_dirty(struct page *page) return 0; } +static sector_t f2fs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping, block, get_data_block_ro); +} + const struct address_space_operations f2fs_dblock_aops = { .readpage = f2fs_read_data_page, .readpages = f2fs_read_data_pages, @@ -700,4 +714,5 @@ const struct address_space_operations f2fs_dblock_aops = { .invalidatepage = f2fs_invalidate_data_page, .releasepage = f2fs_release_data_page, .direct_IO = f2fs_direct_IO, + .bmap = f2fs_bmap, }; diff --git a/trunk/fs/f2fs/debug.c b/trunk/fs/f2fs/debug.c index 0e0380a588ad..c8c37307b326 100644 --- a/trunk/fs/f2fs/debug.c +++ b/trunk/fs/f2fs/debug.c @@ -26,6 +26,7 @@ static LIST_HEAD(f2fs_stat_list); static struct dentry *debugfs_root; +static DEFINE_MUTEX(f2fs_stat_mutex); static void update_general_status(struct f2fs_sb_info *sbi) { @@ -180,18 +181,14 @@ static int stat_show(struct seq_file *s, void *v) int i = 0; int j; + mutex_lock(&f2fs_stat_mutex); list_for_each_entry_safe(si, next, &f2fs_stat_list, stat_list) { - mutex_lock(&si->stat_lock); - if (!si->sbi) { - mutex_unlock(&si->stat_lock); - continue; - } update_general_status(si->sbi); seq_printf(s, "\n=====[ partition info. #%d ]=====\n", i++); - seq_printf(s, "[SB: 1] [CP: 2] [NAT: %d] [SIT: %d] ", - si->nat_area_segs, si->sit_area_segs); + seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ", + si->sit_area_segs, si->nat_area_segs); seq_printf(s, "[SSA: %d] [MAIN: %d", si->ssa_area_segs, si->main_area_segs); seq_printf(s, "(OverProv:%d Resv:%d)]\n\n", @@ -286,8 +283,8 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, "\nMemory: %u KB = static: %u + cached: %u\n", (si->base_mem + si->cache_mem) >> 10, si->base_mem >> 10, si->cache_mem >> 10); - mutex_unlock(&si->stat_lock); } + mutex_unlock(&f2fs_stat_mutex); return 0; } @@ -303,7 +300,7 @@ static const struct file_operations stat_fops = { .release = single_release, }; -static int init_stats(struct f2fs_sb_info *sbi) +int f2fs_build_stats(struct f2fs_sb_info *sbi) { struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); struct f2fs_stat_info *si; @@ -313,9 +310,6 @@ static int init_stats(struct f2fs_sb_info *sbi) return -ENOMEM; si = sbi->stat_info; - mutex_init(&si->stat_lock); - list_add_tail(&si->stat_list, &f2fs_stat_list); - si->all_area_segs = le32_to_cpu(raw_super->segment_count); si->sit_area_segs = le32_to_cpu(raw_super->segment_count_sit); si->nat_area_segs = le32_to_cpu(raw_super->segment_count_nat); @@ -325,21 +319,11 @@ static int init_stats(struct f2fs_sb_info *sbi) si->main_area_zones = si->main_area_sections / le32_to_cpu(raw_super->secs_per_zone); si->sbi = sbi; - return 0; -} -int f2fs_build_stats(struct f2fs_sb_info *sbi) -{ - int retval; - - retval = init_stats(sbi); - if (retval) - return retval; - - if (!debugfs_root) - debugfs_root = debugfs_create_dir("f2fs", NULL); + mutex_lock(&f2fs_stat_mutex); + list_add_tail(&si->stat_list, &f2fs_stat_list); + mutex_unlock(&f2fs_stat_mutex); - debugfs_create_file("status", S_IRUGO, debugfs_root, NULL, &stat_fops); return 0; } @@ -347,14 +331,22 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { struct f2fs_stat_info *si = sbi->stat_info; + mutex_lock(&f2fs_stat_mutex); list_del(&si->stat_list); - mutex_lock(&si->stat_lock); - si->sbi = NULL; - mutex_unlock(&si->stat_lock); + mutex_unlock(&f2fs_stat_mutex); + kfree(sbi->stat_info); } -void destroy_root_stats(void) +void __init f2fs_create_root_stats(void) +{ + debugfs_root = debugfs_create_dir("f2fs", NULL); + if (debugfs_root) + debugfs_create_file("status", S_IRUGO, debugfs_root, + NULL, &stat_fops); +} + +void f2fs_destroy_root_stats(void) { debugfs_remove_recursive(debugfs_root); debugfs_root = NULL; diff --git a/trunk/fs/f2fs/dir.c b/trunk/fs/f2fs/dir.c index 951ed52748f6..989980e16d0b 100644 --- a/trunk/fs/f2fs/dir.c +++ b/trunk/fs/f2fs/dir.c @@ -503,7 +503,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, } if (inode) { - inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + inode->i_ctime = CURRENT_TIME; drop_nlink(inode); if (S_ISDIR(inode->i_mode)) { drop_nlink(inode); diff --git a/trunk/fs/f2fs/f2fs.h b/trunk/fs/f2fs/f2fs.h index 13c6dfbb7183..c8e2d751ef9c 100644 --- a/trunk/fs/f2fs/f2fs.h +++ b/trunk/fs/f2fs/f2fs.h @@ -211,11 +211,11 @@ struct dnode_of_data { static inline void set_new_dnode(struct dnode_of_data *dn, struct inode *inode, struct page *ipage, struct page *npage, nid_t nid) { + memset(dn, 0, sizeof(*dn)); dn->inode = inode; dn->inode_page = ipage; dn->node_page = npage; dn->nid = nid; - dn->inode_page_locked = 0; } /* @@ -877,6 +877,8 @@ bool f2fs_empty_dir(struct inode *); * super.c */ int f2fs_sync_fs(struct super_block *, int); +extern __printf(3, 4) +void f2fs_msg(struct super_block *, const char *, const char *, ...); /* * hash.c @@ -912,7 +914,7 @@ int restore_node_summary(struct f2fs_sb_info *, unsigned int, void flush_nat_entries(struct f2fs_sb_info *); int build_node_manager(struct f2fs_sb_info *); void destroy_node_manager(struct f2fs_sb_info *); -int create_node_manager_caches(void); +int __init create_node_manager_caches(void); void destroy_node_manager_caches(void); /* @@ -964,7 +966,7 @@ void sync_dirty_dir_inodes(struct f2fs_sb_info *); void block_operations(struct f2fs_sb_info *); void write_checkpoint(struct f2fs_sb_info *, bool, bool); void init_orphan_info(struct f2fs_sb_info *); -int create_checkpoint_caches(void); +int __init create_checkpoint_caches(void); void destroy_checkpoint_caches(void); /* @@ -984,9 +986,9 @@ int do_write_data_page(struct page *); int start_gc_thread(struct f2fs_sb_info *); void stop_gc_thread(struct f2fs_sb_info *); block_t start_bidx_of_node(unsigned int); -int f2fs_gc(struct f2fs_sb_info *, int); +int f2fs_gc(struct f2fs_sb_info *); void build_gc_manager(struct f2fs_sb_info *); -int create_gc_caches(void); +int __init create_gc_caches(void); void destroy_gc_caches(void); /* @@ -1058,7 +1060,8 @@ struct f2fs_stat_info { int f2fs_build_stats(struct f2fs_sb_info *); void f2fs_destroy_stats(struct f2fs_sb_info *); -void destroy_root_stats(void); +void __init f2fs_create_root_stats(void); +void f2fs_destroy_root_stats(void); #else #define stat_inc_call_count(si) #define stat_inc_seg_count(si, type) @@ -1068,7 +1071,8 @@ void destroy_root_stats(void); static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; } static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { } -static inline void destroy_root_stats(void) { } +static inline void __init f2fs_create_root_stats(void) { } +static inline void f2fs_destroy_root_stats(void) { } #endif extern const struct file_operations f2fs_dir_operations; diff --git a/trunk/fs/f2fs/file.c b/trunk/fs/f2fs/file.c index 7f9ea9271ebe..3191b52aafb0 100644 --- a/trunk/fs/f2fs/file.c +++ b/trunk/fs/f2fs/file.c @@ -96,8 +96,9 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, } static const struct vm_operations_struct f2fs_file_vm_ops = { - .fault = filemap_fault, - .page_mkwrite = f2fs_vm_page_mkwrite, + .fault = filemap_fault, + .page_mkwrite = f2fs_vm_page_mkwrite, + .remap_pages = generic_file_remap_pages, }; static int need_to_sync_dir(struct f2fs_sb_info *sbi, struct inode *inode) @@ -137,6 +138,9 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) if (ret) return ret; + /* guarantee free sections for fsync */ + f2fs_balance_fs(sbi); + mutex_lock(&inode->i_mutex); if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) @@ -407,6 +411,8 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end) struct dnode_of_data dn; struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + f2fs_balance_fs(sbi); + mutex_lock_op(sbi, DATA_TRUNC); set_new_dnode(&dn, inode, NULL, NULL, 0); err = get_dnode_of_data(&dn, index, RDONLY_NODE); @@ -534,7 +540,6 @@ static long f2fs_fallocate(struct file *file, int mode, loff_t offset, loff_t len) { struct inode *inode = file->f_path.dentry->d_inode; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); long ret; if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) @@ -545,7 +550,10 @@ static long f2fs_fallocate(struct file *file, int mode, else ret = expand_inode_data(inode, offset, len, mode); - f2fs_balance_fs(sbi); + if (!ret) { + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + } return ret; } diff --git a/trunk/fs/f2fs/gc.c b/trunk/fs/f2fs/gc.c index b0ec721e984a..c386910dacc5 100644 --- a/trunk/fs/f2fs/gc.c +++ b/trunk/fs/f2fs/gc.c @@ -78,7 +78,7 @@ static int gc_thread_func(void *data) sbi->bg_gc++; - if (f2fs_gc(sbi, 1) == GC_NONE) + if (f2fs_gc(sbi) == GC_NONE) wait_ms = GC_THREAD_NOGC_SLEEP_TIME; else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME) wait_ms = GC_THREAD_MAX_SLEEP_TIME; @@ -424,7 +424,11 @@ static int gc_node_segment(struct f2fs_sb_info *sbi, } /* - * Calculate start block index that this node page contains + * Calculate start block index indicating the given node offset. + * Be careful, caller should give this node offset only indicating direct node + * blocks. If any node offsets, which point the other types of node blocks such + * as indirect or double indirect node blocks, are given, it must be a caller's + * bug. */ block_t start_bidx_of_node(unsigned int node_ofs) { @@ -651,62 +655,44 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, return ret; } -int f2fs_gc(struct f2fs_sb_info *sbi, int nGC) +int f2fs_gc(struct f2fs_sb_info *sbi) { - unsigned int segno; - int old_free_secs, cur_free_secs; - int gc_status, nfree; struct list_head ilist; + unsigned int segno, i; int gc_type = BG_GC; + int gc_status = GC_NONE; INIT_LIST_HEAD(&ilist); gc_more: - nfree = 0; - gc_status = GC_NONE; + if (!(sbi->sb->s_flags & MS_ACTIVE)) + goto stop; if (has_not_enough_free_secs(sbi)) - old_free_secs = reserved_sections(sbi); - else - old_free_secs = free_sections(sbi); - - while (sbi->sb->s_flags & MS_ACTIVE) { - int i; - if (has_not_enough_free_secs(sbi)) - gc_type = FG_GC; + gc_type = FG_GC; - cur_free_secs = free_sections(sbi) + nfree; + if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) + goto stop; - /* We got free space successfully. */ - if (nGC < cur_free_secs - old_free_secs) - break; - - if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) + for (i = 0; i < sbi->segs_per_sec; i++) { + /* + * do_garbage_collect will give us three gc_status: + * GC_ERROR, GC_DONE, and GC_BLOCKED. + * If GC is finished uncleanly, we have to return + * the victim to dirty segment list. + */ + gc_status = do_garbage_collect(sbi, segno + i, &ilist, gc_type); + if (gc_status != GC_DONE) break; - - for (i = 0; i < sbi->segs_per_sec; i++) { - /* - * do_garbage_collect will give us three gc_status: - * GC_ERROR, GC_DONE, and GC_BLOCKED. - * If GC is finished uncleanly, we have to return - * the victim to dirty segment list. - */ - gc_status = do_garbage_collect(sbi, segno + i, - &ilist, gc_type); - if (gc_status != GC_DONE) - goto stop; - nfree++; - } } -stop: - if (has_not_enough_free_secs(sbi) || gc_status == GC_BLOCKED) { + if (has_not_enough_free_secs(sbi)) { write_checkpoint(sbi, (gc_status == GC_BLOCKED), false); - if (nfree) + if (has_not_enough_free_secs(sbi)) goto gc_more; } +stop: mutex_unlock(&sbi->gc_mutex); put_gc_inode(&ilist); - BUG_ON(!list_empty(&ilist)); return gc_status; } @@ -715,7 +701,7 @@ void build_gc_manager(struct f2fs_sb_info *sbi) DIRTY_I(sbi)->v_ops = &default_v_ops; } -int create_gc_caches(void) +int __init create_gc_caches(void) { winode_slab = f2fs_kmem_cache_create("f2fs_gc_inodes", sizeof(struct inode_entry), NULL); diff --git a/trunk/fs/f2fs/inode.c b/trunk/fs/f2fs/inode.c index bf20b4d03214..794241777322 100644 --- a/trunk/fs/f2fs/inode.c +++ b/trunk/fs/f2fs/inode.c @@ -217,6 +217,9 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) inode->i_ino == F2FS_META_INO(sbi)) return 0; + if (wbc) + f2fs_balance_fs(sbi); + node_page = get_node_page(sbi, inode->i_ino); if (IS_ERR(node_page)) return PTR_ERR(node_page); diff --git a/trunk/fs/f2fs/node.c b/trunk/fs/f2fs/node.c index 5066bfd256c9..9bda63c9c166 100644 --- a/trunk/fs/f2fs/node.c +++ b/trunk/fs/f2fs/node.c @@ -1124,6 +1124,12 @@ static int f2fs_write_node_page(struct page *page, return 0; } +/* + * It is very important to gather dirty pages and write at once, so that we can + * submit a big bio without interfering other data writes. + * Be default, 512 pages (2MB), a segment size, is quite reasonable. + */ +#define COLLECT_DIRTY_NODES 512 static int f2fs_write_node_pages(struct address_space *mapping, struct writeback_control *wbc) { @@ -1131,17 +1137,16 @@ static int f2fs_write_node_pages(struct address_space *mapping, struct block_device *bdev = sbi->sb->s_bdev; long nr_to_write = wbc->nr_to_write; - if (wbc->for_kupdate) - return 0; - - if (get_pages(sbi, F2FS_DIRTY_NODES) == 0) - return 0; - + /* First check balancing cached NAT entries */ if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) { write_checkpoint(sbi, false, false); return 0; } + /* collect a number of dirty node pages and write together */ + if (get_pages(sbi, F2FS_DIRTY_NODES) < COLLECT_DIRTY_NODES) + return 0; + /* if mounting is failed, skip writing node pages */ wbc->nr_to_write = bio_get_nr_vecs(bdev); sync_node_pages(sbi, 0, wbc); @@ -1732,7 +1737,7 @@ void destroy_node_manager(struct f2fs_sb_info *sbi) kfree(nm_i); } -int create_node_manager_caches(void) +int __init create_node_manager_caches(void) { nat_entry_slab = f2fs_kmem_cache_create("nat_entry", sizeof(struct nat_entry), NULL); diff --git a/trunk/fs/f2fs/recovery.c b/trunk/fs/f2fs/recovery.c index b571fee677d5..f42e4060b399 100644 --- a/trunk/fs/f2fs/recovery.c +++ b/trunk/fs/f2fs/recovery.c @@ -67,7 +67,7 @@ static int recover_dentry(struct page *ipage, struct inode *inode) kunmap(page); f2fs_put_page(page, 0); } else { - f2fs_add_link(&dent, inode); + err = f2fs_add_link(&dent, inode); } iput(dir); out: @@ -151,7 +151,6 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) goto out; } - INIT_LIST_HEAD(&entry->list); list_add_tail(&entry->list, head); entry->blkaddr = blkaddr; } @@ -174,10 +173,9 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) static void destroy_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) { - struct list_head *this; - struct fsync_inode_entry *entry; - list_for_each(this, head) { - entry = list_entry(this, struct fsync_inode_entry, list); + struct fsync_inode_entry *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, head, list) { iput(entry->inode); list_del(&entry->list); kmem_cache_free(fsync_entry_slab, entry); diff --git a/trunk/fs/f2fs/segment.c b/trunk/fs/f2fs/segment.c index de6240922b0a..4b0099066582 100644 --- a/trunk/fs/f2fs/segment.c +++ b/trunk/fs/f2fs/segment.c @@ -31,7 +31,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi) */ if (has_not_enough_free_secs(sbi)) { mutex_lock(&sbi->gc_mutex); - f2fs_gc(sbi, 1); + f2fs_gc(sbi); } } diff --git a/trunk/fs/f2fs/super.c b/trunk/fs/f2fs/super.c index 08a94c814bdc..37fad04c8669 100644 --- a/trunk/fs/f2fs/super.c +++ b/trunk/fs/f2fs/super.c @@ -53,6 +53,18 @@ static match_table_t f2fs_tokens = { {Opt_err, NULL}, }; +void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + printk("%sF2FS-fs (%s): %pV\n", level, sb->s_id, &vaf); + va_end(args); +} + static void init_once(void *foo) { struct f2fs_inode_info *fi = (struct f2fs_inode_info *) foo; @@ -125,6 +137,8 @@ int f2fs_sync_fs(struct super_block *sb, int sync) if (sync) write_checkpoint(sbi, false, false); + else + f2fs_balance_fs(sbi); return 0; } @@ -247,7 +261,8 @@ static const struct export_operations f2fs_export_ops = { .get_parent = f2fs_get_parent, }; -static int parse_options(struct f2fs_sb_info *sbi, char *options) +static int parse_options(struct super_block *sb, struct f2fs_sb_info *sbi, + char *options) { substring_t args[MAX_OPT_ARGS]; char *p; @@ -286,7 +301,8 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options) break; #else case Opt_nouser_xattr: - pr_info("nouser_xattr options not supported\n"); + f2fs_msg(sb, KERN_INFO, + "nouser_xattr options not supported"); break; #endif #ifdef CONFIG_F2FS_FS_POSIX_ACL @@ -295,7 +311,7 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options) break; #else case Opt_noacl: - pr_info("noacl options not supported\n"); + f2fs_msg(sb, KERN_INFO, "noacl options not supported"); break; #endif case Opt_active_logs: @@ -309,8 +325,9 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options) set_opt(sbi, DISABLE_EXT_IDENTIFY); break; default: - pr_err("Unrecognized mount option \"%s\" or missing value\n", - p); + f2fs_msg(sb, KERN_ERR, + "Unrecognized mount option \"%s\" or missing value", + p); return -EINVAL; } } @@ -337,23 +354,36 @@ static loff_t max_file_size(unsigned bits) return result; } -static int sanity_check_raw_super(struct f2fs_super_block *raw_super) +static int sanity_check_raw_super(struct super_block *sb, + struct f2fs_super_block *raw_super) { unsigned int blocksize; - if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) + if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) { + f2fs_msg(sb, KERN_INFO, + "Magic Mismatch, valid(0x%x) - read(0x%x)", + F2FS_SUPER_MAGIC, le32_to_cpu(raw_super->magic)); return 1; + } /* Currently, support only 4KB block size */ blocksize = 1 << le32_to_cpu(raw_super->log_blocksize); - if (blocksize != PAGE_CACHE_SIZE) + if (blocksize != PAGE_CACHE_SIZE) { + f2fs_msg(sb, KERN_INFO, + "Invalid blocksize (%u), supports only 4KB\n", + blocksize); return 1; + } if (le32_to_cpu(raw_super->log_sectorsize) != - F2FS_LOG_SECTOR_SIZE) + F2FS_LOG_SECTOR_SIZE) { + f2fs_msg(sb, KERN_INFO, "Invalid log sectorsize"); return 1; + } if (le32_to_cpu(raw_super->log_sectors_per_block) != - F2FS_LOG_SECTORS_PER_BLOCK) + F2FS_LOG_SECTORS_PER_BLOCK) { + f2fs_msg(sb, KERN_INFO, "Invalid log sectors per block"); return 1; + } return 0; } @@ -413,14 +443,17 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) if (!sbi) return -ENOMEM; - /* set a temporary block size */ - if (!sb_set_blocksize(sb, F2FS_BLKSIZE)) + /* set a block size */ + if (!sb_set_blocksize(sb, F2FS_BLKSIZE)) { + f2fs_msg(sb, KERN_ERR, "unable to set blocksize"); goto free_sbi; + } /* read f2fs raw super block */ raw_super_buf = sb_bread(sb, 0); if (!raw_super_buf) { err = -EIO; + f2fs_msg(sb, KERN_ERR, "unable to read superblock"); goto free_sbi; } raw_super = (struct f2fs_super_block *) @@ -438,12 +471,14 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) set_opt(sbi, POSIX_ACL); #endif /* parse mount options */ - if (parse_options(sbi, (char *)data)) + if (parse_options(sb, sbi, (char *)data)) goto free_sb_buf; /* sanity checking of raw super */ - if (sanity_check_raw_super(raw_super)) + if (sanity_check_raw_super(sb, raw_super)) { + f2fs_msg(sb, KERN_ERR, "Can't find a valid F2FS filesystem"); goto free_sb_buf; + } sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize)); sb->s_max_links = F2FS_LINK_MAX; @@ -477,18 +512,23 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) /* get an inode for meta space */ sbi->meta_inode = f2fs_iget(sb, F2FS_META_INO(sbi)); if (IS_ERR(sbi->meta_inode)) { + f2fs_msg(sb, KERN_ERR, "Failed to read F2FS meta data inode"); err = PTR_ERR(sbi->meta_inode); goto free_sb_buf; } err = get_valid_checkpoint(sbi); - if (err) + if (err) { + f2fs_msg(sb, KERN_ERR, "Failed to get valid F2FS checkpoint"); goto free_meta_inode; + } /* sanity checking of checkpoint */ err = -EINVAL; - if (sanity_check_ckpt(raw_super, sbi->ckpt)) + if (sanity_check_ckpt(raw_super, sbi->ckpt)) { + f2fs_msg(sb, KERN_ERR, "Invalid F2FS checkpoint"); goto free_cp; + } sbi->total_valid_node_count = le32_to_cpu(sbi->ckpt->valid_node_count); @@ -502,25 +542,28 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) INIT_LIST_HEAD(&sbi->dir_inode_list); spin_lock_init(&sbi->dir_inode_lock); - /* init super block */ - if (!sb_set_blocksize(sb, sbi->blocksize)) - goto free_cp; - init_orphan_info(sbi); /* setup f2fs internal modules */ err = build_segment_manager(sbi); - if (err) + if (err) { + f2fs_msg(sb, KERN_ERR, + "Failed to initialize F2FS segment manager"); goto free_sm; + } err = build_node_manager(sbi); - if (err) + if (err) { + f2fs_msg(sb, KERN_ERR, + "Failed to initialize F2FS node manager"); goto free_nm; + } build_gc_manager(sbi); /* get an inode for node space */ sbi->node_inode = f2fs_iget(sb, F2FS_NODE_INO(sbi)); if (IS_ERR(sbi->node_inode)) { + f2fs_msg(sb, KERN_ERR, "Failed to read node inode"); err = PTR_ERR(sbi->node_inode); goto free_nm; } @@ -533,6 +576,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) /* read root inode and dentry */ root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); if (IS_ERR(root)) { + f2fs_msg(sb, KERN_ERR, "Failed to read root inode"); err = PTR_ERR(root); goto free_node_inode; } @@ -596,7 +640,7 @@ static struct file_system_type f2fs_fs_type = { .fs_flags = FS_REQUIRES_DEV, }; -static int init_inodecache(void) +static int __init init_inodecache(void) { f2fs_inode_cachep = f2fs_kmem_cache_create("f2fs_inode_cache", sizeof(struct f2fs_inode_info), NULL); @@ -631,14 +675,17 @@ static int __init init_f2fs_fs(void) err = create_checkpoint_caches(); if (err) goto fail; - return register_filesystem(&f2fs_fs_type); + err = register_filesystem(&f2fs_fs_type); + if (err) + goto fail; + f2fs_create_root_stats(); fail: return err; } static void __exit exit_f2fs_fs(void) { - destroy_root_stats(); + f2fs_destroy_root_stats(); unregister_filesystem(&f2fs_fs_type); destroy_checkpoint_caches(); destroy_gc_caches(); diff --git a/trunk/fs/f2fs/xattr.c b/trunk/fs/f2fs/xattr.c index 940136a3d3a6..8038c0496504 100644 --- a/trunk/fs/f2fs/xattr.c +++ b/trunk/fs/f2fs/xattr.c @@ -318,6 +318,8 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, if (name_len > 255 || value_len > MAX_VALUE_LEN) return -ERANGE; + f2fs_balance_fs(sbi); + mutex_lock_op(sbi, NODE_NEW); if (!fi->i_xattr_nid) { /* Allocate new attribute block */ diff --git a/trunk/fs/fuse/Kconfig b/trunk/fs/fuse/Kconfig index 0cf160a94eda..1b2f6c2c3aaf 100644 --- a/trunk/fs/fuse/Kconfig +++ b/trunk/fs/fuse/Kconfig @@ -4,12 +4,24 @@ config FUSE_FS With FUSE it is possible to implement a fully functional filesystem in a userspace program. - There's also companion library: libfuse. This library along with - utilities is available from the FUSE homepage: + There's also a companion library: libfuse2. This library is available + from the FUSE homepage: + although chances are your distribution already has that library + installed if you've installed the "fuse" package itself. See for more information. See for needed library/utility version. If you want to develop a userspace FS, or if you want to use a filesystem based on FUSE, answer Y or M. + +config CUSE + tristate "Character device in Userspace support" + depends on FUSE_FS + help + This FUSE extension allows character devices to be + implemented in userspace. + + If you want to develop or use a userspace character device + based on CUSE, answer Y or M. diff --git a/trunk/fs/fuse/cuse.c b/trunk/fs/fuse/cuse.c index ee8d55042298..e397b675b029 100644 --- a/trunk/fs/fuse/cuse.c +++ b/trunk/fs/fuse/cuse.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include @@ -63,7 +62,7 @@ struct cuse_conn { bool unrestricted_ioctl; }; -static DEFINE_SPINLOCK(cuse_lock); /* protects cuse_conntbl */ +static DEFINE_MUTEX(cuse_lock); /* protects registration */ static struct list_head cuse_conntbl[CUSE_CONNTBL_LEN]; static struct class *cuse_class; @@ -114,14 +113,14 @@ static int cuse_open(struct inode *inode, struct file *file) int rc; /* look up and get the connection */ - spin_lock(&cuse_lock); + mutex_lock(&cuse_lock); list_for_each_entry(pos, cuse_conntbl_head(devt), list) if (pos->dev->devt == devt) { fuse_conn_get(&pos->fc); cc = pos; break; } - spin_unlock(&cuse_lock); + mutex_unlock(&cuse_lock); /* dead? */ if (!cc) @@ -267,7 +266,7 @@ static int cuse_parse_one(char **pp, char *end, char **keyp, char **valp) static int cuse_parse_devinfo(char *p, size_t len, struct cuse_devinfo *devinfo) { char *end = p + len; - char *key, *val; + char *uninitialized_var(key), *uninitialized_var(val); int rc; while (true) { @@ -305,14 +304,14 @@ static void cuse_gendev_release(struct device *dev) */ static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req) { - struct cuse_conn *cc = fc_to_cc(fc); + struct cuse_conn *cc = fc_to_cc(fc), *pos; struct cuse_init_out *arg = req->out.args[0].value; struct page *page = req->pages[0]; struct cuse_devinfo devinfo = { }; struct device *dev; struct cdev *cdev; dev_t devt; - int rc; + int rc, i; if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION || arg->minor < 11) { @@ -356,15 +355,24 @@ static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req) dev_set_drvdata(dev, cc); dev_set_name(dev, "%s", devinfo.name); + mutex_lock(&cuse_lock); + + /* make sure the device-name is unique */ + for (i = 0; i < CUSE_CONNTBL_LEN; ++i) { + list_for_each_entry(pos, &cuse_conntbl[i], list) + if (!strcmp(dev_name(pos->dev), dev_name(dev))) + goto err_unlock; + } + rc = device_add(dev); if (rc) - goto err_device; + goto err_unlock; /* register cdev */ rc = -ENOMEM; cdev = cdev_alloc(); if (!cdev) - goto err_device; + goto err_unlock; cdev->owner = THIS_MODULE; cdev->ops = &cuse_frontend_fops; @@ -377,9 +385,8 @@ static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req) cc->cdev = cdev; /* make the device available */ - spin_lock(&cuse_lock); list_add(&cc->list, cuse_conntbl_head(devt)); - spin_unlock(&cuse_lock); + mutex_unlock(&cuse_lock); /* announce device availability */ dev_set_uevent_suppress(dev, 0); @@ -391,7 +398,8 @@ static void cuse_process_init_reply(struct fuse_conn *fc, struct fuse_req *req) err_cdev: cdev_del(cdev); -err_device: +err_unlock: + mutex_unlock(&cuse_lock); put_device(dev); err_region: unregister_chrdev_region(devt, 1); @@ -520,9 +528,9 @@ static int cuse_channel_release(struct inode *inode, struct file *file) int rc; /* remove from the conntbl, no more access from this point on */ - spin_lock(&cuse_lock); + mutex_lock(&cuse_lock); list_del_init(&cc->list); - spin_unlock(&cuse_lock); + mutex_unlock(&cuse_lock); /* remove device */ if (cc->dev) diff --git a/trunk/fs/fuse/dev.c b/trunk/fs/fuse/dev.c index c16335315e5d..e83351aa5bad 100644 --- a/trunk/fs/fuse/dev.c +++ b/trunk/fs/fuse/dev.c @@ -692,8 +692,6 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) struct page *oldpage = *pagep; struct page *newpage; struct pipe_buffer *buf = cs->pipebufs; - struct address_space *mapping; - pgoff_t index; unlock_request(cs->fc, cs->req); fuse_copy_finish(cs); @@ -724,9 +722,6 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) if (fuse_check_page(newpage) != 0) goto out_fallback_unlock; - mapping = oldpage->mapping; - index = oldpage->index; - /* * This is a new and locked page, it shouldn't be mapped or * have any special flags on it diff --git a/trunk/fs/fuse/file.c b/trunk/fs/fuse/file.c index e21d4d8f87e3..f3ab824fa302 100644 --- a/trunk/fs/fuse/file.c +++ b/trunk/fs/fuse/file.c @@ -2177,8 +2177,8 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, return ret; } -long fuse_file_fallocate(struct file *file, int mode, loff_t offset, - loff_t length) +static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, + loff_t length) { struct fuse_file *ff = file->private_data; struct fuse_conn *fc = ff->fc; @@ -2213,7 +2213,6 @@ long fuse_file_fallocate(struct file *file, int mode, loff_t offset, return err; } -EXPORT_SYMBOL_GPL(fuse_file_fallocate); static const struct file_operations fuse_file_operations = { .llseek = fuse_file_llseek, diff --git a/trunk/fs/jbd/journal.c b/trunk/fs/jbd/journal.c index a2862339323b..81cc7eaff863 100644 --- a/trunk/fs/jbd/journal.c +++ b/trunk/fs/jbd/journal.c @@ -446,7 +446,8 @@ int __log_start_commit(journal_t *journal, tid_t target) * currently running transaction (if it exists). Otherwise, * the target tid must be an old one. */ - if (journal->j_running_transaction && + if (journal->j_commit_request != target && + journal->j_running_transaction && journal->j_running_transaction->t_tid == target) { /* * We want a new commit: OK, mark the request and wakeup the diff --git a/trunk/fs/seq_file.c b/trunk/fs/seq_file.c index 9d863fb501f9..f2bc3dfd0b88 100644 --- a/trunk/fs/seq_file.c +++ b/trunk/fs/seq_file.c @@ -296,7 +296,7 @@ EXPORT_SYMBOL(seq_read); * seq_lseek - ->llseek() method for sequential files. * @file: the file in question * @offset: new position - * @origin: 0 for absolute, 1 for relative position + * @whence: 0 for absolute, 1 for relative position * * Ready-made ->f_op->llseek() */ diff --git a/trunk/fs/udf/super.c b/trunk/fs/udf/super.c index d44fb568abe1..e9be396a558d 100644 --- a/trunk/fs/udf/super.c +++ b/trunk/fs/udf/super.c @@ -307,7 +307,8 @@ static void udf_sb_free_partitions(struct super_block *sb) { struct udf_sb_info *sbi = UDF_SB(sb); int i; - + if (sbi->s_partmaps == NULL) + return; for (i = 0; i < sbi->s_partitions; i++) udf_free_partition(&sbi->s_partmaps[i]); kfree(sbi->s_partmaps); diff --git a/trunk/fs/xfs/xfs_buf.c b/trunk/fs/xfs/xfs_buf.c index 26673a0b20e7..56d1614760cf 100644 --- a/trunk/fs/xfs/xfs_buf.c +++ b/trunk/fs/xfs/xfs_buf.c @@ -175,7 +175,7 @@ xfs_buf_get_maps( bp->b_map_count = map_count; if (map_count == 1) { - bp->b_maps = &bp->b_map; + bp->b_maps = &bp->__b_map; return 0; } @@ -193,7 +193,7 @@ static void xfs_buf_free_maps( struct xfs_buf *bp) { - if (bp->b_maps != &bp->b_map) { + if (bp->b_maps != &bp->__b_map) { kmem_free(bp->b_maps); bp->b_maps = NULL; } @@ -377,8 +377,8 @@ xfs_buf_allocate_memory( } use_alloc_page: - start = BBTOB(bp->b_map.bm_bn) >> PAGE_SHIFT; - end = (BBTOB(bp->b_map.bm_bn + bp->b_length) + PAGE_SIZE - 1) + start = BBTOB(bp->b_maps[0].bm_bn) >> PAGE_SHIFT; + end = (BBTOB(bp->b_maps[0].bm_bn + bp->b_length) + PAGE_SIZE - 1) >> PAGE_SHIFT; page_count = end - start; error = _xfs_buf_get_pages(bp, page_count, flags); @@ -640,7 +640,7 @@ _xfs_buf_read( xfs_buf_flags_t flags) { ASSERT(!(flags & XBF_WRITE)); - ASSERT(bp->b_map.bm_bn != XFS_BUF_DADDR_NULL); + ASSERT(bp->b_maps[0].bm_bn != XFS_BUF_DADDR_NULL); bp->b_flags &= ~(XBF_WRITE | XBF_ASYNC | XBF_READ_AHEAD); bp->b_flags |= flags & (XBF_READ | XBF_ASYNC | XBF_READ_AHEAD); @@ -1709,7 +1709,7 @@ xfs_buf_cmp( struct xfs_buf *bp = container_of(b, struct xfs_buf, b_list); xfs_daddr_t diff; - diff = ap->b_map.bm_bn - bp->b_map.bm_bn; + diff = ap->b_maps[0].bm_bn - bp->b_maps[0].bm_bn; if (diff < 0) return -1; if (diff > 0) diff --git a/trunk/fs/xfs/xfs_buf.h b/trunk/fs/xfs/xfs_buf.h index 23f5642480bb..433a12ed7b17 100644 --- a/trunk/fs/xfs/xfs_buf.h +++ b/trunk/fs/xfs/xfs_buf.h @@ -151,7 +151,7 @@ typedef struct xfs_buf { struct page **b_pages; /* array of page pointers */ struct page *b_page_array[XB_PAGES]; /* inline pages */ struct xfs_buf_map *b_maps; /* compound buffer map */ - struct xfs_buf_map b_map; /* inline compound buffer map */ + struct xfs_buf_map __b_map; /* inline compound buffer map */ int b_map_count; int b_io_length; /* IO size in BBs */ atomic_t b_pin_count; /* pin count */ @@ -330,8 +330,8 @@ void xfs_buf_stale(struct xfs_buf *bp); * In future, uncached buffers will pass the block number directly to the io * request function and hence these macros will go away at that point. */ -#define XFS_BUF_ADDR(bp) ((bp)->b_map.bm_bn) -#define XFS_BUF_SET_ADDR(bp, bno) ((bp)->b_map.bm_bn = (xfs_daddr_t)(bno)) +#define XFS_BUF_ADDR(bp) ((bp)->b_maps[0].bm_bn) +#define XFS_BUF_SET_ADDR(bp, bno) ((bp)->b_maps[0].bm_bn = (xfs_daddr_t)(bno)) static inline void xfs_buf_set_ref(struct xfs_buf *bp, int lru_ref) { diff --git a/trunk/fs/xfs/xfs_buf_item.c b/trunk/fs/xfs/xfs_buf_item.c index becf4a97efc6..77b09750e92c 100644 --- a/trunk/fs/xfs/xfs_buf_item.c +++ b/trunk/fs/xfs/xfs_buf_item.c @@ -71,7 +71,7 @@ xfs_buf_item_log_debug( chunk_num = byte >> XFS_BLF_SHIFT; word_num = chunk_num >> BIT_TO_WORD_SHIFT; bit_num = chunk_num & (NBWORD - 1); - wordp = &(bip->bli_format.blf_data_map[word_num]); + wordp = &(bip->__bli_format.blf_data_map[word_num]); bit_set = *wordp & (1 << bit_num); ASSERT(bit_set); byte++; @@ -237,7 +237,7 @@ xfs_buf_item_size( * cancel flag in it. */ trace_xfs_buf_item_size_stale(bip); - ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL); + ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL); return bip->bli_format_count; } @@ -278,7 +278,7 @@ xfs_buf_item_format_segment( uint buffer_offset; /* copy the flags across from the base format item */ - blfp->blf_flags = bip->bli_format.blf_flags; + blfp->blf_flags = bip->__bli_format.blf_flags; /* * Base size is the actual size of the ondisk structure - it reflects @@ -287,6 +287,17 @@ xfs_buf_item_format_segment( */ base_size = offsetof(struct xfs_buf_log_format, blf_data_map) + (blfp->blf_map_size * sizeof(blfp->blf_data_map[0])); + + nvecs = 0; + first_bit = xfs_next_bit(blfp->blf_data_map, blfp->blf_map_size, 0); + if (!(bip->bli_flags & XFS_BLI_STALE) && first_bit == -1) { + /* + * If the map is not be dirty in the transaction, mark + * the size as zero and do not advance the vector pointer. + */ + goto out; + } + vecp->i_addr = blfp; vecp->i_len = base_size; vecp->i_type = XLOG_REG_TYPE_BFORMAT; @@ -301,15 +312,13 @@ xfs_buf_item_format_segment( */ trace_xfs_buf_item_format_stale(bip); ASSERT(blfp->blf_flags & XFS_BLF_CANCEL); - blfp->blf_size = nvecs; - return vecp; + goto out; } /* * Fill in an iovec for each set of contiguous chunks. */ - first_bit = xfs_next_bit(blfp->blf_data_map, blfp->blf_map_size, 0); - ASSERT(first_bit != -1); + last_bit = first_bit; nbits = 1; for (;;) { @@ -371,7 +380,8 @@ xfs_buf_item_format_segment( nbits++; } } - bip->bli_format.blf_size = nvecs; +out: + blfp->blf_size = nvecs; return vecp; } @@ -405,7 +415,7 @@ xfs_buf_item_format( if (bip->bli_flags & XFS_BLI_INODE_BUF) { if (!((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) && xfs_log_item_in_current_chkpt(lip))) - bip->bli_format.blf_flags |= XFS_BLF_INODE_BUF; + bip->__bli_format.blf_flags |= XFS_BLF_INODE_BUF; bip->bli_flags &= ~XFS_BLI_INODE_BUF; } @@ -485,7 +495,7 @@ xfs_buf_item_unpin( ASSERT(bip->bli_flags & XFS_BLI_STALE); ASSERT(xfs_buf_islocked(bp)); ASSERT(XFS_BUF_ISSTALE(bp)); - ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL); + ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL); trace_xfs_buf_item_unpin_stale(bip); @@ -601,7 +611,7 @@ xfs_buf_item_unlock( { struct xfs_buf_log_item *bip = BUF_ITEM(lip); struct xfs_buf *bp = bip->bli_buf; - int aborted; + int aborted, clean, i; uint hold; /* Clear the buffer's association with this transaction. */ @@ -631,7 +641,7 @@ xfs_buf_item_unlock( */ if (bip->bli_flags & XFS_BLI_STALE) { trace_xfs_buf_item_unlock_stale(bip); - ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL); + ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL); if (!aborted) { atomic_dec(&bip->bli_refcount); return; @@ -644,8 +654,15 @@ xfs_buf_item_unlock( * If the buf item isn't tracking any data, free it, otherwise drop the * reference we hold to it. */ - if (xfs_bitmap_empty(bip->bli_format.blf_data_map, - bip->bli_format.blf_map_size)) + clean = 1; + for (i = 0; i < bip->bli_format_count; i++) { + if (!xfs_bitmap_empty(bip->bli_formats[i].blf_data_map, + bip->bli_formats[i].blf_map_size)) { + clean = 0; + break; + } + } + if (clean) xfs_buf_item_relse(bp); else atomic_dec(&bip->bli_refcount); @@ -716,7 +733,7 @@ xfs_buf_item_get_format( bip->bli_format_count = count; if (count == 1) { - bip->bli_formats = &bip->bli_format; + bip->bli_formats = &bip->__bli_format; return 0; } @@ -731,7 +748,7 @@ STATIC void xfs_buf_item_free_format( struct xfs_buf_log_item *bip) { - if (bip->bli_formats != &bip->bli_format) { + if (bip->bli_formats != &bip->__bli_format) { kmem_free(bip->bli_formats); bip->bli_formats = NULL; } diff --git a/trunk/fs/xfs/xfs_buf_item.h b/trunk/fs/xfs/xfs_buf_item.h index 6850f49f4af3..16def435944a 100644 --- a/trunk/fs/xfs/xfs_buf_item.h +++ b/trunk/fs/xfs/xfs_buf_item.h @@ -104,7 +104,7 @@ typedef struct xfs_buf_log_item { #endif int bli_format_count; /* count of headers */ struct xfs_buf_log_format *bli_formats; /* array of in-log header ptrs */ - struct xfs_buf_log_format bli_format; /* embedded in-log header */ + struct xfs_buf_log_format __bli_format; /* embedded in-log header */ } xfs_buf_log_item_t; void xfs_buf_item_init(struct xfs_buf *, struct xfs_mount *); diff --git a/trunk/fs/xfs/xfs_dir2_block.c b/trunk/fs/xfs/xfs_dir2_block.c index 7536faaa61e7..12afe07a91d7 100644 --- a/trunk/fs/xfs/xfs_dir2_block.c +++ b/trunk/fs/xfs/xfs_dir2_block.c @@ -355,10 +355,12 @@ xfs_dir2_block_addname( /* * If need to compact the leaf entries, do it now. */ - if (compact) + if (compact) { xfs_dir2_block_compact(tp, bp, hdr, btp, blp, &needlog, &lfloghigh, &lfloglow); - else if (btp->stale) { + /* recalculate blp post-compaction */ + blp = xfs_dir2_block_leaf_p(btp); + } else if (btp->stale) { /* * Set leaf logging boundaries to impossible state. * For the no-stale case they're set explicitly. diff --git a/trunk/fs/xfs/xfs_qm_syscalls.c b/trunk/fs/xfs/xfs_qm_syscalls.c index 5f53e75409b8..8a59f8546552 100644 --- a/trunk/fs/xfs/xfs_qm_syscalls.c +++ b/trunk/fs/xfs/xfs_qm_syscalls.c @@ -784,11 +784,11 @@ xfs_qm_scall_getquota( (XFS_IS_OQUOTA_ENFORCED(mp) && (dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) && dst->d_id != 0) { - if (((int) dst->d_bcount > (int) dst->d_blk_softlimit) && + if ((dst->d_bcount > dst->d_blk_softlimit) && (dst->d_blk_softlimit > 0)) { ASSERT(dst->d_btimer != 0); } - if (((int) dst->d_icount > (int) dst->d_ino_softlimit) && + if ((dst->d_icount > dst->d_ino_softlimit) && (dst->d_ino_softlimit > 0)) { ASSERT(dst->d_itimer != 0); } diff --git a/trunk/fs/xfs/xfs_trans_buf.c b/trunk/fs/xfs/xfs_trans_buf.c index 4fc17d479d42..3edf5dbee001 100644 --- a/trunk/fs/xfs/xfs_trans_buf.c +++ b/trunk/fs/xfs/xfs_trans_buf.c @@ -93,7 +93,7 @@ _xfs_trans_bjoin( xfs_buf_item_init(bp, tp->t_mountp); bip = bp->b_fspriv; ASSERT(!(bip->bli_flags & XFS_BLI_STALE)); - ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_CANCEL)); + ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_CANCEL)); ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED)); if (reset_recur) bip->bli_recur = 0; @@ -432,7 +432,7 @@ xfs_trans_brelse(xfs_trans_t *tp, bip = bp->b_fspriv; ASSERT(bip->bli_item.li_type == XFS_LI_BUF); ASSERT(!(bip->bli_flags & XFS_BLI_STALE)); - ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_CANCEL)); + ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_CANCEL)); ASSERT(atomic_read(&bip->bli_refcount) > 0); trace_xfs_trans_brelse(bip); @@ -519,7 +519,7 @@ xfs_trans_bhold(xfs_trans_t *tp, ASSERT(bp->b_transp == tp); ASSERT(bip != NULL); ASSERT(!(bip->bli_flags & XFS_BLI_STALE)); - ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_CANCEL)); + ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_CANCEL)); ASSERT(atomic_read(&bip->bli_refcount) > 0); bip->bli_flags |= XFS_BLI_HOLD; @@ -539,7 +539,7 @@ xfs_trans_bhold_release(xfs_trans_t *tp, ASSERT(bp->b_transp == tp); ASSERT(bip != NULL); ASSERT(!(bip->bli_flags & XFS_BLI_STALE)); - ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_CANCEL)); + ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_CANCEL)); ASSERT(atomic_read(&bip->bli_refcount) > 0); ASSERT(bip->bli_flags & XFS_BLI_HOLD); @@ -598,7 +598,7 @@ xfs_trans_log_buf(xfs_trans_t *tp, bip->bli_flags &= ~XFS_BLI_STALE; ASSERT(XFS_BUF_ISSTALE(bp)); XFS_BUF_UNSTALE(bp); - bip->bli_format.blf_flags &= ~XFS_BLF_CANCEL; + bip->__bli_format.blf_flags &= ~XFS_BLF_CANCEL; } tp->t_flags |= XFS_TRANS_DIRTY; @@ -643,6 +643,7 @@ xfs_trans_binval( xfs_buf_t *bp) { xfs_buf_log_item_t *bip = bp->b_fspriv; + int i; ASSERT(bp->b_transp == tp); ASSERT(bip != NULL); @@ -657,8 +658,8 @@ xfs_trans_binval( */ ASSERT(XFS_BUF_ISSTALE(bp)); ASSERT(!(bip->bli_flags & (XFS_BLI_LOGGED | XFS_BLI_DIRTY))); - ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_INODE_BUF)); - ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL); + ASSERT(!(bip->__bli_format.blf_flags & XFS_BLF_INODE_BUF)); + ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL); ASSERT(bip->bli_item.li_desc->lid_flags & XFS_LID_DIRTY); ASSERT(tp->t_flags & XFS_TRANS_DIRTY); return; @@ -668,10 +669,12 @@ xfs_trans_binval( bip->bli_flags |= XFS_BLI_STALE; bip->bli_flags &= ~(XFS_BLI_INODE_BUF | XFS_BLI_LOGGED | XFS_BLI_DIRTY); - bip->bli_format.blf_flags &= ~XFS_BLF_INODE_BUF; - bip->bli_format.blf_flags |= XFS_BLF_CANCEL; - memset((char *)(bip->bli_format.blf_data_map), 0, - (bip->bli_format.blf_map_size * sizeof(uint))); + bip->__bli_format.blf_flags &= ~XFS_BLF_INODE_BUF; + bip->__bli_format.blf_flags |= XFS_BLF_CANCEL; + for (i = 0; i < bip->bli_format_count; i++) { + memset(bip->bli_formats[i].blf_data_map, 0, + (bip->bli_formats[i].blf_map_size * sizeof(uint))); + } bip->bli_item.li_desc->lid_flags |= XFS_LID_DIRTY; tp->t_flags |= XFS_TRANS_DIRTY; } @@ -775,5 +778,5 @@ xfs_trans_dquot_buf( type == XFS_BLF_GDQUOT_BUF); ASSERT(atomic_read(&bip->bli_refcount) > 0); - bip->bli_format.blf_flags |= type; + bip->__bli_format.blf_flags |= type; } diff --git a/trunk/include/asm-generic/dma-mapping-broken.h b/trunk/include/asm-generic/dma-mapping-broken.h index ccf7b4f34a3c..6c32af918c2f 100644 --- a/trunk/include/asm-generic/dma-mapping-broken.h +++ b/trunk/include/asm-generic/dma-mapping-broken.h @@ -16,6 +16,22 @@ extern void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle); +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) +{ + /* attrs is not supported and ignored */ + return dma_alloc_coherent(dev, size, dma_handle, flag); +} + +static inline void dma_free_attrs(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle, + struct dma_attrs *attrs) +{ + /* attrs is not supported and ignored */ + dma_free_coherent(dev, size, cpu_addr, dma_handle); +} + #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) diff --git a/trunk/include/asm-generic/pgtable.h b/trunk/include/asm-generic/pgtable.h index 701beab27aab..5cf680a98f9b 100644 --- a/trunk/include/asm-generic/pgtable.h +++ b/trunk/include/asm-generic/pgtable.h @@ -461,10 +461,8 @@ static inline int is_zero_pfn(unsigned long pfn) return offset_from_zero_pfn <= (zero_page_mask >> PAGE_SHIFT); } -static inline unsigned long my_zero_pfn(unsigned long addr) -{ - return page_to_pfn(ZERO_PAGE(addr)); -} +#define my_zero_pfn(addr) page_to_pfn(ZERO_PAGE(addr)) + #else static inline int is_zero_pfn(unsigned long pfn) { diff --git a/trunk/include/asm-generic/syscalls.h b/trunk/include/asm-generic/syscalls.h index 58f466ff00d3..1db51b8524e9 100644 --- a/trunk/include/asm-generic/syscalls.h +++ b/trunk/include/asm-generic/syscalls.h @@ -21,10 +21,12 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len, unsigned long fd, off_t pgoff); #endif +#ifndef CONFIG_GENERIC_SIGALTSTACK #ifndef sys_sigaltstack asmlinkage long sys_sigaltstack(const stack_t __user *, stack_t __user *, struct pt_regs *); #endif +#endif #ifndef sys_rt_sigreturn asmlinkage long sys_rt_sigreturn(struct pt_regs *regs); diff --git a/trunk/include/drm/drm_mm.h b/trunk/include/drm/drm_mm.h index 0f4a366f6fa6..3527fb3f75bb 100644 --- a/trunk/include/drm/drm_mm.h +++ b/trunk/include/drm/drm_mm.h @@ -70,7 +70,7 @@ struct drm_mm { unsigned long scan_color; unsigned long scan_size; unsigned long scan_hit_start; - unsigned scan_hit_size; + unsigned long scan_hit_end; unsigned scanned_blocks; unsigned long scan_start; unsigned long scan_end; diff --git a/trunk/include/linux/ata.h b/trunk/include/linux/ata.h index 408da9502177..8f7a3d68371a 100644 --- a/trunk/include/linux/ata.h +++ b/trunk/include/linux/ata.h @@ -297,10 +297,12 @@ enum { ATA_LOG_SATA_NCQ = 0x10, ATA_LOG_SATA_ID_DEV_DATA = 0x30, ATA_LOG_SATA_SETTINGS = 0x08, - ATA_LOG_DEVSLP_MDAT = 0x30, + ATA_LOG_DEVSLP_OFFSET = 0x30, + ATA_LOG_DEVSLP_SIZE = 0x08, + ATA_LOG_DEVSLP_MDAT = 0x00, ATA_LOG_DEVSLP_MDAT_MASK = 0x1F, - ATA_LOG_DEVSLP_DETO = 0x31, - ATA_LOG_DEVSLP_VALID = 0x37, + ATA_LOG_DEVSLP_DETO = 0x01, + ATA_LOG_DEVSLP_VALID = 0x07, ATA_LOG_DEVSLP_VALID_MASK = 0x80, /* READ/WRITE LONG (obsolete) */ diff --git a/trunk/include/linux/audit.h b/trunk/include/linux/audit.h index bce729afbcf9..5a6d718adf34 100644 --- a/trunk/include/linux/audit.h +++ b/trunk/include/linux/audit.h @@ -24,6 +24,7 @@ #define _LINUX_AUDIT_H_ #include +#include #include struct audit_sig_info { @@ -157,7 +158,8 @@ void audit_core_dumps(long signr); static inline void audit_seccomp(unsigned long syscall, long signr, int code) { - if (unlikely(!audit_dummy_context())) + /* Force a record to be reported if a signal was delivered. */ + if (signr || unlikely(!audit_dummy_context())) __audit_seccomp(syscall, signr, code); } diff --git a/trunk/include/linux/compaction.h b/trunk/include/linux/compaction.h index 6ecb6dc2f303..cc7bddeaf553 100644 --- a/trunk/include/linux/compaction.h +++ b/trunk/include/linux/compaction.h @@ -22,7 +22,7 @@ extern int sysctl_extfrag_handler(struct ctl_table *table, int write, extern int fragmentation_index(struct zone *zone, unsigned int order); extern unsigned long try_to_compact_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *mask, - bool sync, bool *contended, struct page **page); + bool sync, bool *contended); extern int compact_pgdat(pg_data_t *pgdat, int order); extern void reset_isolation_suitable(pg_data_t *pgdat); extern unsigned long compaction_suitable(struct zone *zone, int order); @@ -75,7 +75,7 @@ static inline bool compaction_restarting(struct zone *zone, int order) #else static inline unsigned long try_to_compact_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *nodemask, - bool sync, bool *contended, struct page **page) + bool sync, bool *contended) { return COMPACT_CONTINUE; } diff --git a/trunk/include/linux/cpu_rmap.h b/trunk/include/linux/cpu_rmap.h index ac3bbb5b9502..1739510d8994 100644 --- a/trunk/include/linux/cpu_rmap.h +++ b/trunk/include/linux/cpu_rmap.h @@ -13,9 +13,11 @@ #include #include #include +#include /** * struct cpu_rmap - CPU affinity reverse-map + * @refcount: kref for object * @size: Number of objects to be reverse-mapped * @used: Number of objects added * @obj: Pointer to array of object pointers @@ -23,6 +25,7 @@ * based on affinity masks */ struct cpu_rmap { + struct kref refcount; u16 size, used; void **obj; struct { @@ -33,15 +36,7 @@ struct cpu_rmap { #define CPU_RMAP_DIST_INF 0xffff extern struct cpu_rmap *alloc_cpu_rmap(unsigned int size, gfp_t flags); - -/** - * free_cpu_rmap - free CPU affinity reverse-map - * @rmap: Reverse-map allocated with alloc_cpu_rmap(), or %NULL - */ -static inline void free_cpu_rmap(struct cpu_rmap *rmap) -{ - kfree(rmap); -} +extern int cpu_rmap_put(struct cpu_rmap *rmap); extern int cpu_rmap_add(struct cpu_rmap *rmap, void *obj); extern int cpu_rmap_update(struct cpu_rmap *rmap, u16 index, diff --git a/trunk/include/linux/cpuidle.h b/trunk/include/linux/cpuidle.h index 3711b34dc4f9..24cd1037b6d6 100644 --- a/trunk/include/linux/cpuidle.h +++ b/trunk/include/linux/cpuidle.h @@ -126,9 +126,9 @@ struct cpuidle_driver { struct module *owner; int refcnt; - unsigned int power_specified:1; /* set to 1 to use the core cpuidle time keeping (for all states). */ unsigned int en_core_tk_irqen:1; + /* states array must be ordered in decreasing power consumption */ struct cpuidle_state states[CPUIDLE_STATE_MAX]; int state_count; int safe_state_index; diff --git a/trunk/include/linux/init.h b/trunk/include/linux/init.h index a799273714ac..10ed4f436458 100644 --- a/trunk/include/linux/init.h +++ b/trunk/include/linux/init.h @@ -93,14 +93,6 @@ #define __exit __section(.exit.text) __exitused __cold notrace -/* Used for HOTPLUG, but that is always enabled now, so just make them noops */ -#define __devinit -#define __devinitdata -#define __devinitconst -#define __devexit -#define __devexitdata -#define __devexitconst - /* Used for HOTPLUG_CPU */ #define __cpuinit __section(.cpuinit.text) __cold notrace #define __cpuinitdata __section(.cpuinit.data) @@ -337,18 +329,6 @@ void __init parse_early_options(char *cmdline); #define __INITRODATA_OR_MODULE __INITRODATA #endif /*CONFIG_MODULES*/ -/* Functions marked as __devexit may be discarded at kernel link time, depending - on config options. Newer versions of binutils detect references from - retained sections to discarded sections and flag an error. Pointers to - __devexit functions must use __devexit_p(function_name), the wrapper will - insert either the function_name or NULL, depending on the config options. - */ -#if defined(MODULE) || defined(CONFIG_HOTPLUG) -#define __devexit_p(x) x -#else -#define __devexit_p(x) NULL -#endif - #ifdef MODULE #define __exit_p(x) x #else diff --git a/trunk/include/linux/interrupt.h b/trunk/include/linux/interrupt.h index 5e4e6170f43a..5fa5afeeb759 100644 --- a/trunk/include/linux/interrupt.h +++ b/trunk/include/linux/interrupt.h @@ -268,11 +268,6 @@ struct irq_affinity_notify { extern int irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify); -static inline void irq_run_affinity_notifiers(void) -{ - flush_scheduled_work(); -} - #else /* CONFIG_SMP */ static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m) diff --git a/trunk/include/linux/libata.h b/trunk/include/linux/libata.h index 83ba0ab2c915..649e5f86b5f0 100644 --- a/trunk/include/linux/libata.h +++ b/trunk/include/linux/libata.h @@ -652,8 +652,8 @@ struct ata_device { u32 gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */ }; - /* Identify Device Data Log (30h), SATA Settings (page 08h) */ - u8 sata_settings[ATA_SECT_SIZE]; + /* DEVSLP Timing Variables from Identify Device Data Log */ + u8 devslp_timing[ATA_LOG_DEVSLP_SIZE]; /* error history */ int spdn_cnt; diff --git a/trunk/include/linux/lockdep.h b/trunk/include/linux/lockdep.h index 00e46376e28f..2bca44b0893c 100644 --- a/trunk/include/linux/lockdep.h +++ b/trunk/include/linux/lockdep.h @@ -524,14 +524,17 @@ static inline void print_irqtrace_events(struct task_struct *curr) #ifdef CONFIG_DEBUG_LOCK_ALLOC # ifdef CONFIG_PROVE_LOCKING # define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) +# define rwsem_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 2, n, i) # define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 2, NULL, i) # else # define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) +# define rwsem_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 1, n, i) # define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 1, NULL, i) # endif # define rwsem_release(l, n, i) lock_release(l, n, i) #else # define rwsem_acquire(l, s, t, i) do { } while (0) +# define rwsem_acquire_nest(l, s, t, n, i) do { } while (0) # define rwsem_acquire_read(l, s, t, i) do { } while (0) # define rwsem_release(l, n, i) do { } while (0) #endif diff --git a/trunk/include/linux/mm.h b/trunk/include/linux/mm.h index 63204078f72b..66e2f7c61e5c 100644 --- a/trunk/include/linux/mm.h +++ b/trunk/include/linux/mm.h @@ -455,7 +455,6 @@ void put_pages_list(struct list_head *pages); void split_page(struct page *page, unsigned int order); int split_free_page(struct page *page); -int capture_free_page(struct page *page, int alloc_order, int migratetype); /* * Compound pages have a destructor function. Provide a diff --git a/trunk/include/linux/module.h b/trunk/include/linux/module.h index 7760c6d344a3..1375ee3f03aa 100644 --- a/trunk/include/linux/module.h +++ b/trunk/include/linux/module.h @@ -199,11 +199,11 @@ struct module_use { struct module *source, *target; }; -enum module_state -{ - MODULE_STATE_LIVE, - MODULE_STATE_COMING, - MODULE_STATE_GOING, +enum module_state { + MODULE_STATE_LIVE, /* Normal state. */ + MODULE_STATE_COMING, /* Full formed, running module_init. */ + MODULE_STATE_GOING, /* Going away. */ + MODULE_STATE_UNFORMED, /* Still setting it up. */ }; /** diff --git a/trunk/include/linux/netdevice.h b/trunk/include/linux/netdevice.h index c599e4782d45..9ef07d0868b6 100644 --- a/trunk/include/linux/netdevice.h +++ b/trunk/include/linux/netdevice.h @@ -60,6 +60,9 @@ struct wireless_dev; #define SET_ETHTOOL_OPS(netdev,ops) \ ( (netdev)->ethtool_ops = (ops) ) +extern void netdev_set_default_ethtool_ops(struct net_device *dev, + const struct ethtool_ops *ops); + /* hardware address assignment types */ #define NET_ADDR_PERM 0 /* address is permanent (default) */ #define NET_ADDR_RANDOM 1 /* address is generated randomly */ diff --git a/trunk/arch/arm/mach-imx/iram.h b/trunk/include/linux/platform_data/imx-iram.h similarity index 100% rename from trunk/arch/arm/mach-imx/iram.h rename to trunk/include/linux/platform_data/imx-iram.h diff --git a/trunk/include/linux/ptrace.h b/trunk/include/linux/ptrace.h index 1693775ecfe8..89573a33ab3c 100644 --- a/trunk/include/linux/ptrace.h +++ b/trunk/include/linux/ptrace.h @@ -45,7 +45,6 @@ extern long arch_ptrace(struct task_struct *child, long request, extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len); extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len); extern void ptrace_disable(struct task_struct *); -extern int ptrace_check_attach(struct task_struct *task, bool ignore_state); extern int ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data); extern void ptrace_notify(int exit_code); diff --git a/trunk/include/linux/rbtree_augmented.h b/trunk/include/linux/rbtree_augmented.h index 2ac60c9cf644..fea49b5da12a 100644 --- a/trunk/include/linux/rbtree_augmented.h +++ b/trunk/include/linux/rbtree_augmented.h @@ -123,9 +123,9 @@ __rb_change_child(struct rb_node *old, struct rb_node *new, extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root, void (*augment_rotate)(struct rb_node *old, struct rb_node *new)); -static __always_inline void -rb_erase_augmented(struct rb_node *node, struct rb_root *root, - const struct rb_augment_callbacks *augment) +static __always_inline struct rb_node * +__rb_erase_augmented(struct rb_node *node, struct rb_root *root, + const struct rb_augment_callbacks *augment) { struct rb_node *child = node->rb_right, *tmp = node->rb_left; struct rb_node *parent, *rebalance; @@ -217,6 +217,14 @@ rb_erase_augmented(struct rb_node *node, struct rb_root *root, } augment->propagate(tmp, NULL); + return rebalance; +} + +static __always_inline void +rb_erase_augmented(struct rb_node *node, struct rb_root *root, + const struct rb_augment_callbacks *augment) +{ + struct rb_node *rebalance = __rb_erase_augmented(node, root, augment); if (rebalance) __rb_erase_color(rebalance, root, augment->rotate); } diff --git a/trunk/include/linux/rwsem.h b/trunk/include/linux/rwsem.h index 54bd7cd7ecbd..8da67d625e13 100644 --- a/trunk/include/linux/rwsem.h +++ b/trunk/include/linux/rwsem.h @@ -125,8 +125,17 @@ extern void downgrade_write(struct rw_semaphore *sem); */ extern void down_read_nested(struct rw_semaphore *sem, int subclass); extern void down_write_nested(struct rw_semaphore *sem, int subclass); +extern void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest_lock); + +# define down_write_nest_lock(sem, nest_lock) \ +do { \ + typecheck(struct lockdep_map *, &(nest_lock)->dep_map); \ + _down_write_nest_lock(sem, &(nest_lock)->dep_map); \ +} while (0); + #else # define down_read_nested(sem, subclass) down_read(sem) +# define down_write_nest_lock(sem, nest_lock) down_write(sem) # define down_write_nested(sem, subclass) down_write(sem) #endif diff --git a/trunk/include/linux/sched.h b/trunk/include/linux/sched.h index 206bb089c06b..d2112477ff5e 100644 --- a/trunk/include/linux/sched.h +++ b/trunk/include/linux/sched.h @@ -1810,6 +1810,7 @@ extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, #define PF_MEMALLOC 0x00000800 /* Allocating memory */ #define PF_NPROC_EXCEEDED 0x00001000 /* set_user noticed that RLIMIT_NPROC was exceeded */ #define PF_USED_MATH 0x00002000 /* if unset the fpu must be initialized before use */ +#define PF_USED_ASYNC 0x00004000 /* used async_schedule*(), used by module init */ #define PF_NOFREEZE 0x00008000 /* this thread should not be frozen */ #define PF_FROZEN 0x00010000 /* frozen for system suspend */ #define PF_FSTRANS 0x00020000 /* inside a filesystem transaction */ @@ -2713,7 +2714,16 @@ static inline void thread_group_cputime_init(struct signal_struct *sig) extern void recalc_sigpending_and_wake(struct task_struct *t); extern void recalc_sigpending(void); -extern void signal_wake_up(struct task_struct *t, int resume_stopped); +extern void signal_wake_up_state(struct task_struct *t, unsigned int state); + +static inline void signal_wake_up(struct task_struct *t, bool resume) +{ + signal_wake_up_state(t, resume ? TASK_WAKEKILL : 0); +} +static inline void ptrace_signal_wake_up(struct task_struct *t, bool resume) +{ + signal_wake_up_state(t, resume ? __TASK_TRACED : 0); +} /* * Wrappers for p->thread_info->cpu access. No-op on UP. diff --git a/trunk/include/sound/core.h b/trunk/include/sound/core.h index 7cede2d6aa86..93896ad1fcdd 100644 --- a/trunk/include/sound/core.h +++ b/trunk/include/sound/core.h @@ -394,11 +394,8 @@ void __snd_printk(unsigned int level, const char *file, int line, #else /* !CONFIG_SND_DEBUG */ -__printf(1, 2) -static inline void snd_printd(const char *format, ...) {} -__printf(2, 3) -static inline void _snd_printd(int level, const char *format, ...) {} - +#define snd_printd(fmt, args...) do { } while (0) +#define _snd_printd(level, fmt, args...) do { } while (0) #define snd_BUG() do { } while (0) static inline int __snd_bug_on(int cond) { @@ -419,8 +416,7 @@ static inline int __snd_bug_on(int cond) #define snd_printdd(format, args...) \ __snd_printk(2, __FILE__, __LINE__, format, ##args) #else -__printf(1, 2) -static inline void snd_printdd(const char *format, ...) {} +#define snd_printdd(format, args...) do { } while (0) #endif @@ -458,7 +454,6 @@ struct snd_pci_quirk { #define SND_PCI_QUIRK_MASK(vend, mask, dev, xname, val) \ {_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), \ .value = (val), .name = (xname)} -#define snd_pci_quirk_name(q) ((q)->name) #else #define SND_PCI_QUIRK(vend,dev,xname,val) \ {_SND_PCI_QUIRK_ID(vend, dev), .value = (val)} @@ -466,7 +461,6 @@ struct snd_pci_quirk { {_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), .value = (val)} #define SND_PCI_QUIRK_VENDOR(vend, xname, val) \ {_SND_PCI_QUIRK_ID_MASK(vend, 0, 0), .value = (val)} -#define snd_pci_quirk_name(q) "" #endif const struct snd_pci_quirk * diff --git a/trunk/include/sound/memalloc.h b/trunk/include/sound/memalloc.h index cf15b8213df7..844af65af626 100644 --- a/trunk/include/sound/memalloc.h +++ b/trunk/include/sound/memalloc.h @@ -37,7 +37,7 @@ struct snd_dma_device { #ifndef snd_dma_pci_data #define snd_dma_pci_data(pci) (&(pci)->dev) #define snd_dma_isa_data() NULL -#define snd_dma_continuous_data(x) ((struct device *)(__force unsigned long)(x)) +#define snd_dma_continuous_data(x) ((struct device *)(unsigned long)(x)) #endif diff --git a/trunk/include/target/target_core_base.h b/trunk/include/target/target_core_base.h index 7cae2360221e..663e34a5383f 100644 --- a/trunk/include/target/target_core_base.h +++ b/trunk/include/target/target_core_base.h @@ -174,6 +174,7 @@ typedef unsigned __bitwise__ sense_reason_t; enum tcm_sense_reason_table { #define R(x) (__force sense_reason_t )(x) + TCM_NO_SENSE = R(0x00), TCM_NON_EXISTENT_LUN = R(0x01), TCM_UNSUPPORTED_SCSI_OPCODE = R(0x02), TCM_INCORRECT_AMOUNT_OF_DATA = R(0x03), diff --git a/trunk/include/uapi/linux/audit.h b/trunk/include/uapi/linux/audit.h index 76352ac45f24..9f096f1c0907 100644 --- a/trunk/include/uapi/linux/audit.h +++ b/trunk/include/uapi/linux/audit.h @@ -26,7 +26,6 @@ #include #include -#include /* The netlink messages for the audit system is divided into blocks: * 1000 - 1099 are for commanding the audit system @@ -106,6 +105,7 @@ #define AUDIT_MMAP 1323 /* Record showing descriptor and flags in mmap */ #define AUDIT_NETFILTER_PKT 1324 /* Packets traversing netfilter chains */ #define AUDIT_NETFILTER_CFG 1325 /* Netfilter chain modifications */ +#define AUDIT_SECCOMP 1326 /* Secure Computing event */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/trunk/include/uapi/linux/serial_core.h b/trunk/include/uapi/linux/serial_core.h index 78f99d97475b..2c6c85f18ea0 100644 --- a/trunk/include/uapi/linux/serial_core.h +++ b/trunk/include/uapi/linux/serial_core.h @@ -50,7 +50,8 @@ #define PORT_LPC3220 22 /* NXP LPC32xx SoC "Standard" UART */ #define PORT_8250_CIR 23 /* CIR infrared port, has its own driver */ #define PORT_XR17V35X 24 /* Exar XR17V35x UARTs */ -#define PORT_MAX_8250 24 /* max port ID */ +#define PORT_BRCM_TRUMANAGE 24 +#define PORT_MAX_8250 25 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed diff --git a/trunk/init/Kconfig b/trunk/init/Kconfig index 7d30240e5bfe..be8b7f55312d 100644 --- a/trunk/init/Kconfig +++ b/trunk/init/Kconfig @@ -1182,7 +1182,7 @@ config CC_OPTIMIZE_FOR_SIZE Enabling this option will pass "-Os" instead of "-O2" to gcc resulting in a smaller kernel. - If unsure, say Y. + If unsure, say N. config SYSCTL bool diff --git a/trunk/init/do_mounts_initrd.c b/trunk/init/do_mounts_initrd.c index 5e4ded51788e..f9acf71b9810 100644 --- a/trunk/init/do_mounts_initrd.c +++ b/trunk/init/do_mounts_initrd.c @@ -36,6 +36,10 @@ __setup("noinitrd", no_initrd); static int init_linuxrc(struct subprocess_info *info, struct cred *new) { sys_unshare(CLONE_FS | CLONE_FILES); + /* stdin/stdout/stderr for /linuxrc */ + sys_open("/dev/console", O_RDWR, 0); + sys_dup(0); + sys_dup(0); /* move initrd over / and chdir/chroot in initrd root */ sys_chdir("/root"); sys_mount(".", "/", NULL, MS_MOVE, NULL); diff --git a/trunk/init/main.c b/trunk/init/main.c index 85d69dffe864..92d728a32d51 100644 --- a/trunk/init/main.c +++ b/trunk/init/main.c @@ -802,7 +802,7 @@ static int run_init_process(const char *init_filename) (const char __user *const __user *)envp_init); } -static void __init kernel_init_freeable(void); +static noinline void __init kernel_init_freeable(void); static int __ref kernel_init(void *unused) { @@ -845,7 +845,7 @@ static int __ref kernel_init(void *unused) "See Linux Documentation/init.txt for guidance."); } -static void __init kernel_init_freeable(void) +static noinline void __init kernel_init_freeable(void) { /* * Wait until kthreadd is all set-up. diff --git a/trunk/kernel/async.c b/trunk/kernel/async.c index 9d3118384858..6f34904a0b53 100644 --- a/trunk/kernel/async.c +++ b/trunk/kernel/async.c @@ -86,18 +86,27 @@ static atomic_t entry_count; */ static async_cookie_t __lowest_in_progress(struct async_domain *running) { + async_cookie_t first_running = next_cookie; /* infinity value */ + async_cookie_t first_pending = next_cookie; /* ditto */ struct async_entry *entry; + /* + * Both running and pending lists are sorted but not disjoint. + * Take the first cookies from both and return the min. + */ if (!list_empty(&running->domain)) { entry = list_first_entry(&running->domain, typeof(*entry), list); - return entry->cookie; + first_running = entry->cookie; } - list_for_each_entry(entry, &async_pending, list) - if (entry->running == running) - return entry->cookie; + list_for_each_entry(entry, &async_pending, list) { + if (entry->running == running) { + first_pending = entry->cookie; + break; + } + } - return next_cookie; /* "infinity" value */ + return min(first_running, first_pending); } static async_cookie_t lowest_in_progress(struct async_domain *running) @@ -118,13 +127,17 @@ static void async_run_entry_fn(struct work_struct *work) { struct async_entry *entry = container_of(work, struct async_entry, work); + struct async_entry *pos; unsigned long flags; ktime_t uninitialized_var(calltime), delta, rettime; struct async_domain *running = entry->running; - /* 1) move self to the running queue */ + /* 1) move self to the running queue, make sure it stays sorted */ spin_lock_irqsave(&async_lock, flags); - list_move_tail(&entry->list, &running->domain); + list_for_each_entry_reverse(pos, &running->domain, list) + if (entry->cookie < pos->cookie) + break; + list_move_tail(&entry->list, &pos->list); spin_unlock_irqrestore(&async_lock, flags); /* 2) run (and print duration) */ @@ -196,6 +209,9 @@ static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct a atomic_inc(&entry_count); spin_unlock_irqrestore(&async_lock, flags); + /* mark that this task has queued an async job, used by module init */ + current->flags |= PF_USED_ASYNC; + /* schedule for execution */ queue_work(system_unbound_wq, &entry->work); diff --git a/trunk/kernel/audit.c b/trunk/kernel/audit.c index 40414e9143db..d596e5355f15 100644 --- a/trunk/kernel/audit.c +++ b/trunk/kernel/audit.c @@ -272,6 +272,8 @@ static int audit_log_config_change(char *function_name, int new, int old, int rc = 0; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; audit_log_format(ab, "%s=%d old=%d auid=%u ses=%u", function_name, new, old, from_kuid(&init_user_ns, loginuid), sessionid); if (sid) { @@ -619,6 +621,8 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type, } *ab = audit_log_start(NULL, GFP_KERNEL, msg_type); + if (unlikely(!*ab)) + return rc; audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u", task_tgid_vnr(current), from_kuid(&init_user_ns, current_uid()), @@ -1097,6 +1101,23 @@ static inline void audit_get_stamp(struct audit_context *ctx, } } +/* + * Wait for auditd to drain the queue a little + */ +static void wait_for_auditd(unsigned long sleep_time) +{ + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&audit_backlog_wait, &wait); + + if (audit_backlog_limit && + skb_queue_len(&audit_skb_queue) > audit_backlog_limit) + schedule_timeout(sleep_time); + + __set_current_state(TASK_RUNNING); + remove_wait_queue(&audit_backlog_wait, &wait); +} + /* Obtain an audit buffer. This routine does locking to obtain the * audit buffer, but then no locking is required for calls to * audit_log_*format. If the tsk is a task that is currently in a @@ -1142,20 +1163,13 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, while (audit_backlog_limit && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) { - if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time - && time_before(jiffies, timeout_start + audit_backlog_wait_time)) { + if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time) { + unsigned long sleep_time; - /* Wait for auditd to drain the queue a little */ - DECLARE_WAITQUEUE(wait, current); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&audit_backlog_wait, &wait); - - if (audit_backlog_limit && - skb_queue_len(&audit_skb_queue) > audit_backlog_limit) - schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies); - - __set_current_state(TASK_RUNNING); - remove_wait_queue(&audit_backlog_wait, &wait); + sleep_time = timeout_start + audit_backlog_wait_time - + jiffies; + if ((long)sleep_time > 0) + wait_for_auditd(sleep_time); continue; } if (audit_rate_check() && printk_ratelimit()) diff --git a/trunk/kernel/audit_tree.c b/trunk/kernel/audit_tree.c index e81175ef25f8..642a89c4f3d6 100644 --- a/trunk/kernel/audit_tree.c +++ b/trunk/kernel/audit_tree.c @@ -449,11 +449,26 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) return 0; } +static void audit_log_remove_rule(struct audit_krule *rule) +{ + struct audit_buffer *ab; + + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return; + audit_log_format(ab, "op="); + audit_log_string(ab, "remove rule"); + audit_log_format(ab, " dir="); + audit_log_untrustedstring(ab, rule->tree->pathname); + audit_log_key(ab, rule->filterkey); + audit_log_format(ab, " list=%d res=1", rule->listnr); + audit_log_end(ab); +} + static void kill_rules(struct audit_tree *tree) { struct audit_krule *rule, *next; struct audit_entry *entry; - struct audit_buffer *ab; list_for_each_entry_safe(rule, next, &tree->rules, rlist) { entry = container_of(rule, struct audit_entry, rule); @@ -461,14 +476,7 @@ static void kill_rules(struct audit_tree *tree) list_del_init(&rule->rlist); if (rule->tree) { /* not a half-baked one */ - ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); - audit_log_format(ab, "op="); - audit_log_string(ab, "remove rule"); - audit_log_format(ab, " dir="); - audit_log_untrustedstring(ab, rule->tree->pathname); - audit_log_key(ab, rule->filterkey); - audit_log_format(ab, " list=%d res=1", rule->listnr); - audit_log_end(ab); + audit_log_remove_rule(rule); rule->tree = NULL; list_del_rcu(&entry->list); list_del(&entry->rule.list); diff --git a/trunk/kernel/audit_watch.c b/trunk/kernel/audit_watch.c index 4a599f699adc..22831c4d369c 100644 --- a/trunk/kernel/audit_watch.c +++ b/trunk/kernel/audit_watch.c @@ -240,6 +240,8 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc if (audit_enabled) { struct audit_buffer *ab; ab = audit_log_start(NULL, GFP_NOFS, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return; audit_log_format(ab, "auid=%u ses=%u op=", from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); diff --git a/trunk/kernel/auditfilter.c b/trunk/kernel/auditfilter.c index 7f19f23d38a3..f9fc54bbe06f 100644 --- a/trunk/kernel/auditfilter.c +++ b/trunk/kernel/auditfilter.c @@ -1144,7 +1144,6 @@ static void audit_log_rule_change(kuid_t loginuid, u32 sessionid, u32 sid, * audit_receive_filter - apply all rules to the specified message type * @type: audit message type * @pid: target pid for netlink audit messages - * @uid: target uid for netlink audit messages * @seq: netlink audit message sequence (serial) number * @data: payload data * @datasz: size of payload data diff --git a/trunk/kernel/auditsc.c b/trunk/kernel/auditsc.c index e37e6a12c5e3..a371f857a0a9 100644 --- a/trunk/kernel/auditsc.c +++ b/trunk/kernel/auditsc.c @@ -1464,14 +1464,14 @@ static void show_special(struct audit_context *context, int *call_panic) audit_log_end(ab); ab = audit_log_start(context, GFP_KERNEL, AUDIT_IPC_SET_PERM); + if (unlikely(!ab)) + return; audit_log_format(ab, "qbytes=%lx ouid=%u ogid=%u mode=%#ho", context->ipc.qbytes, context->ipc.perm_uid, context->ipc.perm_gid, context->ipc.perm_mode); - if (!ab) - return; } break; } case AUDIT_MQ_OPEN: { @@ -2675,7 +2675,7 @@ void __audit_mmap_fd(int fd, int flags) context->type = AUDIT_MMAP; } -static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr) +static void audit_log_task(struct audit_buffer *ab) { kuid_t auid, uid; kgid_t gid; @@ -2693,6 +2693,11 @@ static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr) audit_log_task_context(ab); audit_log_format(ab, " pid=%d comm=", current->pid); audit_log_untrustedstring(ab, current->comm); +} + +static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr) +{ + audit_log_task(ab); audit_log_format(ab, " reason="); audit_log_string(ab, reason); audit_log_format(ab, " sig=%ld", signr); @@ -2715,6 +2720,8 @@ void audit_core_dumps(long signr) return; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND); + if (unlikely(!ab)) + return; audit_log_abend(ab, "memory violation", signr); audit_log_end(ab); } @@ -2723,8 +2730,11 @@ void __audit_seccomp(unsigned long syscall, long signr, int code) { struct audit_buffer *ab; - ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND); - audit_log_abend(ab, "seccomp", signr); + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_SECCOMP); + if (unlikely(!ab)) + return; + audit_log_task(ab); + audit_log_format(ab, " sig=%ld", signr); audit_log_format(ab, " syscall=%ld", syscall); audit_log_format(ab, " compat=%d", is_compat_task()); audit_log_format(ab, " ip=0x%lx", KSTK_EIP(current)); diff --git a/trunk/kernel/compat.c b/trunk/kernel/compat.c index f6150e92dfc9..36700e9e2be9 100644 --- a/trunk/kernel/compat.c +++ b/trunk/kernel/compat.c @@ -535,9 +535,11 @@ asmlinkage long compat_sys_getrusage(int who, struct compat_rusage __user *ru) return 0; } -asmlinkage long -compat_sys_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options, - struct compat_rusage __user *ru) +COMPAT_SYSCALL_DEFINE4(wait4, + compat_pid_t, pid, + compat_uint_t __user *, stat_addr, + int, options, + struct compat_rusage __user *, ru) { if (!ru) { return sys_wait4(pid, stat_addr, options, NULL); @@ -564,9 +566,10 @@ compat_sys_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options, } } -asmlinkage long compat_sys_waitid(int which, compat_pid_t pid, - struct compat_siginfo __user *uinfo, int options, - struct compat_rusage __user *uru) +COMPAT_SYSCALL_DEFINE5(waitid, + int, which, compat_pid_t, pid, + struct compat_siginfo __user *, uinfo, int, options, + struct compat_rusage __user *, uru) { siginfo_t info; struct rusage ru; @@ -584,7 +587,11 @@ asmlinkage long compat_sys_waitid(int which, compat_pid_t pid, return ret; if (uru) { - ret = put_compat_rusage(&ru, uru); + /* sys_waitid() overwrites everything in ru */ + if (COMPAT_USE_64BIT_TIME) + ret = copy_to_user(uru, &ru, sizeof(ru)); + else + ret = put_compat_rusage(&ru, uru); if (ret) return ret; } @@ -994,7 +1001,7 @@ compat_sys_rt_sigtimedwait (compat_sigset_t __user *uthese, sigset_from_compat(&s, &s32); if (uts) { - if (get_compat_timespec(&t, uts)) + if (compat_get_timespec(&t, uts)) return -EFAULT; } diff --git a/trunk/kernel/debug/kdb/kdb_main.c b/trunk/kernel/debug/kdb/kdb_main.c index 4d5f8d5612f3..8875254120b6 100644 --- a/trunk/kernel/debug/kdb/kdb_main.c +++ b/trunk/kernel/debug/kdb/kdb_main.c @@ -1970,6 +1970,8 @@ static int kdb_lsmod(int argc, const char **argv) kdb_printf("Module Size modstruct Used by\n"); list_for_each_entry(mod, kdb_modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; kdb_printf("%-20s%8u 0x%p ", mod->name, mod->core_size, (void *)mod); diff --git a/trunk/kernel/fork.c b/trunk/kernel/fork.c index 65ca6d27f24e..c535f33bbb9c 100644 --- a/trunk/kernel/fork.c +++ b/trunk/kernel/fork.c @@ -1668,8 +1668,10 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, int, tls_val) #endif { - return do_fork(clone_flags, newsp, 0, - parent_tidptr, child_tidptr); + long ret = do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr); + asmlinkage_protect(5, ret, clone_flags, newsp, + parent_tidptr, child_tidptr, tls_val); + return ret; } #endif diff --git a/trunk/kernel/module.c b/trunk/kernel/module.c index 250092c1d57d..eab08274ec9b 100644 --- a/trunk/kernel/module.c +++ b/trunk/kernel/module.c @@ -188,6 +188,7 @@ struct load_info { ongoing or failed initialization etc. */ static inline int strong_try_module_get(struct module *mod) { + BUG_ON(mod && mod->state == MODULE_STATE_UNFORMED); if (mod && mod->state == MODULE_STATE_COMING) return -EBUSY; if (try_module_get(mod)) @@ -343,6 +344,9 @@ bool each_symbol_section(bool (*fn)(const struct symsearch *arr, #endif }; + if (mod->state == MODULE_STATE_UNFORMED) + continue; + if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data)) return true; } @@ -450,16 +454,24 @@ const struct kernel_symbol *find_symbol(const char *name, EXPORT_SYMBOL_GPL(find_symbol); /* Search for module by name: must hold module_mutex. */ -struct module *find_module(const char *name) +static struct module *find_module_all(const char *name, + bool even_unformed) { struct module *mod; list_for_each_entry(mod, &modules, list) { + if (!even_unformed && mod->state == MODULE_STATE_UNFORMED) + continue; if (strcmp(mod->name, name) == 0) return mod; } return NULL; } + +struct module *find_module(const char *name) +{ + return find_module_all(name, false); +} EXPORT_SYMBOL_GPL(find_module); #ifdef CONFIG_SMP @@ -525,6 +537,8 @@ bool is_module_percpu_address(unsigned long addr) preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if (!mod->percpu_size) continue; for_each_possible_cpu(cpu) { @@ -1048,6 +1062,8 @@ static ssize_t show_initstate(struct module_attribute *mattr, case MODULE_STATE_GOING: state = "going"; break; + default: + BUG(); } return sprintf(buffer, "%s\n", state); } @@ -1786,6 +1802,8 @@ void set_all_modules_text_rw(void) mutex_lock(&module_mutex); list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if ((mod->module_core) && (mod->core_text_size)) { set_page_attributes(mod->module_core, mod->module_core + mod->core_text_size, @@ -1807,6 +1825,8 @@ void set_all_modules_text_ro(void) mutex_lock(&module_mutex); list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if ((mod->module_core) && (mod->core_text_size)) { set_page_attributes(mod->module_core, mod->module_core + mod->core_text_size, @@ -2527,6 +2547,13 @@ static int copy_module_from_fd(int fd, struct load_info *info) err = -EFBIG; goto out; } + + /* Don't hand 0 to vmalloc, it whines. */ + if (stat.size == 0) { + err = -EINVAL; + goto out; + } + info->hdr = vmalloc(stat.size); if (!info->hdr) { err = -ENOMEM; @@ -2990,8 +3017,9 @@ static bool finished_loading(const char *name) bool ret; mutex_lock(&module_mutex); - mod = find_module(name); - ret = !mod || mod->state != MODULE_STATE_COMING; + mod = find_module_all(name, true); + ret = !mod || mod->state == MODULE_STATE_LIVE + || mod->state == MODULE_STATE_GOING; mutex_unlock(&module_mutex); return ret; @@ -3013,6 +3041,12 @@ static int do_init_module(struct module *mod) { int ret = 0; + /* + * We want to find out whether @mod uses async during init. Clear + * PF_USED_ASYNC. async_schedule*() will set it. + */ + current->flags &= ~PF_USED_ASYNC; + blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod); @@ -3058,8 +3092,25 @@ static int do_init_module(struct module *mod) blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_LIVE, mod); - /* We need to finish all async code before the module init sequence is done */ - async_synchronize_full(); + /* + * We need to finish all async code before the module init sequence + * is done. This has potential to deadlock. For example, a newly + * detected block device can trigger request_module() of the + * default iosched from async probing task. Once userland helper + * reaches here, async_synchronize_full() will wait on the async + * task waiting on request_module() and deadlock. + * + * This deadlock is avoided by perfomring async_synchronize_full() + * iff module init queued any async jobs. This isn't a full + * solution as it will deadlock the same if module loading from + * async jobs nests more than once; however, due to the various + * constraints, this hack seems to be the best option for now. + * Please refer to the following thread for details. + * + * http://thread.gmane.org/gmane.linux.kernel/1420814 + */ + if (current->flags & PF_USED_ASYNC) + async_synchronize_full(); mutex_lock(&module_mutex); /* Drop initial reference. */ @@ -3113,6 +3164,32 @@ static int load_module(struct load_info *info, const char __user *uargs, goto free_copy; } + /* + * We try to place it in the list now to make sure it's unique + * before we dedicate too many resources. In particular, + * temporary percpu memory exhaustion. + */ + mod->state = MODULE_STATE_UNFORMED; +again: + mutex_lock(&module_mutex); + if ((old = find_module_all(mod->name, true)) != NULL) { + if (old->state == MODULE_STATE_COMING + || old->state == MODULE_STATE_UNFORMED) { + /* Wait in case it fails to load. */ + mutex_unlock(&module_mutex); + err = wait_event_interruptible(module_wq, + finished_loading(mod->name)); + if (err) + goto free_module; + goto again; + } + err = -EEXIST; + mutex_unlock(&module_mutex); + goto free_module; + } + list_add_rcu(&mod->list, &modules); + mutex_unlock(&module_mutex); + #ifdef CONFIG_MODULE_SIG mod->sig_ok = info->sig_ok; if (!mod->sig_ok) @@ -3122,7 +3199,7 @@ static int load_module(struct load_info *info, const char __user *uargs, /* Now module is in final location, initialize linked lists, etc. */ err = module_unload_init(mod); if (err) - goto free_module; + goto unlink_mod; /* Now we've got everything in the final locations, we can * find optional sections. */ @@ -3157,54 +3234,33 @@ static int load_module(struct load_info *info, const char __user *uargs, goto free_arch_cleanup; } - /* Mark state as coming so strong_try_module_get() ignores us. */ - mod->state = MODULE_STATE_COMING; - - /* Now sew it into the lists so we can get lockdep and oops - * info during argument parsing. No one should access us, since - * strong_try_module_get() will fail. - * lockdep/oops can run asynchronous, so use the RCU list insertion - * function to insert in a way safe to concurrent readers. - * The mutex protects against concurrent writers. - */ -again: - mutex_lock(&module_mutex); - if ((old = find_module(mod->name)) != NULL) { - if (old->state == MODULE_STATE_COMING) { - /* Wait in case it fails to load. */ - mutex_unlock(&module_mutex); - err = wait_event_interruptible(module_wq, - finished_loading(mod->name)); - if (err) - goto free_arch_cleanup; - goto again; - } - err = -EEXIST; - goto unlock; - } - - /* This has to be done once we're sure module name is unique. */ dynamic_debug_setup(info->debug, info->num_debug); - /* Find duplicate symbols */ + mutex_lock(&module_mutex); + /* Find duplicate symbols (must be called under lock). */ err = verify_export_symbols(mod); if (err < 0) - goto ddebug; + goto ddebug_cleanup; + /* This relies on module_mutex for list integrity. */ module_bug_finalize(info->hdr, info->sechdrs, mod); - list_add_rcu(&mod->list, &modules); + + /* Mark state as coming so strong_try_module_get() ignores us, + * but kallsyms etc. can see us. */ + mod->state = MODULE_STATE_COMING; + mutex_unlock(&module_mutex); /* Module is ready to execute: parsing args may do that. */ err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, -32768, 32767, &ddebug_dyndbg_module_param_cb); if (err < 0) - goto unlink; + goto bug_cleanup; /* Link in to syfs. */ err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp); if (err < 0) - goto unlink; + goto bug_cleanup; /* Get rid of temporary copy. */ free_copy(info); @@ -3214,16 +3270,13 @@ static int load_module(struct load_info *info, const char __user *uargs, return do_init_module(mod); - unlink: + bug_cleanup: + /* module_bug_cleanup needs module_mutex protection */ mutex_lock(&module_mutex); - /* Unlink carefully: kallsyms could be walking list. */ - list_del_rcu(&mod->list); module_bug_cleanup(mod); - wake_up_all(&module_wq); - ddebug: - dynamic_debug_remove(info->debug); - unlock: + ddebug_cleanup: mutex_unlock(&module_mutex); + dynamic_debug_remove(info->debug); synchronize_sched(); kfree(mod->args); free_arch_cleanup: @@ -3232,6 +3285,12 @@ static int load_module(struct load_info *info, const char __user *uargs, free_modinfo(mod); free_unload: module_unload_free(mod); + unlink_mod: + mutex_lock(&module_mutex); + /* Unlink carefully: kallsyms could be walking list. */ + list_del_rcu(&mod->list); + wake_up_all(&module_wq); + mutex_unlock(&module_mutex); free_module: module_deallocate(mod, info); free_copy: @@ -3354,6 +3413,8 @@ const char *module_address_lookup(unsigned long addr, preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if (within_module_init(addr, mod) || within_module_core(addr, mod)) { if (modname) @@ -3377,6 +3438,8 @@ int lookup_module_symbol_name(unsigned long addr, char *symname) preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if (within_module_init(addr, mod) || within_module_core(addr, mod)) { const char *sym; @@ -3401,6 +3464,8 @@ int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if (within_module_init(addr, mod) || within_module_core(addr, mod)) { const char *sym; @@ -3428,6 +3493,8 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if (symnum < mod->num_symtab) { *value = mod->symtab[symnum].st_value; *type = mod->symtab[symnum].st_info; @@ -3470,9 +3537,12 @@ unsigned long module_kallsyms_lookup_name(const char *name) ret = mod_find_symname(mod, colon+1); *colon = ':'; } else { - list_for_each_entry_rcu(mod, &modules, list) + list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if ((ret = mod_find_symname(mod, name)) != 0) break; + } } preempt_enable(); return ret; @@ -3487,6 +3557,8 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, int ret; list_for_each_entry(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; for (i = 0; i < mod->num_symtab; i++) { ret = fn(data, mod->strtab + mod->symtab[i].st_name, mod, mod->symtab[i].st_value); @@ -3502,6 +3574,7 @@ static char *module_flags(struct module *mod, char *buf) { int bx = 0; + BUG_ON(mod->state == MODULE_STATE_UNFORMED); if (mod->taints || mod->state == MODULE_STATE_GOING || mod->state == MODULE_STATE_COMING) { @@ -3543,6 +3616,10 @@ static int m_show(struct seq_file *m, void *p) struct module *mod = list_entry(p, struct module, list); char buf[8]; + /* We always ignore unformed modules. */ + if (mod->state == MODULE_STATE_UNFORMED) + return 0; + seq_printf(m, "%s %u", mod->name, mod->init_size + mod->core_size); print_unload_info(m, mod); @@ -3603,6 +3680,8 @@ const struct exception_table_entry *search_module_extables(unsigned long addr) preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if (mod->num_exentries == 0) continue; @@ -3651,10 +3730,13 @@ struct module *__module_address(unsigned long addr) if (addr < module_addr_min || addr > module_addr_max) return NULL; - list_for_each_entry_rcu(mod, &modules, list) + list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; if (within_module_core(addr, mod) || within_module_init(addr, mod)) return mod; + } return NULL; } EXPORT_SYMBOL_GPL(__module_address); @@ -3707,8 +3789,11 @@ void print_modules(void) printk(KERN_DEFAULT "Modules linked in:"); /* Most callers should already have preempt disabled, but make sure */ preempt_disable(); - list_for_each_entry_rcu(mod, &modules, list) + list_for_each_entry_rcu(mod, &modules, list) { + if (mod->state == MODULE_STATE_UNFORMED) + continue; printk(" %s%s", mod->name, module_flags(mod, buf)); + } preempt_enable(); if (last_unloaded_module[0]) printk(" [last unloaded: %s]", last_unloaded_module); diff --git a/trunk/kernel/ptrace.c b/trunk/kernel/ptrace.c index 1599157336a6..6cbeaae4406d 100644 --- a/trunk/kernel/ptrace.c +++ b/trunk/kernel/ptrace.c @@ -117,11 +117,45 @@ void __ptrace_unlink(struct task_struct *child) * TASK_KILLABLE sleeps. */ if (child->jobctl & JOBCTL_STOP_PENDING || task_is_traced(child)) - signal_wake_up(child, task_is_traced(child)); + ptrace_signal_wake_up(child, true); spin_unlock(&child->sighand->siglock); } +/* Ensure that nothing can wake it up, even SIGKILL */ +static bool ptrace_freeze_traced(struct task_struct *task) +{ + bool ret = false; + + /* Lockless, nobody but us can set this flag */ + if (task->jobctl & JOBCTL_LISTENING) + return ret; + + spin_lock_irq(&task->sighand->siglock); + if (task_is_traced(task) && !__fatal_signal_pending(task)) { + task->state = __TASK_TRACED; + ret = true; + } + spin_unlock_irq(&task->sighand->siglock); + + return ret; +} + +static void ptrace_unfreeze_traced(struct task_struct *task) +{ + if (task->state != __TASK_TRACED) + return; + + WARN_ON(!task->ptrace || task->parent != current); + + spin_lock_irq(&task->sighand->siglock); + if (__fatal_signal_pending(task)) + wake_up_state(task, __TASK_TRACED); + else + task->state = TASK_TRACED; + spin_unlock_irq(&task->sighand->siglock); +} + /** * ptrace_check_attach - check whether ptracee is ready for ptrace operation * @child: ptracee to check for @@ -139,7 +173,7 @@ void __ptrace_unlink(struct task_struct *child) * RETURNS: * 0 on success, -ESRCH if %child is not ready. */ -int ptrace_check_attach(struct task_struct *child, bool ignore_state) +static int ptrace_check_attach(struct task_struct *child, bool ignore_state) { int ret = -ESRCH; @@ -151,24 +185,29 @@ int ptrace_check_attach(struct task_struct *child, bool ignore_state) * be changed by us so it's not changing right after this. */ read_lock(&tasklist_lock); - if ((child->ptrace & PT_PTRACED) && child->parent == current) { + if (child->ptrace && child->parent == current) { + WARN_ON(child->state == __TASK_TRACED); /* * child->sighand can't be NULL, release_task() * does ptrace_unlink() before __exit_signal(). */ - spin_lock_irq(&child->sighand->siglock); - WARN_ON_ONCE(task_is_stopped(child)); - if (ignore_state || (task_is_traced(child) && - !(child->jobctl & JOBCTL_LISTENING))) + if (ignore_state || ptrace_freeze_traced(child)) ret = 0; - spin_unlock_irq(&child->sighand->siglock); } read_unlock(&tasklist_lock); - if (!ret && !ignore_state) - ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH; + if (!ret && !ignore_state) { + if (!wait_task_inactive(child, __TASK_TRACED)) { + /* + * This can only happen if may_ptrace_stop() fails and + * ptrace_stop() changes ->state back to TASK_RUNNING, + * so we should not worry about leaking __TASK_TRACED. + */ + WARN_ON(child->state == __TASK_TRACED); + ret = -ESRCH; + } + } - /* All systems go.. */ return ret; } @@ -317,7 +356,7 @@ static int ptrace_attach(struct task_struct *task, long request, */ if (task_is_stopped(task) && task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING)) - signal_wake_up(task, 1); + signal_wake_up_state(task, __TASK_STOPPED); spin_unlock(&task->sighand->siglock); @@ -737,7 +776,7 @@ int ptrace_request(struct task_struct *child, long request, * tracee into STOP. */ if (likely(task_set_jobctl_pending(child, JOBCTL_TRAP_STOP))) - signal_wake_up(child, child->jobctl & JOBCTL_LISTENING); + ptrace_signal_wake_up(child, child->jobctl & JOBCTL_LISTENING); unlock_task_sighand(child, &flags); ret = 0; @@ -763,7 +802,7 @@ int ptrace_request(struct task_struct *child, long request, * start of this trap and now. Trigger re-trap. */ if (child->jobctl & JOBCTL_TRAP_NOTIFY) - signal_wake_up(child, true); + ptrace_signal_wake_up(child, true); ret = 0; } unlock_task_sighand(child, &flags); @@ -900,6 +939,8 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, goto out_put_task_struct; ret = arch_ptrace(child, request, addr, data); + if (ret || request != PTRACE_DETACH) + ptrace_unfreeze_traced(child); out_put_task_struct: put_task_struct(child); @@ -1039,8 +1080,11 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, ret = ptrace_check_attach(child, request == PTRACE_KILL || request == PTRACE_INTERRUPT); - if (!ret) + if (!ret) { ret = compat_arch_ptrace(child, request, addr, data); + if (ret || request != PTRACE_DETACH) + ptrace_unfreeze_traced(child); + } out_put_task_struct: put_task_struct(child); diff --git a/trunk/kernel/rwsem.c b/trunk/kernel/rwsem.c index 6850f53e02d8..b3c6c3fcd847 100644 --- a/trunk/kernel/rwsem.c +++ b/trunk/kernel/rwsem.c @@ -116,6 +116,16 @@ void down_read_nested(struct rw_semaphore *sem, int subclass) EXPORT_SYMBOL(down_read_nested); +void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest) +{ + might_sleep(); + rwsem_acquire_nest(&sem->dep_map, 0, 0, nest, _RET_IP_); + + LOCK_CONTENDED(sem, __down_write_trylock, __down_write); +} + +EXPORT_SYMBOL(_down_write_nest_lock); + void down_write_nested(struct rw_semaphore *sem, int subclass) { might_sleep(); diff --git a/trunk/kernel/sched/core.c b/trunk/kernel/sched/core.c index 257002c13bb0..26058d0bebba 100644 --- a/trunk/kernel/sched/core.c +++ b/trunk/kernel/sched/core.c @@ -1523,7 +1523,8 @@ static void try_to_wake_up_local(struct task_struct *p) */ int wake_up_process(struct task_struct *p) { - return try_to_wake_up(p, TASK_ALL, 0); + WARN_ON(task_is_stopped_or_traced(p)); + return try_to_wake_up(p, TASK_NORMAL, 0); } EXPORT_SYMBOL(wake_up_process); diff --git a/trunk/kernel/signal.c b/trunk/kernel/signal.c index 372771e948c2..3d09cf6cde75 100644 --- a/trunk/kernel/signal.c +++ b/trunk/kernel/signal.c @@ -680,23 +680,17 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) * No need to set need_resched since signal event passing * goes through ->blocked */ -void signal_wake_up(struct task_struct *t, int resume) +void signal_wake_up_state(struct task_struct *t, unsigned int state) { - unsigned int mask; - set_tsk_thread_flag(t, TIF_SIGPENDING); - /* - * For SIGKILL, we want to wake it up in the stopped/traced/killable + * TASK_WAKEKILL also means wake it up in the stopped/traced/killable * case. We don't check t->state here because there is a race with it * executing another processor and just now entering stopped state. * By using wake_up_state, we ensure the process will wake up and * handle its death signal. */ - mask = TASK_INTERRUPTIBLE; - if (resume) - mask |= TASK_WAKEKILL; - if (!wake_up_state(t, mask)) + if (!wake_up_state(t, state | TASK_INTERRUPTIBLE)) kick_process(t); } @@ -844,7 +838,7 @@ static void ptrace_trap_notify(struct task_struct *t) assert_spin_locked(&t->sighand->siglock); task_set_jobctl_pending(t, JOBCTL_TRAP_NOTIFY); - signal_wake_up(t, t->jobctl & JOBCTL_LISTENING); + ptrace_signal_wake_up(t, t->jobctl & JOBCTL_LISTENING); } /* @@ -1800,6 +1794,10 @@ static inline int may_ptrace_stop(void) * If SIGKILL was already sent before the caller unlocked * ->siglock we must see ->core_state != NULL. Otherwise it * is safe to enter schedule(). + * + * This is almost outdated, a task with the pending SIGKILL can't + * block in TASK_TRACED. But PTRACE_EVENT_EXIT can be reported + * after SIGKILL was already dequeued. */ if (unlikely(current->mm->core_state) && unlikely(current->mm == current->parent->mm)) @@ -1925,6 +1923,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, siginfo_t *info) if (gstop_done) do_notify_parent_cldstop(current, false, why); + /* tasklist protects us from ptrace_freeze_traced() */ __set_current_state(TASK_RUNNING); if (clear_code) current->exit_code = 0; @@ -3116,8 +3115,9 @@ int __save_altstack(stack_t __user *uss, unsigned long sp) #ifdef CONFIG_COMPAT #ifdef CONFIG_GENERIC_SIGALTSTACK -asmlinkage long compat_sys_sigaltstack(const compat_stack_t __user *uss_ptr, - compat_stack_t __user *uoss_ptr) +COMPAT_SYSCALL_DEFINE2(sigaltstack, + const compat_stack_t __user *, uss_ptr, + compat_stack_t __user *, uoss_ptr) { stack_t uss, uoss; int ret; diff --git a/trunk/kernel/trace/ftrace.c b/trunk/kernel/trace/ftrace.c index 3ffe4c5ad3f3..41473b4ad7a4 100644 --- a/trunk/kernel/trace/ftrace.c +++ b/trunk/kernel/trace/ftrace.c @@ -3998,7 +3998,7 @@ static int ftrace_module_notify(struct notifier_block *self, struct notifier_block ftrace_module_nb = { .notifier_call = ftrace_module_notify, - .priority = 0, + .priority = INT_MAX, /* Run before anything that can use kprobes */ }; extern unsigned long __start_mcount_loc[]; diff --git a/trunk/kernel/trace/trace.c b/trunk/kernel/trace/trace.c index e5125677efa0..3c13e46d7d24 100644 --- a/trunk/kernel/trace/trace.c +++ b/trunk/kernel/trace/trace.c @@ -2899,6 +2899,8 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf, if (copy_from_user(&buf, ubuf, cnt)) return -EFAULT; + buf[cnt] = 0; + trace_set_options(buf); *ppos += cnt; @@ -3452,7 +3454,7 @@ static int tracing_wait_pipe(struct file *filp) return -EINTR; /* - * We block until we read something and tracing is enabled. + * We block until we read something and tracing is disabled. * We still block if tracing is disabled, but we have never * read anything. This allows a user to cat this file, and * then enable tracing. But after we have read something, @@ -3460,7 +3462,7 @@ static int tracing_wait_pipe(struct file *filp) * * iter->pos will be 0 if we haven't read anything. */ - if (tracing_is_enabled() && iter->pos) + if (!tracing_is_enabled() && iter->pos) break; } @@ -4815,10 +4817,17 @@ rb_simple_write(struct file *filp, const char __user *ubuf, return ret; if (buffer) { - if (val) + mutex_lock(&trace_types_lock); + if (val) { ring_buffer_record_on(buffer); - else + if (current_trace->start) + current_trace->start(tr); + } else { ring_buffer_record_off(buffer); + if (current_trace->stop) + current_trace->stop(tr); + } + mutex_unlock(&trace_types_lock); } (*ppos)++; diff --git a/trunk/lib/bug.c b/trunk/lib/bug.c index a28c1415357c..d0cdf14c651a 100644 --- a/trunk/lib/bug.c +++ b/trunk/lib/bug.c @@ -55,6 +55,7 @@ static inline unsigned long bug_addr(const struct bug_entry *bug) } #ifdef CONFIG_MODULES +/* Updates are protected by module mutex */ static LIST_HEAD(module_bug_list); static const struct bug_entry *module_find_bug(unsigned long bugaddr) diff --git a/trunk/lib/cpu_rmap.c b/trunk/lib/cpu_rmap.c index 145dec5267c9..5fbed5caba6e 100644 --- a/trunk/lib/cpu_rmap.c +++ b/trunk/lib/cpu_rmap.c @@ -45,6 +45,7 @@ struct cpu_rmap *alloc_cpu_rmap(unsigned int size, gfp_t flags) if (!rmap) return NULL; + kref_init(&rmap->refcount); rmap->obj = (void **)((char *)rmap + obj_offset); /* Initially assign CPUs to objects on a rota, since we have @@ -63,6 +64,35 @@ struct cpu_rmap *alloc_cpu_rmap(unsigned int size, gfp_t flags) } EXPORT_SYMBOL(alloc_cpu_rmap); +/** + * cpu_rmap_release - internal reclaiming helper called from kref_put + * @ref: kref to struct cpu_rmap + */ +static void cpu_rmap_release(struct kref *ref) +{ + struct cpu_rmap *rmap = container_of(ref, struct cpu_rmap, refcount); + kfree(rmap); +} + +/** + * cpu_rmap_get - internal helper to get new ref on a cpu_rmap + * @rmap: reverse-map allocated with alloc_cpu_rmap() + */ +static inline void cpu_rmap_get(struct cpu_rmap *rmap) +{ + kref_get(&rmap->refcount); +} + +/** + * cpu_rmap_put - release ref on a cpu_rmap + * @rmap: reverse-map allocated with alloc_cpu_rmap() + */ +int cpu_rmap_put(struct cpu_rmap *rmap) +{ + return kref_put(&rmap->refcount, cpu_rmap_release); +} +EXPORT_SYMBOL(cpu_rmap_put); + /* Reevaluate nearest object for given CPU, comparing with the given * neighbours at the given distance. */ @@ -197,8 +227,7 @@ struct irq_glue { * free_irq_cpu_rmap - free a CPU affinity reverse-map used for IRQs * @rmap: Reverse-map allocated with alloc_irq_cpu_map(), or %NULL * - * Must be called in process context, before freeing the IRQs, and - * without holding any locks required by global workqueue items. + * Must be called in process context, before freeing the IRQs. */ void free_irq_cpu_rmap(struct cpu_rmap *rmap) { @@ -212,12 +241,18 @@ void free_irq_cpu_rmap(struct cpu_rmap *rmap) glue = rmap->obj[index]; irq_set_affinity_notifier(glue->notify.irq, NULL); } - irq_run_affinity_notifiers(); - kfree(rmap); + cpu_rmap_put(rmap); } EXPORT_SYMBOL(free_irq_cpu_rmap); +/** + * irq_cpu_rmap_notify - callback for IRQ subsystem when IRQ affinity updated + * @notify: struct irq_affinity_notify passed by irq/manage.c + * @mask: cpu mask for new SMP affinity + * + * This is executed in workqueue context. + */ static void irq_cpu_rmap_notify(struct irq_affinity_notify *notify, const cpumask_t *mask) { @@ -230,10 +265,16 @@ irq_cpu_rmap_notify(struct irq_affinity_notify *notify, const cpumask_t *mask) pr_warning("irq_cpu_rmap_notify: update failed: %d\n", rc); } +/** + * irq_cpu_rmap_release - reclaiming callback for IRQ subsystem + * @ref: kref to struct irq_affinity_notify passed by irq/manage.c + */ static void irq_cpu_rmap_release(struct kref *ref) { struct irq_glue *glue = container_of(ref, struct irq_glue, notify.kref); + + cpu_rmap_put(glue->rmap); kfree(glue); } @@ -258,10 +299,13 @@ int irq_cpu_rmap_add(struct cpu_rmap *rmap, int irq) glue->notify.notify = irq_cpu_rmap_notify; glue->notify.release = irq_cpu_rmap_release; glue->rmap = rmap; + cpu_rmap_get(rmap); glue->index = cpu_rmap_add(rmap, glue); rc = irq_set_affinity_notifier(irq, &glue->notify); - if (rc) + if (rc) { + cpu_rmap_put(glue->rmap); kfree(glue); + } return rc; } EXPORT_SYMBOL(irq_cpu_rmap_add); diff --git a/trunk/lib/rbtree.c b/trunk/lib/rbtree.c index 4f56a11d67fa..c0e31fe2fabf 100644 --- a/trunk/lib/rbtree.c +++ b/trunk/lib/rbtree.c @@ -194,8 +194,12 @@ __rb_insert(struct rb_node *node, struct rb_root *root, } } -__always_inline void -__rb_erase_color(struct rb_node *parent, struct rb_root *root, +/* + * Inline version for rb_erase() use - we want to be able to inline + * and eliminate the dummy_rotate callback there + */ +static __always_inline void +____rb_erase_color(struct rb_node *parent, struct rb_root *root, void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) { struct rb_node *node = NULL, *sibling, *tmp1, *tmp2; @@ -355,6 +359,13 @@ __rb_erase_color(struct rb_node *parent, struct rb_root *root, } } } + +/* Non-inline version for rb_erase_augmented() use */ +void __rb_erase_color(struct rb_node *parent, struct rb_root *root, + void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) +{ + ____rb_erase_color(parent, root, augment_rotate); +} EXPORT_SYMBOL(__rb_erase_color); /* @@ -380,7 +391,10 @@ EXPORT_SYMBOL(rb_insert_color); void rb_erase(struct rb_node *node, struct rb_root *root) { - rb_erase_augmented(node, root, &dummy_callbacks); + struct rb_node *rebalance; + rebalance = __rb_erase_augmented(node, root, &dummy_callbacks); + if (rebalance) + ____rb_erase_color(rebalance, root, dummy_rotate); } EXPORT_SYMBOL(rb_erase); diff --git a/trunk/mm/bootmem.c b/trunk/mm/bootmem.c index 1324cd74faec..b93376c39b61 100644 --- a/trunk/mm/bootmem.c +++ b/trunk/mm/bootmem.c @@ -185,10 +185,23 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) while (start < end) { unsigned long *map, idx, vec; + unsigned shift; map = bdata->node_bootmem_map; idx = start - bdata->node_min_pfn; + shift = idx & (BITS_PER_LONG - 1); + /* + * vec holds at most BITS_PER_LONG map bits, + * bit 0 corresponds to start. + */ vec = ~map[idx / BITS_PER_LONG]; + + if (shift) { + vec >>= shift; + if (end - start >= BITS_PER_LONG) + vec |= ~map[idx / BITS_PER_LONG + 1] << + (BITS_PER_LONG - shift); + } /* * If we have a properly aligned and fully unreserved * BITS_PER_LONG block of pages in front of us, free @@ -201,19 +214,18 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) count += BITS_PER_LONG; start += BITS_PER_LONG; } else { - unsigned long off = 0; + unsigned long cur = start; - vec >>= start & (BITS_PER_LONG - 1); - while (vec) { + start = ALIGN(start + 1, BITS_PER_LONG); + while (vec && cur != start) { if (vec & 1) { - page = pfn_to_page(start + off); + page = pfn_to_page(cur); __free_pages_bootmem(page, 0); count++; } vec >>= 1; - off++; + ++cur; } - start = ALIGN(start + 1, BITS_PER_LONG); } } diff --git a/trunk/mm/compaction.c b/trunk/mm/compaction.c index 6b807e466497..c62bd063d766 100644 --- a/trunk/mm/compaction.c +++ b/trunk/mm/compaction.c @@ -816,6 +816,7 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone, static int compact_finished(struct zone *zone, struct compact_control *cc) { + unsigned int order; unsigned long watermark; if (fatal_signal_pending(current)) @@ -850,22 +851,16 @@ static int compact_finished(struct zone *zone, return COMPACT_CONTINUE; /* Direct compactor: Is a suitable page free? */ - if (cc->page) { - /* Was a suitable page captured? */ - if (*cc->page) + for (order = cc->order; order < MAX_ORDER; order++) { + struct free_area *area = &zone->free_area[order]; + + /* Job done if page is free of the right migratetype */ + if (!list_empty(&area->free_list[cc->migratetype])) + return COMPACT_PARTIAL; + + /* Job done if allocation would set block type */ + if (cc->order >= pageblock_order && area->nr_free) return COMPACT_PARTIAL; - } else { - unsigned int order; - for (order = cc->order; order < MAX_ORDER; order++) { - struct free_area *area = &zone->free_area[cc->order]; - /* Job done if page is free of the right migratetype */ - if (!list_empty(&area->free_list[cc->migratetype])) - return COMPACT_PARTIAL; - - /* Job done if allocation would set block type */ - if (cc->order >= pageblock_order && area->nr_free) - return COMPACT_PARTIAL; - } } return COMPACT_CONTINUE; @@ -921,60 +916,6 @@ unsigned long compaction_suitable(struct zone *zone, int order) return COMPACT_CONTINUE; } -static void compact_capture_page(struct compact_control *cc) -{ - unsigned long flags; - int mtype, mtype_low, mtype_high; - - if (!cc->page || *cc->page) - return; - - /* - * For MIGRATE_MOVABLE allocations we capture a suitable page ASAP - * regardless of the migratetype of the freelist is is captured from. - * This is fine because the order for a high-order MIGRATE_MOVABLE - * allocation is typically at least a pageblock size and overall - * fragmentation is not impaired. Other allocation types must - * capture pages from their own migratelist because otherwise they - * could pollute other pageblocks like MIGRATE_MOVABLE with - * difficult to move pages and making fragmentation worse overall. - */ - if (cc->migratetype == MIGRATE_MOVABLE) { - mtype_low = 0; - mtype_high = MIGRATE_PCPTYPES; - } else { - mtype_low = cc->migratetype; - mtype_high = cc->migratetype + 1; - } - - /* Speculatively examine the free lists without zone lock */ - for (mtype = mtype_low; mtype < mtype_high; mtype++) { - int order; - for (order = cc->order; order < MAX_ORDER; order++) { - struct page *page; - struct free_area *area; - area = &(cc->zone->free_area[order]); - if (list_empty(&area->free_list[mtype])) - continue; - - /* Take the lock and attempt capture of the page */ - if (!compact_trylock_irqsave(&cc->zone->lock, &flags, cc)) - return; - if (!list_empty(&area->free_list[mtype])) { - page = list_entry(area->free_list[mtype].next, - struct page, lru); - if (capture_free_page(page, cc->order, mtype)) { - spin_unlock_irqrestore(&cc->zone->lock, - flags); - *cc->page = page; - return; - } - } - spin_unlock_irqrestore(&cc->zone->lock, flags); - } - } -} - static int compact_zone(struct zone *zone, struct compact_control *cc) { int ret; @@ -1054,9 +995,6 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) goto out; } } - - /* Capture a page now if it is a suitable size */ - compact_capture_page(cc); } out: @@ -1069,8 +1007,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) static unsigned long compact_zone_order(struct zone *zone, int order, gfp_t gfp_mask, - bool sync, bool *contended, - struct page **page) + bool sync, bool *contended) { unsigned long ret; struct compact_control cc = { @@ -1080,7 +1017,6 @@ static unsigned long compact_zone_order(struct zone *zone, .migratetype = allocflags_to_migratetype(gfp_mask), .zone = zone, .sync = sync, - .page = page, }; INIT_LIST_HEAD(&cc.freepages); INIT_LIST_HEAD(&cc.migratepages); @@ -1110,7 +1046,7 @@ int sysctl_extfrag_threshold = 500; */ unsigned long try_to_compact_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *nodemask, - bool sync, bool *contended, struct page **page) + bool sync, bool *contended) { enum zone_type high_zoneidx = gfp_zone(gfp_mask); int may_enter_fs = gfp_mask & __GFP_FS; @@ -1136,7 +1072,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, int status; status = compact_zone_order(zone, order, gfp_mask, sync, - contended, page); + contended); rc = max(status, rc); /* If a normal allocation would succeed, stop compacting */ @@ -1192,7 +1128,6 @@ int compact_pgdat(pg_data_t *pgdat, int order) struct compact_control cc = { .order = order, .sync = false, - .page = NULL, }; return __compact_pgdat(pgdat, &cc); @@ -1203,14 +1138,13 @@ static int compact_node(int nid) struct compact_control cc = { .order = -1, .sync = true, - .page = NULL, }; return __compact_pgdat(NODE_DATA(nid), &cc); } /* Compact all nodes in the system */ -static int compact_nodes(void) +static void compact_nodes(void) { int nid; @@ -1219,8 +1153,6 @@ static int compact_nodes(void) for_each_online_node(nid) compact_node(nid); - - return COMPACT_COMPLETE; } /* The written value is actually unused, all memory is compacted */ @@ -1231,7 +1163,7 @@ int sysctl_compaction_handler(struct ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) { if (write) - return compact_nodes(); + compact_nodes(); return 0; } diff --git a/trunk/mm/huge_memory.c b/trunk/mm/huge_memory.c index 9e894edc7811..6001ee6347a9 100644 --- a/trunk/mm/huge_memory.c +++ b/trunk/mm/huge_memory.c @@ -1819,9 +1819,19 @@ int split_huge_page(struct page *page) BUG_ON(is_huge_zero_pfn(page_to_pfn(page))); BUG_ON(!PageAnon(page)); - anon_vma = page_lock_anon_vma_read(page); + + /* + * The caller does not necessarily hold an mmap_sem that would prevent + * the anon_vma disappearing so we first we take a reference to it + * and then lock the anon_vma for write. This is similar to + * page_lock_anon_vma_read except the write lock is taken to serialise + * against parallel split or collapse operations. + */ + anon_vma = page_get_anon_vma(page); if (!anon_vma) goto out; + anon_vma_lock_write(anon_vma); + ret = 0; if (!PageCompound(page)) goto out_unlock; @@ -1832,7 +1842,8 @@ int split_huge_page(struct page *page) BUG_ON(PageCompound(page)); out_unlock: - page_unlock_anon_vma_read(anon_vma); + anon_vma_unlock(anon_vma); + put_anon_vma(anon_vma); out: return ret; } diff --git a/trunk/mm/internal.h b/trunk/mm/internal.h index d597f94cc205..9ba21100ebf3 100644 --- a/trunk/mm/internal.h +++ b/trunk/mm/internal.h @@ -135,7 +135,6 @@ struct compact_control { int migratetype; /* MOVABLE, RECLAIMABLE etc */ struct zone *zone; bool contended; /* True if a lock was contended */ - struct page **page; /* Page captured of requested size */ }; unsigned long diff --git a/trunk/mm/memblock.c b/trunk/mm/memblock.c index 625905523c2a..88adc8afb610 100644 --- a/trunk/mm/memblock.c +++ b/trunk/mm/memblock.c @@ -314,7 +314,8 @@ static void __init_memblock memblock_merge_regions(struct memblock_type *type) } this->size += next->size; - memmove(next, next + 1, (type->cnt - (i + 1)) * sizeof(*next)); + /* move forward from next + 1, index of which is i + 2 */ + memmove(next, next + 1, (type->cnt - (i + 2)) * sizeof(*next)); type->cnt--; } } diff --git a/trunk/mm/migrate.c b/trunk/mm/migrate.c index 3b676b0c5c3e..c38778610aa8 100644 --- a/trunk/mm/migrate.c +++ b/trunk/mm/migrate.c @@ -1679,9 +1679,21 @@ int migrate_misplaced_transhuge_page(struct mm_struct *mm, page_xchg_last_nid(new_page, page_last_nid(page)); isolated = numamigrate_isolate_page(pgdat, page); - if (!isolated) { + + /* + * Failing to isolate or a GUP pin prevents migration. The expected + * page count is 2. 1 for anonymous pages without a mapping and 1 + * for the callers pin. If the page was isolated, the page will + * need to be put back on the LRU. + */ + if (!isolated || page_count(page) != 2) { count_vm_events(PGMIGRATE_FAIL, HPAGE_PMD_NR); put_page(new_page); + if (isolated) { + putback_lru_page(page); + isolated = 0; + goto out; + } goto out_keep_locked; } diff --git a/trunk/mm/mmap.c b/trunk/mm/mmap.c index f54b235f29a9..35730ee9d515 100644 --- a/trunk/mm/mmap.c +++ b/trunk/mm/mmap.c @@ -2886,7 +2886,7 @@ static void vm_lock_anon_vma(struct mm_struct *mm, struct anon_vma *anon_vma) * The LSB of head.next can't change from under us * because we hold the mm_all_locks_mutex. */ - down_write(&anon_vma->root->rwsem); + down_write_nest_lock(&anon_vma->root->rwsem, &mm->mmap_sem); /* * We can safely modify head.next after taking the * anon_vma->root->rwsem. If some other vma in this mm shares diff --git a/trunk/mm/page_alloc.c b/trunk/mm/page_alloc.c index bc6cc0e913bd..df2022ff0c8a 100644 --- a/trunk/mm/page_alloc.c +++ b/trunk/mm/page_alloc.c @@ -1384,14 +1384,8 @@ void split_page(struct page *page, unsigned int order) set_page_refcounted(page + i); } -/* - * Similar to the split_page family of functions except that the page - * required at the given order and being isolated now to prevent races - * with parallel allocators - */ -int capture_free_page(struct page *page, int alloc_order, int migratetype) +static int __isolate_free_page(struct page *page, unsigned int order) { - unsigned int order; unsigned long watermark; struct zone *zone; int mt; @@ -1399,7 +1393,6 @@ int capture_free_page(struct page *page, int alloc_order, int migratetype) BUG_ON(!PageBuddy(page)); zone = page_zone(page); - order = page_order(page); mt = get_pageblock_migratetype(page); if (mt != MIGRATE_ISOLATE) { @@ -1408,7 +1401,7 @@ int capture_free_page(struct page *page, int alloc_order, int migratetype) if (!zone_watermark_ok(zone, 0, watermark, 0, 0)) return 0; - __mod_zone_freepage_state(zone, -(1UL << alloc_order), mt); + __mod_zone_freepage_state(zone, -(1UL << order), mt); } /* Remove page from free list */ @@ -1416,11 +1409,7 @@ int capture_free_page(struct page *page, int alloc_order, int migratetype) zone->free_area[order].nr_free--; rmv_page_order(page); - if (alloc_order != order) - expand(zone, page, alloc_order, order, - &zone->free_area[order], migratetype); - - /* Set the pageblock if the captured page is at least a pageblock */ + /* Set the pageblock if the isolated page is at least a pageblock */ if (order >= pageblock_order - 1) { struct page *endpage = page + (1 << order) - 1; for (; page < endpage; page += pageblock_nr_pages) { @@ -1431,7 +1420,7 @@ int capture_free_page(struct page *page, int alloc_order, int migratetype) } } - return 1UL << alloc_order; + return 1UL << order; } /* @@ -1449,10 +1438,9 @@ int split_free_page(struct page *page) unsigned int order; int nr_pages; - BUG_ON(!PageBuddy(page)); order = page_order(page); - nr_pages = capture_free_page(page, order, 0); + nr_pages = __isolate_free_page(page, order); if (!nr_pages) return 0; @@ -2136,8 +2124,6 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, bool *contended_compaction, bool *deferred_compaction, unsigned long *did_some_progress) { - struct page *page = NULL; - if (!order) return NULL; @@ -2149,16 +2135,12 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, current->flags |= PF_MEMALLOC; *did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask, nodemask, sync_migration, - contended_compaction, &page); + contended_compaction); current->flags &= ~PF_MEMALLOC; - /* If compaction captured a page, prep and use it */ - if (page) { - prep_new_page(page, order, gfp_mask); - goto got_page; - } - if (*did_some_progress != COMPACT_SKIPPED) { + struct page *page; + /* Page migration frees to the PCP lists but we want merging */ drain_pages(get_cpu()); put_cpu(); @@ -2168,7 +2150,6 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, alloc_flags & ~ALLOC_NO_WATERMARKS, preferred_zone, migratetype); if (page) { -got_page: preferred_zone->compact_blockskip_flush = false; preferred_zone->compact_considered = 0; preferred_zone->compact_defer_shift = 0; @@ -5604,7 +5585,7 @@ static inline int pfn_to_bitidx(struct zone *zone, unsigned long pfn) pfn &= (PAGES_PER_SECTION-1); return (pfn >> pageblock_order) * NR_PAGEBLOCK_BITS; #else - pfn = pfn - zone->zone_start_pfn; + pfn = pfn - round_down(zone->zone_start_pfn, pageblock_nr_pages); return (pfn >> pageblock_order) * NR_PAGEBLOCK_BITS; #endif /* CONFIG_SPARSEMEM */ } diff --git a/trunk/net/core/dev.c b/trunk/net/core/dev.c index 515473ee52cb..f64e439b4a00 100644 --- a/trunk/net/core/dev.c +++ b/trunk/net/core/dev.c @@ -6121,6 +6121,14 @@ struct netdev_queue *dev_ingress_queue_create(struct net_device *dev) static const struct ethtool_ops default_ethtool_ops; +void netdev_set_default_ethtool_ops(struct net_device *dev, + const struct ethtool_ops *ops) +{ + if (dev->ethtool_ops == &default_ethtool_ops) + dev->ethtool_ops = ops; +} +EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops); + /** * alloc_netdev_mqs - allocate network device * @sizeof_priv: size of private data to allocate space for diff --git a/trunk/net/ipv4/ip_sockglue.c b/trunk/net/ipv4/ip_sockglue.c index 3c9d20880283..d9c4f113d709 100644 --- a/trunk/net/ipv4/ip_sockglue.c +++ b/trunk/net/ipv4/ip_sockglue.c @@ -590,7 +590,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, case IP_TTL: if (optlen < 1) goto e_inval; - if (val != -1 && (val < 0 || val > 255)) + if (val != -1 && (val < 1 || val > 255)) goto e_inval; inet->uc_ttl = val; break; diff --git a/trunk/net/ipv4/tcp.c b/trunk/net/ipv4/tcp.c index 1ca253635f7a..2aa69c8ae60c 100644 --- a/trunk/net/ipv4/tcp.c +++ b/trunk/net/ipv4/tcp.c @@ -1428,12 +1428,12 @@ static void tcp_service_net_dma(struct sock *sk, bool wait) } #endif -static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) +static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) { struct sk_buff *skb; u32 offset; - skb_queue_walk(&sk->sk_receive_queue, skb) { + while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) { offset = seq - TCP_SKB_CB(skb)->seq; if (tcp_hdr(skb)->syn) offset--; @@ -1441,6 +1441,11 @@ static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) *off = offset; return skb; } + /* This looks weird, but this can happen if TCP collapsing + * splitted a fat GRO packet, while we released socket lock + * in skb_splice_bits() + */ + sk_eat_skb(sk, skb, false); } return NULL; } @@ -1482,7 +1487,7 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, break; } used = recv_actor(desc, skb, offset, len); - if (used < 0) { + if (used <= 0) { if (!copied) copied = used; break; @@ -1520,8 +1525,10 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, tcp_rcv_space_adjust(sk); /* Clean up data we have read: This will do ACK frames. */ - if (copied > 0) + if (copied > 0) { + tcp_recv_skb(sk, seq, &offset); tcp_cleanup_rbuf(sk, copied); + } return copied; } EXPORT_SYMBOL(tcp_read_sock); diff --git a/trunk/net/ipv4/tcp_input.c b/trunk/net/ipv4/tcp_input.c index a28e4db8a952..18f97ca76b00 100644 --- a/trunk/net/ipv4/tcp_input.c +++ b/trunk/net/ipv4/tcp_input.c @@ -5543,7 +5543,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb)) goto csum_error; - if (!th->ack) + if (!th->ack && !th->rst) goto discard; /* @@ -5988,7 +5988,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, goto discard; } - if (!th->ack) + if (!th->ack && !th->rst) goto discard; if (!tcp_validate_incoming(sk, skb, th, 0)) diff --git a/trunk/net/ipv6/addrconf.c b/trunk/net/ipv6/addrconf.c index 408cac4ae00a..420e56326384 100644 --- a/trunk/net/ipv6/addrconf.c +++ b/trunk/net/ipv6/addrconf.c @@ -154,6 +154,11 @@ static void addrconf_type_change(struct net_device *dev, unsigned long event); static int addrconf_ifdown(struct net_device *dev, int how); +static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, + int plen, + const struct net_device *dev, + u32 flags, u32 noflags); + static void addrconf_dad_start(struct inet6_ifaddr *ifp); static void addrconf_dad_timer(unsigned long data); static void addrconf_dad_completed(struct inet6_ifaddr *ifp); @@ -250,12 +255,6 @@ static inline bool addrconf_qdisc_ok(const struct net_device *dev) return !qdisc_tx_is_noop(dev); } -/* Check if a route is valid prefix route */ -static inline int addrconf_is_prefix_route(const struct rt6_info *rt) -{ - return (rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0; -} - static void addrconf_del_timer(struct inet6_ifaddr *ifp) { if (del_timer(&ifp->timer)) @@ -941,17 +940,15 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) { struct in6_addr prefix; struct rt6_info *rt; - struct net *net = dev_net(ifp->idev->dev); - struct flowi6 fl6 = {}; ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len); - fl6.flowi6_oif = ifp->idev->dev->ifindex; - fl6.daddr = prefix; - rt = (struct rt6_info *)ip6_route_lookup(net, &fl6, - RT6_LOOKUP_F_IFACE); - if (rt != net->ipv6.ip6_null_entry && - addrconf_is_prefix_route(rt)) { + rt = addrconf_get_prefix_route(&prefix, + ifp->prefix_len, + ifp->idev->dev, + 0, RTF_GATEWAY | RTF_DEFAULT); + + if (rt) { if (onlink == 0) { ip6_del_rt(rt); rt = NULL; @@ -1877,7 +1874,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, continue; if ((rt->rt6i_flags & flags) != flags) continue; - if ((noflags != 0) && ((rt->rt6i_flags & flags) != 0)) + if ((rt->rt6i_flags & noflags) != 0) continue; dst_hold(&rt->dst); break; diff --git a/trunk/net/iucv/iucv.c b/trunk/net/iucv/iucv.c index 3ad1f9db5f8b..df082508362d 100644 --- a/trunk/net/iucv/iucv.c +++ b/trunk/net/iucv/iucv.c @@ -1806,7 +1806,7 @@ static void iucv_external_interrupt(struct ext_code ext_code, struct iucv_irq_data *p; struct iucv_irq_list *work; - kstat_cpu(smp_processor_id()).irqs[EXTINT_IUC]++; + inc_irq_stat(IRQEXT_IUC); p = iucv_irq_data[smp_processor_id()]; if (p->ippathid >= iucv_max_pathid) { WARN_ON(p->ippathid >= iucv_max_pathid); diff --git a/trunk/net/mac80211/cfg.c b/trunk/net/mac80211/cfg.c index 5c61677487cf..47e0aca614b7 100644 --- a/trunk/net/mac80211/cfg.c +++ b/trunk/net/mac80211/cfg.c @@ -1009,6 +1009,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) if (old_probe_resp) kfree_rcu(old_probe_resp, rcu_head); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + sta_info_flush(local, vlan); sta_info_flush(local, sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); diff --git a/trunk/net/mac80211/chan.c b/trunk/net/mac80211/chan.c index 53f03120db55..80e55527504b 100644 --- a/trunk/net/mac80211/chan.c +++ b/trunk/net/mac80211/chan.c @@ -4,6 +4,7 @@ #include #include +#include #include #include "ieee80211_i.h" #include "driver-ops.h" @@ -197,6 +198,15 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) ctx = container_of(conf, struct ieee80211_chanctx, conf); + if (sdata->vif.type == NL80211_IFTYPE_AP) { + struct ieee80211_sub_if_data *vlan; + + /* for the VLAN list */ + ASSERT_RTNL(); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + rcu_assign_pointer(vlan->vif.chanctx_conf, NULL); + } + ieee80211_unassign_vif_chanctx(sdata, ctx); if (ctx->refcount == 0) ieee80211_free_chanctx(local, ctx); @@ -316,6 +326,15 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, goto out; } + if (sdata->vif.type == NL80211_IFTYPE_AP) { + struct ieee80211_sub_if_data *vlan; + + /* for the VLAN list */ + ASSERT_RTNL(); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + rcu_assign_pointer(vlan->vif.chanctx_conf, &ctx->conf); + } + ieee80211_recalc_smps_chanctx(local, ctx); out: mutex_unlock(&local->chanctx_mtx); @@ -331,6 +350,25 @@ void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) mutex_unlock(&sdata->local->chanctx_mtx); } +void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *ap; + struct ieee80211_chanctx_conf *conf; + + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) + return; + + ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); + + mutex_lock(&local->chanctx_mtx); + + conf = rcu_dereference_protected(ap->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + rcu_assign_pointer(sdata->vif.chanctx_conf, conf); + mutex_unlock(&local->chanctx_mtx); +} + void ieee80211_iter_chan_contexts_atomic( struct ieee80211_hw *hw, void (*iter)(struct ieee80211_hw *hw, diff --git a/trunk/net/mac80211/ibss.c b/trunk/net/mac80211/ibss.c index 8881fc77fb13..6b7644e818d8 100644 --- a/trunk/net/mac80211/ibss.c +++ b/trunk/net/mac80211/ibss.c @@ -703,8 +703,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) sdata_info(sdata, "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n"); - ieee80211_request_internal_scan(sdata, - ifibss->ssid, ifibss->ssid_len, NULL); + ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, + NULL); } static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) @@ -802,9 +802,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) IEEE80211_SCAN_INTERVAL)) { sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); - ieee80211_request_internal_scan(sdata, - ifibss->ssid, ifibss->ssid_len, - ifibss->fixed_channel ? ifibss->channel : NULL); + ieee80211_request_ibss_scan(sdata, ifibss->ssid, + ifibss->ssid_len, chan); } else { int interval = IEEE80211_SCAN_INTERVAL; diff --git a/trunk/net/mac80211/ieee80211_i.h b/trunk/net/mac80211/ieee80211_i.h index 42d0d0267730..8563b9a5cac3 100644 --- a/trunk/net/mac80211/ieee80211_i.h +++ b/trunk/net/mac80211/ieee80211_i.h @@ -92,8 +92,6 @@ struct ieee80211_bss { u32 device_ts; - u8 dtim_period; - bool wmm_used; bool uapsd_supported; @@ -140,7 +138,6 @@ enum ieee80211_bss_corrupt_data_flags { /** * enum ieee80211_valid_data_flags - BSS valid data flags - * @IEEE80211_BSS_VALID_DTIM: DTIM data was gathered from non-corrupt IE * @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE * @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE * @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE @@ -151,7 +148,6 @@ enum ieee80211_bss_corrupt_data_flags { * beacon/probe response. */ enum ieee80211_bss_valid_data_flags { - IEEE80211_BSS_VALID_DTIM = BIT(0), IEEE80211_BSS_VALID_WMM = BIT(1), IEEE80211_BSS_VALID_RATES = BIT(2), IEEE80211_BSS_VALID_ERP = BIT(3) @@ -440,6 +436,7 @@ struct ieee80211_if_managed { unsigned long timers_running; /* used for quiesce/restart */ bool powersave; /* powersave requested for this iface */ bool broken_ap; /* AP is broken -- turn off powersave */ + u8 dtim_period; enum ieee80211_smps_mode req_smps, /* requested smps mode */ driver_smps_mode; /* smps mode request */ @@ -773,6 +770,10 @@ struct ieee80211_sub_if_data { u32 mntr_flags; } u; + spinlock_t cleanup_stations_lock; + struct list_head cleanup_stations; + struct work_struct cleanup_stations_wk; + #ifdef CONFIG_MAC80211_DEBUGFS struct { struct dentry *dir; @@ -1329,9 +1330,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, /* scan/BSS handling */ void ieee80211_scan_work(struct work_struct *work); -int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan); +int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, + const u8 *ssid, u8 ssid_len, + struct ieee80211_channel *chan); int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req); void ieee80211_scan_cancel(struct ieee80211_local *local); @@ -1628,6 +1629,7 @@ ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, const struct cfg80211_chan_def *chandef, enum ieee80211_chanctx_mode mode); void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); +void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx); diff --git a/trunk/net/mac80211/iface.c b/trunk/net/mac80211/iface.c index 09a80b55cf5a..8be854e86cd9 100644 --- a/trunk/net/mac80211/iface.c +++ b/trunk/net/mac80211/iface.c @@ -207,17 +207,8 @@ void ieee80211_recalc_idle(struct ieee80211_local *local) static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) { - int meshhdrlen; - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - meshhdrlen = (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ? 5 : 0; - - /* FIX: what would be proper limits for MTU? - * This interface uses 802.3 frames. */ - if (new_mtu < 256 || - new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) { + if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) return -EINVAL; - } dev->mtu = new_mtu; return 0; @@ -586,11 +577,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: - /* no need to tell driver, but set carrier */ - if (rtnl_dereference(sdata->bss->beacon)) + /* no need to tell driver, but set carrier and chanctx */ + if (rtnl_dereference(sdata->bss->beacon)) { + ieee80211_vif_vlan_copy_chanctx(sdata); netif_carrier_on(dev); - else + } else { netif_carrier_off(dev); + } break; case NL80211_IFTYPE_MONITOR: if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { @@ -839,6 +832,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: list_del(&sdata->u.vlan.list); + rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); /* no need to tell driver */ break; case NL80211_IFTYPE_MONITOR: @@ -865,20 +859,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->work); /* * When we get here, the interface is marked down. - * Call rcu_barrier() to wait both for the RX path + * Call synchronize_rcu() to wait for the RX path * should it be using the interface and enqueuing - * frames at this very time on another CPU, and - * for the sta free call_rcu callbacks. - */ - rcu_barrier(); - - /* - * free_sta_rcu() enqueues a work for the actual - * sta cleanup, so we need to flush it while - * sdata is still valid. + * frames at this very time on another CPU. */ - flush_workqueue(local->workqueue); - + synchronize_rcu(); skb_queue_purge(&sdata->skb_queue); /* @@ -1498,6 +1483,15 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, mutex_unlock(&local->iflist_mtx); } +static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk) +{ + struct ieee80211_sub_if_data *sdata; + + sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk); + + ieee80211_cleanup_sdata_stas(sdata); +} + int ieee80211_if_add(struct ieee80211_local *local, const char *name, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params) @@ -1573,6 +1567,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, INIT_LIST_HEAD(&sdata->key_list); + spin_lock_init(&sdata->cleanup_stations_lock); + INIT_LIST_HEAD(&sdata->cleanup_stations); + INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk); + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[i]; diff --git a/trunk/net/mac80211/mesh.c b/trunk/net/mac80211/mesh.c index 1bf03f9ff3ba..649ad513547f 100644 --- a/trunk/net/mac80211/mesh.c +++ b/trunk/net/mac80211/mesh.c @@ -163,7 +163,7 @@ int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) return -ENOMEM; sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1; for (i = 0; i < RMC_BUCKETS; i++) - INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i].list); + INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]); return 0; } @@ -177,7 +177,7 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata) return; for (i = 0; i < RMC_BUCKETS; i++) - list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) { + list_for_each_entry_safe(p, n, &rmc->bucket[i], list) { list_del(&p->list); kmem_cache_free(rm_cache, p); } @@ -210,7 +210,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, /* Don't care about endianness since only match matters */ memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum)); idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask; - list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) { + list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) { ++entries; if (time_after(jiffies, p->exp_time) || (entries == RMC_QUEUE_MAX_LEN)) { @@ -229,7 +229,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, p->seqnum = seqnum; p->exp_time = jiffies + RMC_TIMEOUT; memcpy(p->sa, sa, ETH_ALEN); - list_add(&p->list, &rmc->bucket[idx].list); + list_add(&p->list, &rmc->bucket[idx]); return 0; } diff --git a/trunk/net/mac80211/mesh.h b/trunk/net/mac80211/mesh.h index 7c9215fb2ac8..84c28c6101cd 100644 --- a/trunk/net/mac80211/mesh.h +++ b/trunk/net/mac80211/mesh.h @@ -184,7 +184,7 @@ struct rmc_entry { }; struct mesh_rmc { - struct rmc_entry bucket[RMC_BUCKETS]; + struct list_head bucket[RMC_BUCKETS]; u32 idx_mask; }; diff --git a/trunk/net/mac80211/mlme.c b/trunk/net/mac80211/mlme.c index 7753a9ca98a6..a3552929a21d 100644 --- a/trunk/net/mac80211/mlme.c +++ b/trunk/net/mac80211/mlme.c @@ -1074,12 +1074,8 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) if (beaconint_us > latency) { local->ps_sdata = NULL; } else { - struct ieee80211_bss *bss; int maxslp = 1; - u8 dtimper; - - bss = (void *)found->u.mgd.associated->priv; - dtimper = bss->dtim_period; + u8 dtimper = found->u.mgd.dtim_period; /* If the TIM IE is invalid, pretend the value is 1 */ if (!dtimper) @@ -1410,10 +1406,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_led_assoc(local, 1); - if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) - bss_conf->dtim_period = bss->dtim_period; - else + if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { + /* + * If the AP is buggy we may get here with no DTIM period + * known, so assume it's 1 which is the only safe assumption + * in that case, although if the TIM IE is broken powersave + * probably just won't work at all. + */ + bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1; + } else { bss_conf->dtim_period = 0; + } bss_conf->assoc = 1; @@ -1562,6 +1565,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.timers_running = 0; + sdata->vif.bss_conf.dtim_period = 0; + ifmgd->flags = 0; ieee80211_vif_release_channel(sdata); } @@ -2373,11 +2378,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel; bool need_ps = false; - if (sdata->u.mgd.associated && - ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) { - bss = (void *)sdata->u.mgd.associated->priv; + if ((sdata->u.mgd.associated && + ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) || + (sdata->u.mgd.assoc_data && + ether_addr_equal(mgmt->bssid, + sdata->u.mgd.assoc_data->bss->bssid))) { /* not previously set so we may need to recalc */ - need_ps = !bss->dtim_period; + need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period; + + if (elems->tim && !elems->parse_error) { + struct ieee80211_tim_ie *tim_ie = elems->tim; + sdata->u.mgd.dtim_period = tim_ie->dtim_period; + } } if (elems->ds_params && elems->ds_params_len == 1) @@ -3896,20 +3908,41 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, /* kick off associate process */ ifmgd->assoc_data = assoc_data; + ifmgd->dtim_period = 0; err = ieee80211_prep_connection(sdata, req->bss, true); if (err) goto err_clear; - if (!bss->dtim_period && - sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { - /* - * Wait up to one beacon interval ... - * should this be more if we miss one? - */ - sdata_info(sdata, "waiting for beacon from %pM\n", - ifmgd->bssid); - assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); + if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { + const struct cfg80211_bss_ies *beacon_ies; + + rcu_read_lock(); + beacon_ies = rcu_dereference(req->bss->beacon_ies); + if (!beacon_ies) { + /* + * Wait up to one beacon interval ... + * should this be more if we miss one? + */ + sdata_info(sdata, "waiting for beacon from %pM\n", + ifmgd->bssid); + assoc_data->timeout = + TU_TO_EXP_TIME(req->bss->beacon_interval); + } else { + const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, + beacon_ies->data, + beacon_ies->len); + if (tim_ie && tim_ie[1] >= + sizeof(struct ieee80211_tim_ie)) { + const struct ieee80211_tim_ie *tim; + tim = (void *)(tim_ie + 2); + ifmgd->dtim_period = tim->dtim_period; + } + assoc_data->have_beacon = true; + assoc_data->sent_assoc = false; + assoc_data->timeout = jiffies; + } + rcu_read_unlock(); } else { assoc_data->have_beacon = true; assoc_data->sent_assoc = false; diff --git a/trunk/net/mac80211/scan.c b/trunk/net/mac80211/scan.c index 8ed83dcc149f..d59fc6818b1c 100644 --- a/trunk/net/mac80211/scan.c +++ b/trunk/net/mac80211/scan.c @@ -113,18 +113,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss->valid_data |= IEEE80211_BSS_VALID_ERP; } - if (elems->tim && (!elems->parse_error || - !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) { - struct ieee80211_tim_ie *tim_ie = elems->tim; - bss->dtim_period = tim_ie->dtim_period; - if (!elems->parse_error) - bss->valid_data |= IEEE80211_BSS_VALID_DTIM; - } - - /* If the beacon had no TIM IE, or it was invalid, use 1 */ - if (beacon && !bss->dtim_period) - bss->dtim_period = 1; - /* replace old supported rates if we get new values */ if (!elems->parse_error || !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) { @@ -832,9 +820,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, return res; } -int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan) +int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, + const u8 *ssid, u8 ssid_len, + struct ieee80211_channel *chan) { struct ieee80211_local *local = sdata->local; int ret = -EBUSY; @@ -848,22 +836,36 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, /* fill internal scan request */ if (!chan) { - int i, nchan = 0; + int i, max_n; + int n_ch = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!local->hw.wiphy->bands[band]) continue; - for (i = 0; - i < local->hw.wiphy->bands[band]->n_channels; - i++) { - local->int_scan_req->channels[nchan] = + + max_n = local->hw.wiphy->bands[band]->n_channels; + for (i = 0; i < max_n; i++) { + struct ieee80211_channel *tmp_ch = &local->hw.wiphy->bands[band]->channels[i]; - nchan++; + + if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_DISABLED)) + continue; + + local->int_scan_req->channels[n_ch] = tmp_ch; + n_ch++; } } - local->int_scan_req->n_channels = nchan; + if (WARN_ON_ONCE(n_ch == 0)) + goto unlock; + + local->int_scan_req->n_channels = n_ch; } else { + if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_DISABLED))) + goto unlock; + local->int_scan_req->channels[0] = chan; local->int_scan_req->n_channels = 1; } diff --git a/trunk/net/mac80211/sta_info.c b/trunk/net/mac80211/sta_info.c index f3e502502fee..ca9fde198188 100644 --- a/trunk/net/mac80211/sta_info.c +++ b/trunk/net/mac80211/sta_info.c @@ -91,9 +91,8 @@ static int sta_info_hash_del(struct ieee80211_local *local, return -ENOENT; } -static void free_sta_work(struct work_struct *wk) +static void cleanup_single_sta(struct sta_info *sta) { - struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk); int ac, i; struct tid_ampdu_tx *tid_tx; struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -153,11 +152,35 @@ static void free_sta_work(struct work_struct *wk) sta_info_free(local, sta); } +void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata) +{ + struct sta_info *sta; + + spin_lock_bh(&sdata->cleanup_stations_lock); + while (!list_empty(&sdata->cleanup_stations)) { + sta = list_first_entry(&sdata->cleanup_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&sdata->cleanup_stations_lock); + + cleanup_single_sta(sta); + + spin_lock_bh(&sdata->cleanup_stations_lock); + } + + spin_unlock_bh(&sdata->cleanup_stations_lock); +} + static void free_sta_rcu(struct rcu_head *h) { struct sta_info *sta = container_of(h, struct sta_info, rcu_head); + struct ieee80211_sub_if_data *sdata = sta->sdata; - ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk); + spin_lock(&sdata->cleanup_stations_lock); + list_add_tail(&sta->list, &sdata->cleanup_stations); + spin_unlock(&sdata->cleanup_stations_lock); + + ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk); } /* protected by RCU */ @@ -310,7 +333,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, spin_lock_init(&sta->lock); INIT_WORK(&sta->drv_unblock_wk, sta_unblock); - INIT_WORK(&sta->free_sta_wk, free_sta_work); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); mutex_init(&sta->ampdu_mlme.mtx); @@ -862,7 +884,7 @@ void sta_info_init(struct ieee80211_local *local) void sta_info_stop(struct ieee80211_local *local) { - del_timer(&local->sta_cleanup); + del_timer_sync(&local->sta_cleanup); sta_info_flush(local, NULL); } @@ -891,6 +913,20 @@ int sta_info_flush(struct ieee80211_local *local, } mutex_unlock(&local->sta_mtx); + rcu_barrier(); + + if (sdata) { + ieee80211_cleanup_sdata_stas(sdata); + cancel_work_sync(&sdata->cleanup_stations_wk); + } else { + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) { + ieee80211_cleanup_sdata_stas(sdata); + cancel_work_sync(&sdata->cleanup_stations_wk); + } + mutex_unlock(&local->iflist_mtx); + } + return ret; } diff --git a/trunk/net/mac80211/sta_info.h b/trunk/net/mac80211/sta_info.h index 1489bca9ea97..37c1889afd3a 100644 --- a/trunk/net/mac80211/sta_info.h +++ b/trunk/net/mac80211/sta_info.h @@ -299,7 +299,6 @@ struct sta_info { spinlock_t lock; struct work_struct drv_unblock_wk; - struct work_struct free_sta_wk; u16 listen_interval; @@ -563,4 +562,6 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); +void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata); + #endif /* STA_INFO_H */ diff --git a/trunk/net/sunrpc/clnt.c b/trunk/net/sunrpc/clnt.c index 1915ffe598e3..507b5e84fbdb 100644 --- a/trunk/net/sunrpc/clnt.c +++ b/trunk/net/sunrpc/clnt.c @@ -555,7 +555,7 @@ EXPORT_SYMBOL_GPL(rpc_clone_client); * rpc_clone_client_set_auth - Clone an RPC client structure and set its auth * * @clnt: RPC client whose parameters are copied - * @auth: security flavor for new client + * @flavor: security flavor for new client * * Returns a fresh RPC client or an ERR_PTR. */ diff --git a/trunk/net/sunrpc/sched.c b/trunk/net/sunrpc/sched.c index b4133bd13915..bfa31714581f 100644 --- a/trunk/net/sunrpc/sched.c +++ b/trunk/net/sunrpc/sched.c @@ -972,8 +972,7 @@ static void rpc_async_release(struct work_struct *work) static void rpc_release_resources_task(struct rpc_task *task) { - if (task->tk_rqstp) - xprt_release(task); + xprt_release(task); if (task->tk_msg.rpc_cred) { put_rpccred(task->tk_msg.rpc_cred); task->tk_msg.rpc_cred = NULL; diff --git a/trunk/net/sunrpc/xprt.c b/trunk/net/sunrpc/xprt.c index bd462a532acf..33811db8788a 100644 --- a/trunk/net/sunrpc/xprt.c +++ b/trunk/net/sunrpc/xprt.c @@ -1136,10 +1136,18 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt) void xprt_release(struct rpc_task *task) { struct rpc_xprt *xprt; - struct rpc_rqst *req; + struct rpc_rqst *req = task->tk_rqstp; - if (!(req = task->tk_rqstp)) + if (req == NULL) { + if (task->tk_client) { + rcu_read_lock(); + xprt = rcu_dereference(task->tk_client->cl_xprt); + if (xprt->snd_task == task) + xprt_release_write(xprt, task); + rcu_read_unlock(); + } return; + } xprt = req->rq_xprt; if (task->tk_ops->rpc_count_stats != NULL) diff --git a/trunk/net/wireless/core.c b/trunk/net/wireless/core.c index 14d990400354..b677eab55b68 100644 --- a/trunk/net/wireless/core.c +++ b/trunk/net/wireless/core.c @@ -866,8 +866,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, /* allow mac80211 to determine the timeout */ wdev->ps_timeout = -1; - if (!dev->ethtool_ops) - dev->ethtool_ops = &cfg80211_ethtool_ops; + netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops); if ((wdev->iftype == NL80211_IFTYPE_STATION || wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || diff --git a/trunk/security/device_cgroup.c b/trunk/security/device_cgroup.c index 19ecc8de9e6b..d794abcc4b3b 100644 --- a/trunk/security/device_cgroup.c +++ b/trunk/security/device_cgroup.c @@ -215,7 +215,9 @@ static void devcgroup_css_free(struct cgroup *cgroup) struct dev_cgroup *dev_cgroup; dev_cgroup = cgroup_to_devcgroup(cgroup); + mutex_lock(&devcgroup_mutex); dev_exception_clean(dev_cgroup); + mutex_unlock(&devcgroup_mutex); kfree(dev_cgroup); } diff --git a/trunk/security/integrity/evm/evm_crypto.c b/trunk/security/integrity/evm/evm_crypto.c index dfb26918699c..7dd538ef5b83 100644 --- a/trunk/security/integrity/evm/evm_crypto.c +++ b/trunk/security/integrity/evm/evm_crypto.c @@ -205,9 +205,9 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM, &xattr_data, sizeof(xattr_data), 0); - } - else if (rc == -ENODATA) + } else if (rc == -ENODATA && inode->i_op->removexattr) { rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM); + } return rc; } diff --git a/trunk/sound/drivers/vx/vx_core.c b/trunk/sound/drivers/vx/vx_core.c index c39961c11401..de5055a3b0d0 100644 --- a/trunk/sound/drivers/vx/vx_core.c +++ b/trunk/sound/drivers/vx/vx_core.c @@ -52,6 +52,7 @@ MODULE_LICENSE("GPL"); int snd_vx_check_reg_bit(struct vx_core *chip, int reg, int mask, int bit, int time) { unsigned long end_time = jiffies + (time * HZ + 999) / 1000; +#ifdef CONFIG_SND_DEBUG static char *reg_names[VX_REG_MAX] = { "ICR", "CVR", "ISR", "IVR", "RXH", "RXM", "RXL", "DMA", "CDSP", "RFREQ", "RUER/V2", "DATA", "MEMIRQ", @@ -59,7 +60,7 @@ int snd_vx_check_reg_bit(struct vx_core *chip, int reg, int mask, int bit, int t "MIC3", "INTCSR", "CNTRL", "GPIOC", "LOFREQ", "HIFREQ", "CSUER", "RUER" }; - +#endif do { if ((snd_vx_inb(chip, reg) & mask) == bit) return 0; diff --git a/trunk/sound/pci/atiixp.c b/trunk/sound/pci/atiixp.c index 6e78c6789858..a67743183aaf 100644 --- a/trunk/sound/pci/atiixp.c +++ b/trunk/sound/pci/atiixp.c @@ -567,9 +567,8 @@ static int ac97_probing_bugs(struct pci_dev *pci) q = snd_pci_quirk_lookup(pci, atiixp_quirks); if (q) { - snd_printdd(KERN_INFO - "Atiixp quirk for %s. Forcing codec %d\n", - snd_pci_quirk_name(q), q->value); + snd_printdd(KERN_INFO "Atiixp quirk for %s. " + "Forcing codec %d\n", q->name, q->value); return q->value; } /* this hardware doesn't need workarounds. Probe for codec */ diff --git a/trunk/sound/pci/hda/Kconfig b/trunk/sound/pci/hda/Kconfig index 4466bf63d6bf..6eeb8897624b 100644 --- a/trunk/sound/pci/hda/Kconfig +++ b/trunk/sound/pci/hda/Kconfig @@ -86,7 +86,6 @@ config SND_HDA_PATCH_LOADER config SND_HDA_CODEC_REALTEK bool "Build Realtek HD-audio codec support" default y - select SND_HDA_GENERIC help Say Y here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. @@ -99,7 +98,6 @@ config SND_HDA_CODEC_REALTEK config SND_HDA_CODEC_ANALOG bool "Build Analog Device HD-audio codec support" default y - select SND_HDA_GENERIC help Say Y here to include Analog Device HD-audio codec support in snd-hda-intel driver, such as AD1986A. @@ -112,7 +110,6 @@ config SND_HDA_CODEC_ANALOG config SND_HDA_CODEC_SIGMATEL bool "Build IDT/Sigmatel HD-audio codec support" default y - select SND_HDA_GENERIC help Say Y here to include IDT (Sigmatel) HD-audio codec support in snd-hda-intel driver, such as STAC9200. @@ -125,7 +122,6 @@ config SND_HDA_CODEC_SIGMATEL config SND_HDA_CODEC_VIA bool "Build VIA HD-audio codec support" default y - select SND_HDA_GENERIC help Say Y here to include VIA HD-audio codec support in snd-hda-intel driver, such as VT1708. @@ -151,8 +147,8 @@ config SND_HDA_CODEC_HDMI config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" + depends on SND_HDA_INTEL default y - select SND_HDA_GENERIC help Say Y here to include Cirrus Logic codec support in snd-hda-intel driver, such as CS4206. @@ -165,7 +161,6 @@ config SND_HDA_CODEC_CIRRUS config SND_HDA_CODEC_CONEXANT bool "Build Conexant HD-audio codec support" default y - select SND_HDA_GENERIC help Say Y here to include Conexant HD-audio codec support in snd-hda-intel driver, such as CX20549. @@ -177,8 +172,8 @@ config SND_HDA_CODEC_CONEXANT config SND_HDA_CODEC_CA0110 bool "Build Creative CA0110-IBG codec support" + depends on SND_HDA_INTEL default y - select SND_HDA_GENERIC help Say Y here to include Creative CA0110-IBG codec support in snd-hda-intel driver, found on some Creative X-Fi cards. @@ -190,6 +185,7 @@ config SND_HDA_CODEC_CA0110 config SND_HDA_CODEC_CA0132 bool "Build Creative CA0132 codec support" + depends on SND_HDA_INTEL default y help Say Y here to include Creative CA0132 codec support in @@ -203,7 +199,6 @@ config SND_HDA_CODEC_CA0132 config SND_HDA_CODEC_CMEDIA bool "Build C-Media HD-audio codec support" default y - select SND_HDA_GENERIC help Say Y here to include C-Media HD-audio codec support in snd-hda-intel driver, such as CMI9880. diff --git a/trunk/sound/pci/hda/hda_auto_parser.c b/trunk/sound/pci/hda/hda_auto_parser.c index a3ea76a4c9d2..7da883a464e3 100644 --- a/trunk/sound/pci/hda/hda_auto_parser.c +++ b/trunk/sound/pci/hda/hda_auto_parser.c @@ -97,28 +97,6 @@ static void reorder_outputs(unsigned int nums, hda_nid_t *pins) } } -/* check whether the given pin has a proper pin I/O capability bit */ -static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin, - unsigned int dev) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, pin); - - /* some old hardware don't return the proper pincaps */ - if (!pincap) - return true; - - switch (dev) { - case AC_JACK_LINE_OUT: - case AC_JACK_SPEAKER: - case AC_JACK_HP_OUT: - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - return !!(pincap & AC_PINCAP_OUT); - default: - return !!(pincap & AC_PINCAP_IN); - } -} - /* * Parse all pin widgets and store the useful pin nids to cfg * @@ -148,9 +126,6 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)]; int i; - if (!snd_hda_get_int_hint(codec, "parser_flags", &i)) - cond_flags = i; - memset(cfg, 0, sizeof(*cfg)); memset(line_out, 0, sizeof(line_out)); @@ -181,14 +156,10 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, /* workaround for buggy BIOS setups */ if (dev == AC_JACK_LINE_OUT) { - if (conn == AC_JACK_PORT_FIXED || - conn == AC_JACK_PORT_BOTH) + if (conn == AC_JACK_PORT_FIXED) dev = AC_JACK_SPEAKER; } - if (!check_pincap_validity(codec, nid, dev)) - continue; - switch (dev) { case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); @@ -392,7 +363,7 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, { unsigned int def_conf; static const char * const mic_names[] = { - "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic" + "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", }; int attr; @@ -423,8 +394,6 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, return "SPDIF In"; case AC_JACK_DIG_OTHER_IN: return "Digital In"; - case AC_JACK_HP_OUT: - return "Headphone Mic"; default: return "Misc"; } @@ -583,9 +552,6 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, return 1; } -#define is_hdmi_cfg(conf) \ - (get_defcfg_location(conf) == AC_JACK_LOC_HDMI) - /** * snd_hda_get_pin_label - Get a label for the given I/O pin * @@ -606,7 +572,6 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); const char *name = NULL; int i; - bool hdmi; if (indexp) *indexp = 0; @@ -625,18 +590,16 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, label, maxlen, indexp); case AC_JACK_SPDIF_OUT: case AC_JACK_DIG_OTHER_OUT: - hdmi = is_hdmi_cfg(def_conf); - name = hdmi ? "HDMI" : "SPDIF"; - if (cfg && indexp) - for (i = 0; i < cfg->dig_outs; i++) { - hda_nid_t pin = cfg->dig_out_pins[i]; - unsigned int c; - if (pin == nid) - break; - c = snd_hda_codec_get_pincfg(codec, pin); - if (hdmi == is_hdmi_cfg(c)) - (*indexp)++; - } + if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI) + name = "HDMI"; + else + name = "SPDIF"; + if (cfg && indexp) { + i = find_idx_in_nid_list(nid, cfg->dig_out_pins, + cfg->dig_outs); + if (i >= 0) + *indexp = i; + } break; default: if (cfg) { @@ -659,27 +622,28 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); -int snd_hda_add_verbs(struct hda_codec *codec, - const struct hda_verb *list) +int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, + const struct hda_verb *list) { const struct hda_verb **v; - v = snd_array_new(&codec->verbs); + v = snd_array_new(&spec->verbs); if (!v) return -ENOMEM; *v = list; return 0; } -EXPORT_SYMBOL_HDA(snd_hda_add_verbs); +EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs); -void snd_hda_apply_verbs(struct hda_codec *codec) +void snd_hda_gen_apply_verbs(struct hda_codec *codec) { + struct hda_gen_spec *spec = codec->spec; int i; - for (i = 0; i < codec->verbs.used; i++) { - struct hda_verb **v = snd_array_elem(&codec->verbs, i); + for (i = 0; i < spec->verbs.used; i++) { + struct hda_verb **v = snd_array_elem(&spec->verbs, i); snd_hda_sequence_write(codec, *v); } } -EXPORT_SYMBOL_HDA(snd_hda_apply_verbs); +EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs); void snd_hda_apply_pincfgs(struct hda_codec *codec, const struct hda_pintbl *cfg) @@ -689,22 +653,20 @@ void snd_hda_apply_pincfgs(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs); -static void set_pin_targets(struct hda_codec *codec, - const struct hda_pintbl *cfg) +void snd_hda_apply_fixup(struct hda_codec *codec, int action) { - for (; cfg->nid; cfg++) - snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val); -} + struct hda_gen_spec *spec = codec->spec; + int id = spec->fixup_id; +#ifdef CONFIG_SND_DEBUG_VERBOSE + const char *modelname = spec->fixup_name; +#endif + int depth = 0; -static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) -{ - const char *modelname = codec->fixup_name; + if (!spec->fixup_list) + return; while (id >= 0) { - const struct hda_fixup *fix = codec->fixup_list + id; - - if (fix->chained_before) - apply_fixup(codec, fix->chain_id, action, depth + 1); + const struct hda_fixup *fix = spec->fixup_list + id; switch (fix->type) { case HDA_FIXUP_PINS: @@ -721,7 +683,7 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) snd_printdd(KERN_INFO SFX "%s: Apply fix-verbs for %s\n", codec->chip_name, modelname); - snd_hda_add_verbs(codec, fix->v.verbs); + snd_hda_gen_add_verbs(codec->spec, fix->v.verbs); break; case HDA_FIXUP_FUNC: if (!fix->v.func) @@ -731,33 +693,19 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) codec->chip_name, modelname); fix->v.func(codec, fix, action); break; - case HDA_FIXUP_PINCTLS: - if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins) - break; - snd_printdd(KERN_INFO SFX - "%s: Apply pinctl for %s\n", - codec->chip_name, modelname); - set_pin_targets(codec, fix->v.pins); - break; default: snd_printk(KERN_ERR SFX "%s: Invalid fixup type %d\n", codec->chip_name, fix->type); break; } - if (!fix->chained || fix->chained_before) + if (!fix->chained) break; if (++depth > 10) break; id = fix->chain_id; } } - -void snd_hda_apply_fixup(struct hda_codec *codec, int action) -{ - if (codec->fixup_list) - apply_fixup(codec, codec->fixup_id, action, 0); -} EXPORT_SYMBOL_HDA(snd_hda_apply_fixup); void snd_hda_pick_fixup(struct hda_codec *codec, @@ -765,14 +713,15 @@ void snd_hda_pick_fixup(struct hda_codec *codec, const struct snd_pci_quirk *quirk, const struct hda_fixup *fixlist) { + struct hda_gen_spec *spec = codec->spec; const struct snd_pci_quirk *q; int id = -1; const char *name = NULL; /* when model=nofixup is given, don't pick up any fixups */ if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { - codec->fixup_list = NULL; - codec->fixup_id = -1; + spec->fixup_list = NULL; + spec->fixup_id = -1; return; } @@ -810,10 +759,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec, } } - codec->fixup_id = id; + spec->fixup_id = id; if (id >= 0) { - codec->fixup_list = fixlist; - codec->fixup_name = name; + spec->fixup_list = fixlist; + spec->fixup_name = name; } } EXPORT_SYMBOL_HDA(snd_hda_pick_fixup); diff --git a/trunk/sound/pci/hda/hda_auto_parser.h b/trunk/sound/pci/hda/hda_auto_parser.h index f74807138b49..632ad0ad3007 100644 --- a/trunk/sound/pci/hda/hda_auto_parser.h +++ b/trunk/sound/pci/hda/hda_auto_parser.h @@ -51,9 +51,8 @@ enum { INPUT_PIN_ATTR_INT, /* internal mic/line-in */ INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ - INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ - INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT, + INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ }; int snd_hda_get_input_pin_attr(unsigned int def_conf); @@ -90,4 +89,82 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, #define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) +/* + */ + +struct hda_gen_spec { + /* fix-up list */ + int fixup_id; + const struct hda_fixup *fixup_list; + const char *fixup_name; + + /* additional init verbs */ + struct snd_array verbs; +}; + + +/* + * Fix-up pin default configurations and add default verbs + */ + +struct hda_pintbl { + hda_nid_t nid; + u32 val; +}; + +struct hda_model_fixup { + const int id; + const char *name; +}; + +struct hda_fixup { + int type; + bool chained; + int chain_id; + union { + const struct hda_pintbl *pins; + const struct hda_verb *verbs; + void (*func)(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); + } v; +}; + +/* fixup types */ +enum { + HDA_FIXUP_INVALID, + HDA_FIXUP_PINS, + HDA_FIXUP_VERBS, + HDA_FIXUP_FUNC, +}; + +/* fixup action definitions */ +enum { + HDA_FIXUP_ACT_PRE_PROBE, + HDA_FIXUP_ACT_PROBE, + HDA_FIXUP_ACT_INIT, + HDA_FIXUP_ACT_BUILD, +}; + +int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, + const struct hda_verb *list); +void snd_hda_gen_apply_verbs(struct hda_codec *codec); +void snd_hda_apply_pincfgs(struct hda_codec *codec, + const struct hda_pintbl *cfg); +void snd_hda_apply_fixup(struct hda_codec *codec, int action); +void snd_hda_pick_fixup(struct hda_codec *codec, + const struct hda_model_fixup *models, + const struct snd_pci_quirk *quirk, + const struct hda_fixup *fixlist); + +static inline void snd_hda_gen_init(struct hda_gen_spec *spec) +{ + snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8); +} + +static inline void snd_hda_gen_free(struct hda_gen_spec *spec) +{ + snd_array_free(&spec->verbs); +} + #endif /* __SOUND_HDA_AUTO_PARSER_H */ diff --git a/trunk/sound/pci/hda/hda_codec.c b/trunk/sound/pci/hda/hda_codec.c index f82a64da2f1b..822df971972c 100644 --- a/trunk/sound/pci/hda/hda_codec.c +++ b/trunk/sound/pci/hda/hda_codec.c @@ -222,14 +222,8 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, again: snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); - for (;;) { - trace_hda_send_cmd(codec, cmd); - err = bus->ops.command(bus, cmd); - if (err != -EAGAIN) - break; - /* process pending verbs */ - bus->ops.get_response(bus, codec->addr); - } + trace_hda_send_cmd(codec, cmd); + err = bus->ops.command(bus, cmd); if (!err && res) { *res = bus->ops.get_response(bus, codec->addr); trace_hda_get_response(codec, *res); @@ -334,51 +328,20 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); -/* connection list element */ -struct hda_conn_list { - struct list_head list; - int len; - hda_nid_t nid; - hda_nid_t conns[0]; -}; - /* look up the cached results */ -static struct hda_conn_list * -lookup_conn_list(struct hda_codec *codec, hda_nid_t nid) +static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) { - struct hda_conn_list *p; - list_for_each_entry(p, &codec->conn_list, list) { - if (p->nid == nid) + int i, len; + for (i = 0; i < array->used; ) { + hda_nid_t *p = snd_array_elem(array, i); + if (nid == *p) return p; + len = p[1]; + i += len + 2; } return NULL; } -static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, - const hda_nid_t *list) -{ - struct hda_conn_list *p; - - p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL); - if (!p) - return -ENOMEM; - p->len = len; - p->nid = nid; - memcpy(p->conns, list, len * sizeof(hda_nid_t)); - list_add(&p->list, &codec->conn_list); - return 0; -} - -static void remove_conn_list(struct hda_codec *codec) -{ - while (!list_empty(&codec->conn_list)) { - struct hda_conn_list *p; - p = list_first_entry(&codec->conn_list, typeof(*p), list); - list_del(&p->list); - kfree(p); - } -} - /* read the connection and add to the cache */ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) { @@ -391,49 +354,6 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) return snd_hda_override_conn_list(codec, nid, len, list); } -/** - * snd_hda_get_conn_list - get connection list - * @codec: the HDA codec - * @nid: NID to parse - * @len: number of connection list entries - * @listp: the pointer to store NID list - * - * Parses the connection list of the given widget and stores the pointer - * to the list of NIDs. - * - * Returns the number of connections, or a negative error code. - * - * Note that the returned pointer isn't protected against the list - * modification. If snd_hda_override_conn_list() might be called - * concurrently, protect with a mutex appropriately. - */ -int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, - const hda_nid_t **listp) -{ - bool added = false; - - for (;;) { - int err; - const struct hda_conn_list *p; - - /* if the connection-list is already cached, read it */ - p = lookup_conn_list(codec, nid); - if (p) { - if (listp) - *listp = p->conns; - return p->len; - } - if (snd_BUG_ON(added)) - return -EINVAL; - - err = read_and_add_raw_conns(codec, nid); - if (err < 0) - return err; - added = true; - } -} -EXPORT_SYMBOL_HDA(snd_hda_get_conn_list); - /** * snd_hda_get_connections - copy connection list * @codec: the HDA codec @@ -449,20 +369,39 @@ EXPORT_SYMBOL_HDA(snd_hda_get_conn_list); int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns) { - const hda_nid_t *list; - int len = snd_hda_get_conn_list(codec, nid, &list); + struct snd_array *array = &codec->conn_lists; + int len; + hda_nid_t *p; + bool added = false; - if (len > 0 && conn_list) { - if (len > max_conns) { + again: + mutex_lock(&codec->hash_mutex); + len = -1; + /* if the connection-list is already cached, read it */ + p = lookup_conn_list(array, nid); + if (p) { + len = p[1]; + if (conn_list && len > max_conns) { snd_printk(KERN_ERR "hda_codec: " "Too many connections %d for NID 0x%x\n", len, nid); + mutex_unlock(&codec->hash_mutex); return -EINVAL; } - memcpy(conn_list, list, len * sizeof(hda_nid_t)); + if (conn_list && len) + memcpy(conn_list, p + 2, len * sizeof(hda_nid_t)); } + mutex_unlock(&codec->hash_mutex); + if (len >= 0) + return len; + if (snd_BUG_ON(added)) + return -EINVAL; - return len; + len = read_and_add_raw_conns(codec, nid); + if (len < 0) + return len; + added = true; + goto again; } EXPORT_SYMBOL_HDA(snd_hda_get_connections); @@ -485,7 +424,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, unsigned int shift, num_elems, mask; unsigned int wcaps; hda_nid_t prev_nid; - int null_count = 0; if (snd_BUG_ON(!conn_list || max_conns <= 0)) return -EINVAL; @@ -536,7 +474,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, } range_val = !!(parm & (1 << (shift-1))); /* ranges */ val = parm & mask; - if (val == 0 && null_count++) { /* no second chance */ + if (val == 0) { snd_printk(KERN_WARNING "hda_codec: " "invalid CONNECT_LIST verb %x[%i]:%x\n", nid, i, parm); @@ -574,6 +512,15 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, return conns; } +static bool add_conn_list(struct snd_array *array, hda_nid_t nid) +{ + hda_nid_t *p = snd_array_new(array); + if (!p) + return false; + *p = nid; + return true; +} + /** * snd_hda_override_conn_list - add/modify the connection-list to cache * @codec: the HDA codec @@ -589,15 +536,28 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, const hda_nid_t *list) { - struct hda_conn_list *p; + struct snd_array *array = &codec->conn_lists; + hda_nid_t *p; + int i, old_used; - p = lookup_conn_list(codec, nid); - if (p) { - list_del(&p->list); - kfree(p); - } + mutex_lock(&codec->hash_mutex); + p = lookup_conn_list(array, nid); + if (p) + *p = -1; /* invalidate the old entry */ + + old_used = array->used; + if (!add_conn_list(array, nid) || !add_conn_list(array, len)) + goto error_add; + for (i = 0; i < len; i++) + if (!add_conn_list(array, list[i])) + goto error_add; + mutex_unlock(&codec->hash_mutex); + return 0; - return add_conn_list(codec, nid, len, list); + error_add: + array->used = old_used; + mutex_unlock(&codec->hash_mutex); + return -ENOMEM; } EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); @@ -615,16 +575,16 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive) { - const hda_nid_t *conn; + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; int i, nums; - nums = snd_hda_get_conn_list(codec, mux, &conn); + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); for (i = 0; i < nums; i++) if (conn[i] == nid) return i; if (!recursive) return -1; - if (recursive > 10) { + if (recursive > 5) { snd_printd("hda_codec: too deep connection for 0x%x\n", nid); return -1; } @@ -1086,16 +1046,9 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) struct hda_pincfg *pin; #ifdef CONFIG_SND_HDA_HWDEP - { - unsigned int cfg = 0; - mutex_lock(&codec->user_mutex); - pin = look_up_pincfg(codec, &codec->user_pins, nid); - if (pin) - cfg = pin->cfg; - mutex_unlock(&codec->user_mutex); - if (cfg) - return cfg; - } + pin = look_up_pincfg(codec, &codec->user_pins, nid); + if (pin) + return pin->cfg; #endif pin = look_up_pincfg(codec, &codec->driver_pins, nid); if (pin) @@ -1107,32 +1060,6 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg); -/* remember the current pinctl target value */ -int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, - unsigned int val) -{ - struct hda_pincfg *pin; - - pin = look_up_pincfg(codec, &codec->init_pins, nid); - if (!pin) - return -EINVAL; - pin->target = val; - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_set_pin_target); - -/* return the current pinctl target value */ -int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_pincfg *pin; - - pin = look_up_pincfg(codec, &codec->init_pins, nid); - if (!pin) - return 0; - return pin->target; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_get_pin_target); - /** * snd_hda_shutup_pins - Shut up all pins * @codec: the HDA codec @@ -1252,8 +1179,8 @@ static void snd_hda_codec_free(struct hda_codec *codec) snd_array_free(&codec->mixers); snd_array_free(&codec->nids); snd_array_free(&codec->cvt_setups); + snd_array_free(&codec->conn_lists); snd_array_free(&codec->spdif_out); - remove_conn_list(codec); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -1276,8 +1203,6 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state); -static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid, - unsigned int power_state); /** * snd_hda_codec_new - create a HDA codec @@ -1325,11 +1250,9 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); + snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); - snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); - INIT_LIST_HEAD(&codec->conn_list); - INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); #ifdef CONFIG_PM @@ -1398,7 +1321,6 @@ int snd_hda_codec_new(struct hda_bus *bus, #endif codec->epss = snd_hda_codec_get_supported_ps(codec, fg, AC_PWRST_EPSS); - codec->power_filter = default_power_filter; /* power-up all before initialization */ hda_set_power_state(codec, AC_PWRST_D0); @@ -1529,7 +1451,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", nid, stream_tag, channel_id, format); p = get_hda_cvt_setup(codec, nid); - if (!p || p->active) + if (!p) return; if (codec->pcm_format_first) @@ -1576,7 +1498,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); p = get_hda_cvt_setup(codec, nid); - if (p && p->active) { + if (p) { /* here we just clear the active flag when do_now isn't set; * actual clean-ups will be done later in * purify_inactive_streams() called from snd_hda_codec_prpapre() @@ -1688,7 +1610,6 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, cur = snd_array_index(&cache->buf, info); info->key = key; info->val = 0; - info->dirty = 0; idx = key % (u16)ARRAY_SIZE(cache->hash); info->next = cache->hash[idx]; cache->hash[idx] = cur; @@ -1843,7 +1764,7 @@ EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps); */ static struct hda_amp_info * update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index, bool init_only) + int direction, int index) { struct hda_amp_info *info; unsigned int parm, val = 0; @@ -1869,15 +1790,14 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, } info->vol[ch] = val; info->head.val |= INFO_AMP_VOL(ch); - } else if (init_only) - return NULL; + } return info; } /* * write the current volume in info to the h/w */ -static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps, +static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, hda_nid_t nid, int ch, int direction, int index, int val) { @@ -1886,8 +1806,8 @@ static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps, parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; parm |= index << AC_AMP_SET_INDEX_SHIFT; - if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) && - (amp_caps & AC_AMPCAP_MIN_MUTE)) + if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) && + (info->amp_caps & AC_AMPCAP_MIN_MUTE)) ; /* set the zero value as a fake mute */ else parm |= val; @@ -1911,7 +1831,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, unsigned int val = 0; mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, index, false); + info = update_amp_hash(codec, nid, ch, direction, index); if (info) val = info->vol[ch]; mutex_unlock(&codec->hash_mutex); @@ -1919,20 +1839,30 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); -static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val, - bool init_only) +/** + * snd_hda_codec_amp_update - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed. + */ +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val) { struct hda_amp_info *info; - unsigned int caps; - unsigned int cache_only; if (snd_BUG_ON(mask & ~0xff)) mask &= 0xff; val &= mask; mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, idx, init_only); + info = update_amp_hash(codec, nid, ch, direction, idx); if (!info) { mutex_unlock(&codec->hash_mutex); return 0; @@ -1943,32 +1873,10 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, return 0; } info->vol[ch] = val; - cache_only = info->head.dirty = codec->cached_write; - caps = info->amp_caps; mutex_unlock(&codec->hash_mutex); - if (!cache_only) - put_vol_mute(codec, caps, nid, ch, direction, idx, val); + put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } - -/** - * snd_hda_codec_amp_update - update the AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed. - */ -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val) -{ - return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false); -} EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); /** @@ -1997,31 +1905,7 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); -/* Works like snd_hda_codec_amp_update() but it writes the value only at - * the first access. If the amp was already initialized / updated beforehand, - * this does nothing. - */ -int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, - int dir, int idx, int mask, int val) -{ - return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true); -} -EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init); - -int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx, int mask, int val) -{ - int ch, ret = 0; - - if (snd_BUG_ON(mask & ~0xff)) - mask &= 0xff; - for (ch = 0; ch < 2; ch++) - ret |= snd_hda_codec_amp_init(codec, nid, ch, dir, - idx, mask, val); - return ret; -} -EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo); - +#ifdef CONFIG_PM /** * snd_hda_codec_resume_amp - Resume all AMP commands from the cache * @codec: HD-audio codec @@ -2030,40 +1914,28 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo); */ void snd_hda_codec_resume_amp(struct hda_codec *codec) { + struct hda_amp_info *buffer = codec->amp_cache.buf.list; int i; - mutex_lock(&codec->hash_mutex); - codec->cached_write = 0; - for (i = 0; i < codec->amp_cache.buf.used; i++) { - struct hda_amp_info *buffer; - u32 key; + for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) { + u32 key = buffer->head.key; hda_nid_t nid; unsigned int idx, dir, ch; - struct hda_amp_info info; - - buffer = snd_array_elem(&codec->amp_cache.buf, i); - if (!buffer->head.dirty) - continue; - buffer->head.dirty = 0; - info = *buffer; - key = info.head.key; if (!key) continue; nid = key & 0xff; idx = (key >> 16) & 0xff; dir = (key >> 24) & 0xff; for (ch = 0; ch < 2; ch++) { - if (!(info.head.val & INFO_AMP_VOL(ch))) + if (!(buffer->head.val & INFO_AMP_VOL(ch))) continue; - mutex_unlock(&codec->hash_mutex); - put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx, - info.vol[ch]); - mutex_lock(&codec->hash_mutex); + put_vol_mute(codec, buffer, nid, ch, dir, idx, + buffer->vol[ch]); } } - mutex_unlock(&codec->hash_mutex); } EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); +#endif /* CONFIG_PM */ static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int ofs) @@ -2490,7 +2362,6 @@ int snd_hda_codec_reset(struct hda_codec *codec) snd_array_free(&codec->driver_pins); snd_array_free(&codec->cvt_setups); snd_array_free(&codec->spdif_out); - snd_array_free(&codec->verbs); codec->num_pcms = 0; codec->pcm_info = NULL; codec->preset = NULL; @@ -3504,11 +3375,12 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); +#ifdef CONFIG_PM /* * command cache */ -/* build a 31bit cache key with the widget id and the command parameter */ +/* build a 32bit cache key with the widget id and the command parameter */ #define build_cmd_cache_key(nid, verb) ((verb << 8) | nid) #define get_cmd_cache_nid(key) ((key) & 0xff) #define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff) @@ -3528,28 +3400,20 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { - int err; + int err = snd_hda_codec_write(codec, nid, direct, verb, parm); struct hda_cache_head *c; u32 key; - unsigned int cache_only; - - cache_only = codec->cached_write; - if (!cache_only) { - err = snd_hda_codec_write(codec, nid, direct, verb, parm); - if (err < 0) - return err; - } + if (err < 0) + return err; /* parm may contain the verb stuff for get/set amp */ verb = verb | (parm >> 8); parm &= 0xff; key = build_cmd_cache_key(nid, verb); mutex_lock(&codec->bus->cmd_mutex); c = get_alloc_hash(&codec->cmd_cache, key); - if (c) { + if (c) c->val = parm; - c->dirty = cache_only; - } mutex_unlock(&codec->bus->cmd_mutex); return 0; } @@ -3598,27 +3462,16 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache); */ void snd_hda_codec_resume_cache(struct hda_codec *codec) { + struct hda_cache_head *buffer = codec->cmd_cache.buf.list; int i; - mutex_lock(&codec->hash_mutex); - codec->cached_write = 0; - for (i = 0; i < codec->cmd_cache.buf.used; i++) { - struct hda_cache_head *buffer; - u32 key; - - buffer = snd_array_elem(&codec->cmd_cache.buf, i); - key = buffer->key; + for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) { + u32 key = buffer->key; if (!key) continue; - if (!buffer->dirty) - continue; - buffer->dirty = 0; - mutex_unlock(&codec->hash_mutex); snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0, get_cmd_cache_cmd(key), buffer->val); - mutex_lock(&codec->hash_mutex); } - mutex_unlock(&codec->hash_mutex); } EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache); @@ -3639,36 +3492,32 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, seq->param); } EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); - -/** - * snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs - * @codec: HD-audio codec - */ -void snd_hda_codec_flush_cache(struct hda_codec *codec) -{ - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); -} -EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache); +#endif /* CONFIG_PM */ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) + unsigned int power_state, + bool eapd_workaround) { hda_nid_t nid = codec->start_nid; int i; for (i = 0; i < codec->num_nodes; i++, nid++) { unsigned int wcaps = get_wcaps(codec, nid); - unsigned int state = power_state; if (!(wcaps & AC_WCAP_POWER)) continue; - if (codec->power_filter) { - state = codec->power_filter(codec, nid, power_state); - if (state != power_state && power_state == AC_PWRST_D3) + /* don't power down the widget if it controls eapd and + * EAPD_BTLENABLE is set. + */ + if (eapd_workaround && power_state == AC_PWRST_D3 && + get_wcaps_type(wcaps) == AC_WID_PIN && + (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { + int eapd = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, 0); + if (eapd & 0x02) continue; } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, - state); + power_state); } } EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); @@ -3715,21 +3564,6 @@ static unsigned int hda_sync_power_state(struct hda_codec *codec, return state; } -/* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */ -static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid, - unsigned int power_state) -{ - if (power_state == AC_PWRST_D3 && - get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN && - (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { - int eapd = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_EAPD_BTLENABLE, 0); - if (eapd & 0x02) - return AC_PWRST_D0; - } - return power_state; -} - /* * set power state of the codec, and return the power state */ @@ -3755,7 +3589,8 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state, + true); } state = hda_sync_power_state(codec, fg, power_state); if (!(state & AC_PWRST_ERROR)) @@ -3765,32 +3600,6 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, return state; } -/* sync power states of all widgets; - * this is called at the end of codec parsing - */ -static void sync_power_up_states(struct hda_codec *codec) -{ - hda_nid_t nid = codec->start_nid; - int i; - - /* don't care if no or standard filter is used */ - if (!codec->power_filter || codec->power_filter == default_power_filter) - return; - - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int target; - if (!(wcaps & AC_WCAP_POWER)) - continue; - target = codec->power_filter(codec, nid, AC_PWRST_D0); - if (target == AC_PWRST_D0) - continue; - if (!snd_hda_check_power_state(codec, nid, target)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, target); - } -} - #ifdef CONFIG_SND_HDA_HWDEP /* execute additional init verbs */ static void hda_exec_init_verbs(struct hda_codec *codec) @@ -3831,22 +3640,6 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) return state; } -/* mark all entries of cmd and amp caches dirty */ -static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) -{ - int i; - for (i = 0; i < codec->cmd_cache.buf.used; i++) { - struct hda_cache_head *cmd; - cmd = snd_array_elem(&codec->cmd_cache.buf, i); - cmd->dirty = 1; - } - for (i = 0; i < codec->amp_cache.buf.used; i++) { - struct hda_amp_info *amp; - amp = snd_array_elem(&codec->amp_cache.buf, i); - amp->head.dirty = 1; - } -} - /* * kick up codec; used both from PM and power-save */ @@ -3854,8 +3647,6 @@ static void hda_call_codec_resume(struct hda_codec *codec) { codec->in_pm = 1; - hda_mark_cmd_cache_dirty(codec); - /* set as if powered on for avoiding re-entering the resume * in the resume / power-save sequence */ @@ -3978,7 +3769,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) hda_jackpoll_work(&codec->jackpoll_work.work); else snd_hda_jack_report_sync(codec); /* call at the last init point */ - sync_power_up_states(codec); return 0; } @@ -5330,62 +5120,23 @@ unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin) } EXPORT_SYMBOL_HDA(snd_hda_get_default_vref); -/* correct the pin ctl value for matching with the pin cap */ -unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, - hda_nid_t pin, unsigned int val) -{ - static unsigned int cap_lists[][2] = { - { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 }, - { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 }, - { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 }, - { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD }, - }; - unsigned int cap; - - if (!val) - return 0; - cap = snd_hda_query_pin_caps(codec, pin); - if (!cap) - return val; /* don't know what to do... */ - - if (val & AC_PINCTL_OUT_EN) { - if (!(cap & AC_PINCAP_OUT)) - val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); - else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV)) - val &= ~AC_PINCTL_HP_EN; - } - - if (val & AC_PINCTL_IN_EN) { - if (!(cap & AC_PINCAP_IN)) - val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); - else { - unsigned int vcap, vref; - int i; - vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - vref = val & AC_PINCTL_VREFEN; - for (i = 0; i < ARRAY_SIZE(cap_lists); i++) { - if (vref == cap_lists[i][0] && - !(vcap & cap_lists[i][1])) { - if (i == ARRAY_SIZE(cap_lists) - 1) - vref = AC_PINCTL_VREF_HIZ; - else - vref = cap_lists[i + 1][0]; - } - } - val &= ~AC_PINCTL_VREFEN; - val |= vref; - } - } - - return val; -} -EXPORT_SYMBOL_HDA(snd_hda_correct_pin_ctl); - int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool cached) { - val = snd_hda_correct_pin_ctl(codec, pin, val); - snd_hda_codec_set_pin_target(codec, pin, val); + if (val) { + unsigned int cap = snd_hda_query_pin_caps(codec, pin); + if (cap && (val & AC_PINCTL_OUT_EN)) { + if (!(cap & AC_PINCAP_OUT)) + val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); + else if ((val & AC_PINCTL_HP_EN) && + !(cap & AC_PINCAP_HP_DRV)) + val &= ~AC_PINCTL_HP_EN; + } + if (cap && (val & AC_PINCTL_IN_EN)) { + if (!(cap & AC_PINCAP_IN)) + val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); + } + } if (cached) return snd_hda_codec_update_cache(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); diff --git a/trunk/sound/pci/hda/hda_codec.h b/trunk/sound/pci/hda/hda_codec.h index fbedcf3c9d0b..8665540e55aa 100644 --- a/trunk/sound/pci/hda/hda_codec.h +++ b/trunk/sound/pci/hda/hda_codec.h @@ -719,10 +719,9 @@ struct hda_codec_ops { /* record for amp information cache */ struct hda_cache_head { - u32 key:31; /* hash key */ - u32 dirty:1; + u32 key; /* hash key */ u16 val; /* assigned value */ - u16 next; + u16 next; /* next link; -1 = terminal */ }; struct hda_amp_info { @@ -831,7 +830,7 @@ struct hda_codec { struct hda_cache_rec amp_cache; /* cache for amp access */ struct hda_cache_rec cmd_cache; /* cache for other commands */ - struct list_head conn_list; /* linked-list of connection-list */ + struct snd_array conn_lists; /* connection-list array */ struct mutex spdif_mutex; struct mutex control_mutex; @@ -845,7 +844,6 @@ struct hda_codec { struct snd_array cvt_setups; /* audio convert setups */ #ifdef CONFIG_SND_HDA_HWDEP - struct mutex user_mutex; struct snd_hwdep *hwdep; /* assigned hwdep device */ struct snd_array init_verbs; /* additional init verbs */ struct snd_array hints; /* additional hints */ @@ -867,11 +865,8 @@ struct hda_codec { unsigned int pins_shutup:1; /* pins are shut up */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ unsigned int no_jack_detect:1; /* Machine has no jack-detection */ - unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */ - unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */ unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ - unsigned int cached_write:1; /* write only to caches */ #ifdef CONFIG_PM unsigned int power_on :1; /* current (global) power-state */ unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ @@ -886,10 +881,6 @@ struct hda_codec { spinlock_t power_lock; #endif - /* filter the requested power state per nid */ - unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid, - unsigned int power_state); - /* codec-specific additional proc output */ void (*proc_widget_hook)(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid); @@ -903,14 +894,6 @@ struct hda_codec { /* jack detection */ struct snd_array jacks; #endif - - /* fix-up list */ - int fixup_id; - const struct hda_fixup *fixup_list; - const char *fixup_name; - - /* additional init verbs */ - struct snd_array verbs; }; /* direction */ @@ -949,8 +932,6 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) } int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); -int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, - const hda_nid_t **listp); int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, const hda_nid_t *list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, @@ -971,6 +952,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); /* cached write */ +#ifdef CONFIG_PM int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_sequence_write_cache(struct hda_codec *codec, @@ -978,14 +960,17 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_codec_resume_cache(struct hda_codec *codec); -/* both for cmd & amp caches */ -void snd_hda_codec_flush_cache(struct hda_codec *codec); +#else +#define snd_hda_codec_write_cache snd_hda_codec_write +#define snd_hda_codec_update_cache snd_hda_codec_write +#define snd_hda_sequence_write_cache snd_hda_sequence_write +#endif /* the struct for codec->pin_configs */ struct hda_pincfg { hda_nid_t nid; - unsigned char ctrl; /* original pin control value */ - unsigned char target; /* target pin control value */ + unsigned char ctrl; /* current pin control value */ + unsigned char pad; /* reserved */ unsigned int cfg; /* default configuration */ }; @@ -1051,7 +1036,8 @@ extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); void snd_hda_bus_reboot_notify(struct hda_bus *bus); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state); + unsigned int power_state, + bool eapd_workaround); int snd_hda_lock_devices(struct hda_bus *bus); void snd_hda_unlock_devices(struct hda_bus *bus); diff --git a/trunk/sound/pci/hda/hda_generic.c b/trunk/sound/pci/hda/hda_generic.c index c4ba3066a013..b81d3d0b952d 100644 --- a/trunk/sound/pci/hda/hda_generic.c +++ b/trunk/sound/pci/hda/hda_generic.c @@ -23,4907 +23,1063 @@ #include #include #include -#include -#include -#include -#include -#include #include -#include #include "hda_codec.h" #include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_generic.h" +/* widget node for parsing */ +struct hda_gnode { + hda_nid_t nid; /* NID of this widget */ + unsigned short nconns; /* number of input connections */ + hda_nid_t *conn_list; + hda_nid_t slist[2]; /* temporay list */ + unsigned int wid_caps; /* widget capabilities */ + unsigned char type; /* widget type */ + unsigned char pin_ctl; /* pin controls */ + unsigned char checked; /* the flag indicates that the node is already parsed */ + unsigned int pin_caps; /* pin widget capabilities */ + unsigned int def_cfg; /* default configuration */ + unsigned int amp_out_caps; /* AMP out capabilities */ + unsigned int amp_in_caps; /* AMP in capabilities */ + struct list_head list; +}; -/* initialize hda_gen_spec struct */ -int snd_hda_gen_spec_init(struct hda_gen_spec *spec) -{ - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - snd_array_init(&spec->paths, sizeof(struct nid_path), 8); - mutex_init(&spec->pcm_mutex); - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init); +/* patch-specific record */ -struct snd_kcontrol_new * -snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, - const struct snd_kcontrol_new *temp) -{ - struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *temp; - if (name) - knew->name = kstrdup(name, GFP_KERNEL); - else if (knew->name) - knew->name = kstrdup(knew->name, GFP_KERNEL); - if (!knew->name) - return NULL; - return knew; -} -EXPORT_SYMBOL_HDA(snd_hda_gen_add_kctl); +#define MAX_PCM_VOLS 2 +struct pcm_vol { + struct hda_gnode *node; /* Node for PCM volume */ + unsigned int index; /* connection of PCM volume */ +}; -static void free_kctls(struct hda_gen_spec *spec) -{ - if (spec->kctls.list) { - struct snd_kcontrol_new *kctl = spec->kctls.list; - int i; - for (i = 0; i < spec->kctls.used; i++) - kfree(kctl[i].name); - } - snd_array_free(&spec->kctls); -} +struct hda_gspec { + struct hda_gnode *dac_node[2]; /* DAC node */ + struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */ + struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */ + unsigned int pcm_vol_nodes; /* number of PCM volumes */ -void snd_hda_gen_spec_free(struct hda_gen_spec *spec) -{ - if (!spec) - return; - free_kctls(spec); - snd_array_free(&spec->paths); -} -EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); + struct hda_gnode *adc_node; /* ADC node */ + struct hda_gnode *cap_vol_node; /* Node for capture volume */ + unsigned int cur_cap_src; /* current capture source */ + struct hda_input_mux input_mux; -/* - * store user hints - */ -static void parse_user_hints(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int val; + unsigned int def_amp_in_caps; + unsigned int def_amp_out_caps; - val = snd_hda_get_bool_hint(codec, "jack_detect"); - if (val >= 0) - codec->no_jack_detect = !val; - val = snd_hda_get_bool_hint(codec, "inv_jack_detect"); - if (val >= 0) - codec->inv_jack_detect = !!val; - val = snd_hda_get_bool_hint(codec, "trigger_sense"); - if (val >= 0) - codec->no_trigger_sense = !val; - val = snd_hda_get_bool_hint(codec, "inv_eapd"); - if (val >= 0) - codec->inv_eapd = !!val; - val = snd_hda_get_bool_hint(codec, "pcm_format_first"); - if (val >= 0) - codec->pcm_format_first = !!val; - val = snd_hda_get_bool_hint(codec, "sticky_stream"); - if (val >= 0) - codec->no_sticky_stream = !val; - val = snd_hda_get_bool_hint(codec, "spdif_status_reset"); - if (val >= 0) - codec->spdif_status_reset = !!val; - val = snd_hda_get_bool_hint(codec, "pin_amp_workaround"); - if (val >= 0) - codec->pin_amp_workaround = !!val; - val = snd_hda_get_bool_hint(codec, "single_adc_amp"); - if (val >= 0) - codec->single_adc_amp = !!val; + struct hda_pcm pcm_rec; /* PCM information */ - val = snd_hda_get_bool_hint(codec, "auto_mute"); - if (val >= 0) - spec->suppress_auto_mute = !val; - val = snd_hda_get_bool_hint(codec, "auto_mic"); - if (val >= 0) - spec->suppress_auto_mic = !val; - val = snd_hda_get_bool_hint(codec, "line_in_auto_switch"); - if (val >= 0) - spec->line_in_auto_switch = !!val; - val = snd_hda_get_bool_hint(codec, "need_dac_fix"); - if (val >= 0) - spec->need_dac_fix = !!val; - val = snd_hda_get_bool_hint(codec, "primary_hp"); - if (val >= 0) - spec->no_primary_hp = !val; - val = snd_hda_get_bool_hint(codec, "multi_cap_vol"); - if (val >= 0) - spec->multi_cap_vol = !!val; - val = snd_hda_get_bool_hint(codec, "inv_dmic_split"); - if (val >= 0) - spec->inv_dmic_split = !!val; - val = snd_hda_get_bool_hint(codec, "indep_hp"); - if (val >= 0) - spec->indep_hp = !!val; - val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input"); - if (val >= 0) - spec->add_stereo_mix_input = !!val; - val = snd_hda_get_bool_hint(codec, "add_out_jack_modes"); - if (val >= 0) - spec->add_out_jack_modes = !!val; - val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); - if (val >= 0) - spec->add_in_jack_modes = !!val; - val = snd_hda_get_bool_hint(codec, "power_down_unused"); - if (val >= 0) - spec->power_down_unused = !!val; + struct list_head nid_list; /* list of widgets */ - if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) - spec->mixer_nid = val; -} +#ifdef CONFIG_PM +#define MAX_LOOPBACK_AMPS 7 + struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1]; +#endif +}; /* - * pin control value accesses + * retrieve the default device type from the default config value */ +#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \ + AC_DEFCFG_DEVICE_SHIFT) +#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \ + AC_DEFCFG_LOCATION_SHIFT) +#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \ + AC_DEFCFG_PORT_CONN_SHIFT) -#define update_pin_ctl(codec, pin, val) \ - snd_hda_codec_update_cache(codec, pin, 0, \ - AC_VERB_SET_PIN_WIDGET_CONTROL, val) - -/* restore the pinctl based on the cached value */ -static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin) +/* + * destructor + */ +static void snd_hda_generic_free(struct hda_codec *codec) { - update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin)); -} + struct hda_gspec *spec = codec->spec; + struct hda_gnode *node, *n; -/* set the pinctl target value and write it if requested */ -static void set_pin_target(struct hda_codec *codec, hda_nid_t pin, - unsigned int val, bool do_write) -{ - if (!pin) + if (! spec) return; - val = snd_hda_correct_pin_ctl(codec, pin, val); - snd_hda_codec_set_pin_target(codec, pin, val); - if (do_write) - update_pin_ctl(codec, pin, val); + /* free all widgets */ + list_for_each_entry_safe(node, n, &spec->nid_list, list) { + if (node->conn_list != node->slist) + kfree(node->conn_list); + kfree(node); + } + kfree(spec); } -/* set pinctl target values for all given pins */ -static void set_pin_targets(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, unsigned int val) -{ - int i; - for (i = 0; i < num_pins; i++) - set_pin_target(codec, pins[i], val, false); -} /* - * parsing paths + * add a new widget node and read its attributes */ - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid) { - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} + struct hda_gnode *node; + int nconns; + hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; -/* return true if the given NID is contained in the path */ -static bool is_nid_contained(struct nid_path *path, hda_nid_t nid) -{ - return find_idx_in_nid_list(nid, path->path, path->depth) >= 0; -} + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (node == NULL) + return -ENOMEM; + node->nid = nid; + node->wid_caps = get_wcaps(codec, nid); + node->type = get_wcaps_type(node->wid_caps); + if (node->wid_caps & AC_WCAP_CONN_LIST) { + nconns = snd_hda_get_connections(codec, nid, conn_list, + HDA_MAX_CONNECTIONS); + if (nconns < 0) { + kfree(node); + return nconns; + } + } else { + nconns = 0; + } + if (nconns <= ARRAY_SIZE(node->slist)) + node->conn_list = node->slist; + else { + node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns, + GFP_KERNEL); + if (! node->conn_list) { + snd_printk(KERN_ERR "hda-generic: cannot malloc\n"); + kfree(node); + return -ENOMEM; + } + } + memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t)); + node->nconns = nconns; -static struct nid_path *get_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int anchor_nid) -{ - struct hda_gen_spec *spec = codec->spec; - int i; + if (node->type == AC_WID_PIN) { + node->pin_caps = snd_hda_query_pin_caps(codec, node->nid); + node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid); + } - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); - if (path->depth <= 0) - continue; - if ((!from_nid || path->path[0] == from_nid) && - (!to_nid || path->path[path->depth - 1] == to_nid)) { - if (!anchor_nid || - (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) || - (anchor_nid < 0 && !is_nid_contained(path, anchor_nid))) - return path; - } + if (node->wid_caps & AC_WCAP_OUT_AMP) { + if (node->wid_caps & AC_WCAP_AMP_OVRD) + node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP); + if (! node->amp_out_caps) + node->amp_out_caps = spec->def_amp_out_caps; } - return NULL; + if (node->wid_caps & AC_WCAP_IN_AMP) { + if (node->wid_caps & AC_WCAP_AMP_OVRD) + node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP); + if (! node->amp_in_caps) + node->amp_in_caps = spec->def_amp_in_caps; + } + list_add_tail(&node->list, &spec->nid_list); + return 0; } -/* get the path between the given NIDs; - * passing 0 to either @pin or @dac behaves as a wildcard +/* + * build the AFG subtree */ -struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid) +static int build_afg_tree(struct hda_codec *codec) { - return get_nid_path(codec, from_nid, to_nid, 0); -} -EXPORT_SYMBOL_HDA(snd_hda_get_nid_path); + struct hda_gspec *spec = codec->spec; + int i, nodes, err; + hda_nid_t nid; -/* get the index number corresponding to the path instance; - * the index starts from 1, for easier checking the invalid value - */ -int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *array = spec->paths.list; - ssize_t idx; + if (snd_BUG_ON(!spec)) + return -EINVAL; - if (!spec->paths.used) - return 0; - idx = path - array; - if (idx < 0 || idx >= spec->paths.used) - return 0; - return idx + 1; -} -EXPORT_SYMBOL_HDA(snd_hda_get_path_idx); + spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP); + spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP); -/* get the path instance corresponding to the given index number */ -struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) -{ - struct hda_gen_spec *spec = codec->spec; + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + if (! nid || nodes < 0) { + printk(KERN_ERR "Invalid AFG subtree\n"); + return -EINVAL; + } + + /* parse all nodes belonging to the AFG */ + for (i = 0; i < nodes; i++, nid++) { + if ((err = add_new_node(codec, spec, nid)) < 0) + return err; + } - if (idx <= 0 || idx > spec->paths.used) - return NULL; - return snd_array_elem(&spec->paths, idx - 1); + return 0; } -EXPORT_SYMBOL_HDA(snd_hda_get_path_from_idx); -/* check whether the given DAC is already found in any existing paths */ -static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) + +/* + * look for the node record for the given NID + */ +/* FIXME: should avoid the braindead linear search */ +static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid) { - struct hda_gen_spec *spec = codec->spec; - int i; + struct hda_gnode *node; - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); - if (path->path[0] == nid) - return true; + list_for_each_entry(node, &spec->nid_list, list) { + if (node->nid == nid) + return node; } - return false; + return NULL; } -/* check whether the given two widgets can be connected */ -static bool is_reachable_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid) -{ - if (!from_nid || !to_nid) - return false; - return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; +/* + * unmute (and set max vol) the output amplifier + */ +static int unmute_output(struct hda_codec *codec, struct hda_gnode *node) +{ + unsigned int val, ofs; + snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid); + val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + if (val >= ofs) + val -= ofs; + snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val); + return 0; } -/* nid, dir and idx */ -#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) - -/* check whether the given ctl is already assigned in any path elements */ -static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - val &= AMP_VAL_COMPARE_MASK; - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); - if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) - return true; - } - return false; +/* + * unmute (and set max vol) the input amplifier + */ +static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index) +{ + unsigned int val, ofs; + snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index); + val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + if (val >= ofs) + val -= ofs; + snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val); + return 0; } -/* check whether a control with the given (nid, dir, idx) was assigned */ -static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx, int type) +/* + * select the input connection of the given node. + */ +static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node, + unsigned int index) { - unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); - return is_ctl_used(codec, val, type); + snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index); + return snd_hda_codec_write_cache(codec, node->nid, 0, + AC_VERB_SET_CONNECT_SEL, index); } -static void print_nid_path(const char *pfx, struct nid_path *path) +/* + * clear checked flag of each node in the node list + */ +static void clear_check_flags(struct hda_gspec *spec) { - char buf[40]; - int i; + struct hda_gnode *node; - - buf[0] = 0; - for (i = 0; i < path->depth; i++) { - char tmp[4]; - sprintf(tmp, ":%02x", path->path[i]); - strlcat(buf, tmp, sizeof(buf)); + list_for_each_entry(node, &spec->nid_list, list) { + node->checked = 0; } - snd_printdd("%s path: depth=%d %s\n", pfx, path->depth, buf); } -/* called recursively */ -static bool __parse_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int anchor_nid, struct nid_path *path, - int depth) +/* + * parse the output path recursively until reach to an audio output widget + * + * returns 0 if not found, 1 if found, or a negative error code. + */ +static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, + struct hda_gnode *node, int dac_idx) { - const hda_nid_t *conn; - int i, nums; + int i, err; + struct hda_gnode *child; - if (to_nid == anchor_nid) - anchor_nid = 0; /* anchor passed */ - else if (to_nid == (hda_nid_t)(-anchor_nid)) - return false; /* hit the exclusive nid */ + if (node->checked) + return 0; - nums = snd_hda_get_conn_list(codec, to_nid, &conn); - for (i = 0; i < nums; i++) { - if (conn[i] != from_nid) { - /* special case: when from_nid is 0, - * try to find an empty DAC - */ - if (from_nid || - get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || - is_dac_already_used(codec, conn[i])) - continue; + node->checked = 1; + if (node->type == AC_WID_AUD_OUT) { + if (node->wid_caps & AC_WCAP_DIGITAL) { + snd_printdd("Skip Digital OUT node %x\n", node->nid); + return 0; } - /* anchor is not requested or already passed? */ - if (anchor_nid <= 0) - goto found; - } - if (depth >= MAX_NID_PATH_DEPTH) - return false; - for (i = 0; i < nums; i++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || - type == AC_WID_PIN) - continue; - if (__parse_nid_path(codec, from_nid, conn[i], - anchor_nid, path, depth + 1)) - goto found; + snd_printdd("AUD_OUT found %x\n", node->nid); + if (spec->dac_node[dac_idx]) { + /* already DAC node is assigned, just unmute & connect */ + return node == spec->dac_node[dac_idx]; + } + spec->dac_node[dac_idx] = node; + if ((node->wid_caps & AC_WCAP_OUT_AMP) && + spec->pcm_vol_nodes < MAX_PCM_VOLS) { + spec->pcm_vol[spec->pcm_vol_nodes].node = node; + spec->pcm_vol[spec->pcm_vol_nodes].index = 0; + spec->pcm_vol_nodes++; + } + return 1; /* found */ } - return false; - - found: - path->path[path->depth] = conn[i]; - path->idx[path->depth + 1] = i; - if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) - path->multi[path->depth + 1] = 1; - path->depth++; - return true; -} -/* parse the widget path from the given nid to the target nid; - * when @from_nid is 0, try to find an empty DAC; - * when @anchor_nid is set to a positive value, only paths through the widget - * with the given value are evaluated. - * when @anchor_nid is set to a negative value, paths through the widget - * with the negative of given value are excluded, only other paths are chosen. - * when @anchor_nid is zero, no special handling about path selection. - */ -bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int anchor_nid, - struct nid_path *path) -{ - if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) { - path->path[path->depth] = to_nid; - path->depth++; - return true; + for (i = 0; i < node->nconns; i++) { + child = hda_get_node(spec, node->conn_list[i]); + if (! child) + continue; + err = parse_output_path(codec, spec, child, dac_idx); + if (err < 0) + return err; + else if (err > 0) { + /* found one, + * select the path, unmute both input and output + */ + if (node->nconns > 1) + select_input_connection(codec, node, i); + unmute_input(codec, node, i); + unmute_output(codec, node); + if (spec->dac_node[dac_idx] && + spec->pcm_vol_nodes < MAX_PCM_VOLS && + !(spec->dac_node[dac_idx]->wid_caps & + AC_WCAP_OUT_AMP)) { + if ((node->wid_caps & AC_WCAP_IN_AMP) || + (node->wid_caps & AC_WCAP_OUT_AMP)) { + int n = spec->pcm_vol_nodes; + spec->pcm_vol[n].node = node; + spec->pcm_vol[n].index = i; + spec->pcm_vol_nodes++; + } + } + return 1; + } } - return false; + return 0; } -EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path); /* - * parse the path between the given NIDs and add to the path list. - * if no valid path is found, return NULL + * Look for the output PIN widget with the given jack type + * and parse the output path to that PIN. + * + * Returns the PIN node when the path to DAC is established. */ -struct nid_path * -snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int anchor_nid) +static struct hda_gnode *parse_output_jack(struct hda_codec *codec, + struct hda_gspec *spec, + int jack_type) { - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - - if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) - return NULL; - - /* check whether the path has been already added */ - path = get_nid_path(codec, from_nid, to_nid, anchor_nid); - if (path) - return path; + struct hda_gnode *node; + int err; - path = snd_array_new(&spec->paths); - if (!path) - return NULL; - memset(path, 0, sizeof(*path)); - if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path)) - return path; - /* push back */ - spec->paths.used--; + list_for_each_entry(node, &spec->nid_list, list) { + if (node->type != AC_WID_PIN) + continue; + /* output capable? */ + if (! (node->pin_caps & AC_PINCAP_OUT)) + continue; + if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) + continue; /* unconnected */ + if (jack_type >= 0) { + if (jack_type != defcfg_type(node)) + continue; + if (node->wid_caps & AC_WCAP_DIGITAL) + continue; /* skip SPDIF */ + } else { + /* output as default? */ + if (! (node->pin_ctl & AC_PINCTL_OUT_EN)) + continue; + } + clear_check_flags(spec); + err = parse_output_path(codec, spec, node, 0); + if (err < 0) + return NULL; + if (! err && spec->out_pin_node[0]) { + err = parse_output_path(codec, spec, node, 1); + if (err < 0) + return NULL; + } + if (err > 0) { + /* unmute the PIN output */ + unmute_output(codec, node); + /* set PIN-Out enable */ + snd_hda_codec_write_cache(codec, node->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + AC_PINCTL_OUT_EN | + ((node->pin_caps & AC_PINCAP_HP_DRV) ? + AC_PINCTL_HP_EN : 0)); + return node; + } + } return NULL; } -EXPORT_SYMBOL_HDA(snd_hda_add_new_path); -/* clear the given path as invalid so that it won't be picked up later */ -static void invalidate_nid_path(struct hda_codec *codec, int idx) -{ - struct nid_path *path = snd_hda_get_path_from_idx(codec, idx); - if (!path) - return; - memset(path, 0, sizeof(*path)); -} -/* look for an empty DAC slot */ -static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin, - bool is_digital) +/* + * parse outputs + */ +static int parse_output(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; - bool cap_digital; - int i; + struct hda_gspec *spec = codec->spec; + struct hda_gnode *node; - for (i = 0; i < spec->num_all_dacs; i++) { - hda_nid_t nid = spec->all_dacs[i]; - if (!nid || is_dac_already_used(codec, nid)) - continue; - cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); - if (is_digital != cap_digital) - continue; - if (is_reachable_path(codec, nid, pin)) - return nid; + /* + * Look for the output PIN widget + */ + /* first, look for the line-out pin */ + node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT); + if (node) /* found, remember the PIN node */ + spec->out_pin_node[0] = node; + else { + /* if no line-out is found, try speaker out */ + node = parse_output_jack(codec, spec, AC_JACK_SPEAKER); + if (node) + spec->out_pin_node[0] = node; + } + /* look for the HP-out pin */ + node = parse_output_jack(codec, spec, AC_JACK_HP_OUT); + if (node) { + if (! spec->out_pin_node[0]) + spec->out_pin_node[0] = node; + else + spec->out_pin_node[1] = node; } - return 0; -} -/* replace the channels in the composed amp value with the given number */ -static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) -{ - val &= ~(0x3U << 16); - val |= chs << 16; - return val; -} + if (! spec->out_pin_node[0]) { + /* no line-out or HP pins found, + * then choose for the first output pin + */ + spec->out_pin_node[0] = parse_output_jack(codec, spec, -1); + if (! spec->out_pin_node[0]) + snd_printd("hda_generic: no proper output path found\n"); + } -/* check whether the widget has the given amp capability for the direction */ -static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int bits) -{ - if (!nid) - return false; - if (get_wcaps(codec, nid) & (1 << (dir + 1))) - if (query_amp_caps(codec, nid, dir) & bits) - return true; - return false; + return 0; } -static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, - hda_nid_t nid2, int dir) +/* + * input MUX + */ + +/* control callbacks */ +static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - if (!(get_wcaps(codec, nid1) & (1 << (dir + 1)))) - return !(get_wcaps(codec, nid2) & (1 << (dir + 1))); - return (query_amp_caps(codec, nid1, dir) == - query_amp_caps(codec, nid2, dir)); + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gspec *spec = codec->spec; + return snd_hda_input_mux_info(&spec->input_mux, uinfo); } -#define nid_has_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) -#define nid_has_volume(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) - -/* look for a widget suitable for assigning a mute switch in the path */ -static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, - struct nid_path *path) +static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - int i; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gspec *spec = codec->spec; - for (i = path->depth - 1; i >= 0; i--) { - if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) - return path->path[i]; - if (i != path->depth - 1 && i != 0 && - nid_has_mute(codec, path->path[i], HDA_INPUT)) - return path->path[i]; - } + ucontrol->value.enumerated.item[0] = spec->cur_cap_src; return 0; } -/* look for a widget suitable for assigning a volume ctl in the path */ -static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, - struct nid_path *path) +static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - int i; - - for (i = path->depth - 1; i >= 0; i--) { - if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) - return path->path[i]; - } - return 0; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gspec *spec = codec->spec; + return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, + spec->adc_node->nid, &spec->cur_cap_src); } /* - * path activation / deactivation + * return the string name of the given input PIN widget */ - -/* can have the amp-in capability? */ -static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) -{ - hda_nid_t nid = path->path[idx]; - unsigned int caps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_IN_AMP)) - return false; - if (type == AC_WID_PIN && idx > 0) /* only for input pins */ - return false; - return true; +static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl) +{ + unsigned int location = defcfg_location(node); + switch (defcfg_type(node)) { + case AC_JACK_LINE_IN: + if ((location & 0x0f) == AC_JACK_LOC_FRONT) + return "Front Line"; + return "Line"; + case AC_JACK_CD: +#if 0 + if (pinctl) + *pinctl |= AC_PINCTL_VREF_GRD; +#endif + return "CD"; + case AC_JACK_AUX: + if ((location & 0x0f) == AC_JACK_LOC_FRONT) + return "Front Aux"; + return "Aux"; + case AC_JACK_MIC_IN: + if (pinctl && + (node->pin_caps & + (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT))) + *pinctl |= AC_PINCTL_VREF_80; + if ((location & 0x0f) == AC_JACK_LOC_FRONT) + return "Front Mic"; + return "Mic"; + case AC_JACK_SPDIF_IN: + return "SPDIF"; + case AC_JACK_DIG_OTHER_IN: + return "Digital"; + } + return NULL; } -/* can have the amp-out capability? */ -static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) +/* + * parse the nodes recursively until reach to the input PIN + * + * returns 0 if not found, 1 if found, or a negative error code. + */ +static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, + struct hda_gnode *node, int idx) { - hda_nid_t nid = path->path[idx]; - unsigned int caps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_OUT_AMP)) - return false; - if (type == AC_WID_PIN && !idx) /* only for output pins */ - return false; - return true; -} + int i, err; + unsigned int pinctl; + const char *type; -/* check whether the given (nid,dir,idx) is active */ -static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, - unsigned int dir, unsigned int idx) -{ - struct hda_gen_spec *spec = codec->spec; - int i, n; + if (node->checked) + return 0; - for (n = 0; n < spec->paths.used; n++) { - struct nid_path *path = snd_array_elem(&spec->paths, n); - if (!path->active) - continue; - for (i = 0; i < path->depth; i++) { - if (path->path[i] == nid) { - if (dir == HDA_OUTPUT || path->idx[i] == idx) - return true; - break; + node->checked = 1; + if (node->type != AC_WID_PIN) { + for (i = 0; i < node->nconns; i++) { + struct hda_gnode *child; + child = hda_get_node(spec, node->conn_list[i]); + if (! child) + continue; + err = parse_adc_sub_nodes(codec, spec, child, idx); + if (err < 0) + return err; + if (err > 0) { + /* found one, + * select the path, unmute both input and output + */ + if (node->nconns > 1) + select_input_connection(codec, node, i); + unmute_input(codec, node, i); + unmute_output(codec, node); + return err; } } + return 0; } - return false; -} -/* get the default amp value for the target state */ -static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int caps, bool enable) -{ - unsigned int val = 0; + /* input capable? */ + if (! (node->pin_caps & AC_PINCAP_IN)) + return 0; - if (caps & AC_AMPCAP_NUM_STEPS) { - /* set to 0dB */ - if (enable) - val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) + return 0; /* unconnected */ + + if (node->wid_caps & AC_WCAP_DIGITAL) + return 0; /* skip SPDIF */ + + if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) { + snd_printk(KERN_ERR "hda_generic: Too many items for capture\n"); + return -EINVAL; } - if (caps & AC_AMPCAP_MUTE) { - if (!enable) - val |= HDA_AMP_MUTE; + + pinctl = AC_PINCTL_IN_EN; + /* create a proper capture source label */ + type = get_input_type(node, &pinctl); + if (! type) { + /* input as default? */ + if (! (node->pin_ctl & AC_PINCTL_IN_EN)) + return 0; + type = "Input"; } - return val; -} + snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL); -/* initialize the amp value (only at the first time) */ -static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) -{ - unsigned int caps = query_amp_caps(codec, nid, dir); - int val = get_amp_val_to_activate(codec, nid, dir, caps, false); - snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); + /* unmute the PIN external input */ + unmute_input(codec, node, 0); /* index = 0? */ + /* set PIN-In enable */ + snd_hda_codec_write_cache(codec, node->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + + return 1; /* found */ } -/* calculate amp value mask we can modify; - * if the given amp is controlled by mixers, don't touch it +/* + * parse input */ -static unsigned int get_amp_mask_to_modify(struct hda_codec *codec, - hda_nid_t nid, int dir, int idx, - unsigned int caps) +static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node) { - unsigned int mask = 0xff; + struct hda_gspec *spec = codec->spec; + struct hda_gnode *node; + int i, err; - if (caps & AC_AMPCAP_MUTE) { - if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL)) - mask &= ~0x80; + snd_printdd("AUD_IN = %x\n", adc_node->nid); + clear_check_flags(spec); + + // awk added - fixed no recording due to muted widget + unmute_input(codec, adc_node, 0); + + /* + * check each connection of the ADC + * if it reaches to a proper input PIN, add the path as the + * input path. + */ + /* first, check the direct connections to PIN widgets */ + for (i = 0; i < adc_node->nconns; i++) { + node = hda_get_node(spec, adc_node->conn_list[i]); + if (node && node->type == AC_WID_PIN) { + err = parse_adc_sub_nodes(codec, spec, node, i); + if (err < 0) + return err; + } } - if (caps & AC_AMPCAP_NUM_STEPS) { - if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || - is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) - mask &= ~0x7f; + /* ... then check the rests, more complicated connections */ + for (i = 0; i < adc_node->nconns; i++) { + node = hda_get_node(spec, adc_node->conn_list[i]); + if (node && node->type != AC_WID_PIN) { + err = parse_adc_sub_nodes(codec, spec, node, i); + if (err < 0) + return err; + } } - return mask; -} - -static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, - int idx, int idx_to_check, bool enable) -{ - unsigned int caps; - unsigned int mask, val; - - if (!enable && is_active_nid(codec, nid, dir, idx_to_check)) - return; - caps = query_amp_caps(codec, nid, dir); - val = get_amp_val_to_activate(codec, nid, dir, caps, enable); - mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps); - if (!mask) - return; + if (! spec->input_mux.num_items) + return 0; /* no input path found... */ - val &= mask; - snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val); -} + snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items); + for (i = 0; i < spec->input_mux.num_items; i++) + snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label, + spec->input_mux.items[i].index); -static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, - int i, bool enable) -{ - hda_nid_t nid = path->path[i]; - init_amp(codec, nid, HDA_OUTPUT, 0); - activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable); + spec->adc_node = adc_node; + return 1; } -static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, - int i, bool enable, bool add_aamix) +/* + * parse input + */ +static int parse_input(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; - const hda_nid_t *conn; - int n, nums, idx; - int type; - hda_nid_t nid = path->path[i]; - - nums = snd_hda_get_conn_list(codec, nid, &conn); - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_PIN || - (type == AC_WID_AUD_IN && codec->single_adc_amp)) { - nums = 1; - idx = 0; - } else - idx = path->idx[i]; - - for (n = 0; n < nums; n++) - init_amp(codec, nid, HDA_INPUT, n); + struct hda_gspec *spec = codec->spec; + struct hda_gnode *node; + int err; - /* here is a little bit tricky in comparison with activate_amp_out(); - * when aa-mixer is available, we need to enable the path as well + /* + * At first we look for an audio input widget. + * If it reaches to certain input PINs, we take it as the + * input path. */ - for (n = 0; n < nums; n++) { - if (n != idx && (!add_aamix || conn[n] != spec->mixer_merge_nid)) - continue; - activate_amp(codec, nid, HDA_INPUT, n, idx, enable); + list_for_each_entry(node, &spec->nid_list, list) { + if (node->wid_caps & AC_WCAP_DIGITAL) + continue; /* skip SPDIF */ + if (node->type == AC_WID_AUD_IN) { + err = parse_input_path(codec, node); + if (err < 0) + return err; + else if (err > 0) + return 0; + } } + snd_printd("hda_generic: no proper input path found\n"); + return 0; } -/* activate or deactivate the given path - * if @add_aamix is set, enable the input from aa-mix NID as well (if any) - */ -void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool add_aamix) +#ifdef CONFIG_PM +static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx) { - struct hda_gen_spec *spec = codec->spec; - int i; + struct hda_gspec *spec = codec->spec; + struct hda_amp_list *p; - if (!enable) - path->active = false; - - for (i = path->depth - 1; i >= 0; i--) { - hda_nid_t nid = path->path[i]; - if (enable && spec->power_down_unused) { - /* make sure the widget is powered up */ - if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - } - if (enable && path->multi[i]) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - path->idx[i]); - if (has_amp_in(codec, path, i)) - activate_amp_in(codec, path, i, enable, add_aamix); - if (has_amp_out(codec, path, i)) - activate_amp_out(codec, path, i, enable); + if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) { + snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n"); + return; } - - if (enable) - path->active = true; + p = &spec->loopback_list[spec->num_loopbacks++]; + p->nid = nid; + p->dir = dir; + p->idx = idx; + spec->loopback.amplist = spec->loopback_list; } -EXPORT_SYMBOL_HDA(snd_hda_activate_path); +#else +#define add_input_loopback(codec,nid,dir,idx) +#endif -/* if the given path is inactive, put widgets into D3 (only if suitable) */ -static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path) +/* + * create mixer controls if possible + */ +static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, + unsigned int index, const char *type, + const char *dir_sfx, int is_loopback) { - struct hda_gen_spec *spec = codec->spec; - bool changed; - int i; - - if (!spec->power_down_unused || path->active) - return; + char name[32]; + int err; + int created = 0; + struct snd_kcontrol_new knew; - for (i = 0; i < path->depth; i++) { - hda_nid_t nid = path->path[i]; - if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D3)) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - changed = true; - } + if (type) + sprintf(name, "%s %s Switch", type, dir_sfx); + else + sprintf(name, "%s Switch", dir_sfx); + if ((node->wid_caps & AC_WCAP_IN_AMP) && + (node->amp_in_caps & AC_AMPCAP_MUTE)) { + knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT); + if (is_loopback) + add_input_loopback(codec, node->nid, HDA_INPUT, index); + snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); + if (err < 0) + return err; + created = 1; + } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && + (node->amp_out_caps & AC_AMPCAP_MUTE)) { + knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT); + if (is_loopback) + add_input_loopback(codec, node->nid, HDA_OUTPUT, 0); + snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); + if (err < 0) + return err; + created = 1; } - if (changed) { - msleep(10); - snd_hda_codec_read(codec, path->path[0], 0, - AC_VERB_GET_POWER_STATE, 0); + if (type) + sprintf(name, "%s %s Volume", type, dir_sfx); + else + sprintf(name, "%s Volume", dir_sfx); + if ((node->wid_caps & AC_WCAP_IN_AMP) && + (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) { + knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT); + snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); + if (err < 0) + return err; + created = 1; + } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && + (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) { + knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT); + snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); + if (err < 0) + return err; + created = 1; } -} -/* turn on/off EAPD on the given pin */ -static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->own_eapd_ctl || - !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) - return; - if (codec->inv_eapd) - enable = !enable; - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, - enable ? 0x02 : 0x00); + return created; } -/* re-initialize the path specified by the given path index */ -static void resume_path_from_idx(struct hda_codec *codec, int path_idx) -{ - struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); - if (path) - snd_hda_activate_path(codec, path, path->active, false); +/* + * check whether the controls with the given name and direction suffix already exist + */ +static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir) +{ + struct snd_ctl_elem_id id; + memset(&id, 0, sizeof(id)); + sprintf(id.name, "%s %s Volume", type, dir); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + if (snd_ctl_find_id(codec->bus->card, &id)) + return 1; + sprintf(id.name, "%s %s Switch", type, dir); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + if (snd_ctl_find_id(codec->bus->card, &id)) + return 1; + return 0; } - /* - * Helper functions for creating mixer ctl elements + * build output mixer controls */ - -enum { - HDA_CTL_WIDGET_VOL, - HDA_CTL_WIDGET_MUTE, - HDA_CTL_BIND_MUTE, -}; -static const struct snd_kcontrol_new control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - HDA_BIND_MUTE(NULL, 0, 0, 0), -}; - -/* add dynamic controls from template */ -static struct snd_kcontrol_new * -add_control(struct hda_gen_spec *spec, int type, const char *name, - int cidx, unsigned long val) +static int create_output_mixers(struct hda_codec *codec, + const char * const *names) { - struct snd_kcontrol_new *knew; + struct hda_gspec *spec = codec->spec; + int i, err; - knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); - if (!knew) - return NULL; - knew->index = cidx; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - knew->private_value = val; - return knew; + for (i = 0; i < spec->pcm_vol_nodes; i++) { + err = create_mixer(codec, spec->pcm_vol[i].node, + spec->pcm_vol[i].index, + names[i], "Playback", 0); + if (err < 0) + return err; + } + return 0; } -static int add_control_with_pfx(struct hda_gen_spec *spec, int type, - const char *pfx, const char *dir, - const char *sfx, int cidx, unsigned long val) +static int build_output_controls(struct hda_codec *codec) { - char name[32]; - snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); - if (!add_control(spec, type, name, cidx, val)) - return -ENOMEM; - return 0; -} - -#define add_pb_vol_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) -#define add_pb_sw_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) -#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) -#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) - -static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, - unsigned int chs, struct nid_path *path) -{ - unsigned int val; - if (!path) - return 0; - val = path->ctls[NID_PATH_VOL_CTL]; - if (!val) - return 0; - val = amp_val_replace_channels(val, chs); - return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val); -} - -/* return the channel bits suitable for the given path->ctls[] */ -static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, - int type) -{ - int chs = 1; /* mono (left only) */ - if (path) { - hda_nid_t nid = get_amp_nid_(path->ctls[type]); - if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) - chs = 3; /* stereo */ - } - return chs; -} - -static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, - struct nid_path *path) -{ - int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); - return add_vol_ctl(codec, pfx, cidx, chs, path); -} - -/* create a mute-switch for the given mixer widget; - * if it has multiple sources (e.g. DAC and loopback), create a bind-mute - */ -static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, - unsigned int chs, struct nid_path *path) -{ - unsigned int val; - int type = HDA_CTL_WIDGET_MUTE; - - if (!path) - return 0; - val = path->ctls[NID_PATH_MUTE_CTL]; - if (!val) - return 0; - val = amp_val_replace_channels(val, chs); - if (get_amp_direction_(val) == HDA_INPUT) { - hda_nid_t nid = get_amp_nid_(val); - int nums = snd_hda_get_num_conns(codec, nid); - if (nums > 1) { - type = HDA_CTL_BIND_MUTE; - val |= nums << 19; - } - } - return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); -} - -static int add_stereo_sw(struct hda_codec *codec, const char *pfx, - int cidx, struct nid_path *path) -{ - int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); - return add_sw_ctl(codec, pfx, cidx, chs, path); -} - -/* any ctl assigned to the path with the given index? */ -static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) -{ - struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx); - return path && path->ctls[ctl_type]; -} - -static const char * const channel_name[4] = { - "Front", "Surround", "CLFE", "Side" -}; - -/* give some appropriate ctl name prefix for the given line out channel */ -static const char *get_line_out_pfx(struct hda_codec *codec, int ch, - int *index, int ctl_type) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - *index = 0; - if (cfg->line_outs == 1 && !spec->multi_ios && - !cfg->hp_outs && !cfg->speaker_outs) - return spec->vmaster_mute.hook ? "PCM" : "Master"; - - /* if there is really a single DAC used in the whole output paths, - * use it master (or "PCM" if a vmaster hook is present) - */ - if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && - !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) - return spec->vmaster_mute.hook ? "PCM" : "Master"; - - /* multi-io channels */ - if (ch >= cfg->line_outs) - return channel_name[ch]; - - switch (cfg->line_out_type) { - case AUTO_PIN_SPEAKER_OUT: - /* if the primary channel vol/mute is shared with HP volume, - * don't name it as Speaker - */ - if (!ch && cfg->hp_outs && - !path_has_mixer(codec, spec->hp_paths[0], ctl_type)) - break; - if (cfg->line_outs == 1) - return "Speaker"; - if (cfg->line_outs == 2) - return ch ? "Bass Speaker" : "Speaker"; - break; - case AUTO_PIN_HP_OUT: - /* if the primary channel vol/mute is shared with spk volume, - * don't name it as Headphone - */ - if (!ch && cfg->speaker_outs && - !path_has_mixer(codec, spec->speaker_paths[0], ctl_type)) - break; - /* for multi-io case, only the primary out */ - if (ch && spec->multi_ios) - break; - *index = ch; - return "Headphone"; - } - - /* for a single channel output, we don't have to name the channel */ - if (cfg->line_outs == 1 && !spec->multi_ios) - return "PCM"; - - if (ch >= ARRAY_SIZE(channel_name)) { - snd_BUG(); - return "PCM"; - } - - return channel_name[ch]; -} - -/* - * Parse output paths - */ - -/* badness definition */ -enum { - /* No primary DAC is found for the main output */ - BAD_NO_PRIMARY_DAC = 0x10000, - /* No DAC is found for the extra output */ - BAD_NO_DAC = 0x4000, - /* No possible multi-ios */ - BAD_MULTI_IO = 0x103, - /* No individual DAC for extra output */ - BAD_NO_EXTRA_DAC = 0x102, - /* No individual DAC for extra surrounds */ - BAD_NO_EXTRA_SURR_DAC = 0x101, - /* Primary DAC shared with main surrounds */ - BAD_SHARED_SURROUND = 0x100, - /* Primary DAC shared with main CLFE */ - BAD_SHARED_CLFE = 0x10, - /* Primary DAC shared with extra surrounds */ - BAD_SHARED_EXTRA_SURROUND = 0x10, - /* Volume widget is shared */ - BAD_SHARED_VOL = 0x10, -}; - -/* look for widgets in the given path which are appropriate for - * volume and mute controls, and assign the values to ctls[]. - * - * When no appropriate widget is found in the path, the badness value - * is incremented depending on the situation. The function returns the - * total badness for both volume and mute controls. - */ -static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path) -{ - hda_nid_t nid; - unsigned int val; - int badness = 0; - - if (!path) - return BAD_SHARED_VOL * 2; - - if (path->ctls[NID_PATH_VOL_CTL] || - path->ctls[NID_PATH_MUTE_CTL]) - return 0; /* already evaluated */ - - nid = look_for_out_vol_nid(codec, path); - if (nid) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) - badness += BAD_SHARED_VOL; - else - path->ctls[NID_PATH_VOL_CTL] = val; - } else - badness += BAD_SHARED_VOL; - nid = look_for_out_mute_nid(codec, path); - if (nid) { - unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || - nid_has_mute(codec, nid, HDA_OUTPUT)) - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) - badness += BAD_SHARED_VOL; - else - path->ctls[NID_PATH_MUTE_CTL] = val; - } else - badness += BAD_SHARED_VOL; - return badness; -} - -struct badness_table { - int no_primary_dac; /* no primary DAC */ - int no_dac; /* no secondary DACs */ - int shared_primary; /* primary DAC is shared with main output */ - int shared_surr; /* secondary DAC shared with main or primary */ - int shared_clfe; /* third DAC shared with main or primary */ - int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ -}; - -static struct badness_table main_out_badness = { - .no_primary_dac = BAD_NO_PRIMARY_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_PRIMARY_DAC, - .shared_surr = BAD_SHARED_SURROUND, - .shared_clfe = BAD_SHARED_CLFE, - .shared_surr_main = BAD_SHARED_SURROUND, -}; - -static struct badness_table extra_out_badness = { - .no_primary_dac = BAD_NO_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_EXTRA_DAC, - .shared_surr = BAD_SHARED_EXTRA_SURROUND, - .shared_clfe = BAD_SHARED_EXTRA_SURROUND, - .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, -}; - -/* get the DAC of the primary output corresponding to the given array index */ -static hda_nid_t get_primary_out(struct hda_codec *codec, int idx) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - - if (cfg->line_outs > idx) - return spec->private_dac_nids[idx]; - idx -= cfg->line_outs; - if (spec->multi_ios > idx) - return spec->multi_io[idx].dac; - return 0; -} - -/* return the DAC if it's reachable, otherwise zero */ -static inline hda_nid_t try_dac(struct hda_codec *codec, - hda_nid_t dac, hda_nid_t pin) -{ - return is_reachable_path(codec, dac, pin) ? dac : 0; -} - -/* try to assign DACs to pins and return the resultant badness */ -static int try_assign_dacs(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, hda_nid_t *dacs, - int *path_idx, - const struct badness_table *bad) -{ - struct hda_gen_spec *spec = codec->spec; - int i, j; - int badness = 0; - hda_nid_t dac; - - if (!num_outs) - return 0; - - for (i = 0; i < num_outs; i++) { - struct nid_path *path; - hda_nid_t pin = pins[i]; - - path = snd_hda_get_path_from_idx(codec, path_idx[i]); - if (path) { - badness += assign_out_path_ctls(codec, path); - continue; - } - - dacs[i] = look_for_dac(codec, pin, false); - if (!dacs[i] && !i) { - /* try to steal the DAC of surrounds for the front */ - for (j = 1; j < num_outs; j++) { - if (is_reachable_path(codec, dacs[j], pin)) { - dacs[0] = dacs[j]; - dacs[j] = 0; - invalidate_nid_path(codec, path_idx[j]); - path_idx[j] = 0; - break; - } - } - } - dac = dacs[i]; - if (!dac) { - if (num_outs > 2) - dac = try_dac(codec, get_primary_out(codec, i), pin); - if (!dac) - dac = try_dac(codec, dacs[0], pin); - if (!dac) - dac = try_dac(codec, get_primary_out(codec, i), pin); - if (dac) { - if (!i) - badness += bad->shared_primary; - else if (i == 1) - badness += bad->shared_surr; - else - badness += bad->shared_clfe; - } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { - dac = spec->private_dac_nids[0]; - badness += bad->shared_surr_main; - } else if (!i) - badness += bad->no_primary_dac; - else - badness += bad->no_dac; - } - if (!dac) - continue; - path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid); - if (!path && !i && spec->mixer_nid) { - /* try with aamix */ - path = snd_hda_add_new_path(codec, dac, pin, 0); - } - if (!path) { - dac = dacs[i] = 0; - badness += bad->no_dac; - } else { - /* print_nid_path("output", path); */ - path->active = true; - path_idx[i] = snd_hda_get_path_idx(codec, path); - badness += assign_out_path_ctls(codec, path); - } - } - - return badness; -} - -/* return NID if the given pin has only a single connection to a certain DAC */ -static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - hda_nid_t nid_found = 0; - - for (i = 0; i < spec->num_all_dacs; i++) { - hda_nid_t nid = spec->all_dacs[i]; - if (!nid || is_dac_already_used(codec, nid)) - continue; - if (is_reachable_path(codec, nid, pin)) { - if (nid_found) - return 0; - nid_found = nid; - } - } - return nid_found; -} - -/* check whether the given pin can be a multi-io pin */ -static bool can_be_multiio_pin(struct hda_codec *codec, - unsigned int location, hda_nid_t nid) -{ - unsigned int defcfg, caps; - - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) - return false; - if (location && get_defcfg_location(defcfg) != location) - return false; - caps = snd_hda_query_pin_caps(codec, nid); - if (!(caps & AC_PINCAP_OUT)) - return false; - return true; -} - -/* count the number of input pins that are capable to be multi-io */ -static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); - unsigned int location = get_defcfg_location(defcfg); - int type, i; - int num_pins = 0; - - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != type) - continue; - if (can_be_multiio_pin(codec, location, - cfg->inputs[i].pin)) - num_pins++; - } - } - return num_pins; -} - -/* - * multi-io helper - * - * When hardwired is set, try to fill ony hardwired pins, and returns - * zero if any pins are filled, non-zero if nothing found. - * When hardwired is off, try to fill possible input pins, and returns - * the badness value. - */ -static int fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, j, num_pins, old_pins; - unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); - unsigned int location = get_defcfg_location(defcfg); - int badness = 0; - struct nid_path *path; - - old_pins = spec->multi_ios; - if (old_pins >= 2) - goto end_fill; - - num_pins = count_multiio_pins(codec, reference_pin); - if (num_pins < 2) - goto end_fill; - - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - hda_nid_t dac = 0; - - if (cfg->inputs[i].type != type) - continue; - if (!can_be_multiio_pin(codec, location, nid)) - continue; - for (j = 0; j < spec->multi_ios; j++) { - if (nid == spec->multi_io[j].pin) - break; - } - if (j < spec->multi_ios) - continue; - - if (hardwired) - dac = get_dac_if_single(codec, nid); - else if (!dac) - dac = look_for_dac(codec, nid, false); - if (!dac) { - badness++; - continue; - } - path = snd_hda_add_new_path(codec, dac, nid, - -spec->mixer_nid); - if (!path) { - badness++; - continue; - } - /* print_nid_path("multiio", path); */ - spec->multi_io[spec->multi_ios].pin = nid; - spec->multi_io[spec->multi_ios].dac = dac; - spec->out_paths[cfg->line_outs + spec->multi_ios] = - snd_hda_get_path_idx(codec, path); - spec->multi_ios++; - if (spec->multi_ios >= 2) - break; - } - } - end_fill: - if (badness) - badness = BAD_MULTI_IO; - if (old_pins == spec->multi_ios) { - if (hardwired) - return 1; /* nothing found */ - else - return badness; /* no badness if nothing found */ - } - if (!hardwired && spec->multi_ios < 2) { - /* cancel newly assigned paths */ - spec->paths.used -= spec->multi_ios - old_pins; - spec->multi_ios = old_pins; - return badness; - } - - /* assign volume and mute controls */ - for (i = old_pins; i < spec->multi_ios; i++) { - path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]); - badness += assign_out_path_ctls(codec, path); - } - - return badness; -} - -/* map DACs for all pins in the list if they are single connections */ -static bool map_singles(struct hda_codec *codec, int outs, - const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - bool found = false; - for (i = 0; i < outs; i++) { - struct nid_path *path; - hda_nid_t dac; - if (dacs[i]) - continue; - dac = get_dac_if_single(codec, pins[i]); - if (!dac) - continue; - path = snd_hda_add_new_path(codec, dac, pins[i], - -spec->mixer_nid); - if (!path && !i && spec->mixer_nid) - path = snd_hda_add_new_path(codec, dac, pins[i], 0); - if (path) { - dacs[i] = dac; - found = true; - /* print_nid_path("output", path); */ - path->active = true; - path_idx[i] = snd_hda_get_path_idx(codec, path); - } - } - return found; -} - -/* create a new path including aamix if available, and return its index */ -static int check_aamix_out_path(struct hda_codec *codec, int path_idx) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - hda_nid_t dac, pin; - - path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path || !path->depth || - is_nid_contained(path, spec->mixer_nid)) - return 0; - dac = path->path[0]; - pin = path->path[path->depth - 1]; - path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid); - if (!path) { - if (dac != spec->multiout.dac_nids[0]) - dac = spec->multiout.dac_nids[0]; - else if (spec->multiout.hp_out_nid[0]) - dac = spec->multiout.hp_out_nid[0]; - else if (spec->multiout.extra_out_nid[0]) - dac = spec->multiout.extra_out_nid[0]; - if (dac) - path = snd_hda_add_new_path(codec, dac, pin, - spec->mixer_nid); - } - if (!path) - return 0; - /* print_nid_path("output-aamix", path); */ - path->active = false; /* unused as default */ - return snd_hda_get_path_idx(codec, path); -} - -/* fill the empty entries in the dac array for speaker/hp with the - * shared dac pointed by the paths - */ -static void refill_shared_dacs(struct hda_codec *codec, int num_outs, - hda_nid_t *dacs, int *path_idx) -{ - struct nid_path *path; - int i; - - for (i = 0; i < num_outs; i++) { - if (dacs[i]) - continue; - path = snd_hda_get_path_from_idx(codec, path_idx[i]); - if (!path) - continue; - dacs[i] = path->path[0]; - } -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int fill_and_eval_dacs(struct hda_codec *codec, - bool fill_hardwired, - bool fill_mio_first) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err, badness; - - /* set num_dacs once to full for look_for_dac() */ - spec->multiout.num_dacs = cfg->line_outs; - spec->multiout.dac_nids = spec->private_dac_nids; - memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); - memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); - memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); - spec->multi_ios = 0; - snd_array_free(&spec->paths); - - /* clear path indices */ - memset(spec->out_paths, 0, sizeof(spec->out_paths)); - memset(spec->hp_paths, 0, sizeof(spec->hp_paths)); - memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths)); - memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths)); - memset(spec->digout_paths, 0, sizeof(spec->digout_paths)); - memset(spec->input_paths, 0, sizeof(spec->input_paths)); - memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths)); - memset(&spec->digin_path, 0, sizeof(spec->digin_path)); - - badness = 0; - - /* fill hard-wired DACs first */ - if (fill_hardwired) { - bool mapped; - do { - mapped = map_singles(codec, cfg->line_outs, - cfg->line_out_pins, - spec->private_dac_nids, - spec->out_paths); - mapped |= map_singles(codec, cfg->hp_outs, - cfg->hp_pins, - spec->multiout.hp_out_nid, - spec->hp_paths); - mapped |= map_singles(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid, - spec->speaker_paths); - if (fill_mio_first && cfg->line_outs == 1 && - cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = fill_multi_ios(codec, cfg->line_out_pins[0], true); - if (!err) - mapped = true; - } - } while (mapped); - } - - badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids, spec->out_paths, - &main_out_badness); - - if (fill_mio_first && - cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - /* try to fill multi-io first */ - err = fill_multi_ios(codec, cfg->line_out_pins[0], false); - if (err < 0) - return err; - /* we don't count badness at this stage yet */ - } - - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid, - spec->hp_paths, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = try_assign_dacs(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid, - spec->speaker_paths, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = fill_multi_ios(codec, cfg->line_out_pins[0], false); - if (err < 0) - return err; - badness += err; - } - - if (spec->mixer_nid) { - spec->aamix_out_paths[0] = - check_aamix_out_path(codec, spec->out_paths[0]); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - spec->aamix_out_paths[1] = - check_aamix_out_path(codec, spec->hp_paths[0]); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) - spec->aamix_out_paths[2] = - check_aamix_out_path(codec, spec->speaker_paths[0]); - } - - if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) - spec->multi_ios = 1; /* give badness */ - - /* re-count num_dacs and squash invalid entries */ - spec->multiout.num_dacs = 0; - for (i = 0; i < cfg->line_outs; i++) { - if (spec->private_dac_nids[i]) - spec->multiout.num_dacs++; - else { - memmove(spec->private_dac_nids + i, - spec->private_dac_nids + i + 1, - sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); - spec->private_dac_nids[cfg->line_outs - 1] = 0; - } - } - - spec->ext_channel_count = spec->min_channel_count = - spec->multiout.num_dacs * 2; - - if (spec->multi_ios == 2) { - for (i = 0; i < 2; i++) - spec->private_dac_nids[spec->multiout.num_dacs++] = - spec->multi_io[i].dac; - } else if (spec->multi_ios) { - spec->multi_ios = 0; - badness += BAD_MULTI_IO; - } - - /* re-fill the shared DAC for speaker / headphone */ - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - refill_shared_dacs(codec, cfg->hp_outs, - spec->multiout.hp_out_nid, - spec->hp_paths); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) - refill_shared_dacs(codec, cfg->speaker_outs, - spec->multiout.extra_out_nid, - spec->speaker_paths); - - return badness; -} - -#define DEBUG_BADNESS - -#ifdef DEBUG_BADNESS -#define debug_badness snd_printdd -#else -#define debug_badness(...) -#endif - -#ifdef DEBUG_BADNESS -static inline void print_nid_path_idx(struct hda_codec *codec, - const char *pfx, int idx) -{ - struct nid_path *path; - - path = snd_hda_get_path_from_idx(codec, idx); - if (path) - print_nid_path(pfx, path); -} - -static void debug_show_configs(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct hda_gen_spec *spec = codec->spec; - static const char * const lo_type[3] = { "LO", "SP", "HP" }; - int i; - - debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n", - cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[3], - spec->multiout.dac_nids[0], - spec->multiout.dac_nids[1], - spec->multiout.dac_nids[2], - spec->multiout.dac_nids[3], - lo_type[cfg->line_out_type]); - for (i = 0; i < cfg->line_outs; i++) - print_nid_path_idx(codec, " out", spec->out_paths[i]); - if (spec->multi_ios > 0) - debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", - spec->multi_ios, - spec->multi_io[0].pin, spec->multi_io[1].pin, - spec->multi_io[0].dac, spec->multi_io[1].dac); - for (i = 0; i < spec->multi_ios; i++) - print_nid_path_idx(codec, " mio", - spec->out_paths[cfg->line_outs + i]); - if (cfg->hp_outs) - debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->hp_pins[0], cfg->hp_pins[1], - cfg->hp_pins[2], cfg->hp_pins[3], - spec->multiout.hp_out_nid[0], - spec->multiout.hp_out_nid[1], - spec->multiout.hp_out_nid[2], - spec->multiout.hp_out_nid[3]); - for (i = 0; i < cfg->hp_outs; i++) - print_nid_path_idx(codec, " hp ", spec->hp_paths[i]); - if (cfg->speaker_outs) - debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->speaker_pins[0], cfg->speaker_pins[1], - cfg->speaker_pins[2], cfg->speaker_pins[3], - spec->multiout.extra_out_nid[0], - spec->multiout.extra_out_nid[1], - spec->multiout.extra_out_nid[2], - spec->multiout.extra_out_nid[3]); - for (i = 0; i < cfg->speaker_outs; i++) - print_nid_path_idx(codec, " spk", spec->speaker_paths[i]); - for (i = 0; i < 3; i++) - print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]); -} -#else -#define debug_show_configs(codec, cfg) /* NOP */ -#endif - -/* find all available DACs of the codec */ -static void fill_all_dac_nids(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - hda_nid_t nid = codec->start_nid; - - spec->num_all_dacs = 0; - memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); - for (i = 0; i < codec->num_nodes; i++, nid++) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) - continue; - if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { - snd_printk(KERN_ERR "hda: Too many DACs!\n"); - break; - } - spec->all_dacs[spec->num_all_dacs++] = nid; - } -} - -static int parse_output_paths(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct auto_pin_cfg *best_cfg; - unsigned int val; - int best_badness = INT_MAX; - int badness; - bool fill_hardwired = true, fill_mio_first = true; - bool best_wired = true, best_mio = true; - bool hp_spk_swapped = false; - - best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); - if (!best_cfg) - return -ENOMEM; - *best_cfg = *cfg; - - for (;;) { - badness = fill_and_eval_dacs(codec, fill_hardwired, - fill_mio_first); - if (badness < 0) { - kfree(best_cfg); - return badness; - } - debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", - cfg->line_out_type, fill_hardwired, fill_mio_first, - badness); - debug_show_configs(codec, cfg); - if (badness < best_badness) { - best_badness = badness; - *best_cfg = *cfg; - best_wired = fill_hardwired; - best_mio = fill_mio_first; - } - if (!badness) - break; - fill_mio_first = !fill_mio_first; - if (!fill_mio_first) - continue; - fill_hardwired = !fill_hardwired; - if (!fill_hardwired) - continue; - if (hp_spk_swapped) - break; - hp_spk_swapped = true; - if (cfg->speaker_outs > 0 && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - cfg->hp_outs = cfg->line_outs; - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->line_outs = cfg->speaker_outs; - memcpy(cfg->line_out_pins, cfg->speaker_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = 0; - memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); - cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - fill_hardwired = true; - continue; - } - if (cfg->hp_outs > 0 && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - fill_hardwired = true; - continue; - } - break; - } - - if (badness) { - debug_badness("==> restoring best_cfg\n"); - *cfg = *best_cfg; - fill_and_eval_dacs(codec, best_wired, best_mio); - } - debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", - cfg->line_out_type, best_wired, best_mio); - debug_show_configs(codec, cfg); - - if (cfg->line_out_pins[0]) { - struct nid_path *path; - path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); - if (path) - spec->vmaster_nid = look_for_out_vol_nid(codec, path); - if (spec->vmaster_nid) - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, spec->vmaster_tlv); - } - - /* set initial pinctl targets */ - if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT) - val = PIN_HP; - else - val = PIN_OUT; - set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP); - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT; - set_pin_targets(codec, cfg->speaker_outs, - cfg->speaker_pins, val); - } - - kfree(best_cfg); - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct hda_gen_spec *spec = codec->spec; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0 && cfg->line_outs < 3) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - struct nid_path *path; - - path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); - if (!path) - continue; - - name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL); - if (!name || !strcmp(name, "CLFE")) { - /* Center/LFE */ - err = add_vol_ctl(codec, "Center", 0, 1, path); - if (err < 0) - return err; - err = add_vol_ctl(codec, "LFE", 0, 2, path); - if (err < 0) - return err; - } else { - err = add_stereo_vol(codec, name, index, path); - if (err < 0) - return err; - } - - name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL); - if (!name || !strcmp(name, "CLFE")) { - err = add_sw_ctl(codec, "Center", 0, 1, path); - if (err < 0) - return err; - err = add_sw_ctl(codec, "LFE", 0, 2, path); - if (err < 0) - return err; - } else { - err = add_stereo_sw(codec, name, index, path); - if (err < 0) - return err; - } - } - return 0; -} - -static int create_extra_out(struct hda_codec *codec, int path_idx, - const char *pfx, int cidx) -{ - struct nid_path *path; - int err; - - path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path) - return 0; - err = add_stereo_vol(codec, pfx, cidx, path); - if (err < 0) - return err; - err = add_stereo_sw(codec, pfx, cidx, path); - if (err < 0) - return err; - return 0; -} - -/* add playback controls for speaker and HP outputs */ -static int create_extra_outs(struct hda_codec *codec, int num_pins, - const int *paths, const char *pfx) -{ - int i; - - for (i = 0; i < num_pins; i++) { - const char *name; - char tmp[44]; - int err, idx = 0; - - if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) - name = "Bass Speaker"; - else if (num_pins >= 3) { - snprintf(tmp, sizeof(tmp), "%s %s", - pfx, channel_name[i]); - name = tmp; - } else { - name = pfx; - idx = i; - } - err = create_extra_out(codec, paths[i], name, idx); - if (err < 0) - return err; - } - return 0; -} - -static int create_hp_out_ctls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - return create_extra_outs(codec, spec->autocfg.hp_outs, - spec->hp_paths, - "Headphone"); -} - -static int create_speaker_out_ctls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - return create_extra_outs(codec, spec->autocfg.speaker_outs, - spec->speaker_paths, - "Speaker"); -} - -/* - * independent HP controls - */ - -static int indep_hp_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - return snd_hda_enum_bool_helper_info(kcontrol, uinfo); -} - -static int indep_hp_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled; - return 0; -} - -static void update_aamix_paths(struct hda_codec *codec, bool do_mix, - int nomix_path_idx, int mix_path_idx, - int out_type); - -static int indep_hp_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - unsigned int select = ucontrol->value.enumerated.item[0]; - int ret = 0; - - mutex_lock(&spec->pcm_mutex); - if (spec->active_streams) { - ret = -EBUSY; - goto unlock; - } - - if (spec->indep_hp_enabled != select) { - hda_nid_t *dacp; - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - dacp = &spec->private_dac_nids[0]; - else - dacp = &spec->multiout.hp_out_nid[0]; - - /* update HP aamix paths in case it conflicts with indep HP */ - if (spec->have_aamix_ctl) { - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - update_aamix_paths(codec, spec->aamix_mode, - spec->out_paths[0], - spec->aamix_out_paths[0], - spec->autocfg.line_out_type); - else - update_aamix_paths(codec, spec->aamix_mode, - spec->hp_paths[0], - spec->aamix_out_paths[1], - AUTO_PIN_HP_OUT); - } - - spec->indep_hp_enabled = select; - if (spec->indep_hp_enabled) - *dacp = 0; - else - *dacp = spec->alt_dac_nid; - - /* update HP auto-mute state too */ - if (spec->hp_automute_hook) - spec->hp_automute_hook(codec, NULL); - else - snd_hda_gen_hp_automute(codec, NULL); - - ret = 1; - } - unlock: - mutex_unlock(&spec->pcm_mutex); - return ret; -} - -static const struct snd_kcontrol_new indep_hp_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Independent HP", - .info = indep_hp_info, - .get = indep_hp_get, - .put = indep_hp_put, -}; - - -static int create_indep_hp_ctls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t dac; - - if (!spec->indep_hp) - return 0; - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - dac = spec->multiout.dac_nids[0]; - else - dac = spec->multiout.hp_out_nid[0]; - if (!dac) { - spec->indep_hp = 0; - return 0; - } - - spec->indep_hp_enabled = false; - spec->alt_dac_nid = dac; - if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl)) - return -ENOMEM; - return 0; -} - -/* - * channel mode enum control - */ - -static int ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - int chs; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->multi_ios + 1; - if (uinfo->value.enumerated.item > spec->multi_ios) - uinfo->value.enumerated.item = spec->multi_ios; - chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count; - sprintf(uinfo->value.enumerated.name, "%dch", chs); - return 0; -} - -static int ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = - (spec->ext_channel_count - spec->min_channel_count) / 2; - return 0; -} - -static inline struct nid_path * -get_multiio_path(struct hda_codec *codec, int idx) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_get_path_from_idx(codec, - spec->out_paths[spec->autocfg.line_outs + idx]); -} - -static void update_automute_all(struct hda_codec *codec); - -static int set_multi_io(struct hda_codec *codec, int idx, bool output) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid = spec->multi_io[idx].pin; - struct nid_path *path; - - path = get_multiio_path(codec, idx); - if (!path) - return -EINVAL; - - if (path->active == output) - return 0; - - if (output) { - set_pin_target(codec, nid, PIN_OUT, true); - snd_hda_activate_path(codec, path, true, true); - set_pin_eapd(codec, nid, true); - } else { - set_pin_eapd(codec, nid, false); - snd_hda_activate_path(codec, path, false, true); - set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true); - path_power_down_sync(codec, path); - } - - /* update jack retasking in case it modifies any of them */ - update_automute_all(codec); - - return 0; -} - -static int ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - int i, ch; - - ch = ucontrol->value.enumerated.item[0]; - if (ch < 0 || ch > spec->multi_ios) - return -EINVAL; - if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2) - return 0; - spec->ext_channel_count = ch * 2 + spec->min_channel_count; - for (i = 0; i < spec->multi_ios; i++) - set_multi_io(codec, i, i < ch); - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - if (spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - return 1; -} - -static const struct snd_kcontrol_new channel_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ch_mode_info, - .get = ch_mode_get, - .put = ch_mode_put, -}; - -static int create_multi_channel_mode(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (spec->multi_ios > 0) { - if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum)) - return -ENOMEM; - } - return 0; -} - -/* - * aamix loopback enable/disable switch - */ - -#define loopback_mixing_info indep_hp_info - -static int loopback_mixing_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->aamix_mode; - return 0; -} - -static void update_aamix_paths(struct hda_codec *codec, bool do_mix, - int nomix_path_idx, int mix_path_idx, - int out_type) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *nomix_path, *mix_path; - - nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); - mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); - if (!nomix_path || !mix_path) - return; - - /* if HP aamix path is driven from a different DAC and the - * independent HP mode is ON, can't turn on aamix path - */ - if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled && - mix_path->path[0] != spec->alt_dac_nid) - do_mix = false; - - if (do_mix) { - snd_hda_activate_path(codec, nomix_path, false, true); - snd_hda_activate_path(codec, mix_path, true, true); - path_power_down_sync(codec, nomix_path); - } else { - snd_hda_activate_path(codec, mix_path, false, true); - snd_hda_activate_path(codec, nomix_path, true, true); - path_power_down_sync(codec, mix_path); - } -} - -static int loopback_mixing_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - unsigned int val = ucontrol->value.enumerated.item[0]; - - if (val == spec->aamix_mode) - return 0; - spec->aamix_mode = val; - update_aamix_paths(codec, val, spec->out_paths[0], - spec->aamix_out_paths[0], - spec->autocfg.line_out_type); - update_aamix_paths(codec, val, spec->hp_paths[0], - spec->aamix_out_paths[1], - AUTO_PIN_HP_OUT); - update_aamix_paths(codec, val, spec->speaker_paths[0], - spec->aamix_out_paths[2], - AUTO_PIN_SPEAKER_OUT); - return 1; -} - -static const struct snd_kcontrol_new loopback_mixing_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Loopback Mixing", - .info = loopback_mixing_info, - .get = loopback_mixing_get, - .put = loopback_mixing_put, -}; - -static int create_loopback_mixing_ctl(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (!spec->mixer_nid) - return 0; - if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] || - spec->aamix_out_paths[2])) - return 0; - if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) - return -ENOMEM; - spec->have_aamix_ctl = 1; - return 0; -} - -/* - * shared headphone/mic handling - */ - -static void call_update_outputs(struct hda_codec *codec); - -/* for shared I/O, change the pin-control accordingly */ -static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) -{ - struct hda_gen_spec *spec = codec->spec; - unsigned int val; - hda_nid_t pin = spec->autocfg.inputs[1].pin; - /* NOTE: this assumes that there are only two inputs, the - * first is the real internal mic and the second is HP/mic jack. - */ - - val = snd_hda_get_default_vref(codec, pin); - - /* This pin does not have vref caps - let's enable vref on pin 0x18 - instead, as suggested by Realtek */ - if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { - const hda_nid_t vref_pin = spec->shared_mic_vref_pin; - unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); - if (vref_val != AC_PINCTL_VREF_HIZ) - snd_hda_set_pin_ctl_cache(codec, vref_pin, - PIN_IN | (set_as_mic ? vref_val : 0)); - } - - val = set_as_mic ? val | PIN_IN : PIN_HP; - set_pin_target(codec, pin, val, true); - - spec->automute_speaker = !set_as_mic; - call_update_outputs(codec); -} - -/* create a shared input with the headphone out */ -static int create_shared_input(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int defcfg; - hda_nid_t nid; - - /* only one internal input pin? */ - if (cfg->num_inputs != 1) - return 0; - defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); - if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) - return 0; - - if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */ - else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT) - nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */ - else - return 0; /* both not available */ - - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) - return 0; /* no input */ - - cfg->inputs[1].pin = nid; - cfg->inputs[1].type = AUTO_PIN_MIC; - cfg->num_inputs = 2; - spec->shared_mic_hp = 1; - snd_printdd("hda-codec: Enable shared I/O jack on NID 0x%x\n", nid); - return 0; -} - -/* - * output jack mode - */ -static int out_jack_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "Line Out", "Headphone Out", - }; - return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts); -} - -static int out_jack_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP) - ucontrol->value.enumerated.item[0] = 1; - else - ucontrol->value.enumerated.item[0] = 0; - return 0; -} - -static int out_jack_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int val; - - val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT; - if (snd_hda_codec_get_pin_target(codec, nid) == val) - return 0; - snd_hda_set_pin_ctl_cache(codec, nid, val); - return 1; -} - -static const struct snd_kcontrol_new out_jack_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = out_jack_mode_info, - .get = out_jack_mode_get, - .put = out_jack_mode_put, -}; - -static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->kctls.used; i++) { - struct snd_kcontrol_new *kctl = snd_array_elem(&spec->kctls, i); - if (!strcmp(kctl->name, name) && kctl->index == idx) - return true; - } - return false; -} - -static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, - char *name, size_t name_len) -{ - struct hda_gen_spec *spec = codec->spec; - int idx = 0; - - snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx); - strlcat(name, " Jack Mode", name_len); - - for (; find_kctl_name(codec, name, idx); idx++) - ; -} - -static int create_out_jack_modes(struct hda_codec *codec, int num_pins, - hda_nid_t *pins) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = 0; i < num_pins; i++) { - hda_nid_t pin = pins[i]; - unsigned int pincap = snd_hda_query_pin_caps(codec, pin); - if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) { - struct snd_kcontrol_new *knew; - char name[44]; - get_jack_mode_name(codec, pin, name, sizeof(name)); - knew = snd_hda_gen_add_kctl(spec, name, - &out_jack_mode_enum); - if (!knew) - return -ENOMEM; - knew->private_value = pin; - } - } - - return 0; -} - -/* - * input jack mode - */ - -/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */ -#define NUM_VREFS 6 - -static const char * const vref_texts[NUM_VREFS] = { - "Line In", "Mic 50pc Bias", "Mic 0V Bias", - "", "Mic 80pc Bias", "Mic 100pc Bias" -}; - -static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin) -{ - unsigned int pincap; - - pincap = snd_hda_query_pin_caps(codec, pin); - pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - /* filter out unusual vrefs */ - pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100); - return pincap; -} - -/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */ -static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx) -{ - unsigned int i, n = 0; - - for (i = 0; i < NUM_VREFS; i++) { - if (vref_caps & (1 << i)) { - if (n == item_idx) - return i; - n++; - } - } - return 0; -} - -/* convert back from the vref ctl index to the enum item index */ -static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx) -{ - unsigned int i, n = 0; - - for (i = 0; i < NUM_VREFS; i++) { - if (i == idx) - return n; - if (vref_caps & (1 << i)) - n++; - } - return 0; -} - -static int in_jack_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int vref_caps = get_vref_caps(codec, nid); - - snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), - vref_texts); - /* set the right text */ - strcpy(uinfo->value.enumerated.name, - vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); - return 0; -} - -static int in_jack_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int vref_caps = get_vref_caps(codec, nid); - unsigned int idx; - - idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN; - ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx); - return 0; -} - -static int in_jack_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = kcontrol->private_value; - unsigned int vref_caps = get_vref_caps(codec, nid); - unsigned int val, idx; - - val = snd_hda_codec_get_pin_target(codec, nid); - idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN); - if (idx == ucontrol->value.enumerated.item[0]) - return 0; - - val &= ~AC_PINCTL_VREFEN; - val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]); - snd_hda_set_pin_ctl_cache(codec, nid, val); - return 1; -} - -static const struct snd_kcontrol_new in_jack_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = in_jack_mode_info, - .get = in_jack_mode_get, - .put = in_jack_mode_put, -}; - -static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) -{ - struct hda_gen_spec *spec = codec->spec; - unsigned int defcfg; - struct snd_kcontrol_new *knew; - char name[44]; - - /* no jack mode for fixed pins */ - defcfg = snd_hda_codec_get_pincfg(codec, pin); - if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) - return 0; - - /* no multiple vref caps? */ - if (hweight32(get_vref_caps(codec, pin)) <= 1) - return 0; - - get_jack_mode_name(codec, pin, name, sizeof(name)); - knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); - if (!knew) - return -ENOMEM; - knew->private_value = pin; - return 0; -} - - -/* - * Parse input paths - */ - -#ifdef CONFIG_PM -/* add the powersave loopback-list entry */ -static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) -{ - struct hda_amp_list *list; - - if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) - return; - list = spec->loopback_list + spec->num_loopbacks; - list->nid = mix; - list->dir = HDA_INPUT; - list->idx = idx; - spec->num_loopbacks++; - spec->loopback.amplist = spec->loopback_list; -} -#else -#define add_loopback_list(spec, mix, idx) /* NOP */ -#endif - -/* create input playback/capture controls for the given pin */ -static int new_analog_input(struct hda_codec *codec, int input_idx, - hda_nid_t pin, const char *ctlname, int ctlidx, - hda_nid_t mix_nid) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - unsigned int val; - int err, idx; - - if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && - !nid_has_mute(codec, mix_nid, HDA_INPUT)) - return 0; /* no need for analog loopback */ - - path = snd_hda_add_new_path(codec, pin, mix_nid, 0); - if (!path) - return -EINVAL; - print_nid_path("loopback", path); - spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path); - - idx = path->idx[path->depth - 1]; - if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { - val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); - err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, val); - if (err < 0) - return err; - path->ctls[NID_PATH_VOL_CTL] = val; - } - - if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { - val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); - err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, val); - if (err < 0) - return err; - path->ctls[NID_PATH_MUTE_CTL] = val; - } - - path->active = true; - add_loopback_list(spec, mix_nid, idx); - - if (spec->mixer_nid != spec->mixer_merge_nid && - !spec->loopback_merge_path) { - path = snd_hda_add_new_path(codec, spec->mixer_nid, - spec->mixer_merge_nid, 0); - if (path) { - print_nid_path("loopback-merge", path); - path->active = true; - spec->loopback_merge_path = - snd_hda_get_path_idx(codec, path); - } - } - - return 0; -} - -static int is_input_pin(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, nid); - return (pincap & AC_PINCAP_IN) != 0; -} - -/* Parse the codec tree and retrieve ADCs */ -static int fill_adc_nids(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid; - hda_nid_t *adc_nids = spec->adc_nids; - int max_nums = ARRAY_SIZE(spec->adc_nids); - int i, nums = 0; - - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int caps = get_wcaps(codec, nid); - int type = get_wcaps_type(caps); - - if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) - continue; - adc_nids[nums] = nid; - if (++nums >= max_nums) - break; - } - spec->num_adc_nids = nums; - - /* copy the detected ADCs to all_adcs[] */ - spec->num_all_adcs = nums; - memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t)); - - return nums; -} - -/* filter out invalid adc_nids that don't give all active input pins; - * if needed, check whether dynamic ADC-switching is available - */ -static int check_dyn_adc_switch(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - unsigned int ok_bits; - int i, n, nums; - - again: - nums = 0; - ok_bits = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - for (i = 0; i < imux->num_items; i++) { - if (!spec->input_paths[i][n]) - break; - } - if (i >= imux->num_items) { - ok_bits |= (1 << n); - nums++; - } - } - - if (!ok_bits) { - if (spec->shared_mic_hp) { - spec->shared_mic_hp = 0; - imux->num_items = 1; - goto again; - } - - /* check whether ADC-switch is possible */ - for (i = 0; i < imux->num_items; i++) { - for (n = 0; n < spec->num_adc_nids; n++) { - if (spec->input_paths[i][n]) { - spec->dyn_adc_idx[i] = n; - break; - } - } - } - - snd_printdd("hda-codec: enabling ADC switching\n"); - spec->dyn_adc_switch = 1; - } else if (nums != spec->num_adc_nids) { - /* shrink the invalid adcs and input paths */ - nums = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - if (!(ok_bits & (1 << n))) - continue; - if (n != nums) { - spec->adc_nids[nums] = spec->adc_nids[n]; - for (i = 0; i < imux->num_items; i++) { - invalidate_nid_path(codec, - spec->input_paths[i][nums]); - spec->input_paths[i][nums] = - spec->input_paths[i][n]; - } - } - nums++; - } - spec->num_adc_nids = nums; - } - - if (imux->num_items == 1 || spec->shared_mic_hp) { - snd_printdd("hda-codec: reducing to a single ADC\n"); - spec->num_adc_nids = 1; /* reduce to a single ADC */ - } - - /* single index for individual volumes ctls */ - if (!spec->dyn_adc_switch && spec->multi_cap_vol) - spec->num_adc_nids = 1; - - return 0; -} - -/* parse capture source paths from the given pin and create imux items */ -static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin, - int cfg_idx, int num_adcs, - const char *label, int anchor) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int imux_idx = imux->num_items; - bool imux_added = false; - int c; - - for (c = 0; c < num_adcs; c++) { - struct nid_path *path; - hda_nid_t adc = spec->adc_nids[c]; - - if (!is_reachable_path(codec, pin, adc)) - continue; - path = snd_hda_add_new_path(codec, pin, adc, anchor); - if (!path) - continue; - print_nid_path("input", path); - spec->input_paths[imux_idx][c] = - snd_hda_get_path_idx(codec, path); - - if (!imux_added) { - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, cfg_idx, NULL); - imux_added = true; - } - } - - return 0; -} - -/* - * create playback/capture controls for input pins - */ - -/* fill the label for each input at first */ -static int fill_input_pin_labels(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin = cfg->inputs[i].pin; - const char *label; - int j, idx; - - if (!is_input_pin(codec, pin)) - continue; - - label = hda_get_autocfg_input_label(codec, cfg, i); - idx = 0; - for (j = i - 1; j >= 0; j--) { - if (spec->input_labels[j] && - !strcmp(spec->input_labels[j], label)) { - idx = spec->input_label_idxs[j] + 1; - break; - } - } - - spec->input_labels[i] = label; - spec->input_label_idxs[i] = idx; - } - - return 0; -} - -#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */ - -static int create_input_ctls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t mixer = spec->mixer_nid; - int num_adcs; - int i, err; - unsigned int val; - - num_adcs = fill_adc_nids(codec); - if (num_adcs < 0) - return 0; - - err = fill_input_pin_labels(codec); - if (err < 0) - return err; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin; - - pin = cfg->inputs[i].pin; - if (!is_input_pin(codec, pin)) - continue; - - val = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) - val |= snd_hda_get_default_vref(codec, pin); - set_pin_target(codec, pin, val, false); - - if (mixer) { - if (is_reachable_path(codec, pin, mixer)) { - err = new_analog_input(codec, i, pin, - spec->input_labels[i], - spec->input_label_idxs[i], - mixer); - if (err < 0) - return err; - } - } - - err = parse_capture_source(codec, pin, i, num_adcs, - spec->input_labels[i], -mixer); - if (err < 0) - return err; - - if (spec->add_in_jack_modes) { - err = create_in_jack_mode(codec, pin); - if (err < 0) - return err; - } - } - - if (mixer && spec->add_stereo_mix_input) { - err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs, - "Stereo Mix", 0); - if (err < 0) - return err; - } - - return 0; -} - - -/* - * input source mux - */ - -/* get the input path specified by the given adc and imux indices */ -static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) -{ - struct hda_gen_spec *spec = codec->spec; - if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) { - snd_BUG(); - return NULL; - } - if (spec->dyn_adc_switch) - adc_idx = spec->dyn_adc_idx[imux_idx]; - if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) { - snd_BUG(); - return NULL; - } - return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); -} - -static int mux_select(struct hda_codec *codec, unsigned int adc_idx, - unsigned int idx); - -static int mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux, uinfo); -} - -static int mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - /* the ctls are created at once with multiple counts */ - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static int mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - return mux_select(codec, adc_idx, - ucontrol->value.enumerated.item[0]); -} - -static const struct snd_kcontrol_new cap_src_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .info = mux_enum_info, - .get = mux_enum_get, - .put = mux_enum_put, -}; - -/* - * capture volume and capture switch ctls - */ - -typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -/* call the given amp update function for all amps in the imux list at once */ -static int cap_put_caller(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - put_call_t func, int type) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - const struct hda_input_mux *imux; - struct nid_path *path; - int i, adc_idx, err = 0; - - imux = &spec->input_mux; - adc_idx = kcontrol->id.index; - mutex_lock(&codec->control_mutex); - /* we use the cache-only update at first since multiple input paths - * may shared the same amp; by updating only caches, the redundant - * writes to hardware can be reduced. - */ - codec->cached_write = 1; - for (i = 0; i < imux->num_items; i++) { - path = get_input_path(codec, adc_idx, i); - if (!path || !path->ctls[type]) - continue; - kcontrol->private_value = path->ctls[type]; - err = func(kcontrol, ucontrol); - if (err < 0) - goto error; - } - error: - codec->cached_write = 0; - mutex_unlock(&codec->control_mutex); - snd_hda_codec_flush_cache(codec); /* flush the updates */ - if (err >= 0 && spec->cap_sync_hook) - spec->cap_sync_hook(codec, ucontrol); - return err; -} - -/* capture volume ctl callbacks */ -#define cap_vol_info snd_hda_mixer_amp_volume_info -#define cap_vol_get snd_hda_mixer_amp_volume_get -#define cap_vol_tlv snd_hda_mixer_amp_tlv - -static int cap_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return cap_put_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_volume_put, - NID_PATH_VOL_CTL); -} - -static const struct snd_kcontrol_new cap_vol_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Volume", - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), - .info = cap_vol_info, - .get = cap_vol_get, - .put = cap_vol_put, - .tlv = { .c = cap_vol_tlv }, -}; - -/* capture switch ctl callbacks */ -#define cap_sw_info snd_ctl_boolean_stereo_info -#define cap_sw_get snd_hda_mixer_amp_switch_get - -static int cap_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return cap_put_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_switch_put, - NID_PATH_MUTE_CTL); -} - -static const struct snd_kcontrol_new cap_sw_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Switch", - .info = cap_sw_info, - .get = cap_sw_get, - .put = cap_sw_put, -}; - -static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) -{ - hda_nid_t nid; - int i, depth; - - path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; - for (depth = 0; depth < 3; depth++) { - if (depth >= path->depth) - return -EINVAL; - i = path->depth - depth - 1; - nid = path->path[i]; - if (!path->ctls[NID_PATH_VOL_CTL]) { - if (nid_has_volume(codec, nid, HDA_OUTPUT)) - path->ctls[NID_PATH_VOL_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else if (nid_has_volume(codec, nid, HDA_INPUT)) { - int idx = path->idx[i]; - if (!depth && codec->single_adc_amp) - idx = 0; - path->ctls[NID_PATH_VOL_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); - } - } - if (!path->ctls[NID_PATH_MUTE_CTL]) { - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - path->ctls[NID_PATH_MUTE_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else if (nid_has_mute(codec, nid, HDA_INPUT)) { - int idx = path->idx[i]; - if (!depth && codec->single_adc_amp) - idx = 0; - path->ctls[NID_PATH_MUTE_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); - } - } - } - return 0; -} - -static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int val; - int i; - - if (!spec->inv_dmic_split) - return false; - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].pin != nid) - continue; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return false; - val = snd_hda_codec_get_pincfg(codec, nid); - return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; - } - return false; -} - -/* capture switch put callback for a single control with hook call */ -static int cap_single_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - int ret; - - ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (ret < 0) - return ret; - - if (spec->cap_sync_hook) - spec->cap_sync_hook(codec, ucontrol); - - return ret; -} - -static int add_single_cap_ctl(struct hda_codec *codec, const char *label, - int idx, bool is_switch, unsigned int ctl, - bool inv_dmic) -{ - struct hda_gen_spec *spec = codec->spec; - char tmpname[44]; - int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; - const char *sfx = is_switch ? "Switch" : "Volume"; - unsigned int chs = inv_dmic ? 1 : 3; - struct snd_kcontrol_new *knew; - - if (!ctl) - return 0; - - if (label) - snprintf(tmpname, sizeof(tmpname), - "%s Capture %s", label, sfx); - else - snprintf(tmpname, sizeof(tmpname), - "Capture %s", sfx); - knew = add_control(spec, type, tmpname, idx, - amp_val_replace_channels(ctl, chs)); - if (!knew) - return -ENOMEM; - if (is_switch) - knew->put = cap_single_sw_put; - if (!inv_dmic) - return 0; - - /* Make independent right kcontrol */ - if (label) - snprintf(tmpname, sizeof(tmpname), - "Inverted %s Capture %s", label, sfx); - else - snprintf(tmpname, sizeof(tmpname), - "Inverted Capture %s", sfx); - knew = add_control(spec, type, tmpname, idx, - amp_val_replace_channels(ctl, 2)); - if (!knew) - return -ENOMEM; - if (is_switch) - knew->put = cap_single_sw_put; - return 0; -} - -/* create single (and simple) capture volume and switch controls */ -static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl, - bool inv_dmic) -{ - int err; - err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); - if (err < 0) - return err; - err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); - if (err < 0) - return err; - return 0; -} - -/* create bound capture volume and switch controls */ -static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl) -{ - struct hda_gen_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - - if (vol_ctl) { - knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp); - if (!knew) - return -ENOMEM; - knew->index = idx; - knew->private_value = vol_ctl; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } - if (sw_ctl) { - knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp); - if (!knew) - return -ENOMEM; - knew->index = idx; - knew->private_value = sw_ctl; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } - return 0; -} - -/* return the vol ctl when used first in the imux list */ -static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) -{ - struct nid_path *path; - unsigned int ctl; - int i; - - path = get_input_path(codec, 0, idx); - if (!path) - return 0; - ctl = path->ctls[type]; - if (!ctl) - return 0; - for (i = 0; i < idx - 1; i++) { - path = get_input_path(codec, 0, i); - if (path && path->ctls[type] == ctl) - return 0; - } - return ctl; -} - -/* create individual capture volume and switch controls per input */ -static int create_multi_cap_vol_ctl(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int i, err, type; - - for (i = 0; i < imux->num_items; i++) { - bool inv_dmic; - int idx; - - idx = imux->items[i].index; - if (idx >= spec->autocfg.num_inputs) - continue; - inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); - - for (type = 0; type < 2; type++) { - err = add_single_cap_ctl(codec, - spec->input_labels[idx], - spec->input_label_idxs[idx], - type, - get_first_cap_ctl(codec, i, type), - inv_dmic); - if (err < 0) - return err; - } - } - return 0; -} - -static int create_capture_mixers(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int i, n, nums, err; - - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - - if (!spec->auto_mic && imux->num_items > 1) { - struct snd_kcontrol_new *knew; - const char *name; - name = nums > 1 ? "Input Source" : "Capture Source"; - knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp); - if (!knew) - return -ENOMEM; - knew->count = nums; - } - - for (n = 0; n < nums; n++) { - bool multi = false; - bool multi_cap_vol = spec->multi_cap_vol; - bool inv_dmic = false; - int vol, sw; - - vol = sw = 0; - for (i = 0; i < imux->num_items; i++) { - struct nid_path *path; - path = get_input_path(codec, n, i); - if (!path) - continue; - parse_capvol_in_path(codec, path); - if (!vol) - vol = path->ctls[NID_PATH_VOL_CTL]; - else if (vol != path->ctls[NID_PATH_VOL_CTL]) { - multi = true; - if (!same_amp_caps(codec, vol, - path->ctls[NID_PATH_VOL_CTL], HDA_INPUT)) - multi_cap_vol = true; - } - if (!sw) - sw = path->ctls[NID_PATH_MUTE_CTL]; - else if (sw != path->ctls[NID_PATH_MUTE_CTL]) { - multi = true; - if (!same_amp_caps(codec, sw, - path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT)) - multi_cap_vol = true; - } - if (is_inv_dmic_pin(codec, spec->imux_pins[i])) - inv_dmic = true; - } - - if (!multi) - err = create_single_cap_vol_ctl(codec, n, vol, sw, - inv_dmic); - else if (!multi_cap_vol) - err = create_bind_cap_vol_ctl(codec, n, vol, sw); - else - err = create_multi_cap_vol_ctl(codec); - if (err < 0) - return err; - } - - return 0; -} - -/* - * add mic boosts if needed - */ - -/* check whether the given amp is feasible as a boost volume */ -static bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) -{ - unsigned int step; - - if (!nid_has_volume(codec, nid, dir) || - is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) || - is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL)) - return false; - - step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE) - >> AC_AMPCAP_STEP_SIZE_SHIFT; - if (step < 0x20) - return false; - return true; -} - -/* look for a boost amp in a widget close to the pin */ -static unsigned int look_for_boost_amp(struct hda_codec *codec, - struct nid_path *path) -{ - unsigned int val = 0; - hda_nid_t nid; - int depth; - - for (depth = 0; depth < 3; depth++) { - if (depth >= path->depth - 1) - break; - nid = path->path[depth]; - if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - break; - } else if (check_boost_vol(codec, nid, HDA_INPUT, - path->idx[depth])) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth], - HDA_INPUT); - break; - } - } - - return val; -} - -static int parse_mic_boost(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct hda_input_mux *imux = &spec->input_mux; - int i; - - if (!spec->num_adc_nids) - return 0; - - for (i = 0; i < imux->num_items; i++) { - struct nid_path *path; - unsigned int val; - int idx; - char boost_label[44]; - - idx = imux->items[i].index; - if (idx >= imux->num_items) - continue; - - /* check only line-in and mic pins */ - if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) - continue; - - path = get_input_path(codec, 0, i); - if (!path) - continue; - - val = look_for_boost_amp(codec, path); - if (!val) - continue; - - /* create a boost control */ - snprintf(boost_label, sizeof(boost_label), - "%s Boost Volume", spec->input_labels[idx]); - if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label, - spec->input_label_idxs[idx], val)) - return -ENOMEM; - - path->ctls[NID_PATH_BOOST_CTL] = val; - } - return 0; -} - -/* - * parse digital I/Os and set up NIDs in BIOS auto-parse mode - */ -static void parse_digital(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct nid_path *path; - int i, nums; - hda_nid_t dig_nid, pin; - - /* support multiple SPDIFs; the secondary is set up as a slave */ - nums = 0; - for (i = 0; i < spec->autocfg.dig_outs; i++) { - pin = spec->autocfg.dig_out_pins[i]; - dig_nid = look_for_dac(codec, pin, true); - if (!dig_nid) - continue; - path = snd_hda_add_new_path(codec, dig_nid, pin, 0); - if (!path) - continue; - print_nid_path("digout", path); - path->active = true; - spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); - set_pin_target(codec, pin, PIN_OUT, false); - if (!nums) { - spec->multiout.dig_out_nid = dig_nid; - spec->dig_out_type = spec->autocfg.dig_out_type[0]; - } else { - spec->multiout.slave_dig_outs = spec->slave_dig_outs; - if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) - break; - spec->slave_dig_outs[nums - 1] = dig_nid; - } - nums++; - } - - if (spec->autocfg.dig_in_pin) { - pin = spec->autocfg.dig_in_pin; - dig_nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, dig_nid++) { - unsigned int wcaps = get_wcaps(codec, dig_nid); - if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) - continue; - if (!(wcaps & AC_WCAP_DIGITAL)) - continue; - path = snd_hda_add_new_path(codec, pin, dig_nid, 0); - if (path) { - print_nid_path("digin", path); - path->active = true; - spec->dig_in_nid = dig_nid; - spec->digin_path = snd_hda_get_path_idx(codec, path); - set_pin_target(codec, pin, PIN_IN, false); - break; - } - } - } -} - - -/* - * input MUX handling - */ - -static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur); - -/* select the given imux item; either unmute exclusively or select the route */ -static int mux_select(struct hda_codec *codec, unsigned int adc_idx, - unsigned int idx) -{ - struct hda_gen_spec *spec = codec->spec; - const struct hda_input_mux *imux; - struct nid_path *old_path, *path; - - imux = &spec->input_mux; - if (!imux->num_items) - return 0; - - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (spec->cur_mux[adc_idx] == idx) - return 0; - - old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); - if (!old_path) - return 0; - if (old_path->active) - snd_hda_activate_path(codec, old_path, false, false); - - spec->cur_mux[adc_idx] = idx; - - if (spec->shared_mic_hp) - update_shared_mic_hp(codec, spec->cur_mux[adc_idx]); - - if (spec->dyn_adc_switch) - dyn_adc_pcm_resetup(codec, idx); - - path = get_input_path(codec, adc_idx, idx); - if (!path) - return 0; - if (path->active) - return 0; - snd_hda_activate_path(codec, path, true, false); - if (spec->cap_sync_hook) - spec->cap_sync_hook(codec, NULL); - path_power_down_sync(codec, old_path); - return 1; -} - - -/* - * Jack detections for HP auto-mute and mic-switch - */ - -/* check each pin in the given array; returns true if any of them is plugged */ -static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) -{ - int i, present = 0; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - if (!nid) - break; - /* don't detect pins retasked as inputs */ - if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN) - continue; - present |= snd_hda_jack_detect(codec, nid); - } - return present; -} - -/* standard HP/line-out auto-mute helper */ -static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, - bool mute) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - unsigned int val; - if (!nid) - break; - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) - val = snd_hda_codec_get_pin_target(codec, nid) & ~PIN_HP; - else - val = 0; - if (!mute) - val |= snd_hda_codec_get_pin_target(codec, nid); - /* here we call update_pin_ctl() so that the pinctl is changed - * without changing the pinctl target value; - * the original target value will be still referred at the - * init / resume again - */ - update_pin_ctl(codec, nid, val); - set_pin_eapd(codec, nid, !mute); - } -} - -/* Toggle outputs muting */ -void snd_hda_gen_update_outputs(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int on; - - /* Control HP pins/amps depending on master_mute state; - * in general, HP pins/amps control should be enabled in all cases, - * but currently set only for master_mute, just to be safe - */ - if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */ - do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins, spec->master_mute); - - if (!spec->automute_speaker) - on = 0; - else - on = spec->hp_jack_present | spec->line_jack_present; - on |= spec->master_mute; - spec->speaker_muted = on; - do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), - spec->autocfg.speaker_pins, on); - - /* toggle line-out mutes if needed, too */ - /* if LO is a copy of either HP or Speaker, don't need to handle it */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || - spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) - return; - if (!spec->automute_lo) - on = 0; - else - on = spec->hp_jack_present; - on |= spec->master_mute; - spec->line_out_muted = on; - do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins, on); -} -EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs); - -static void call_update_outputs(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->automute_hook) - spec->automute_hook(codec); - else - snd_hda_gen_update_outputs(codec); -} - -/* standard HP-automute helper */ -void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct hda_gen_spec *spec = codec->spec; - hda_nid_t *pins = spec->autocfg.hp_pins; - int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins); - - /* No detection for the first HP jack during indep-HP mode */ - if (spec->indep_hp_enabled) { - pins++; - num_pins--; - } - - spec->hp_jack_present = detect_jacks(codec, num_pins, pins); - if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) - return; - call_update_outputs(codec); -} -EXPORT_SYMBOL_HDA(snd_hda_gen_hp_automute); - -/* standard line-out-automute helper */ -void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct hda_gen_spec *spec = codec->spec; - - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - return; - /* check LO jack only when it's different from HP */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) - return; - - spec->line_jack_present = - detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins); - if (!spec->automute_speaker || !spec->detect_lo) - return; - call_update_outputs(codec); -} -EXPORT_SYMBOL_HDA(snd_hda_gen_line_automute); - -/* standard mic auto-switch helper */ -void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - if (!spec->auto_mic) - return; - - for (i = spec->am_num_entries - 1; i > 0; i--) { - hda_nid_t pin = spec->am_entry[i].pin; - /* don't detect pins retasked as outputs */ - if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) - continue; - if (snd_hda_jack_detect(codec, pin)) { - mux_select(codec, 0, spec->am_entry[i].idx); - return; - } - } - mux_select(codec, 0, spec->am_entry[0].idx); -} -EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch); - -/* update jack retasking */ -static void update_automute_all(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (spec->hp_automute_hook) - spec->hp_automute_hook(codec, NULL); - else - snd_hda_gen_hp_automute(codec, NULL); - if (spec->line_automute_hook) - spec->line_automute_hook(codec, NULL); - else - snd_hda_gen_line_automute(codec, NULL); - if (spec->mic_autoswitch_hook) - spec->mic_autoswitch_hook(codec, NULL); - else - snd_hda_gen_mic_autoswitch(codec, NULL); -} - -/* - * Auto-Mute mode mixer enum support - */ -static int automute_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - static const char * const texts3[] = { - "Disabled", "Speaker Only", "Line Out+Speaker" - }; - - if (spec->automute_speaker_possible && spec->automute_lo_possible) - return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); - return snd_hda_enum_bool_helper_info(kcontrol, uinfo); -} - -static int automute_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - unsigned int val = 0; - if (spec->automute_speaker) - val++; - if (spec->automute_lo) - val++; - - ucontrol->value.enumerated.item[0] = val; - return 0; -} + struct hda_gspec *spec = codec->spec; + static const char * const types_speaker[] = { "Speaker", "Headphone" }; + static const char * const types_line[] = { "Front", "Headphone" }; -static int automute_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gen_spec *spec = codec->spec; - - switch (ucontrol->value.enumerated.item[0]) { - case 0: - if (!spec->automute_speaker && !spec->automute_lo) - return 0; - spec->automute_speaker = 0; - spec->automute_lo = 0; - break; + switch (spec->pcm_vol_nodes) { case 1: - if (spec->automute_speaker_possible) { - if (!spec->automute_lo && spec->automute_speaker) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 0; - } else if (spec->automute_lo_possible) { - if (spec->automute_lo) - return 0; - spec->automute_lo = 1; - } else - return -EINVAL; - break; + return create_mixer(codec, spec->pcm_vol[0].node, + spec->pcm_vol[0].index, + "Master", "Playback", 0); case 2: - if (!spec->automute_lo_possible || !spec->automute_speaker_possible) - return -EINVAL; - if (spec->automute_speaker && spec->automute_lo) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 1; - break; - default: - return -EINVAL; - } - call_update_outputs(codec); - return 1; -} - -static const struct snd_kcontrol_new automute_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Auto-Mute Mode", - .info = automute_mode_info, - .get = automute_mode_get, - .put = automute_mode_put, -}; - -static int add_automute_mode_enum(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum)) - return -ENOMEM; - return 0; -} - -/* - * Check the availability of HP/line-out auto-mute; - * Set up appropriately if really supported - */ -static int check_auto_mute_availability(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int present = 0; - int i, err; - - if (spec->suppress_auto_mute) - return 0; - - if (cfg->hp_pins[0]) - present++; - if (cfg->line_out_pins[0]) - present++; - if (cfg->speaker_pins[0]) - present++; - if (present < 2) /* need two different output types */ - return 0; - - if (!cfg->speaker_pins[0] && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = cfg->line_outs; - } - - if (!cfg->hp_pins[0] && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = cfg->line_outs; - } - - for (i = 0; i < cfg->hp_outs; i++) { - hda_nid_t nid = cfg->hp_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n", - nid); - snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT, - spec->hp_automute_hook ? - spec->hp_automute_hook : - snd_hda_gen_hp_automute); - spec->detect_hp = 1; - } - - if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { - if (cfg->speaker_outs) - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t nid = cfg->line_out_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable_callback(codec, nid, - HDA_GEN_FRONT_EVENT, - spec->line_automute_hook ? - spec->line_automute_hook : - snd_hda_gen_line_automute); - spec->detect_lo = 1; - } - spec->automute_lo_possible = spec->detect_hp; - } - - spec->automute_speaker_possible = cfg->speaker_outs && - (spec->detect_hp || spec->detect_lo); - - spec->automute_lo = spec->automute_lo_possible; - spec->automute_speaker = spec->automute_speaker_possible; - - if (spec->automute_speaker_possible || spec->automute_lo_possible) { - /* create a control for automute mode */ - err = add_automute_mode_enum(codec); - if (err < 0) - return err; - } - return 0; -} - -/* check whether all auto-mic pins are valid; setup indices if OK */ -static bool auto_mic_check_imux(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - const struct hda_input_mux *imux; - int i; - - imux = &spec->input_mux; - for (i = 0; i < spec->am_num_entries; i++) { - spec->am_entry[i].idx = - find_idx_in_nid_list(spec->am_entry[i].pin, - spec->imux_pins, imux->num_items); - if (spec->am_entry[i].idx < 0) - return false; /* no corresponding imux */ - } - - /* we don't need the jack detection for the first pin */ - for (i = 1; i < spec->am_num_entries; i++) - snd_hda_jack_detect_enable_callback(codec, - spec->am_entry[i].pin, - HDA_GEN_MIC_EVENT, - spec->mic_autoswitch_hook ? - spec->mic_autoswitch_hook : - snd_hda_gen_mic_autoswitch); - return true; -} - -static int compare_attr(const void *ap, const void *bp) -{ - const struct automic_entry *a = ap; - const struct automic_entry *b = bp; - return (int)(a->attr - b->attr); -} - -/* - * Check the availability of auto-mic switch; - * Set up if really supported - */ -static int check_auto_mic_availability(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int types; - int i, num_pins; - - if (spec->suppress_auto_mic) - return 0; - - types = 0; - num_pins = 0; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - unsigned int attr; - attr = snd_hda_codec_get_pincfg(codec, nid); - attr = snd_hda_get_input_pin_attr(attr); - if (types & (1 << attr)) - return 0; /* already occupied */ - switch (attr) { - case INPUT_PIN_ATTR_INT: - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* invalid type */ - break; - case INPUT_PIN_ATTR_UNUSED: - return 0; /* invalid entry */ - default: - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - return 0; /* invalid type */ - if (!spec->line_in_auto_switch && - cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* only mic is allowed */ - if (!is_jack_detectable(codec, nid)) - return 0; /* no unsol support */ - break; - } - if (num_pins >= MAX_AUTO_MIC_PINS) - return 0; - types |= (1 << attr); - spec->am_entry[num_pins].pin = nid; - spec->am_entry[num_pins].attr = attr; - num_pins++; - } - - if (num_pins < 2) - return 0; - - spec->am_num_entries = num_pins; - /* sort the am_entry in the order of attr so that the pin with a - * higher attr will be selected when the jack is plugged. - */ - sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), - compare_attr, NULL); - - if (!auto_mic_check_imux(codec)) - return 0; - - spec->auto_mic = 1; - spec->num_adc_nids = 1; - spec->cur_mux[0] = spec->am_entry[0].idx; - snd_printdd("hda-codec: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", - spec->am_entry[0].pin, - spec->am_entry[1].pin, - spec->am_entry[2].pin); - - return 0; -} - -/* power_filter hook; make inactive widgets into power down */ -static unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state) -{ - if (power_state != AC_PWRST_D0) - return power_state; - if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER) - return power_state; - if (is_active_nid(codec, nid, HDA_OUTPUT, 0)) - return power_state; - return AC_PWRST_D3; -} - - -/* - * Parse the given BIOS configuration and set up the hda_gen_spec - * - * return 1 if successful, 0 if the proper config is not found, - * or a negative error code - */ -int snd_hda_gen_parse_auto_config(struct hda_codec *codec, - struct auto_pin_cfg *cfg) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - parse_user_hints(codec); - - if (spec->mixer_nid && !spec->mixer_merge_nid) - spec->mixer_merge_nid = spec->mixer_nid; - - if (cfg != &spec->autocfg) { - spec->autocfg = *cfg; - cfg = &spec->autocfg; - } - - fill_all_dac_nids(codec); - - if (!cfg->line_outs) { - if (cfg->dig_outs || cfg->dig_in_pin) { - spec->multiout.max_channels = 2; - spec->no_analog = 1; - goto dig_only; - } - return 0; /* can't find valid BIOS pin config */ - } - - if (!spec->no_primary_hp && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->line_outs <= cfg->hp_outs) { - /* use HP as primary out */ - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - } - - err = parse_output_paths(codec); - if (err < 0) - return err; - err = create_multi_channel_mode(codec); - if (err < 0) - return err; - err = create_multi_out_ctls(codec, cfg); - if (err < 0) - return err; - err = create_hp_out_ctls(codec); - if (err < 0) - return err; - err = create_speaker_out_ctls(codec); - if (err < 0) - return err; - err = create_indep_hp_ctls(codec); - if (err < 0) - return err; - err = create_loopback_mixing_ctl(codec); - if (err < 0) - return err; - err = create_shared_input(codec); - if (err < 0) - return err; - err = create_input_ctls(codec); - if (err < 0) - return err; - - spec->const_channel_count = spec->ext_channel_count; - /* check the multiple speaker and headphone pins */ - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) - spec->const_channel_count = max(spec->const_channel_count, - cfg->speaker_outs * 2); - if (cfg->line_out_type != AUTO_PIN_HP_OUT) - spec->const_channel_count = max(spec->const_channel_count, - cfg->hp_outs * 2); - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - - err = check_auto_mute_availability(codec); - if (err < 0) - return err; - - err = check_dyn_adc_switch(codec); - if (err < 0) - return err; - - if (!spec->shared_mic_hp) { - err = check_auto_mic_availability(codec); - if (err < 0) - return err; - } - - err = create_capture_mixers(codec); - if (err < 0) - return err; - - err = parse_mic_boost(codec); - if (err < 0) - return err; - - if (spec->add_out_jack_modes) { - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = create_out_jack_modes(codec, cfg->line_outs, - cfg->line_out_pins); - if (err < 0) - return err; - } - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = create_out_jack_modes(codec, cfg->hp_outs, - cfg->hp_pins); - if (err < 0) - return err; - } - } - - dig_only: - parse_digital(codec); - - if (spec->power_down_unused) - codec->power_filter = snd_hda_gen_path_power_filter; - - return 1; -} -EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config); - - -/* - * Build control elements - */ - -/* slave controls for virtual master */ -static const char * const slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Mono", "Line Out", - "CLFE", "Bass Speaker", "PCM", - "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", - "Headphone Front", "Headphone Surround", "Headphone CLFE", - "Headphone Side", - NULL, -}; - -int snd_hda_gen_build_controls(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - if (spec->kctls.used) { - err = snd_hda_add_new_ctls(codec, spec->kctls.list); - if (err < 0) - return err; - } - - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_dig_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid, - spec->pcm_rec[1].pcm_type); - if (err < 0) - return err; - if (!spec->no_analog) { - 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); - if (err < 0) - return err; - } - - /* if we have no master control, let's create it */ - if (!spec->no_analog && - !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - spec->vmaster_tlv, slave_pfxs, - "Playback Volume"); - if (err < 0) - return err; - } - if (!spec->no_analog && - !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_pfxs, - "Playback Switch", - true, &spec->vmaster_mute.sw_kctl); - if (err < 0) - return err; - if (spec->vmaster_mute.hook) - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, - spec->vmaster_mute_enum); - } - - free_kctls(spec); /* no longer needed */ - - if (spec->shared_mic_hp) { - int err; - int nid = spec->autocfg.inputs[1].pin; - err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0); - if (err < 0) - return err; - err = snd_hda_jack_detect_enable(codec, nid, 0); - if (err < 0) - return err; - } - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls); - - -/* - * PCM definitions - */ - -static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->pcm_playback_hook) - spec->pcm_playback_hook(hinfo, codec, substream, action); -} - -static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct hda_gen_spec *spec = codec->spec; - if (spec->pcm_capture_hook) - spec->pcm_capture_hook(hinfo, codec, substream, action); -} - -/* - * Analog playback callbacks - */ -static int playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - int err; - - mutex_lock(&spec->pcm_mutex); - err = snd_hda_multi_out_analog_open(codec, - &spec->multiout, substream, - hinfo); - if (!err) { - spec->active_streams |= 1 << STREAM_MULTI_OUT; - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_OPEN); + if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER) + return create_output_mixers(codec, types_speaker); + else + return create_output_mixers(codec, types_line); } - mutex_unlock(&spec->pcm_mutex); - return err; + return 0; } -static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +/* create capture volume/switch */ +static int build_input_controls(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; - int err; + struct hda_gspec *spec = codec->spec; + struct hda_gnode *adc_node = spec->adc_node; + int i, err; + static struct snd_kcontrol_new cap_sel = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = capture_source_info, + .get = capture_source_get, + .put = capture_source_put, + }; - err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); - if (!err) - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_PREPARE); - return err; -} + if (! adc_node || ! spec->input_mux.num_items) + return 0; /* not found */ -static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - int err; + spec->cur_cap_src = 0; + select_input_connection(codec, adc_node, + spec->input_mux.items[0].index); - err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); - if (!err) - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLEANUP); - return err; -} + /* create capture volume and switch controls if the ADC has an amp */ + /* do we have only a single item? */ + if (spec->input_mux.num_items == 1) { + err = create_mixer(codec, adc_node, + spec->input_mux.items[0].index, + NULL, "Capture", 0); + if (err < 0) + return err; + return 0; + } -static int playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - mutex_lock(&spec->pcm_mutex); - spec->active_streams &= ~(1 << STREAM_MULTI_OUT); - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLOSE); - mutex_unlock(&spec->pcm_mutex); - return 0; -} + /* create input MUX if multiple sources are available */ + err = snd_hda_ctl_add(codec, spec->adc_node->nid, + snd_ctl_new1(&cap_sel, codec)); + if (err < 0) + return err; -static int capture_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN); - return 0; -} + /* no volume control? */ + if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) || + ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) + return 0; -static int capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); - call_pcm_capture_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_PREPARE); - return 0; -} + for (i = 0; i < spec->input_mux.num_items; i++) { + struct snd_kcontrol_new knew; + char name[32]; + sprintf(name, "%s Capture Volume", + spec->input_mux.items[i].label); + knew = (struct snd_kcontrol_new) + HDA_CODEC_VOLUME(name, adc_node->nid, + spec->input_mux.items[i].index, + HDA_INPUT); + err = snd_hda_ctl_add(codec, adc_node->nid, + snd_ctl_new1(&knew, codec)); + if (err < 0) + return err; + } -static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - call_pcm_capture_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLEANUP); return 0; } -static int capture_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE); - return 0; -} -static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +/* + * parse the nodes recursively until reach to the output PIN. + * + * returns 0 - if not found, + * 1 - if found, but no mixer is created + * 2 - if found and mixer was already created, (just skip) + * a negative error code + */ +static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec, + struct hda_gnode *node, struct hda_gnode *dest_node, + const char *type) { - struct hda_gen_spec *spec = codec->spec; - int err = 0; - - mutex_lock(&spec->pcm_mutex); - if (!spec->indep_hp_enabled) - err = -EBUSY; - else - spec->active_streams |= 1 << STREAM_INDEP_HP; - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_OPEN); - mutex_unlock(&spec->pcm_mutex); - return err; -} + int i, err; -static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - mutex_lock(&spec->pcm_mutex); - spec->active_streams &= ~(1 << STREAM_INDEP_HP); - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLOSE); - mutex_unlock(&spec->pcm_mutex); - return 0; -} + if (node->checked) + return 0; -static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_PREPARE); - return 0; -} + node->checked = 1; + if (node == dest_node) { + /* loopback connection found */ + return 1; + } -static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - call_pcm_playback_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLEANUP); + for (i = 0; i < node->nconns; i++) { + struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]); + if (! child) + continue; + err = parse_loopback_path(codec, spec, child, dest_node, type); + if (err < 0) + return err; + else if (err >= 1) { + if (err == 1) { + err = create_mixer(codec, node, i, type, + "Playback", 1); + if (err < 0) + return err; + if (err > 0) + return 2; /* ok, created */ + /* not created, maybe in the lower path */ + err = 1; + } + /* connect and unmute */ + if (node->nconns > 1) + select_input_connection(codec, node, i); + unmute_input(codec, node, i); + unmute_output(codec, node); + return err; + } + } return 0; } /* - * Digital out + * parse the tree and build the loopback controls */ -static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static int build_loopback_controls(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} + struct hda_gspec *spec = codec->spec; + struct hda_gnode *node; + int err; + const char *type; -static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} + if (! spec->out_pin_node[0]) + return 0; -static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); + list_for_each_entry(node, &spec->nid_list, list) { + if (node->type != AC_WID_PIN) + continue; + /* input capable? */ + if (! (node->pin_caps & AC_PINCAP_IN)) + return 0; + type = get_input_type(node, NULL); + if (type) { + if (check_existing_control(codec, type, "Playback")) + continue; + clear_check_flags(spec); + err = parse_loopback_path(codec, spec, + spec->out_pin_node[0], + node, type); + if (err < 0) + return err; + if (! err) + continue; + } + } + return 0; } /* - * Analog capture + * build mixer controls */ -#define alt_capture_pcm_open capture_pcm_open -#define alt_capture_pcm_close capture_pcm_close - -static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static int build_generic_controls(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], - stream_tag, 0, format); - call_pcm_capture_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_PREPARE); - return 0; -} + int err; -static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; + if ((err = build_input_controls(codec)) < 0 || + (err = build_output_controls(codec)) < 0 || + (err = build_loopback_controls(codec)) < 0) + return err; - snd_hda_codec_cleanup_stream(codec, - spec->adc_nids[substream->number + 1]); - call_pcm_capture_hook(hinfo, codec, substream, - HDA_GEN_PCM_ACT_CLEANUP); return 0; } /* + * PCM */ -static const struct hda_pcm_stream pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - /* NID is set in build_pcms */ - .ops = { - .open = playback_pcm_open, - .close = playback_pcm_close, - .prepare = playback_pcm_prepare, - .cleanup = playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in build_pcms */ - .ops = { - .open = capture_pcm_open, - .close = capture_pcm_close, - .prepare = capture_pcm_prepare, - .cleanup = capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in build_pcms */ - .ops = { - .open = alt_playback_pcm_open, - .close = alt_playback_pcm_close, - .prepare = alt_playback_pcm_prepare, - .cleanup = alt_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_analog_alt_capture = { - .substreams = 2, /* can be overridden */ - .channels_min = 2, - .channels_max = 2, - /* NID is set in build_pcms */ - .ops = { - .open = alt_capture_pcm_open, - .close = alt_capture_pcm_close, - .prepare = alt_capture_pcm_prepare, - .cleanup = alt_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in build_pcms */ - .ops = { - .open = dig_playback_pcm_open, - .close = dig_playback_pcm_close, - .prepare = dig_playback_pcm_prepare, - .cleanup = dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream pcm_digital_capture = { +static struct hda_pcm_stream generic_pcm_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, - /* NID is set in build_pcms */ -}; - -/* Used by build_pcms to flag that a PCM has no playback stream */ -static const struct hda_pcm_stream pcm_null_stream = { - .substreams = 0, - .channels_min = 0, - .channels_max = 0, }; -/* - * dynamic changing ADC PCM streams - */ -static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) +static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) { - struct hda_gen_spec *spec = codec->spec; - hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; - - if (spec->cur_adc && spec->cur_adc != new_adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = new_adc; - snd_hda_codec_setup_stream(codec, new_adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - return true; - } - return false; -} + struct hda_gspec *spec = codec->spec; -/* analog capture with dynamic dual-adc changes */ -static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, + stream_tag, 0, format); return 0; } -static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - struct hda_gen_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; + struct hda_gspec *spec = codec->spec; + + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid); return 0; } -static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = dyn_adc_capture_pcm_prepare, - .cleanup = dyn_adc_capture_pcm_cleanup - }, -}; - -static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, - const char *chip_name) +static int build_generic_pcms(struct hda_codec *codec) { - char *p; - - if (*str) - return; - strlcpy(str, chip_name, len); + struct hda_gspec *spec = codec->spec; + struct hda_pcm *info = &spec->pcm_rec; - /* drop non-alnum chars after a space */ - for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) { - if (!isalnum(p[1])) { - *p = 0; - break; - } + if (! spec->dac_node[0] && ! spec->adc_node) { + snd_printd("hda_generic: no PCM found\n"); + return 0; } - strlcat(str, sfx, len); -} - -/* build PCM streams based on the parsed results */ -int snd_hda_gen_build_pcms(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - const struct hda_pcm_stream *p; - bool have_multi_adcs; codec->num_pcms = 1; codec->pcm_info = info; - if (spec->no_analog) - goto skip_analog; - - fill_pcm_stream_name(spec->stream_name_analog, - sizeof(spec->stream_name_analog), - " Analog", codec->chip_name); - info->name = spec->stream_name_analog; - - if (spec->multiout.num_dacs > 0) { - p = spec->stream_analog_playback; - if (!p) - p = &pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && - spec->autocfg.line_outs == 2) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; - } - if (spec->num_adc_nids) { - p = spec->stream_analog_capture; - if (!p) { - if (spec->dyn_adc_switch) - p = &dyn_adc_pcm_analog_capture; - else - p = &pcm_analog_capture; - } - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - } - - skip_analog: - /* SPDIF for stream index #1 */ - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - fill_pcm_stream_name(spec->stream_name_digital, - sizeof(spec->stream_name_digital), - " Digital", codec->chip_name); - codec->num_pcms = 2; - codec->slave_dig_outs = spec->multiout.slave_dig_outs; - info = spec->pcm_rec + 1; - info->name = spec->stream_name_digital; - if (spec->dig_out_type) - info->pcm_type = spec->dig_out_type; - else - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - p = spec->stream_digital_playback; - if (!p) - p = &pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - p = spec->stream_digital_capture; - if (!p) - p = &pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - } - - if (spec->no_analog) - return 0; - - /* If the use of more than one ADC is requested for the current - * model, configure a second analog capture-only PCM. - */ - have_multi_adcs = (spec->num_adc_nids > 1) && - !spec->dyn_adc_switch && !spec->auto_mic; - /* Additional Analaog capture for index #2 */ - if (spec->alt_dac_nid || have_multi_adcs) { - fill_pcm_stream_name(spec->stream_name_alt_analog, - sizeof(spec->stream_name_alt_analog), - " Alt Analog", codec->chip_name); - codec->num_pcms = 3; - info = spec->pcm_rec + 2; - info->name = spec->stream_name_alt_analog; - if (spec->alt_dac_nid) { - p = spec->stream_analog_alt_playback; - if (!p) - p = &pcm_analog_alt_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->alt_dac_nid; - } else { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; - } - if (have_multi_adcs) { - p = spec->stream_analog_alt_capture; - if (!p) - p = &pcm_analog_alt_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nids[1]; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids - 1; - } else { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; - } - } - - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); - - -/* - * Standard auto-parser initializations - */ - -/* configure the given path as a proper output */ -static void set_output_and_unmute(struct hda_codec *codec, int path_idx) -{ - struct nid_path *path; - hda_nid_t pin; - - path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path || !path->depth) - return; - pin = path->path[path->depth - 1]; - restore_pin_ctl(codec, pin); - snd_hda_activate_path(codec, path, path->active, true); - set_pin_eapd(codec, pin, path->active); -} - -/* initialize primary output paths */ -static void init_multi_out(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.line_outs; i++) - set_output_and_unmute(codec, spec->out_paths[i]); -} - - -static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths) -{ - int i; - - for (i = 0; i < num_outs; i++) - set_output_and_unmute(codec, paths[i]); -} - -/* initialize hp and speaker paths */ -static void init_extra_out(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) - __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths); - if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) - __init_extra_out(codec, spec->autocfg.speaker_outs, - spec->speaker_paths); -} - -/* initialize multi-io paths */ -static void init_multi_io(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->multi_ios; i++) { - hda_nid_t pin = spec->multi_io[i].pin; - struct nid_path *path; - path = get_multiio_path(codec, i); - if (!path) - continue; - if (!spec->multi_io[i].ctl_in) - spec->multi_io[i].ctl_in = - snd_hda_codec_get_pin_target(codec, pin); - snd_hda_activate_path(codec, path, path->active, true); - } -} - -/* set up input pins and loopback paths */ -static void init_analog_input(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (is_input_pin(codec, nid)) - restore_pin_ctl(codec, nid); - - /* init loopback inputs */ - if (spec->mixer_nid) { - resume_path_from_idx(codec, spec->loopback_paths[i]); - resume_path_from_idx(codec, spec->loopback_merge_path); - } - } -} - -/* initialize ADC paths */ -static void init_input_src(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - struct nid_path *path; - int i, c, nums; - - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - - for (c = 0; c < nums; c++) { - for (i = 0; i < imux->num_items; i++) { - path = get_input_path(codec, c, i); - if (path) { - bool active = path->active; - if (i == spec->cur_mux[c]) - active = true; - snd_hda_activate_path(codec, path, active, false); - } + info->name = "HDA Generic"; + if (spec->dac_node[0]) { + info->stream[0] = generic_pcm_playback; + info->stream[0].nid = spec->dac_node[0]->nid; + if (spec->dac_node[1]) { + info->stream[0].ops.prepare = generic_pcm2_prepare; + info->stream[0].ops.cleanup = generic_pcm2_cleanup; } } - - if (spec->shared_mic_hp) - update_shared_mic_hp(codec, spec->cur_mux[0]); - - if (spec->cap_sync_hook) - spec->cap_sync_hook(codec, NULL); -} - -/* set right pin controls for digital I/O */ -static void init_digital(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - int i; - hda_nid_t pin; - - for (i = 0; i < spec->autocfg.dig_outs; i++) - set_output_and_unmute(codec, spec->digout_paths[i]); - pin = spec->autocfg.dig_in_pin; - if (pin) { - restore_pin_ctl(codec, pin); - resume_path_from_idx(codec, spec->digin_path); - } -} - -/* clear unsol-event tags on unused pins; Conexant codecs seem to leave - * invalid unsol tags by some reason - */ -static void clear_unsol_on_unused_pins(struct hda_codec *codec) -{ - int i; - - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - hda_nid_t nid = pin->nid; - if (is_jack_detectable(codec, nid) && - !snd_hda_jack_tbl_get(codec, nid)) - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, 0); + if (spec->adc_node) { + info->stream[1] = generic_pcm_playback; + info->stream[1].nid = spec->adc_node->nid; } -} - -/* - * initialize the generic spec; - * this can be put as patch_ops.init function - */ -int snd_hda_gen_init(struct hda_codec *codec) -{ - struct hda_gen_spec *spec = codec->spec; - - if (spec->init_hook) - spec->init_hook(codec); - - snd_hda_apply_verbs(codec); - - codec->cached_write = 1; - init_multi_out(codec); - init_extra_out(codec); - init_multi_io(codec); - init_analog_input(codec); - init_input_src(codec); - init_digital(codec); - - clear_unsol_on_unused_pins(codec); - - /* call init functions of standard auto-mute helpers */ - update_automute_all(codec); - - snd_hda_codec_flush_cache(codec); - - if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - - hda_call_check_power_status(codec, 0x01); return 0; } -EXPORT_SYMBOL_HDA(snd_hda_gen_init); - -/* - * free the generic spec; - * this can be put as patch_ops.free function - */ -void snd_hda_gen_free(struct hda_codec *codec) -{ - snd_hda_gen_spec_free(codec->spec); - kfree(codec->spec); - codec->spec = NULL; -} -EXPORT_SYMBOL_HDA(snd_hda_gen_free); #ifdef CONFIG_PM -/* - * check the loopback power save state; - * this can be put as patch_ops.check_power_status function - */ -int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) +static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid) { - struct hda_gen_spec *spec = codec->spec; + struct hda_gspec *spec = codec->spec; return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); } -EXPORT_SYMBOL_HDA(snd_hda_gen_check_power_status); #endif /* - * the generic codec support */ - -static const struct hda_codec_ops generic_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, +static struct hda_codec_ops generic_patch_ops = { + .build_controls = build_generic_controls, + .build_pcms = build_generic_pcms, + .free = snd_hda_generic_free, #ifdef CONFIG_PM - .check_power_status = snd_hda_gen_check_power_status, + .check_power_status = generic_check_power_status, #endif }; +/* + * the generic parser + */ int snd_hda_parse_generic_codec(struct hda_codec *codec) { - struct hda_gen_spec *spec; + struct hda_gspec *spec; int err; + if(!codec->afg) + return 0; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) + if (spec == NULL) { + printk(KERN_ERR "hda_generic: can't allocate spec\n"); return -ENOMEM; - snd_hda_gen_spec_init(spec); + } codec->spec = spec; + INIT_LIST_HEAD(&spec->nid_list); - err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); - if (err < 0) - return err; + if ((err = build_afg_tree(codec)) < 0) + goto error; - err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); - if (err < 0) + if ((err = parse_input(codec)) < 0 || + (err = parse_output(codec)) < 0) goto error; codec->patch_ops = generic_patch_ops; + return 0; -error: - snd_hda_gen_free(codec); + error: + snd_hda_generic_free(codec); return err; } -EXPORT_SYMBOL_HDA(snd_hda_parse_generic_codec); +EXPORT_SYMBOL(snd_hda_parse_generic_codec); diff --git a/trunk/sound/pci/hda/hda_generic.h b/trunk/sound/pci/hda/hda_generic.h deleted file mode 100644 index 065fcc77b860..000000000000 --- a/trunk/sound/pci/hda/hda_generic.h +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Generic BIOS auto-parser helper functions for HD-audio - * - * Copyright (c) 2012 Takashi Iwai - * - * This driver is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __SOUND_HDA_GENERIC_H -#define __SOUND_HDA_GENERIC_H - -/* unsol event tags */ -enum { - HDA_GEN_HP_EVENT = 1, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT, - HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT -}; - -/* table entry for multi-io paths */ -struct hda_multi_io { - hda_nid_t pin; /* multi-io widget pin NID */ - hda_nid_t dac; /* DAC to be connected */ - unsigned int ctl_in; /* cached input-pin control value */ -}; - -/* Widget connection path - * - * For output, stored in the order of DAC -> ... -> pin, - * for input, pin -> ... -> ADC. - * - * idx[i] contains the source index number to select on of the widget path[i]; - * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget - * multi[] indicates whether it's a selector widget with multi-connectors - * (i.e. the connection selection is mandatory) - * vol_ctl and mute_ctl contains the NIDs for the assigned mixers - */ - -#define MAX_NID_PATH_DEPTH 10 - -enum { - NID_PATH_VOL_CTL, - NID_PATH_MUTE_CTL, - NID_PATH_BOOST_CTL, - NID_PATH_NUM_CTLS -}; - -struct nid_path { - int depth; - hda_nid_t path[MAX_NID_PATH_DEPTH]; - unsigned char idx[MAX_NID_PATH_DEPTH]; - unsigned char multi[MAX_NID_PATH_DEPTH]; - unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ - bool active; -}; - -/* mic/line-in auto switching entry */ - -#define MAX_AUTO_MIC_PINS 3 - -struct automic_entry { - hda_nid_t pin; /* pin */ - int idx; /* imux index, -1 = invalid */ - unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ -}; - -/* active stream id */ -enum { STREAM_MULTI_OUT, STREAM_INDEP_HP }; - -/* PCM hook action */ -enum { - HDA_GEN_PCM_ACT_OPEN, - HDA_GEN_PCM_ACT_PREPARE, - HDA_GEN_PCM_ACT_CLEANUP, - HDA_GEN_PCM_ACT_CLOSE, -}; - -struct hda_gen_spec { - char stream_name_analog[32]; /* analog PCM stream */ - const struct hda_pcm_stream *stream_analog_playback; - const struct hda_pcm_stream *stream_analog_capture; - - char stream_name_alt_analog[32]; /* alternative analog PCM stream */ - const struct hda_pcm_stream *stream_analog_alt_playback; - const struct hda_pcm_stream *stream_analog_alt_capture; - - char stream_name_digital[32]; /* digital PCM stream */ - const struct hda_pcm_stream *stream_digital_playback; - const struct hda_pcm_stream *stream_digital_capture; - - /* PCM */ - unsigned int active_streams; - struct mutex pcm_mutex; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - hda_nid_t alt_dac_nid; - hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */ - int dig_out_type; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t adc_nids[AUTO_CFG_MAX_INS]; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - hda_nid_t mixer_nid; /* analog-mixer NID */ - hda_nid_t mixer_merge_nid; /* aamix merge-point NID (optional) */ - const char *input_labels[HDA_MAX_NUM_INPUTS]; - int input_label_idxs[HDA_MAX_NUM_INPUTS]; - - /* capture setup for dynamic dual-adc switch */ - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - - /* capture source */ - struct hda_input_mux input_mux; - unsigned int cur_mux[3]; - - /* channel model */ - /* min_channel_count contains the minimum channel count for primary - * outputs. When multi_ios is set, the channels can be configured - * between min_channel_count and (min_channel_count + multi_ios * 2). - * - * ext_channel_count contains the current channel count of the primary - * out. This varies in the range above. - * - * Meanwhile, const_channel_count is the channel count for all outputs - * including headphone and speakers. It's a constant value, and the - * PCM is set up as max(ext_channel_count, const_channel_count). - */ - int min_channel_count; /* min. channel count for primary out */ - int ext_channel_count; /* current channel count for primary */ - int const_channel_count; /* channel count for all */ - - /* PCM information */ - struct hda_pcm pcm_rec[3]; /* used in build_pcms() */ - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; - unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; - hda_nid_t shared_mic_vref_pin; - - /* DAC/ADC lists */ - int num_all_dacs; - hda_nid_t all_dacs[16]; - int num_all_adcs; - hda_nid_t all_adcs[AUTO_CFG_MAX_INS]; - - /* path list */ - struct snd_array paths; - - /* path indices */ - int out_paths[AUTO_CFG_MAX_OUTS]; - int hp_paths[AUTO_CFG_MAX_OUTS]; - int speaker_paths[AUTO_CFG_MAX_OUTS]; - int aamix_out_paths[3]; - int digout_paths[AUTO_CFG_MAX_OUTS]; - int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS]; - int loopback_paths[HDA_MAX_NUM_INPUTS]; - int loopback_merge_path; - int digin_path; - - /* auto-mic stuff */ - int am_num_entries; - struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; - - /* for pin sensing */ - /* current status; set in hda_geneic.c */ - unsigned int hp_jack_present:1; - unsigned int line_jack_present:1; - unsigned int speaker_muted:1; /* current status of speaker mute */ - unsigned int line_out_muted:1; /* current status of LO mute */ - - /* internal states of automute / autoswitch behavior */ - unsigned int auto_mic:1; - unsigned int automute_speaker:1; /* automute speaker outputs */ - unsigned int automute_lo:1; /* automute LO outputs */ - - /* capabilities detected by parser */ - unsigned int detect_hp:1; /* Headphone detection enabled */ - unsigned int detect_lo:1; /* Line-out detection enabled */ - unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ - unsigned int automute_lo_possible:1; /* there are line outs and HP */ - - /* additional parameters set by codec drivers */ - unsigned int master_mute:1; /* master mute over all */ - unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ - unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ - - /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */ - unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ - unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */ - - /* other parse behavior flags */ - unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ - unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ - unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ - unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ - unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ - unsigned int own_eapd_ctl:1; /* set EAPD by own function */ - unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ - unsigned int indep_hp:1; /* independent HP supported */ - unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ - unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ - unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ - unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */ - unsigned int power_down_unused:1; /* power down unused widgets */ - - /* other internal flags */ - unsigned int no_analog:1; /* digital I/O only */ - unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ - unsigned int indep_hp_enabled:1; /* independent HP enabled */ - unsigned int have_aamix_ctl:1; - - /* loopback mixing mode */ - bool aamix_mode; - - /* for virtual master */ - hda_nid_t vmaster_nid; - unsigned int vmaster_tlv[4]; - struct hda_vmaster_mute_hook vmaster_mute; -#ifdef CONFIG_PM - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[8]; -#endif - - /* multi-io */ - int multi_ios; - struct hda_multi_io multi_io[4]; - - /* hooks */ - void (*init_hook)(struct hda_codec *codec); - void (*automute_hook)(struct hda_codec *codec); - void (*cap_sync_hook)(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol); - - /* PCM hooks */ - void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action); - void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action); - - /* automute / autoswitch hooks */ - void (*hp_automute_hook)(struct hda_codec *codec, - struct hda_jack_tbl *tbl); - void (*line_automute_hook)(struct hda_codec *codec, - struct hda_jack_tbl *tbl); - void (*mic_autoswitch_hook)(struct hda_codec *codec, - struct hda_jack_tbl *tbl); -}; - -int snd_hda_gen_spec_init(struct hda_gen_spec *spec); -void snd_hda_gen_spec_free(struct hda_gen_spec *spec); - -int snd_hda_gen_init(struct hda_codec *codec); -void snd_hda_gen_free(struct hda_codec *codec); - -struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid); -int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); -struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx); -bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int anchor_nid, - struct nid_path *path); -struct nid_path * -snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int anchor_nid); -void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool add_aamix); - -struct snd_kcontrol_new * -snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, - const struct snd_kcontrol_new *temp); - -int snd_hda_gen_parse_auto_config(struct hda_codec *codec, - struct auto_pin_cfg *cfg); -int snd_hda_gen_build_controls(struct hda_codec *codec); -int snd_hda_gen_build_pcms(struct hda_codec *codec); - -/* standard jack event callbacks */ -void snd_hda_gen_hp_automute(struct hda_codec *codec, - struct hda_jack_tbl *jack); -void snd_hda_gen_line_automute(struct hda_codec *codec, - struct hda_jack_tbl *jack); -void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, - struct hda_jack_tbl *jack); -void snd_hda_gen_update_outputs(struct hda_codec *codec); - -#ifdef CONFIG_PM -int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid); -#endif - -#endif /* __SOUND_HDA_GENERIC_H */ diff --git a/trunk/sound/pci/hda/hda_hwdep.c b/trunk/sound/pci/hda/hda_hwdep.c index ce67608734b5..a5c9411bb367 100644 --- a/trunk/sound/pci/hda/hda_hwdep.c +++ b/trunk/sound/pci/hda/hda_hwdep.c @@ -148,7 +148,6 @@ int snd_hda_create_hwdep(struct hda_codec *codec) hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; #endif - mutex_init(&codec->user_mutex); snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32); snd_array_init(&codec->hints, sizeof(struct hda_hint), 32); snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16); @@ -347,14 +346,12 @@ static ssize_t init_verbs_show(struct device *dev, struct snd_hwdep *hwdep = dev_get_drvdata(dev); struct hda_codec *codec = hwdep->private_data; int i, len = 0; - mutex_lock(&codec->user_mutex); for (i = 0; i < codec->init_verbs.used; i++) { struct hda_verb *v = snd_array_elem(&codec->init_verbs, i); len += snprintf(buf + len, PAGE_SIZE - len, "0x%02x 0x%03x 0x%04x\n", v->nid, v->verb, v->param); } - mutex_unlock(&codec->user_mutex); return len; } @@ -367,16 +364,12 @@ static int parse_init_verbs(struct hda_codec *codec, const char *buf) return -EINVAL; if (!nid || !verb) return -EINVAL; - mutex_lock(&codec->user_mutex); v = snd_array_new(&codec->init_verbs); - if (!v) { - mutex_unlock(&codec->user_mutex); + if (!v) return -ENOMEM; - } v->nid = nid; v->verb = verb; v->param = param; - mutex_unlock(&codec->user_mutex); return 0; } @@ -399,13 +392,11 @@ static ssize_t hints_show(struct device *dev, struct snd_hwdep *hwdep = dev_get_drvdata(dev); struct hda_codec *codec = hwdep->private_data; int i, len = 0; - mutex_lock(&codec->user_mutex); for (i = 0; i < codec->hints.used; i++) { struct hda_hint *hint = snd_array_elem(&codec->hints, i); len += snprintf(buf + len, PAGE_SIZE - len, "%s = %s\n", hint->key, hint->val); } - mutex_unlock(&codec->user_mutex); return len; } @@ -440,7 +431,6 @@ static int parse_hints(struct hda_codec *codec, const char *buf) { char *key, *val; struct hda_hint *hint; - int err = 0; buf = skip_spaces(buf); if (!*buf || *buf == '#' || *buf == '\n') @@ -460,31 +450,26 @@ static int parse_hints(struct hda_codec *codec, const char *buf) val = skip_spaces(val); remove_trail_spaces(key); remove_trail_spaces(val); - mutex_lock(&codec->user_mutex); hint = get_hint(codec, key); if (hint) { /* replace */ kfree(hint->key); hint->key = key; hint->val = val; - goto unlock; + return 0; } /* allocate a new hint entry */ if (codec->hints.used >= MAX_HINTS) hint = NULL; else hint = snd_array_new(&codec->hints); - if (hint) { - hint->key = key; - hint->val = val; - } else { - err = -ENOMEM; - } - unlock: - mutex_unlock(&codec->user_mutex); - if (err) + if (!hint) { kfree(key); - return err; + return -ENOMEM; + } + hint->key = key; + hint->val = val; + return 0; } static ssize_t hints_store(struct device *dev, @@ -504,13 +489,11 @@ static ssize_t pin_configs_show(struct hda_codec *codec, char *buf) { int i, len = 0; - mutex_lock(&codec->user_mutex); for (i = 0; i < list->used; i++) { struct hda_pincfg *pin = snd_array_elem(list, i); len += sprintf(buf + len, "0x%02x 0x%08x\n", pin->nid, pin->cfg); } - mutex_unlock(&codec->user_mutex); return len; } @@ -545,16 +528,13 @@ static ssize_t driver_pin_configs_show(struct device *dev, static int parse_user_pin_configs(struct hda_codec *codec, const char *buf) { - int nid, cfg, err; + int nid, cfg; if (sscanf(buf, "%i %i", &nid, &cfg) != 2) return -EINVAL; if (!nid) return -EINVAL; - mutex_lock(&codec->user_mutex); - err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); - mutex_unlock(&codec->user_mutex); - return err; + return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg); } static ssize_t user_pin_configs_store(struct device *dev, @@ -620,50 +600,19 @@ EXPORT_SYMBOL_HDA(snd_hda_get_hint); int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) { - const char *p; - int ret; - - mutex_lock(&codec->user_mutex); - p = snd_hda_get_hint(codec, key); + const char *p = snd_hda_get_hint(codec, key); if (!p || !*p) - ret = -ENOENT; - else { - switch (toupper(*p)) { - case 'T': /* true */ - case 'Y': /* yes */ - case '1': - ret = 1; - break; - default: - ret = 0; - break; - } + return -ENOENT; + switch (toupper(*p)) { + case 'T': /* true */ + case 'Y': /* yes */ + case '1': + return 1; } - mutex_unlock(&codec->user_mutex); - return ret; + return 0; } EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint); -int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) -{ - const char *p; - unsigned long val; - int ret; - - mutex_lock(&codec->user_mutex); - p = snd_hda_get_hint(codec, key); - if (!p) - ret = -ENOENT; - else if (strict_strtoul(p, 0, &val)) - ret = -EINVAL; - else { - *valp = val; - ret = 0; - } - mutex_unlock(&codec->user_mutex); - return ret; -} -EXPORT_SYMBOL_HDA(snd_hda_get_int_hint); #endif /* CONFIG_SND_HDA_RECONFIG */ #ifdef CONFIG_SND_HDA_PATCH_LOADER diff --git a/trunk/sound/pci/hda/hda_intel.c b/trunk/sound/pci/hda/hda_intel.c index 3d8df7c6a3b9..0b6aebacc56b 100644 --- a/trunk/sound/pci/hda/hda_intel.c +++ b/trunk/sound/pci/hda/hda_intel.c @@ -134,8 +134,8 @@ MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " * this may give more power-saving, but will take longer time to * wake up. */ -static int power_save_controller = -1; -module_param(power_save_controller, bint, 0644); +static bool power_save_controller = 1; +module_param(power_save_controller, bool, 0644); MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); #endif /* CONFIG_PM */ @@ -797,7 +797,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) { struct azx *chip = bus->private_data; unsigned int addr = azx_command_addr(val); - unsigned int wp, rp; + unsigned int wp; spin_lock_irq(&chip->reg_lock); @@ -806,18 +806,11 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) if (wp == 0xffff) { /* something wrong, controller likely turned to D3 */ spin_unlock_irq(&chip->reg_lock); - return -EIO; + return -1; } wp++; wp %= ICH6_MAX_CORB_ENTRIES; - rp = azx_readw(chip, CORBRP); - if (wp == rp) { - /* oops, it's full */ - spin_unlock_irq(&chip->reg_lock); - return -EAGAIN; - } - chip->rirb.cmds[addr]++; chip->corb.buf[wp] = cpu_to_le32(val); azx_writel(chip, CORBWP, wp); @@ -2721,8 +2714,6 @@ static int azx_runtime_idle(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; - if (power_save_controller > 0) - return 0; if (!power_save_controller || !(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) return -EBUSY; @@ -3615,8 +3606,6 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { { PCI_DEVICE(0x8086, 0x9c21), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ - { PCI_DEVICE(0x8086, 0x0a0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, { PCI_DEVICE(0x8086, 0x0c0c), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, { PCI_DEVICE(0x8086, 0x0d0c), diff --git a/trunk/sound/pci/hda/hda_jack.c b/trunk/sound/pci/hda/hda_jack.c index 1d035efeff4f..6e9f57bbe667 100644 --- a/trunk/sound/pci/hda/hda_jack.c +++ b/trunk/sound/pci/hda/hda_jack.c @@ -29,8 +29,7 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & AC_DEFCFG_MISC_NO_PRESENCE) return false; - if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) && - !codec->jackpoll_interval) + if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) return false; return true; } @@ -40,7 +39,6 @@ EXPORT_SYMBOL_HDA(is_jack_detectable); static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) { u32 pincap; - u32 val; if (!codec->no_trigger_sense) { pincap = snd_hda_query_pin_caps(codec, nid); @@ -48,11 +46,8 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); } - val = snd_hda_codec_read(codec, nid, 0, + return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); - if (codec->inv_jack_detect) - val ^= AC_PINSENSE_PRESENCE; - return val; } /** diff --git a/trunk/sound/pci/hda/hda_local.h b/trunk/sound/pci/hda/hda_local.h index 05f1d594d17b..4b40a5e7a8f5 100644 --- a/trunk/sound/pci/hda/hda_local.h +++ b/trunk/sound/pci/hda/hda_local.h @@ -133,11 +133,9 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); -int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val); -int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx, int mask, int val); +#ifdef CONFIG_PM void snd_hda_codec_resume_amp(struct hda_codec *codec); +#endif void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv); @@ -385,61 +383,6 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, int snd_hda_add_new_ctls(struct hda_codec *codec, const struct snd_kcontrol_new *knew); -/* - * Fix-up pin default configurations and add default verbs - */ - -struct hda_pintbl { - hda_nid_t nid; - u32 val; -}; - -struct hda_model_fixup { - const int id; - const char *name; -}; - -struct hda_fixup { - int type; - bool chained:1; /* call the chained fixup(s) after this */ - bool chained_before:1; /* call the chained fixup(s) before this */ - int chain_id; - union { - const struct hda_pintbl *pins; - const struct hda_verb *verbs; - void (*func)(struct hda_codec *codec, - const struct hda_fixup *fix, - int action); - } v; -}; - -/* fixup types */ -enum { - HDA_FIXUP_INVALID, - HDA_FIXUP_PINS, - HDA_FIXUP_VERBS, - HDA_FIXUP_FUNC, - HDA_FIXUP_PINCTLS, -}; - -/* fixup action definitions */ -enum { - HDA_FIXUP_ACT_PRE_PROBE, - HDA_FIXUP_ACT_PROBE, - HDA_FIXUP_ACT_INIT, - HDA_FIXUP_ACT_BUILD, -}; - -int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list); -void snd_hda_apply_verbs(struct hda_codec *codec); -void snd_hda_apply_pincfgs(struct hda_codec *codec, - const struct hda_pintbl *cfg); -void snd_hda_apply_fixup(struct hda_codec *codec, int action); -void snd_hda_pick_fixup(struct hda_codec *codec, - const struct hda_model_fixup *models, - const struct snd_pci_quirk *quirk, - const struct hda_fixup *fixlist); - /* * unsolicited event handler */ @@ -488,8 +431,6 @@ struct hda_bus_unsolicited { #define PIN_HP_AMP (AC_PINCTL_HP_EN) unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin); -unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, - hda_nid_t pin, unsigned int val); int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool cached); @@ -529,10 +470,6 @@ snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin, return _snd_hda_set_pin_ctl(codec, pin, val, true); } -int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, - unsigned int val); - /* * get widget capabilities */ @@ -615,7 +552,6 @@ static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_RECONFIG const char *snd_hda_get_hint(struct hda_codec *codec, const char *key); int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key); -int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp); #else static inline const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) @@ -628,12 +564,6 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) { return -ENOENT; } - -static inline -int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) -{ - return -ENOENT; -} #endif /* @@ -657,19 +587,6 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, hda_nid_t nid); -/* check whether the actual power state matches with the target state */ -static inline bool -snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int target_state) -{ - unsigned int state = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0); - if (state & AC_PWRST_ERROR) - return true; - state = (state >> 4) & 0x0f; - return (state != target_state); -} - /* * AMP control callbacks */ @@ -679,8 +596,7 @@ snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid, #define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) #define get_amp_direction_(pv) (((pv) >> 18) & 0x1) #define get_amp_direction(kc) get_amp_direction_((kc)->private_value) -#define get_amp_index_(pv) (((pv) >> 19) & 0xf) -#define get_amp_index(kc) get_amp_index_((kc)->private_value) +#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) #define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) #define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1) diff --git a/trunk/sound/pci/hda/hda_proc.c b/trunk/sound/pci/hda/hda_proc.c index 5e02f26606b6..045e5d32f5de 100644 --- a/trunk/sound/pci/hda/hda_proc.c +++ b/trunk/sound/pci/hda/hda_proc.c @@ -138,17 +138,16 @@ static void print_amp_vals(struct snd_info_buffer *buffer, dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; for (i = 0; i < indices; i++) { snd_iprintf(buffer, " ["); - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_LEFT | dir | i); - snd_iprintf(buffer, "0x%02x", val); if (stereo) { val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_RIGHT | dir | i); - snd_iprintf(buffer, " 0x%02x", val); + AC_AMP_GET_LEFT | dir | i); + snd_iprintf(buffer, "0x%02x ", val); } - snd_iprintf(buffer, "]"); + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_AMP_GAIN_MUTE, + AC_AMP_GET_RIGHT | dir | i); + snd_iprintf(buffer, "0x%02x]", val); } snd_iprintf(buffer, "\n"); } @@ -604,8 +603,6 @@ static void print_codec_info(struct snd_info_entry *entry, print_amp_caps(buffer, codec, codec->afg, HDA_INPUT); snd_iprintf(buffer, "Default Amp-Out caps: "); print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT); - snd_iprintf(buffer, "State of AFG node 0x%02x:\n", codec->afg); - print_power_state(buffer, codec, codec->afg); nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); if (! nid || nodes < 0) { diff --git a/trunk/sound/pci/hda/patch_analog.c b/trunk/sound/pci/hda/patch_analog.c index df8014b27596..89fc5030ec79 100644 --- a/trunk/sound/pci/hda/patch_analog.c +++ b/trunk/sound/pci/hda/patch_analog.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -30,24 +31,11 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" - -#define ENABLE_AD_STATIC_QUIRKS struct ad198x_spec { - struct hda_gen_spec gen; - - /* for auto parser */ - int smux_paths[4]; - unsigned int cur_smux; - hda_nid_t eapd_nid; - - unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - hda_nid_t beep_dev_nid; - -#ifdef ENABLE_AD_STATIC_QUIRKS const struct snd_kcontrol_new *mixers[6]; int num_mixers; + unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ const struct hda_verb *init_verbs[6]; /* initialization verbs * don't forget NULL termination! */ @@ -61,6 +49,11 @@ struct ad198x_spec { unsigned int cur_eapd; unsigned int need_dac_fix; + const hda_nid_t *alt_dac_nid; + const struct hda_pcm_stream *stream_analog_alt_playback; + int independent_hp; + int num_active_streams; + /* capture */ unsigned int num_adc_nids; const hda_nid_t *adc_nids; @@ -80,8 +73,15 @@ struct ad198x_spec { unsigned int spdif_route; + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + struct hda_input_mux private_imux; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + unsigned int jack_present: 1; unsigned int inv_jack_detect: 1;/* inverted jack-detection */ + unsigned int inv_eapd: 1; /* inverted EAPD implementation */ unsigned int analog_beep: 1; /* analog beep input present */ unsigned int avoid_init_slave_vol:1; @@ -92,10 +92,8 @@ struct ad198x_spec { hda_nid_t vmaster_nid; const char * const *slave_vols; const char * const *slave_sws; -#endif /* ENABLE_AD_STATIC_QUIRKS */ }; -#ifdef ENABLE_AD_STATIC_QUIRKS /* * input MUX handling (common part) */ @@ -151,7 +149,8 @@ static const char * const ad1988_6stack_fp_slave_pfxs[] = { "Front", "Surround", "Center", "LFE", "Side", "IEC958", NULL }; -#endif /* ENABLE_AD_STATIC_QUIRKS */ + +static void ad198x_free_kctls(struct hda_codec *codec); #ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ @@ -173,34 +172,6 @@ static const struct snd_kcontrol_new ad_beep2_mixer[] = { #define set_beep_amp(spec, nid, idx, dir) /* NOP */ #endif -#ifdef CONFIG_SND_HDA_INPUT_BEEP -static int create_beep_ctls(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - const struct snd_kcontrol_new *knew; - - if (!spec->beep_amp) - return 0; - - knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; - for ( ; knew->name; knew++) { - int err; - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - return 0; -} -#else -#define create_beep_ctls(codec) 0 -#endif - -#ifdef ENABLE_AD_STATIC_QUIRKS static int ad198x_build_controls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -232,9 +203,22 @@ static int ad198x_build_controls(struct hda_codec *codec) } /* create beep controls if needed */ - err = create_beep_ctls(codec); - if (err < 0) - return err; +#ifdef CONFIG_SND_HDA_INPUT_BEEP + if (spec->beep_amp) { + const struct snd_kcontrol_new *knew; + knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; + for ( ; knew->name; knew++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + } + } +#endif /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { @@ -260,6 +244,8 @@ static int ad198x_build_controls(struct hda_codec *codec) return err; } + ad198x_free_kctls(codec); /* no longer needed */ + /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); if (!kctl) @@ -291,6 +277,72 @@ static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) } #endif +static void activate_ctl(struct hda_codec *codec, const char *name, int active) +{ + struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); + if (ctl) { + ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->vd[0].access |= active ? 0 : + SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl->vd[0].access |= active ? + SNDRV_CTL_ELEM_ACCESS_WRITE : 0; + snd_ctl_notify(codec->bus->card, + SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); + } +} + +static void set_stream_active(struct hda_codec *codec, bool active) +{ + struct ad198x_spec *spec = codec->spec; + if (active) + spec->num_active_streams++; + else + spec->num_active_streams--; + activate_ctl(codec, "Independent HP", spec->num_active_streams == 0); +} + +static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { "OFF", "ON", NULL}; + int index; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + index = uinfo->value.enumerated.item; + if (index >= 2) + index = 1; + strcpy(uinfo->value.enumerated.name, texts[index]); + return 0; +} + +static int ad1988_independent_hp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->independent_hp; + return 0; +} + +static int ad1988_independent_hp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + unsigned int select = ucontrol->value.enumerated.item[0]; + if (spec->independent_hp != select) { + spec->independent_hp = select; + if (spec->independent_hp) + spec->multiout.hp_nid = 0; + else + spec->multiout.hp_nid = spec->alt_dac_nid[0]; + return 1; + } + return 0; +} + /* * Analog playback callbacks */ @@ -299,8 +351,15 @@ 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, + int err; + set_stream_active(codec, true); + err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); + if (err < 0) { + set_stream_active(codec, false); + return err; + } + return 0; } static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -322,6 +381,43 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } +static int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_active(codec, false); + return 0; +} + +static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ad198x_spec *spec = codec->spec; + if (!spec->independent_hp) + return -EBUSY; + set_stream_active(codec, true); + return 0; +} + +static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_active(codec, false); + return 0; +} + +static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = ad1988_alt_playback_pcm_open, + .close = ad1988_alt_playback_pcm_close + }, +}; + /* * Digital out */ @@ -395,6 +491,7 @@ static const struct hda_pcm_stream ad198x_pcm_analog_playback = { .open = ad198x_playback_pcm_open, .prepare = ad198x_playback_pcm_prepare, .cleanup = ad198x_playback_pcm_cleanup, + .close = ad198x_playback_pcm_close }, }; @@ -459,19 +556,43 @@ static int ad198x_build_pcms(struct hda_codec *codec) } } + if (spec->alt_dac_nid && spec->stream_analog_alt_playback) { + codec->num_pcms++; + info = spec->pcm_rec + 2; + info->name = "AD198x Headphone"; + info->pcm_type = HDA_PCM_TYPE_AUDIO; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *spec->stream_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->alt_dac_nid[0]; + } + return 0; } -#endif /* ENABLE_AD_STATIC_QUIRKS */ + +static void ad198x_free_kctls(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +} static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, hda_nid_t hp) { + struct ad198x_spec *spec = codec->spec; if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD) snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE, - !codec->inv_eapd ? 0x00 : 0x02); + !spec->inv_eapd ? 0x00 : 0x02); if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD) snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE, - !codec->inv_eapd ? 0x00 : 0x02); + !spec->inv_eapd ? 0x00 : 0x02); } static void ad198x_power_eapd(struct hda_codec *codec) @@ -515,7 +636,7 @@ static void ad198x_free(struct hda_codec *codec) if (!spec) return; - snd_hda_gen_spec_free(&spec->gen); + ad198x_free_kctls(codec); kfree(spec); snd_hda_detach_beep_device(codec); } @@ -528,7 +649,6 @@ static int ad198x_suspend(struct hda_codec *codec) } #endif -#ifdef ENABLE_AD_STATIC_QUIRKS static const struct hda_codec_ops ad198x_patch_ops = { .build_controls = ad198x_build_controls, .build_pcms = ad198x_build_pcms, @@ -553,7 +673,7 @@ static int ad198x_eapd_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; - if (codec->inv_eapd) + if (spec->inv_eapd) ucontrol->value.integer.value[0] = ! spec->cur_eapd; else ucontrol->value.integer.value[0] = spec->cur_eapd; @@ -568,7 +688,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol, hda_nid_t nid = kcontrol->private_value & 0xff; unsigned int eapd; eapd = !!ucontrol->value.integer.value[0]; - if (codec->inv_eapd) + if (spec->inv_eapd) eapd = !eapd; if (eapd == spec->cur_eapd) return 0; @@ -585,75 +705,12 @@ static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -#endif /* ENABLE_AD_STATIC_QUIRKS */ -/* - * Automatic parse of I/O pins from the BIOS configuration - */ - -static int ad198x_auto_build_controls(struct hda_codec *codec) -{ - int err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - err = create_beep_ctls(codec); - if (err < 0) - return err; - return 0; -} - -static const struct hda_codec_ops ad198x_auto_patch_ops = { - .build_controls = ad198x_auto_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = ad198x_free, - .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM - .check_power_status = snd_hda_gen_check_power_status, - .suspend = ad198x_suspend, -#endif - .reboot_notify = ad198x_shutup, -}; - - -static int ad198x_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int err; - - codec->spdif_status_reset = 1; - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - spec->gen.indep_hp = 1; - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - return err; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - return err; - - if (spec->beep_dev_nid) { - err = snd_hda_attach_beep_device(codec, spec->beep_dev_nid); - if (err < 0) - return err; - } - - codec->patch_ops = ad198x_auto_patch_ops; - - return 0; -} - /* * AD1986A specific */ -#ifdef ENABLE_AD_STATIC_QUIRKS #define AD1986A_SPDIF_OUT 0x02 #define AD1986A_FRONT_DAC 0x03 #define AD1986A_SURR_DAC 0x04 @@ -938,7 +995,15 @@ static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + long *valp = ucontrol->value.integer.value; + int change; + + change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp[0] ? 0 : HDA_AMP_MUTE); + change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp[1] ? 0 : HDA_AMP_MUTE); if (change) ad1986a_update_hp(codec); return change; @@ -1111,7 +1176,6 @@ static int ad1986a_samsung_p50_init(struct hda_codec *codec) /* models */ enum { - AD1986A_AUTO, AD1986A_6STACK, AD1986A_3STACK, AD1986A_LAPTOP, @@ -1124,7 +1188,6 @@ enum { }; static const char * const ad1986a_models[AD1986A_MODELS] = { - [AD1986A_AUTO] = "auto", [AD1986A_6STACK] = "6stack", [AD1986A_3STACK] = "3stack", [AD1986A_LAPTOP] = "laptop", @@ -1182,7 +1245,6 @@ static int is_jack_available(struct hda_codec *codec, hda_nid_t nid) unsigned int conf = snd_hda_codec_get_pincfg(codec, nid); return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; } -#endif /* ENABLE_AD_STATIC_QUIRKS */ static int alloc_ad_spec(struct hda_codec *codec) { @@ -1192,97 +1254,15 @@ static int alloc_ad_spec(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); return 0; } -/* - * AD1986A fixup codes - */ - -/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */ -static void ad_fixup_inv_jack_detect(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - codec->inv_jack_detect = 1; -} - -enum { - AD1986A_FIXUP_INV_JACK_DETECT, -}; - -static const struct hda_fixup ad1986a_fixups[] = { - [AD1986A_FIXUP_INV_JACK_DETECT] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad_fixup_inv_jack_detect, - }, -}; - -static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { - SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT), - {} -}; - -/* - */ -static int ad1986a_parse_auto_config(struct hda_codec *codec) -{ - int err; - struct ad198x_spec *spec; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - /* AD1986A has the inverted EAPD implementation */ - codec->inv_eapd = 1; - - spec->gen.mixer_nid = 0x07; - spec->beep_dev_nid = 0x19; - set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); - - /* AD1986A has a hardware problem that it can't share a stream - * with multiple output pins. The copy of front to surrounds - * causes noisy or silent outputs at a certain timing, e.g. - * changing the volume. - * So, let's disable the shared stream. - */ - spec->gen.multiout.no_share_stream = 1; - - snd_hda_pick_fixup(codec, NULL, ad1986a_fixup_tbl, ad1986a_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = ad198x_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; -} - -#ifdef ENABLE_AD_STATIC_QUIRKS static int patch_ad1986a(struct hda_codec *codec) { struct ad198x_spec *spec; int err, board_config; - board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, - ad1986a_models, - ad1986a_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1986A_AUTO; - } - - if (board_config == AD1986A_AUTO) - return ad1986a_parse_auto_config(codec); - err = alloc_ad_spec(codec); if (err < 0) return err; @@ -1311,11 +1291,14 @@ static int patch_ad1986a(struct hda_codec *codec) spec->loopback.amplist = ad1986a_loopbacks; #endif spec->vmaster_nid = 0x1b; - codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ + spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ codec->patch_ops = ad198x_patch_ops; /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, + ad1986a_models, + ad1986a_cfg_tbl); switch (board_config) { case AD1986A_3STACK: spec->num_mixers = 2; @@ -1426,15 +1409,11 @@ static int patch_ad1986a(struct hda_codec *codec) return 0; } -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1986a ad1986a_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ /* * AD1983 specific */ -#ifdef ENABLE_AD_STATIC_QUIRKS #define AD1983_SPDIF_OUT 0x02 #define AD1983_DAC 0x03 #define AD1983_ADC 0x04 @@ -1575,137 +1554,11 @@ static const struct hda_amp_list ad1983_loopbacks[] = { }; #endif -/* models */ -enum { - AD1983_AUTO, - AD1983_BASIC, - AD1983_MODELS -}; - -static const char * const ad1983_models[AD1983_MODELS] = { - [AD1983_AUTO] = "auto", - [AD1983_BASIC] = "basic", -}; -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - -/* - * SPDIF mux control for AD1983 auto-parser - */ -static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - static const char * const texts2[] = { "PCM", "ADC" }; - static const char * const texts3[] = { "PCM", "ADC1", "ADC2" }; - hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; - int num_conns = snd_hda_get_num_conns(codec, dig_out); - - if (num_conns == 2) - return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2); - else if (num_conns == 3) - return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); - else - return -EINVAL; -} - -static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->cur_smux; - return 0; -} - -static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int val = ucontrol->value.enumerated.item[0]; - hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; - int num_conns = snd_hda_get_num_conns(codec, dig_out); - - if (val >= num_conns) - return -EINVAL; - if (spec->cur_smux == val) - return 0; - spec->cur_smux = val; - snd_hda_codec_write_cache(codec, dig_out, 0, - AC_VERB_SET_CONNECT_SEL, val); - return 1; -} - -static struct snd_kcontrol_new ad1983_auto_smux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - .info = ad1983_auto_smux_enum_info, - .get = ad1983_auto_smux_enum_get, - .put = ad1983_auto_smux_enum_put, -}; - -static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; - int num_conns; - - if (!dig_out) - return 0; - num_conns = snd_hda_get_num_conns(codec, dig_out); - if (num_conns != 2 && num_conns != 3) - return 0; - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer)) - return -ENOMEM; - return 0; -} - -static int ad1983_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - err = ad198x_parse_auto_config(codec); - if (err < 0) - goto error; - err = ad1983_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - return 0; - - error: - ad198x_free(codec); - return err; -} - -#ifdef ENABLE_AD_STATIC_QUIRKS static int patch_ad1983(struct hda_codec *codec) { struct ad198x_spec *spec; - int board_config; int err; - board_config = snd_hda_check_board_config(codec, AD1983_MODELS, - ad1983_models, NULL); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1983_AUTO; - } - - if (board_config == AD1983_AUTO) - return ad1983_parse_auto_config(codec); - err = alloc_ad_spec(codec); if (err < 0) return err; @@ -1743,16 +1596,12 @@ static int patch_ad1983(struct hda_codec *codec) return 0; } -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1983 ad1983_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ /* * AD1981 HD specific */ -#ifdef ENABLE_AD_STATIC_QUIRKS #define AD1981_SPDIF_OUT 0x02 #define AD1981_DAC 0x03 #define AD1981_ADC 0x04 @@ -2083,7 +1932,6 @@ static const struct hda_input_mux ad1981_thinkpad_capture_source = { /* models */ enum { - AD1981_AUTO, AD1981_BASIC, AD1981_HP, AD1981_THINKPAD, @@ -2092,7 +1940,6 @@ enum { }; static const char * const ad1981_models[AD1981_MODELS] = { - [AD1981_AUTO] = "auto", [AD1981_HP] = "hp", [AD1981_THINKPAD] = "thinkpad", [AD1981_BASIC] = "basic", @@ -2111,122 +1958,12 @@ static const struct snd_pci_quirk ad1981_cfg_tbl[] = { SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), {} }; -#endif /* ENABLE_AD_STATIC_QUIRKS */ - -/* follow EAPD via vmaster hook */ -static void ad_vmaster_eapd_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - struct ad198x_spec *spec = codec->spec; - snd_hda_codec_update_cache(codec, spec->eapd_nid, 0, - AC_VERB_SET_EAPD_BTLENABLE, - enabled ? 0x02 : 0x00); -} - -static void ad1981_fixup_hp_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct ad198x_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; - spec->eapd_nid = 0x05; - } -} - -/* set the upper-limit for mixer amp to 0dB for avoiding the possible - * damage by overloading - */ -static void ad1981_fixup_amp_override(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); -} - -enum { - AD1981_FIXUP_AMP_OVERRIDE, - AD1981_FIXUP_HP_EAPD, -}; - -static const struct hda_fixup ad1981_fixups[] = { - [AD1981_FIXUP_AMP_OVERRIDE] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1981_fixup_amp_override, - }, - [AD1981_FIXUP_HP_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1981_fixup_hp_eapd, - .chained = true, - .chain_id = AD1981_FIXUP_AMP_OVERRIDE, - }, -}; - -static const struct snd_pci_quirk ad1981_fixup_tbl[] = { - SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), - SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), - /* HP nx6320 (reversed SSID, H/W bug) */ - SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD), - {} -}; - -static int ad1981_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return -ENOMEM; - spec = codec->spec; - - spec->gen.mixer_nid = 0x0e; - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); - - snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = ad198x_parse_auto_config(codec); - if (err < 0) - goto error; - err = ad1983_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - ad198x_free(codec); - return err; -} - -#ifdef ENABLE_AD_STATIC_QUIRKS static int patch_ad1981(struct hda_codec *codec) { struct ad198x_spec *spec; int err, board_config; - board_config = snd_hda_check_board_config(codec, AD1981_MODELS, - ad1981_models, - ad1981_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1981_AUTO; - } - - if (board_config == AD1981_AUTO) - return ad1981_parse_auto_config(codec); - err = alloc_ad_spec(codec); if (err < 0) return -ENOMEM; @@ -2260,6 +1997,9 @@ static int patch_ad1981(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1981_MODELS, + ad1981_models, + ad1981_cfg_tbl); switch (board_config) { case AD1981_HP: spec->mixers[0] = ad1981_hp_mixers; @@ -2309,9 +2049,6 @@ static int patch_ad1981(struct hda_codec *codec) return 0; } -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1981 ad1981_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ /* @@ -2400,16 +2137,15 @@ static int patch_ad1981(struct hda_codec *codec) */ -#ifdef ENABLE_AD_STATIC_QUIRKS /* models */ enum { - AD1988_AUTO, AD1988_6STACK, AD1988_6STACK_DIG, AD1988_3STACK, AD1988_3STACK_DIG, AD1988_LAPTOP, AD1988_LAPTOP_DIG, + AD1988_AUTO, AD1988_MODEL_LAST, }; @@ -2514,6 +2250,17 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, return err; } +static const struct snd_kcontrol_new ad1988_hp_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Independent HP", + .info = ad1988_independent_hp_info, + .get = ad1988_independent_hp_get, + .put = ad1988_independent_hp_put, + }, + { } /* end */ +}; + /* 6-stack mode */ static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), @@ -3064,197 +2811,433 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) if (snd_hda_jack_detect(codec, 0x11)) snd_hda_sequence_write(codec, ad1988_laptop_hp_on); else - snd_hda_sequence_write(codec, ad1988_laptop_hp_off); -} + snd_hda_sequence_write(codec, ad1988_laptop_hp_off); +} + +#ifdef CONFIG_PM +static const struct hda_amp_list ad1988_loopbacks[] = { + { 0x20, HDA_INPUT, 0 }, /* Front Mic */ + { 0x20, HDA_INPUT, 1 }, /* Line */ + { 0x20, HDA_INPUT, 4 }, /* Mic */ + { 0x20, HDA_INPUT, 6 }, /* CD */ + { } /* end */ +}; +#endif + +/* + * Automatic parse of I/O pins from the BIOS configuration + */ + +enum { + AD_CTL_WIDGET_VOL, + AD_CTL_WIDGET_MUTE, + AD_CTL_BIND_MUTE, +}; +static const struct snd_kcontrol_new ad1988_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_BIND_MUTE(NULL, 0, 0, 0), +}; + +/* add dynamic controls */ +static int add_control(struct ad198x_spec *spec, int type, const char *name, + unsigned long val) +{ + struct snd_kcontrol_new *knew; + + knew = snd_array_new(&spec->kctls); + if (!knew) + return -ENOMEM; + *knew = ad1988_control_templates[type]; + knew->name = kstrdup(name, GFP_KERNEL); + if (! knew->name) + return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + knew->private_value = val; + return 0; +} + +#define AD1988_PIN_CD_NID 0x18 +#define AD1988_PIN_BEEP_NID 0x10 + +static const hda_nid_t ad1988_mixer_nids[8] = { + /* A B C D E F G H */ + 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28 +}; + +static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx) +{ + static const hda_nid_t idx_to_dac[8] = { + /* A B C D E F G H */ + 0x03, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a + }; + static const hda_nid_t idx_to_dac_rev2[8] = { + /* A B C D E F G H */ + 0x03, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06 + }; + if (is_rev2(codec)) + return idx_to_dac_rev2[idx]; + else + return idx_to_dac[idx]; +} -#ifdef CONFIG_PM -static const struct hda_amp_list ad1988_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Line */ - { 0x20, HDA_INPUT, 4 }, /* Mic */ - { 0x20, HDA_INPUT, 6 }, /* CD */ - { } /* end */ +static const hda_nid_t ad1988_boost_nids[8] = { + 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0 }; -#endif -#endif /* ENABLE_AD_STATIC_QUIRKS */ -static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int ad1988_pin_idx(hda_nid_t nid) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - static const char * const texts[] = { - "PCM", "ADC1", "ADC2", "ADC3", + static const hda_nid_t ad1988_io_pins[8] = { + 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25 }; - int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1; - if (num_conns > 4) - num_conns = 4; - return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts); + int i; + for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++) + if (ad1988_io_pins[i] == nid) + return i; + return 0; /* should be -1 */ } -static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int ad1988_pin_to_loopback_idx(hda_nid_t nid) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; + static const int loopback_idx[8] = { + 2, 0, 1, 3, 4, 5, 1, 4 + }; + switch (nid) { + case AD1988_PIN_CD_NID: + return 6; + default: + return loopback_idx[ad1988_pin_idx(nid)]; + } +} - ucontrol->value.enumerated.item[0] = spec->cur_smux; - return 0; +static int ad1988_pin_to_adc_idx(hda_nid_t nid) +{ + static const int adc_idx[8] = { + 0, 1, 2, 8, 4, 3, 6, 7 + }; + switch (nid) { + case AD1988_PIN_CD_NID: + return 5; + default: + return adc_idx[ad1988_pin_idx(nid)]; + } } -static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/* fill in the dac_nids table from the parsed pin configuration */ +static int ad1988_auto_fill_dac_nids(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; - unsigned int val = ucontrol->value.enumerated.item[0]; - struct nid_path *path; - int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1; + int i, idx; - if (val >= num_conns) - return -EINVAL; - if (spec->cur_smux == val) - return 0; + spec->multiout.dac_nids = spec->private_dac_nids; - mutex_lock(&codec->control_mutex); - codec->cached_write = 1; - path = snd_hda_get_path_from_idx(codec, - spec->smux_paths[spec->cur_smux]); - if (path) - snd_hda_activate_path(codec, path, false, true); - path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]); - if (path) - snd_hda_activate_path(codec, path, true, true); - spec->cur_smux = val; - codec->cached_write = 0; - mutex_unlock(&codec->control_mutex); - snd_hda_codec_flush_cache(codec); /* flush the updates */ - return 1; + /* check the pins hardwired to audio widget */ + for (i = 0; i < cfg->line_outs; i++) { + idx = ad1988_pin_idx(cfg->line_out_pins[i]); + spec->private_dac_nids[i] = ad1988_idx_to_dac(codec, idx); + } + spec->multiout.num_dacs = cfg->line_outs; + return 0; } -static struct snd_kcontrol_new ad1988_auto_smux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - .info = ad1988_auto_smux_enum_info, - .get = ad1988_auto_smux_enum_get, - .put = ad1988_auto_smux_enum_put, -}; +/* add playback controls from the parsed DAC table */ +static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char * const chname[4] = { + "Front", "Surround", NULL /*CLFE*/, "Side" + }; + hda_nid_t nid; + int i, err; -static int ad1988_auto_init(struct hda_codec *codec) + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t dac = spec->multiout.dac_nids[i]; + if (! dac) + continue; + nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])]; + if (i == 2) { + /* Center/LFE */ + err = add_control(spec, AD_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = add_control(spec, AD_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = add_control(spec, AD_CTL_BIND_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT)); + if (err < 0) + return err; + err = add_control(spec, AD_CTL_BIND_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = add_control(spec, AD_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = add_control(spec, AD_CTL_BIND_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +/* add playback controls for speaker and HP outputs */ +static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, + const char *pfx) { struct ad198x_spec *spec = codec->spec; - int i, err; + hda_nid_t nid; + int i, idx, err; + char name[32]; - err = snd_hda_gen_init(codec); - if (err < 0) - return err; - if (!spec->gen.autocfg.dig_outs) + if (! pin) return 0; - for (i = 0; i < 4; i++) { - struct nid_path *path; - path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]); - if (path) - snd_hda_activate_path(codec, path, path->active, false); + idx = ad1988_pin_idx(pin); + nid = ad1988_idx_to_dac(codec, idx); + /* check whether the corresponding DAC was already taken */ + for (i = 0; i < spec->autocfg.line_outs; i++) { + hda_nid_t pin = spec->autocfg.line_out_pins[i]; + hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin)); + if (dac == nid) + break; + } + if (i >= spec->autocfg.line_outs) { + /* specify the DAC as the extra output */ + if (!spec->multiout.hp_nid) + spec->multiout.hp_nid = nid; + else + spec->multiout.extra_out_nid[0] = nid; + /* control HP volume/switch on the output mixer amp */ + sprintf(name, "%s Playback Volume", pfx); + err = add_control(spec, AD_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; } + nid = ad1988_mixer_nids[idx]; + sprintf(name, "%s Playback Switch", pfx); + if ((err = add_control(spec, AD_CTL_BIND_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0) + return err; + return 0; +} + +/* create input playback/capture controls for the given pin */ +static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin, + const char *ctlname, int ctlidx, int boost) +{ + char name[32]; + int err, idx; + sprintf(name, "%s Playback Volume", ctlname); + idx = ad1988_pin_to_loopback_idx(pin); + if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) + return err; + sprintf(name, "%s Playback Switch", ctlname); + if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) + return err; + if (boost) { + hda_nid_t bnid; + idx = ad1988_pin_idx(pin); + bnid = ad1988_boost_nids[idx]; + if (bnid) { + sprintf(name, "%s Boost Volume", ctlname); + return add_control(spec, AD_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT)); + + } + } return 0; } -static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) +/* create playback/capture controls for input pins */ +static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) { struct ad198x_spec *spec = codec->spec; - int i, num_conns; - /* we create four static faked paths, since AD codecs have odd - * widget connections regarding the SPDIF out source - */ - static struct nid_path fake_paths[4] = { - { - .depth = 3, - .path = { 0x02, 0x1d, 0x1b }, - .idx = { 0, 0, 0 }, - .multi = { 0, 0, 0 }, - }, - { - .depth = 4, - .path = { 0x08, 0x0b, 0x1d, 0x1b }, - .idx = { 0, 0, 1, 0 }, - .multi = { 0, 1, 0, 0 }, - }, - { - .depth = 4, - .path = { 0x09, 0x0b, 0x1d, 0x1b }, - .idx = { 0, 1, 1, 0 }, - .multi = { 0, 1, 0, 0 }, - }, - { - .depth = 4, - .path = { 0x0f, 0x0b, 0x1d, 0x1b }, - .idx = { 0, 2, 1, 0 }, - .multi = { 0, 1, 0, 0 }, - }, - }; + struct hda_input_mux *imux = &spec->private_imux; + int i, err, type, type_idx; + + for (i = 0; i < cfg->num_inputs; i++) { + const char *label; + type = cfg->inputs[i].type; + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, + ad1988_pin_to_adc_idx(cfg->inputs[i].pin), + &type_idx); + err = new_analog_input(spec, cfg->inputs[i].pin, + label, type_idx, + type == AUTO_PIN_MIC); + if (err < 0) + return err; + } + snd_hda_add_imux_item(imux, "Mix", 9, NULL); - /* SPDIF source mux appears to be present only on AD1988A */ - if (!spec->gen.autocfg.dig_outs || - get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX) - return 0; + if ((err = add_control(spec, AD_CTL_WIDGET_VOL, + "Analog Mix Playback Volume", + HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) + return err; + if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, + "Analog Mix Playback Switch", + HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) + return err; - num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1; - if (num_conns != 3 && num_conns != 4) - return 0; + return 0; +} - for (i = 0; i < num_conns; i++) { - struct nid_path *path = snd_array_new(&spec->gen.paths); - if (!path) - return -ENOMEM; - *path = fake_paths[i]; - if (!i) - path->active = 1; - spec->smux_paths[i] = snd_hda_get_path_idx(codec, path); +static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + int dac_idx) +{ + /* set as output */ + snd_hda_set_pin_ctl(codec, nid, pin_type); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + switch (nid) { + case 0x11: /* port-A - DAC 03 */ + snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x00); + break; + case 0x14: /* port-B - DAC 06 */ + snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02); + break; + case 0x15: /* port-C - DAC 05 */ + snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00); + break; + case 0x17: /* port-E - DAC 0a */ + snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01); + break; + case 0x13: /* mono - DAC 04 */ + snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01); + break; } +} - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer)) - return -ENOMEM; +static void ad1988_auto_init_multi_out(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + int i; - codec->patch_ops.init = ad1988_auto_init; + for (i = 0; i < spec->autocfg.line_outs; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); + } +} - return 0; +static void ad1988_auto_init_extra_out(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.speaker_pins[0]; + if (pin) /* connect to front */ + ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); + pin = spec->autocfg.hp_pins[0]; + if (pin) /* connect to front */ + ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); } -/* - */ +static void ad1988_auto_init_analog_input(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i, idx; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + int type = cfg->inputs[i].type; + int val; + switch (nid) { + case 0x15: /* port-C */ + snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); + break; + case 0x17: /* port-E */ + snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0); + break; + } + val = PIN_IN; + if (type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, nid); + snd_hda_set_pin_ctl(codec, nid, val); + if (nid != AD1988_PIN_CD_NID) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + idx = ad1988_pin_idx(nid); + if (ad1988_boost_nids[idx]) + snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); + } +} +/* parse the BIOS configuration and set up the alc_spec */ +/* return 1 if successful, 0 if the proper config is not found, or a negative error code */ static int ad1988_parse_auto_config(struct hda_codec *codec) { - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) + if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0) + return err; + if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) + return err; + if (! spec->autocfg.line_outs) + return 0; /* can't find valid BIOS pin config */ + if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || + (err = ad1988_auto_create_extra_out(codec, + spec->autocfg.speaker_pins[0], + "Speaker")) < 0 || + (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], + "Headphone")) < 0 || + (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) return err; - spec = codec->spec; - spec->gen.mixer_nid = 0x20; - spec->gen.mixer_merge_nid = 0x21; - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - err = ad198x_parse_auto_config(codec); - if (err < 0) - goto error; - err = ad1988_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - return 0; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; - error: - ad198x_free(codec); - return err; + if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = AD1988_SPDIF_IN; + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs; + + spec->input_mux = &spec->private_imux; + + return 1; +} + +/* init callback for auto-configuration model -- overriding the default init */ +static int ad1988_auto_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1988_auto_init_multi_out(codec); + ad1988_auto_init_extra_out(codec); + ad1988_auto_init_analog_input(codec); + return 0; } /* */ -#ifdef ENABLE_AD_STATIC_QUIRKS static const char * const ad1988_models[AD1988_MODEL_LAST] = { [AD1988_6STACK] = "6stack", [AD1988_6STACK_DIG] = "6stack-dig", @@ -3279,6 +3262,14 @@ static int patch_ad1988(struct hda_codec *codec) struct ad198x_spec *spec; int err, board_config; + err = alloc_ad_spec(codec); + if (err < 0) + return err; + spec = codec->spec; + + if (is_rev2(codec)) + snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); + board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, ad1988_models, ad1988_cfg_tbl); if (board_config < 0) { @@ -3287,16 +3278,17 @@ static int patch_ad1988(struct hda_codec *codec) board_config = AD1988_AUTO; } - if (board_config == AD1988_AUTO) - return ad1988_parse_auto_config(codec); - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - if (is_rev2(codec)) - snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); + if (board_config == AD1988_AUTO) { + /* automatic parse from the BIOS config */ + err = ad1988_parse_auto_config(codec); + if (err < 0) { + ad198x_free(codec); + return err; + } else if (! err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n"); + board_config = AD1988_6STACK; + } + } err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { @@ -3360,7 +3352,7 @@ static int patch_ad1988(struct hda_codec *codec) spec->input_mux = &ad1988_laptop_capture_source; spec->num_mixers = 1; spec->mixers[0] = ad1988_laptop_mixers; - codec->inv_eapd = 1; /* inverted EAPD */ + spec->inv_eapd = 1; /* inverted EAPD */ spec->num_init_verbs = 1; spec->init_verbs[0] = ad1988_laptop_init_verbs; if (board_config == AD1988_LAPTOP_DIG) @@ -3368,6 +3360,15 @@ static int patch_ad1988(struct hda_codec *codec) break; } + if (spec->autocfg.hp_pins[0]) { + spec->mixers[spec->num_mixers++] = ad1988_hp_mixers; + spec->slave_vols = ad1988_6stack_fp_slave_pfxs; + spec->slave_sws = ad1988_6stack_fp_slave_pfxs; + spec->alt_dac_nid = ad1988_alt_dac_nid; + spec->stream_analog_alt_playback = + &ad198x_pcm_analog_alt_playback; + } + spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids); spec->adc_nids = ad1988_adc_nids; spec->capsrc_nids = ad1988_capsrc_nids; @@ -3395,6 +3396,9 @@ static int patch_ad1988(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; switch (board_config) { + case AD1988_AUTO: + codec->patch_ops.init = ad1988_auto_init; + break; case AD1988_LAPTOP: case AD1988_LAPTOP_DIG: codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; @@ -3410,9 +3414,6 @@ static int patch_ad1988(struct hda_codec *codec) return 0; } -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1988 ad1988_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ /* @@ -3433,7 +3434,6 @@ static int patch_ad1988(struct hda_codec *codec) * but no build-up framework is given, so far. */ -#ifdef ENABLE_AD_STATIC_QUIRKS static const hda_nid_t ad1884_dac_nids[1] = { 0x04, }; @@ -3576,107 +3576,7 @@ static const char * const ad1884_slave_vols[] = { NULL }; -enum { - AD1884_AUTO, - AD1884_BASIC, - AD1884_MODELS -}; - -static const char * const ad1884_models[AD1884_MODELS] = { - [AD1884_AUTO] = "auto", - [AD1884_BASIC] = "basic", -}; -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - -/* set the upper-limit for mixer amp to 0dB for avoiding the possible - * damage by overloading - */ -static void ad1884_fixup_amp_override(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); -} - -static void ad1884_fixup_hp_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct ad198x_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - spec->eapd_nid = spec->gen.autocfg.line_out_pins[0]; - else - spec->eapd_nid = spec->gen.autocfg.speaker_pins[0]; - if (spec->eapd_nid) - spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; - } -} - -enum { - AD1884_FIXUP_AMP_OVERRIDE, - AD1884_FIXUP_HP_EAPD, -}; - -static const struct hda_fixup ad1884_fixups[] = { - [AD1884_FIXUP_AMP_OVERRIDE] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1884_fixup_amp_override, - }, - [AD1884_FIXUP_HP_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = ad1884_fixup_hp_eapd, - .chained = true, - .chain_id = AD1884_FIXUP_AMP_OVERRIDE, - }, -}; - -static const struct snd_pci_quirk ad1884_fixup_tbl[] = { - SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), - {} -}; - - -static int ad1884_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - spec->gen.mixer_nid = 0x20; - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = ad198x_parse_auto_config(codec); - if (err < 0) - goto error; - err = ad1983_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - ad198x_free(codec); - return err; -} - -#ifdef ENABLE_AD_STATIC_QUIRKS -static int patch_ad1884_basic(struct hda_codec *codec) +static int patch_ad1884(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -3723,29 +3623,6 @@ static int patch_ad1884_basic(struct hda_codec *codec) return 0; } -static int patch_ad1884(struct hda_codec *codec) -{ - int board_config; - - board_config = snd_hda_check_board_config(codec, AD1884_MODELS, - ad1884_models, NULL); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1884_AUTO; - } - - if (board_config == AD1884_AUTO) - return ad1884_parse_auto_config(codec); - else - return patch_ad1884_basic(codec); -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1884 ad1884_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - -#ifdef ENABLE_AD_STATIC_QUIRKS /* * Lenovo Thinkpad T61/X61 */ @@ -3918,7 +3795,6 @@ static int ad1984_build_pcms(struct hda_codec *codec) /* models */ enum { - AD1984_AUTO, AD1984_BASIC, AD1984_THINKPAD, AD1984_DELL_DESKTOP, @@ -3926,7 +3802,6 @@ enum { }; static const char * const ad1984_models[AD1984_MODELS] = { - [AD1984_AUTO] = "auto", [AD1984_BASIC] = "basic", [AD1984_THINKPAD] = "thinkpad", [AD1984_DELL_DESKTOP] = "dell_desktop", @@ -3945,22 +3820,12 @@ static int patch_ad1984(struct hda_codec *codec) struct ad198x_spec *spec; int board_config, err; - board_config = snd_hda_check_board_config(codec, AD1984_MODELS, - ad1984_models, ad1984_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1984_AUTO; - } - - if (board_config == AD1984_AUTO) - return ad1884_parse_auto_config(codec); - - err = patch_ad1884_basic(codec); + err = patch_ad1884(codec); if (err < 0) return err; spec = codec->spec; - + board_config = snd_hda_check_board_config(codec, AD1984_MODELS, + ad1984_models, ad1984_cfg_tbl); switch (board_config) { case AD1984_BASIC: /* additional digital mics */ @@ -3987,9 +3852,6 @@ static int patch_ad1984(struct hda_codec *codec) } return 0; } -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1984 ad1884_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ /* @@ -4010,7 +3872,6 @@ static int patch_ad1984(struct hda_codec *codec) * We share the single DAC for both HP and line-outs (see AD1884/1984). */ -#ifdef ENABLE_AD_STATIC_QUIRKS static const hda_nid_t ad1884a_dac_nids[1] = { 0x03, }; @@ -4681,7 +4542,6 @@ static int ad1984a_touchsmart_init(struct hda_codec *codec) */ enum { - AD1884A_AUTO, AD1884A_DESKTOP, AD1884A_LAPTOP, AD1884A_MOBILE, @@ -4692,7 +4552,6 @@ enum { }; static const char * const ad1884a_models[AD1884A_MODELS] = { - [AD1884A_AUTO] = "auto", [AD1884A_DESKTOP] = "desktop", [AD1884A_LAPTOP] = "laptop", [AD1884A_MOBILE] = "mobile", @@ -4721,18 +4580,6 @@ static int patch_ad1884a(struct hda_codec *codec) struct ad198x_spec *spec; int err, board_config; - board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, - ad1884a_models, - ad1884a_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1884A_AUTO; - } - - if (board_config == AD1884A_AUTO) - return ad1884_parse_auto_config(codec); - err = alloc_ad_spec(codec); if (err < 0) return err; @@ -4764,6 +4611,9 @@ static int patch_ad1884a(struct hda_codec *codec) 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; @@ -4834,9 +4684,6 @@ static int patch_ad1884a(struct hda_codec *codec) return 0; } -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1884a ad1884_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ /* @@ -4851,7 +4698,6 @@ static int patch_ad1884a(struct hda_codec *codec) * port-G - rear clfe-out (6stack) */ -#ifdef ENABLE_AD_STATIC_QUIRKS static const hda_nid_t ad1882_dac_nids[3] = { 0x04, 0x03, 0x05 }; @@ -5128,7 +4974,6 @@ static const struct hda_amp_list ad1882_loopbacks[] = { /* models */ enum { - AD1882_AUTO, AD1882_3STACK, AD1882_6STACK, AD1882_3STACK_AUTOMUTE, @@ -5136,57 +4981,17 @@ enum { }; static const char * const ad1882_models[AD1986A_MODELS] = { - [AD1882_AUTO] = "auto", [AD1882_3STACK] = "3stack", [AD1882_6STACK] = "6stack", [AD1882_3STACK_AUTOMUTE] = "3stack-automute", }; -#endif /* ENABLE_AD_STATIC_QUIRKS */ - -static int ad1882_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - spec->gen.mixer_nid = 0x20; - spec->gen.mixer_merge_nid = 0x21; - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - err = ad198x_parse_auto_config(codec); - if (err < 0) - goto error; - err = ad1988_add_spdif_mux_ctl(codec); - if (err < 0) - goto error; - return 0; - error: - ad198x_free(codec); - return err; -} -#ifdef ENABLE_AD_STATIC_QUIRKS static int patch_ad1882(struct hda_codec *codec) { struct ad198x_spec *spec; int err, board_config; - board_config = snd_hda_check_board_config(codec, AD1882_MODELS, - ad1882_models, NULL); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1882_AUTO; - } - - if (board_config == AD1882_AUTO) - return ad1882_parse_auto_config(codec); - err = alloc_ad_spec(codec); if (err < 0) return err; @@ -5227,6 +5032,8 @@ static int patch_ad1882(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1882_MODELS, + ad1882_models, NULL); switch (board_config) { default: case AD1882_3STACK: @@ -5256,9 +5063,6 @@ static int patch_ad1882(struct hda_codec *codec) return 0; } -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1882 ad1882_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ /* diff --git a/trunk/sound/pci/hda/patch_ca0110.c b/trunk/sound/pci/hda/patch_ca0110.c index 30b3a4bc06ee..19ae14f739cb 100644 --- a/trunk/sound/pci/hda/patch_ca0110.c +++ b/trunk/sound/pci/hda/patch_ca0110.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -26,46 +27,502 @@ #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_generic.h" +/* + */ + +struct ca0110_spec { + struct auto_pin_cfg autocfg; + struct hda_multi_out multiout; + hda_nid_t out_pins[AUTO_CFG_MAX_OUTS]; + hda_nid_t dacs[AUTO_CFG_MAX_OUTS]; + hda_nid_t hp_dac; + hda_nid_t input_pins[AUTO_PIN_LAST]; + hda_nid_t adcs[AUTO_PIN_LAST]; + hda_nid_t dig_out; + hda_nid_t dig_in; + unsigned int num_inputs; + char input_labels[AUTO_PIN_LAST][32]; + struct hda_pcm pcm_rec[2]; /* PCM information */ +}; + +/* + * PCM callbacks + */ +static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} + +static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, + format, substream); +} + +/* + * Analog capture + */ +static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adcs[substream->number], + stream_tag, 0, format); + return 0; +} + +static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ca0110_spec *spec = codec->spec; + + snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]); + return 0; +} + +/* + */ + +static const char * const dirstr[2] = { "Playback", "Capture" }; + +static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, + int chan, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); + sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, + int chan, int dir) +{ + char namestr[44]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); + sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) +#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0) +#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1) +#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1) +#define add_mono_switch(codec, nid, pfx, chan) \ + _add_switch(codec, nid, pfx, chan, 0) +#define add_mono_volume(codec, nid, pfx, chan) \ + _add_volume(codec, nid, pfx, chan, 0) + +static int ca0110_build_controls(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + static const char * const prefix[AUTO_CFG_MAX_OUTS] = { + "Front", "Surround", NULL, "Side", "Multi" + }; + hda_nid_t mutenid; + int i, err; + + for (i = 0; i < spec->multiout.num_dacs; i++) { + if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP) + mutenid = spec->out_pins[i]; + else + mutenid = spec->multiout.dac_nids[i]; + if (!prefix[i]) { + err = add_mono_switch(codec, mutenid, + "Center", 1); + if (err < 0) + return err; + err = add_mono_switch(codec, mutenid, + "LFE", 1); + if (err < 0) + return err; + err = add_mono_volume(codec, spec->multiout.dac_nids[i], + "Center", 1); + if (err < 0) + return err; + err = add_mono_volume(codec, spec->multiout.dac_nids[i], + "LFE", 1); + if (err < 0) + return err; + } else { + err = add_out_switch(codec, mutenid, + prefix[i]); + if (err < 0) + return err; + err = add_out_volume(codec, spec->multiout.dac_nids[i], + prefix[i]); + if (err < 0) + return err; + } + } + if (cfg->hp_outs) { + if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP) + mutenid = cfg->hp_pins[0]; + else + mutenid = spec->multiout.dac_nids[i]; + + err = add_out_switch(codec, mutenid, "Headphone"); + if (err < 0) + return err; + if (spec->hp_dac) { + err = add_out_volume(codec, spec->hp_dac, "Headphone"); + if (err < 0) + return err; + } + } + for (i = 0; i < spec->num_inputs; i++) { + const char *label = spec->input_labels[i]; + if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP) + mutenid = spec->input_pins[i]; + else + mutenid = spec->adcs[i]; + err = add_in_switch(codec, mutenid, label); + if (err < 0) + return err; + err = add_in_volume(codec, spec->adcs[i], label); + if (err < 0) + return err; + } + + if (spec->dig_out) { + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, + spec->dig_out); + 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) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + if (err < 0) + return err; + err = add_in_volume(codec, spec->dig_in, "IEC958"); + } + return 0; +} + +/* + */ +static const struct hda_pcm_stream ca0110_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .ops = { + .open = ca0110_playback_pcm_open, + .prepare = ca0110_playback_pcm_prepare, + .cleanup = ca0110_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream ca0110_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = ca0110_capture_pcm_prepare, + .cleanup = ca0110_capture_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream ca0110_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = ca0110_dig_playback_pcm_open, + .close = ca0110_dig_playback_pcm_close, + .prepare = ca0110_dig_playback_pcm_prepare + }, +}; + +static const struct hda_pcm_stream ca0110_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int ca0110_build_pcms(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->pcm_info = info; + codec->num_pcms = 0; + + info->name = "CA0110 Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; + codec->num_pcms++; + + if (!spec->dig_out && !spec->dig_in) + return 0; + + info++; + info->name = "CA0110 Digital"; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->dig_out) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + ca0110_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + ca0110_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + codec->num_pcms++; + + return 0; +} + +static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) +{ + if (pin) { + snd_hda_set_pin_ctl(codec, pin, PIN_HP); + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + } + if (dac) + snd_hda_codec_write(codec, dac, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO); +} + +static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) +{ + if (pin) { + snd_hda_set_pin_ctl(codec, pin, PIN_IN | + snd_hda_get_default_vref(codec, pin)); + if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + if (adc) + snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); +} + +static int ca0110_init(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) + init_output(codec, spec->out_pins[i], + spec->multiout.dac_nids[i]); + init_output(codec, cfg->hp_pins[0], spec->hp_dac); + init_output(codec, cfg->dig_out_pins[0], spec->dig_out); + + for (i = 0; i < spec->num_inputs; i++) + init_input(codec, spec->input_pins[i], spec->adcs[i]); + init_input(codec, cfg->dig_in_pin, spec->dig_in); + return 0; +} + +static void ca0110_free(struct hda_codec *codec) +{ + kfree(codec->spec); +} static const struct hda_codec_ops ca0110_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, + .build_controls = ca0110_build_controls, + .build_pcms = ca0110_build_pcms, + .init = ca0110_init, + .free = ca0110_free, }; + +static void parse_line_outs(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, n; + unsigned int def_conf; + hda_nid_t nid; + + n = 0; + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (!def_conf) + continue; /* invalid pin */ + if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1) + continue; + spec->out_pins[n++] = nid; + } + spec->multiout.dac_nids = spec->dacs; + spec->multiout.num_dacs = n; + spec->multiout.max_channels = n * 2; +} + +static void parse_hp_out(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + unsigned int def_conf; + hda_nid_t nid, dac; + + if (!cfg->hp_outs) + return; + nid = cfg->hp_pins[0]; + def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (!def_conf) { + cfg->hp_outs = 0; + return; + } + if (snd_hda_get_connections(codec, nid, &dac, 1) != 1) + return; + + for (i = 0; i < cfg->line_outs; i++) + if (dac == spec->dacs[i]) + break; + if (i >= cfg->line_outs) { + spec->hp_dac = dac; + spec->multiout.hp_nid = dac; + } +} + +static void parse_input(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid, pin; + int n, i, j; + + n = 0; + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(wcaps); + if (type != AC_WID_AUD_IN) + continue; + if (snd_hda_get_connections(codec, nid, &pin, 1) != 1) + continue; + if (pin == cfg->dig_in_pin) { + spec->dig_in = nid; + continue; + } + for (j = 0; j < cfg->num_inputs; j++) + if (cfg->inputs[j].pin == pin) + break; + if (j >= cfg->num_inputs) + continue; + spec->input_pins[n] = pin; + snd_hda_get_pin_label(codec, pin, cfg, + spec->input_labels[n], + sizeof(spec->input_labels[n]), NULL); + spec->adcs[n] = nid; + n++; + } + spec->num_inputs = n; +} + +static void parse_digital(struct hda_codec *codec) +{ + struct ca0110_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (cfg->dig_outs && + snd_hda_get_connections(codec, cfg->dig_out_pins[0], + &spec->dig_out, 1) == 1) + spec->multiout.dig_out_nid = spec->dig_out; +} + static int ca0110_parse_auto_config(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; + struct ca0110_spec *spec = codec->spec; int err; - err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); - if (err < 0) - return err; - err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; + parse_line_outs(codec); + parse_hp_out(codec); + parse_digital(codec); + parse_input(codec); return 0; } static int patch_ca0110(struct hda_codec *codec) { - struct hda_gen_spec *spec; + struct ca0110_spec *spec; int err; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; - snd_hda_gen_spec_init(spec); codec->spec = spec; - spec->multi_cap_vol = 1; codec->bus->needs_damn_long_delay = 1; err = ca0110_parse_auto_config(codec); @@ -77,7 +534,8 @@ static int patch_ca0110(struct hda_codec *codec) return 0; error: - snd_hda_gen_free(codec); + kfree(codec->spec); + codec->spec = NULL; return err; } diff --git a/trunk/sound/pci/hda/patch_cirrus.c b/trunk/sound/pci/hda/patch_cirrus.c index 72ebb8a36b13..a2537b2f8724 100644 --- a/trunk/sound/pci/hda/patch_cirrus.c +++ b/trunk/sound/pci/hda/patch_cirrus.c @@ -19,16 +19,16 @@ */ #include +#include #include #include #include #include -#include #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include /* */ @@ -36,17 +36,45 @@ struct cs_spec { struct hda_gen_spec gen; + struct auto_pin_cfg autocfg; + struct hda_multi_out multiout; + struct snd_kcontrol *vmaster_sw; + struct snd_kcontrol *vmaster_vol; + + hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS]; + hda_nid_t slave_dig_outs[2]; + + unsigned int input_idx[AUTO_PIN_LAST]; + unsigned int capsrc_idx[AUTO_PIN_LAST]; + hda_nid_t adc_nid[AUTO_PIN_LAST]; + unsigned int adc_idx[AUTO_PIN_LAST]; + unsigned int num_inputs; + unsigned int cur_input; + unsigned int automic_idx; + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + hda_nid_t dig_in; + + const struct hda_bind_ctls *capture_bind[2]; + unsigned int gpio_mask; unsigned int gpio_dir; unsigned int gpio_data; unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ + struct hda_pcm pcm_rec[2]; /* PCM information */ + + unsigned int hp_detect:1; + unsigned int mic_detect:1; + unsigned int speaker_2_1:1; /* CS421x */ unsigned int spdif_detect:1; - unsigned int spdif_present:1; unsigned int sense_b:1; hda_nid_t vendor_nid; + struct hda_input_mux input_mux; + unsigned int last_input; }; /* available models with CS420x */ @@ -122,34 +150,756 @@ enum { #define CS421X_DMIC_PIN_NID 0x09 /* Port E */ #define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ -#define CS421X_IDX_DEV_CFG 0x01 -#define CS421X_IDX_ADC_CFG 0x02 -#define CS421X_IDX_DAC_CFG 0x03 -#define CS421X_IDX_SPK_CTL 0x04 +#define CS421X_IDX_DEV_CFG 0x01 +#define CS421X_IDX_ADC_CFG 0x02 +#define CS421X_IDX_DAC_CFG 0x03 +#define CS421X_IDX_SPK_CTL 0x04 + +#define SPDIF_EVENT 0x04 + +/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ +#define CS4213_VENDOR_NID 0x09 + + +static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +{ + struct cs_spec *spec = codec->spec; + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_COEF_INDEX, idx); + return snd_hda_codec_read(codec, spec->vendor_nid, 0, + AC_VERB_GET_PROC_COEF, 0); +} + +static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, + unsigned int coef) +{ + struct cs_spec *spec = codec->spec; + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_COEF_INDEX, idx); + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_PROC_COEF, coef); +} + + +#define HP_EVENT 1 +#define MIC_EVENT 2 + +/* + * PCM callbacks + */ +static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} + +static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int cs_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, + format, substream); +} + +static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); +} + +static void cs_update_input_select(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + if (spec->cur_adc) + snd_hda_codec_write(codec, spec->cur_adc, 0, + AC_VERB_SET_CONNECT_SEL, + spec->adc_idx[spec->cur_input]); +} + +/* + * Analog capture + */ +static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + spec->cur_adc = spec->adc_nid[spec->cur_input]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + cs_update_input_select(codec); + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + return 0; +} + +static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + return 0; +} + +/* + */ +static const struct hda_pcm_stream cs_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = cs_playback_pcm_open, + .prepare = cs_playback_pcm_prepare, + .cleanup = cs_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream cs_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = cs_capture_pcm_prepare, + .cleanup = cs_capture_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream cs_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = cs_dig_playback_pcm_open, + .close = cs_dig_playback_pcm_close, + .prepare = cs_dig_playback_pcm_prepare, + .cleanup = cs_dig_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream cs_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int cs_build_pcms(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->pcm_info = info; + codec->num_pcms = 0; + + info->name = "Cirrus Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + if (spec->speaker_2_1) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nid[spec->cur_input]; + codec->num_pcms++; + + if (!spec->multiout.dig_out_nid && !spec->dig_in) + return 0; + + info++; + info->name = "Cirrus Digital"; + info->pcm_type = spec->autocfg.dig_out_type[0]; + if (!info->pcm_type) + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->multiout.dig_out_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + cs_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dig_out_nid; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + cs_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + codec->num_pcms++; + + return 0; +} + +/* + * parse codec topology + */ + +static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t dac; + if (!pin) + return 0; + if (snd_hda_get_connections(codec, pin, &dac, 1) != 1) + return 0; + return dac; +} + +static int is_ext_mic(struct hda_codec *codec, unsigned int idx) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t pin = cfg->inputs[idx].pin; + unsigned int val; + if (!is_jack_detectable(codec, pin)) + return 0; + val = snd_hda_codec_get_pincfg(codec, pin); + return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT); +} + +static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, + unsigned int *idxp) +{ + int i, idx; + hda_nid_t nid; + + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, nid)); + if (type != AC_WID_AUD_IN) + continue; + idx = snd_hda_get_conn_index(codec, nid, pin, false); + if (idx >= 0) { + *idxp = idx; + return nid; + } + } + return 0; +} + +static int is_active_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int val; + val = snd_hda_codec_get_pincfg(codec, nid); + return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); +} + +static int parse_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, extra_nids; + hda_nid_t dac; + + for (i = 0; i < cfg->line_outs; i++) { + dac = get_dac(codec, cfg->line_out_pins[i]); + if (!dac) + break; + spec->dac_nid[i] = dac; + } + spec->multiout.num_dacs = i; + spec->multiout.dac_nids = spec->dac_nid; + spec->multiout.max_channels = i * 2; + + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && i == 2) + spec->speaker_2_1 = 1; /* assume 2.1 speakers */ + + /* add HP and speakers */ + extra_nids = 0; + for (i = 0; i < cfg->hp_outs; i++) { + dac = get_dac(codec, cfg->hp_pins[i]); + if (!dac) + break; + if (!i) + spec->multiout.hp_nid = dac; + else + spec->multiout.extra_out_nid[extra_nids++] = dac; + } + for (i = 0; i < cfg->speaker_outs; i++) { + dac = get_dac(codec, cfg->speaker_pins[i]); + if (!dac) + break; + spec->multiout.extra_out_nid[extra_nids++] = dac; + } + + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = 0; + memset(cfg->line_out_pins, 0, sizeof(cfg->line_out_pins)); + } + + return 0; +} + +static int parse_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + spec->input_idx[spec->num_inputs] = i; + spec->capsrc_idx[i] = spec->num_inputs++; + spec->cur_input = i; + spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); + } + if (!spec->num_inputs) + return 0; + + /* check whether the automatic mic switch is available */ + if (spec->num_inputs == 2 && + cfg->inputs[0].type == AUTO_PIN_MIC && + cfg->inputs[1].type == AUTO_PIN_MIC) { + if (is_ext_mic(codec, cfg->inputs[0].pin)) { + if (!is_ext_mic(codec, cfg->inputs[1].pin)) { + spec->mic_detect = 1; + spec->automic_idx = 0; + } + } else { + if (is_ext_mic(codec, cfg->inputs[1].pin)) { + spec->mic_detect = 1; + spec->automic_idx = 1; + } + } + } + return 0; +} + + +static int parse_digital_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + + if (!cfg->dig_outs) + return 0; + if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1) + return 0; + spec->multiout.dig_out_nid = nid; + spec->multiout.share_spdif = 1; + if (cfg->dig_outs > 1 && + snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) { + spec->slave_dig_outs[0] = nid; + codec->slave_dig_outs = spec->slave_dig_outs; + } + return 0; +} + +static int parse_digital_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int idx; + + if (cfg->dig_in_pin) + spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx); + return 0; +} + +/* + * create mixer controls + */ + +static const char * const dir_sfx[2] = { "Playback", "Capture" }; + +static int add_mute(struct hda_codec *codec, const char *name, int index, + unsigned int pval, int dir, struct snd_kcontrol **kctlp) +{ + char tmp[44]; + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT); + knew.private_value = pval; + snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); + *kctlp = snd_ctl_new1(&knew, codec); + (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; + return snd_hda_ctl_add(codec, 0, *kctlp); +} + +static int add_volume(struct hda_codec *codec, const char *name, + int index, unsigned int pval, int dir, + struct snd_kcontrol **kctlp) +{ + char tmp[44]; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT); + knew.private_value = pval; + snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); + *kctlp = snd_ctl_new1(&knew, codec); + (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; + return snd_hda_ctl_add(codec, 0, *kctlp); +} + +static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) +{ + unsigned int caps; + + /* set the upper-limit for mixer amp to 0dB */ + caps = query_amp_caps(codec, dac, HDA_OUTPUT); + caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); + caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) + << AC_AMPCAP_NUM_STEPS_SHIFT; + snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); +} + +static int add_vmaster(struct hda_codec *codec, hda_nid_t dac) +{ + struct cs_spec *spec = codec->spec; + unsigned int tlv[4]; + int err; + + spec->vmaster_sw = + snd_ctl_make_virtual_master("Master Playback Switch", NULL); + err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw); + if (err < 0) + return err; + + snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv); + spec->vmaster_vol = + snd_ctl_make_virtual_master("Master Playback Volume", tlv); + err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol); + if (err < 0) + return err; + return 0; +} + +static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx, + int num_ctls, int type) +{ + struct cs_spec *spec = codec->spec; + const char *name; + int err, index; + struct snd_kcontrol *kctl; + static const char * const speakers[] = { + "Front Speaker", "Surround Speaker", "Bass Speaker" + }; + static const char * const line_outs[] = { + "Front Line Out", "Surround Line Out", "Bass Line Out" + }; + + fix_volume_caps(codec, dac); + if (!spec->vmaster_sw) { + err = add_vmaster(codec, dac); + if (err < 0) + return err; + } + + index = 0; + switch (type) { + case AUTO_PIN_HP_OUT: + name = "Headphone"; + index = idx; + break; + case AUTO_PIN_SPEAKER_OUT: + if (spec->speaker_2_1) + name = idx ? "Bass Speaker" : "Speaker"; + else if (num_ctls > 1) + name = speakers[idx]; + else + name = "Speaker"; + break; + default: + if (num_ctls > 1) + name = line_outs[idx]; + else + name = "Line Out"; + break; + } + + err = add_mute(codec, name, index, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + err = snd_ctl_add_slave(spec->vmaster_sw, kctl); + if (err < 0) + return err; + + err = add_volume(codec, name, index, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + err = snd_ctl_add_slave(spec->vmaster_vol, kctl); + if (err < 0) + return err; + + return 0; +} + +static int build_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + + for (i = 0; i < cfg->line_outs; i++) { + err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]), + i, cfg->line_outs, cfg->line_out_type); + if (err < 0) + return err; + } + for (i = 0; i < cfg->hp_outs; i++) { + err = add_output(codec, get_dac(codec, cfg->hp_pins[i]), + i, cfg->hp_outs, AUTO_PIN_HP_OUT); + if (err < 0) + return err; + } + for (i = 0; i < cfg->speaker_outs; i++) { + err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]), + i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT); + if (err < 0) + return err; + } + return 0; +} + +/* + */ + +static const struct snd_kcontrol_new cs_capture_ctls[] = { + HDA_BIND_SW("Capture Switch", 0), + HDA_BIND_VOL("Capture Volume", 0), +}; + +static int change_cur_input(struct hda_codec *codec, unsigned int idx, + int force) +{ + struct cs_spec *spec = codec->spec; + + if (spec->cur_input == idx && !force) + return 0; + if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = spec->adc_nid[idx]; + snd_hda_codec_setup_stream(codec, spec->cur_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } + spec->cur_input = idx; + cs_update_input_select(codec); + return 1; +} + +static int cs_capture_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int idx; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->num_inputs; + if (uinfo->value.enumerated.item >= spec->num_inputs) + uinfo->value.enumerated.item = spec->num_inputs - 1; + idx = spec->input_idx[uinfo->value.enumerated.item]; + snd_hda_get_pin_label(codec, cfg->inputs[idx].pin, cfg, + uinfo->value.enumerated.name, + sizeof(uinfo->value.enumerated.name), NULL); + return 0; +} + +static int cs_capture_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input]; + return 0; +} + +static int cs_capture_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + unsigned int idx = ucontrol->value.enumerated.item[0]; + + if (idx >= spec->num_inputs) + return -EINVAL; + idx = spec->input_idx[idx]; + return change_cur_input(codec, idx, 0); +} + +static const struct snd_kcontrol_new cs_capture_source = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = cs_capture_source_info, + .get = cs_capture_source_get, + .put = cs_capture_source_put, +}; + +static const struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec, + struct hda_ctl_ops *ops) +{ + struct cs_spec *spec = codec->spec; + struct hda_bind_ctls *bind; + int i, n; + + bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1), + GFP_KERNEL); + if (!bind) + return NULL; + bind->ops = ops; + n = 0; + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!spec->adc_nid[i]) + continue; + bind->values[n++] = + HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3, + spec->adc_idx[i], HDA_INPUT); + } + return bind; +} + +/* add a (input-boost) volume control to the given input pin */ +static int add_input_volume_control(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + int item) +{ + hda_nid_t pin = cfg->inputs[item].pin; + u32 caps; + const char *label; + struct snd_kcontrol *kctl; + + if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP)) + return 0; + caps = query_amp_caps(codec, pin, HDA_INPUT); + caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + if (caps <= 1) + return 0; + label = hda_get_autocfg_input_label(codec, cfg, item); + return add_volume(codec, label, 0, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); +} + +static int build_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int i, err; + + if (!spec->num_inputs) + return 0; + + /* make bind-capture */ + spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); + spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); + for (i = 0; i < 2; i++) { + struct snd_kcontrol *kctl; + int n; + if (!spec->capture_bind[i]) + return -ENOMEM; + kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = (long)spec->capture_bind[i]; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + for (n = 0; n < AUTO_PIN_LAST; n++) { + if (!spec->adc_nid[n]) + continue; + err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); + if (err < 0) + return err; + } + } + + if (spec->num_inputs > 1 && !spec->mic_detect) { + err = snd_hda_ctl_add(codec, 0, + snd_ctl_new1(&cs_capture_source, codec)); + if (err < 0) + return err; + } -#define SPDIF_EVENT 0x04 + for (i = 0; i < spec->num_inputs; i++) { + err = add_input_volume_control(codec, &spec->autocfg, i); + if (err < 0) + return err; + } -/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ -#define CS4213_VENDOR_NID 0x09 + return 0; +} +/* + */ -static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +static int build_digital_output(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_COEF_INDEX, idx); - return snd_hda_codec_read(codec, spec->vendor_nid, 0, - AC_VERB_GET_PROC_COEF, 0); + int err; + + if (!spec->multiout.dig_out_nid) + return 0; + + err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid, + spec->pcm_rec[1].pcm_type); + if (err < 0) + return err; + err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); + if (err < 0) + return err; + return 0; } -static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, - unsigned int coef) +static int build_digital_input(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_COEF_INDEX, idx); - snd_hda_codec_write(codec, spec->vendor_nid, 0, - AC_VERB_SET_PROC_COEF, coef); + if (spec->dig_in) + return snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + return 0; } /* @@ -158,37 +908,187 @@ static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, * HP/SPK/SPDIF */ -static void cs_automute(struct hda_codec *codec) +static void cs_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) { struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int hp_present; + unsigned int spdif_present; + hda_nid_t nid; + int i; - /* mute HPs if spdif jack (SENSE_B) is present */ - spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b); + spdif_present = 0; + if (cfg->dig_outs) { + nid = cfg->dig_out_pins[0]; + if (is_jack_detectable(codec, nid)) { + /* + TODO: SPDIF output redirect when SENSE_B is enabled. + Shared (SENSE_A) jack (e.g HP/mini-TOSLINK) + assumed. + */ + if (snd_hda_jack_detect(codec, nid) + /* && spec->sense_b */) + spdif_present = 1; + } + } + + hp_present = 0; + for (i = 0; i < cfg->hp_outs; i++) { + nid = cfg->hp_pins[i]; + if (!is_jack_detectable(codec, nid)) + continue; + hp_present = snd_hda_jack_detect(codec, nid); + if (hp_present) + break; + } - snd_hda_gen_update_outputs(codec); + /* mute speakers if spdif or hp jack is plugged in */ + for (i = 0; i < cfg->speaker_outs; i++) { + int pin_ctl = hp_present ? 0 : PIN_OUT; + /* detect on spdif is specific to CS4210 */ + if (spdif_present && (spec->vendor_nid == CS4210_VENDOR_NID)) + pin_ctl = 0; + nid = cfg->speaker_pins[i]; + snd_hda_set_pin_ctl(codec, nid, pin_ctl); + } if (spec->gpio_eapd_hp) { - unsigned int gpio = spec->gen.hp_jack_present ? + unsigned int gpio = hp_present ? spec->gpio_eapd_hp : spec->gpio_eapd_speaker; snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, gpio); } + + /* specific to CS4210 */ + if (spec->vendor_nid == CS4210_VENDOR_NID) { + /* mute HPs if spdif jack (SENSE_B) is present */ + for (i = 0; i < cfg->hp_outs; i++) { + nid = cfg->hp_pins[i]; + snd_hda_set_pin_ctl(codec, nid, + (spdif_present && spec->sense_b) ? 0 : PIN_HP); + } + + /* SPDIF TX on/off */ + if (cfg->dig_outs) { + nid = cfg->dig_out_pins[0]; + snd_hda_set_pin_ctl(codec, nid, + spdif_present ? PIN_OUT : 0); + + } + /* Update board GPIOs if neccessary ... */ + } } -static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid) +/* + * Auto-input redirect for CS421x + * Switch max 3 inputs of a single ADC (nid 3) +*/ + +static void cs_automic(struct hda_codec *codec, struct hda_jack_tbl *tbl) { - unsigned int val; - val = snd_hda_codec_get_pincfg(codec, nid); - return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + unsigned int present; + + nid = cfg->inputs[spec->automic_idx].pin; + present = snd_hda_jack_detect(codec, nid); + + /* specific to CS421x, single ADC */ + if (spec->vendor_nid == CS420X_VENDOR_NID) { + if (present) + change_cur_input(codec, spec->automic_idx, 0); + else + change_cur_input(codec, !spec->automic_idx, 0); + } else { + if (present) { + if (spec->cur_input != spec->automic_idx) { + spec->last_input = spec->cur_input; + spec->cur_input = spec->automic_idx; + } + } else { + spec->cur_input = spec->last_input; + } + cs_update_input_select(codec); + } +} + +/* + */ + +static void init_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + /* mute first */ + for (i = 0; i < spec->multiout.num_dacs; i++) + snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (spec->multiout.hp_nid) + snd_hda_codec_write(codec, spec->multiout.hp_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) { + if (!spec->multiout.extra_out_nid[i]) + break; + snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + } + + /* set appropriate pin controls */ + for (i = 0; i < cfg->line_outs; i++) + snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT); + /* HP */ + for (i = 0; i < cfg->hp_outs; i++) { + hda_nid_t nid = cfg->hp_pins[i]; + snd_hda_set_pin_ctl(codec, nid, PIN_HP); + if (!cfg->speaker_outs) + continue; + if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { + snd_hda_jack_detect_enable_callback(codec, nid, HP_EVENT, cs_automute); + spec->hp_detect = 1; + } + } + + /* Speaker */ + for (i = 0; i < cfg->speaker_outs; i++) + snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT); + + /* SPDIF is enabled on presence detect for CS421x */ + if (spec->hp_detect || spec->spdif_detect) + cs_automute(codec, NULL); } -static void init_input_coef(struct hda_codec *codec) +static void init_input(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int coef; + int i; + for (i = 0; i < cfg->num_inputs; i++) { + unsigned int ctl; + hda_nid_t pin = cfg->inputs[i].pin; + if (!spec->adc_nid[i]) + continue; + /* set appropriate pin control and mute first */ + ctl = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + ctl |= snd_hda_get_default_vref(codec, pin); + snd_hda_set_pin_ctl(codec, pin, ctl); + snd_hda_codec_write(codec, spec->adc_nid[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(spec->adc_idx[i])); + if (spec->mic_detect && spec->automic_idx == i) + snd_hda_jack_detect_enable_callback(codec, pin, MIC_EVENT, cs_automic); + } /* CS420x has multiple ADC, CS421x has single ADC */ if (spec->vendor_nid == CS420X_VENDOR_NID) { + change_cur_input(codec, spec->cur_input, 1); + if (spec->mic_detect) + cs_automic(codec, NULL); + coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG); if (is_active_pin(codec, CS_DMIC2_PIN_NID)) coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */ @@ -199,6 +1099,13 @@ static void init_input_coef(struct hda_codec *codec) */ cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef); + } else { + if (spec->mic_detect) + cs_automic(codec, NULL); + else { + spec->cur_adc = spec->adc_nid[spec->cur_input]; + cs_update_input_select(codec); + } } } @@ -271,7 +1178,7 @@ static const struct hda_verb cs_errata_init_verbs[] = { }; /* SPDIF setup */ -static void init_digital_coef(struct hda_codec *codec) +static void init_digital(struct hda_codec *codec) { unsigned int coef; @@ -294,7 +1201,7 @@ static int cs_init(struct hda_codec *codec) snd_hda_sequence_write(codec, cs_coef_init_verbs); - snd_hda_gen_init(codec); + snd_hda_gen_apply_verbs(codec); if (spec->gpio_mask) { snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, @@ -305,17 +1212,53 @@ static int cs_init(struct hda_codec *codec) spec->gpio_data); } - init_input_coef(codec); - init_digital_coef(codec); + init_output(codec); + init_input(codec); + init_digital(codec); + + return 0; +} + +static int cs_build_controls(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int err; + + err = build_output(codec); + if (err < 0) + return err; + err = build_input(codec); + if (err < 0) + return err; + err = build_digital_output(codec); + if (err < 0) + return err; + err = build_digital_input(codec); + if (err < 0) + return err; + err = cs_init(codec); + if (err < 0) + return err; + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; return 0; } -#define cs_free snd_hda_gen_free +static void cs_free(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + kfree(spec->capture_bind[0]); + kfree(spec->capture_bind[1]); + snd_hda_gen_free(&spec->gen); + kfree(codec->spec); +} static const struct hda_codec_ops cs_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, + .build_controls = cs_build_controls, + .build_pcms = cs_build_pcms, .init = cs_init, .free = cs_free, .unsol_event = snd_hda_jack_unsol_event, @@ -326,14 +1269,22 @@ static int cs_parse_auto_config(struct hda_codec *codec) struct cs_spec *spec = codec->spec; int err; - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + err = parse_output(codec); + if (err < 0) + return err; + err = parse_input(codec); + if (err < 0) + return err; + err = parse_digital_output(codec); + if (err < 0) + return err; + err = parse_digital_input(codec); if (err < 0) return err; - return 0; } @@ -483,28 +1434,18 @@ static const struct hda_fixup cs420x_fixups[] = { }, }; -static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) -{ - struct cs_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return NULL; - codec->spec = spec; - spec->vendor_nid = vendor_nid; - snd_hda_gen_spec_init(&spec->gen); - - return spec; -} - static int patch_cs420x(struct hda_codec *codec) { struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS420X_VENDOR_NID); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + codec->spec = spec; + snd_hda_gen_init(&spec->gen); + + spec->vendor_nid = CS420X_VENDOR_NID; snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, cs420x_fixups); @@ -522,6 +1463,7 @@ static int patch_cs420x(struct hda_codec *codec) error: cs_free(codec); + codec->spec = NULL; return err; } @@ -680,7 +1622,7 @@ static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, } } -static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = { +static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | @@ -725,44 +1667,20 @@ static void cs4210_pinmux_init(struct hda_codec *codec) } } -static void cs4210_spdif_automute(struct hda_codec *codec, - struct hda_jack_tbl *tbl) -{ - struct cs_spec *spec = codec->spec; - bool spdif_present = false; - hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0]; - - /* detect on spdif is specific to CS4210 */ - if (!spec->spdif_detect || - spec->vendor_nid != CS4210_VENDOR_NID) - return; - - spdif_present = snd_hda_jack_detect(codec, spdif_pin); - if (spdif_present == spec->spdif_present) - return; - - spec->spdif_present = spdif_present; - /* SPDIF TX on/off */ - if (spdif_present) - snd_hda_set_pin_ctl(codec, spdif_pin, - spdif_present ? PIN_OUT : 0); - - cs_automute(codec); -} - -static void parse_cs421x_digital(struct hda_codec *codec) +static void init_cs421x_digital(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; + struct auto_pin_cfg *cfg = &spec->autocfg; int i; + for (i = 0; i < cfg->dig_outs; i++) { hda_nid_t nid = cfg->dig_out_pins[i]; + if (!cfg->speaker_outs) + continue; if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { + snd_hda_jack_detect_enable_callback(codec, nid, SPDIF_EVENT, cs_automute); spec->spdif_detect = 1; - snd_hda_jack_detect_enable_callback(codec, nid, - SPDIF_EVENT, - cs4210_spdif_automute); } } } @@ -777,8 +1695,6 @@ static int cs421x_init(struct hda_codec *codec) cs4210_pinmux_init(codec); } - snd_hda_gen_init(codec); - if (spec->gpio_mask) { snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, spec->gpio_mask); @@ -788,61 +1704,233 @@ static int cs421x_init(struct hda_codec *codec) spec->gpio_data); } - init_input_coef(codec); + init_output(codec); + init_input(codec); + init_cs421x_digital(codec); + + return 0; +} + +/* + * CS4210 Input MUX (1 ADC) + */ +static int cs421x_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + + return snd_hda_input_mux_info(&spec->input_mux, uinfo); +} - cs4210_spdif_automute(codec, NULL); +static int cs421x_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->cur_input; return 0; } -static int cs421x_build_controls(struct hda_codec *codec) +static int cs421x_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + + return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, + spec->adc_nid[0], &spec->cur_input); + +} + +static const struct snd_kcontrol_new cs421x_capture_source = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = cs421x_mux_enum_info, + .get = cs421x_mux_enum_get, + .put = cs421x_mux_enum_put, +}; + +static int cs421x_add_input_volume_control(struct hda_codec *codec, int item) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + const struct hda_input_mux *imux = &spec->input_mux; + hda_nid_t pin = cfg->inputs[item].pin; + struct snd_kcontrol *kctl; + u32 caps; + + if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP)) + return 0; + + caps = query_amp_caps(codec, pin, HDA_INPUT); + caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + if (caps <= 1) + return 0; + + return add_volume(codec, imux->items[item].label, 0, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); +} + +/* add a (input-boost) volume control to the given input pin */ +static int build_cs421x_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux = &spec->input_mux; + int i, err, type_idx; + const char *label; + + if (!spec->num_inputs) + return 0; + + /* make bind-capture */ + spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); + spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); + for (i = 0; i < 2; i++) { + struct snd_kcontrol *kctl; + int n; + if (!spec->capture_bind[i]) + return -ENOMEM; + kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = (long)spec->capture_bind[i]; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + for (n = 0; n < AUTO_PIN_LAST; n++) { + if (!spec->adc_nid[n]) + continue; + err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); + if (err < 0) + return err; + } + } + + /* Add Input MUX Items + Capture Volume/Switch */ + for (i = 0; i < spec->num_inputs; i++) { + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, spec->adc_idx[i], &type_idx); + + err = cs421x_add_input_volume_control(codec, i); + if (err < 0) + return err; + } + + /* + Add 'Capture Source' Switch if + * 2 inputs and no mic detec + * 3 inputs + */ + if ((spec->num_inputs == 2 && !spec->mic_detect) || + (spec->num_inputs == 3)) { + + err = snd_hda_ctl_add(codec, spec->adc_nid[0], + snd_ctl_new1(&cs421x_capture_source, codec)); + if (err < 0) + return err; + } + + return 0; +} + +/* Single DAC (Mute/Gain) */ +static int build_cs421x_output(struct hda_codec *codec) { + hda_nid_t dac = CS4210_DAC_NID; struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct snd_kcontrol *kctl; int err; + char *name = "Master"; + + fix_volume_caps(codec, dac); - err = snd_hda_gen_build_controls(codec); + err = add_mute(codec, name, 0, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); if (err < 0) return err; - if (spec->gen.autocfg.speaker_outs && - spec->vendor_nid == CS4210_VENDOR_NID) { + err = add_volume(codec, name, 0, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + + if (cfg->speaker_outs && (spec->vendor_nid == CS4210_VENDOR_NID)) { err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&cs421x_speaker_boost_ctl, codec)); + snd_ctl_new1(&cs421x_speaker_bost_ctl, codec)); if (err < 0) return err; } + return err; +} + +static int cs421x_build_controls(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int err; + + err = build_cs421x_output(codec); + if (err < 0) + return err; + err = build_cs421x_input(codec); + if (err < 0) + return err; + err = build_digital_output(codec); + if (err < 0) + return err; + err = cs421x_init(codec); + if (err < 0) + return err; + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + return 0; } -static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) +static int parse_cs421x_input(struct hda_codec *codec) { - unsigned int caps; + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; - /* set the upper-limit for mixer amp to 0dB */ - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); - caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) - << AC_AMPCAP_NUM_STEPS_SHIFT; - snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); + spec->cur_input = spec->last_input = i; + spec->num_inputs++; + + /* check whether the automatic mic switch is available */ + if (is_ext_mic(codec, i) && cfg->num_inputs >= 2) { + spec->mic_detect = 1; + spec->automic_idx = i; + } + } + return 0; } static int cs421x_parse_auto_config(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - hda_nid_t dac = CS4210_DAC_NID; int err; - fix_volume_caps(codec, dac); - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + err = parse_output(codec); + if (err < 0) + return err; + err = parse_cs421x_input(codec); + if (err < 0) + return err; + err = parse_digital_output(codec); if (err < 0) return err; - - parse_cs421x_digital(codec); return 0; } @@ -875,7 +1963,7 @@ static int cs421x_suspend(struct hda_codec *codec) static const struct hda_codec_ops cs421x_patch_ops = { .build_controls = cs421x_build_controls, - .build_pcms = snd_hda_gen_build_pcms, + .build_pcms = cs_build_pcms, .init = cs421x_init, .free = cs_free, .unsol_event = snd_hda_jack_unsol_event, @@ -889,9 +1977,13 @@ static int patch_cs4210(struct hda_codec *codec) struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS4210_VENDOR_NID); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + codec->spec = spec; + snd_hda_gen_init(&spec->gen); + + spec->vendor_nid = CS4210_VENDOR_NID; snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, cs421x_fixups); @@ -916,6 +2008,7 @@ static int patch_cs4210(struct hda_codec *codec) error: cs_free(codec); + codec->spec = NULL; return err; } @@ -924,9 +2017,13 @@ static int patch_cs4213(struct hda_codec *codec) struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS4213_VENDOR_NID); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + codec->spec = spec; + snd_hda_gen_init(&spec->gen); + + spec->vendor_nid = CS4213_VENDOR_NID; err = cs421x_parse_auto_config(codec); if (err < 0) @@ -937,6 +2034,7 @@ static int patch_cs4213(struct hda_codec *codec) error: cs_free(codec); + codec->spec = NULL; return err; } diff --git a/trunk/sound/pci/hda/patch_cmedia.c b/trunk/sound/pci/hda/patch_cmedia.c index 9c6ce73b03c5..c8fdaaefe702 100644 --- a/trunk/sound/pci/hda/patch_cmedia.c +++ b/trunk/sound/pci/hda/patch_cmedia.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -29,9 +30,6 @@ #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" -#include "hda_jack.h" -#include "hda_generic.h" - #define NUM_PINS 11 @@ -47,10 +45,6 @@ enum { }; struct cmi_spec { - struct hda_gen_spec gen; - - /* below are only for static models */ - int board_config; unsigned int no_line_in: 1; /* no line-in (5-jack) */ unsigned int front_panel: 1; /* has front-panel 2-jack */ @@ -362,6 +356,77 @@ static int cmi9880_build_controls(struct hda_codec *codec) return 0; } +/* fill in the multi_dac_nids table, which will decide + which audio widget to use for each channel */ +static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg) +{ + struct cmi_spec *spec = codec->spec; + hda_nid_t nid; + int assigned[4]; + int i, j; + + /* clear the table, only one c-media dac assumed here */ + memset(spec->dac_nids, 0, sizeof(spec->dac_nids)); + memset(assigned, 0, sizeof(assigned)); + /* check the pins we found */ + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */ + if (nid >= 0x0b && nid <= 0x0e) { + spec->dac_nids[i] = (nid - 0x0b) + 0x03; + assigned[nid - 0x0b] = 1; + } + } + /* left pin can be connect to any audio widget */ + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + if (nid <= 0x0e) + continue; + /* search for an empty channel */ + for (j = 0; j < cfg->line_outs; j++) { + if (! assigned[j]) { + spec->dac_nids[i] = j + 0x03; + assigned[j] = 1; + break; + } + } + } + spec->num_dacs = cfg->line_outs; + return 0; +} + +/* create multi_init table, which is used for multichannel initialization */ +static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg) +{ + struct cmi_spec *spec = codec->spec; + hda_nid_t nid; + int i, j, k; + + /* clear the table, only one c-media dac assumed here */ + memset(spec->multi_init, 0, sizeof(spec->multi_init)); + for (j = 0, i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + /* set as output */ + spec->multi_init[j].nid = nid; + spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL; + spec->multi_init[j].param = PIN_OUT; + j++; + if (nid > 0x0e) { + /* set connection */ + spec->multi_init[j].nid = nid; + spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL; + spec->multi_init[j].param = 0; + /* find the index in connect list */ + k = snd_hda_get_conn_index(codec, nid, + spec->dac_nids[i], 0); + if (k >= 0) + spec->multi_init[j].param = k; + j++; + } + } + return 0; +} + static int cmi9880_init(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; @@ -567,36 +632,6 @@ static const struct hda_codec_ops cmi9880_patch_ops = { .free = cmi9880_free, }; -/* - * stuff for auto-parser - */ -static const struct hda_codec_ops cmi_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, -}; - -static int cmi_parse_auto_config(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int err; - - snd_hda_gen_spec_init(&spec->gen); - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - return err; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - return err; - - codec->patch_ops = cmi_auto_patch_ops; - return 0; -} - static int patch_cmi9880(struct hda_codec *codec) { struct cmi_spec *spec; @@ -615,15 +650,6 @@ static int patch_cmi9880(struct hda_codec *codec) spec->board_config = CMI_AUTO; /* try everything */ } - if (spec->board_config == CMI_AUTO) { - int err = cmi_parse_auto_config(codec); - if (err < 0) { - snd_hda_gen_free(codec); - return err; - } - return 0; - } - /* copy default DAC NIDs */ memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids)); spec->num_dacs = 4; @@ -652,13 +678,59 @@ static int patch_cmi9880(struct hda_codec *codec) } break; case CMI_ALLOUT: - default: spec->front_panel = 1; spec->multiout.max_channels = 8; spec->no_line_in = 1; spec->input_mux = &cmi9880_no_line_mux; spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; break; + case CMI_AUTO: + { + unsigned int port_e, port_f, port_g, port_h; + unsigned int port_spdifi, port_spdifo; + struct auto_pin_cfg cfg; + + /* collect pin default configuration */ + port_e = snd_hda_codec_get_pincfg(codec, 0x0f); + port_f = snd_hda_codec_get_pincfg(codec, 0x10); + spec->front_panel = 1; + if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE || + get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) { + port_g = snd_hda_codec_get_pincfg(codec, 0x1f); + port_h = snd_hda_codec_get_pincfg(codec, 0x20); + spec->channel_modes = cmi9880_channel_modes; + /* no front panel */ + if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE || + get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) { + /* no optional rear panel */ + spec->board_config = CMI_MINIMAL; + spec->front_panel = 0; + spec->num_channel_modes = 2; + } else { + spec->board_config = CMI_MIN_FP; + spec->num_channel_modes = 3; + } + spec->input_mux = &cmi9880_basic_mux; + spec->multiout.max_channels = cmi9880_channel_modes[0].channels; + } else { + spec->input_mux = &cmi9880_basic_mux; + port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13); + port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12); + if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE) + spec->multiout.dig_out_nid = CMI_DIG_OUT_NID; + if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE) + spec->dig_in_nid = CMI_DIG_IN_NID; + spec->multiout.max_channels = 8; + } + snd_hda_parse_pin_def_config(codec, &cfg, NULL); + if (cfg.line_outs) { + spec->multiout.max_channels = cfg.line_outs * 2; + cmi9880_fill_multi_dac_nids(codec, &cfg); + cmi9880_fill_multi_init(codec, &cfg); + } else + snd_printd("patch_cmedia: cannot detect association in defcfg\n"); + break; + } } spec->multiout.num_dacs = spec->num_dacs; diff --git a/trunk/sound/pci/hda/patch_conexant.c b/trunk/sound/pci/hda/patch_conexant.c index 7d941ef54172..009b77a693cf 100644 --- a/trunk/sound/pci/hda/patch_conexant.c +++ b/trunk/sound/pci/hda/patch_conexant.c @@ -33,9 +33,6 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" - -#define ENABLE_CXT_STATIC_QUIRKS #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -56,19 +53,27 @@ #define AUTO_MIC_PORTB (1 << 1) #define AUTO_MIC_PORTC (1 << 2) -struct conexant_spec { - struct hda_gen_spec gen; +struct pin_dac_pair { + hda_nid_t pin; + hda_nid_t dac; + int type; +}; - unsigned int beep_amp; +struct imux_info { + hda_nid_t pin; /* input pin NID */ + hda_nid_t adc; /* connected ADC NID */ + hda_nid_t boost; /* optional boost volume NID */ + int index; /* corresponding to autocfg.input */ +}; - /* extra EAPD pins */ - unsigned int num_eapds; - hda_nid_t eapds[4]; +struct conexant_spec { + struct hda_gen_spec gen; -#ifdef ENABLE_CXT_STATIC_QUIRKS const struct snd_kcontrol_new *mixers[5]; int num_mixers; hda_nid_t vmaster_nid; + struct hda_vmaster_mute_hook vmaster_mute; + bool vmaster_mute_led; const struct hda_verb *init_verbs[5]; /* initialization verbs * don't forget NULL @@ -85,6 +90,11 @@ struct conexant_spec { unsigned int hp_present; unsigned int line_present; unsigned int auto_mic; + int auto_mic_ext; /* imux_pins[] index for ext mic */ + int auto_mic_dock; /* imux_pins[] index for dock mic */ + int auto_mic_int; /* imux_pins[] index for int mic */ + unsigned int need_dac_fix; + hda_nid_t slave_dig_outs[2]; /* capture */ unsigned int num_adc_nids; @@ -112,13 +122,30 @@ struct conexant_spec { unsigned int spdif_route; + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct hda_input_mux private_imux; + struct imux_info imux_info[HDA_MAX_NUM_INPUTS]; + hda_nid_t private_adc_nids[HDA_MAX_NUM_INPUTS]; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + struct pin_dac_pair dac_info[8]; + int dac_info_filled; + unsigned int port_d_mode; + unsigned int auto_mute:1; /* used in auto-parser */ + unsigned int detect_line:1; /* Line-out detection enabled */ + unsigned int automute_lines:1; /* automute line-out as well */ + unsigned int automute_hp_lo:1; /* both HP and LO available */ unsigned int dell_automute:1; unsigned int dell_vostro:1; unsigned int ideapad:1; unsigned int thinkpad:1; unsigned int hp_laptop:1; unsigned int asus:1; + unsigned int pin_eapd_ctrls:1; + unsigned int fixup_stereo_dmic:1; + + unsigned int adc_switching:1; unsigned int ext_mic_present; unsigned int recording; @@ -134,48 +161,14 @@ struct conexant_spec { unsigned int dc_enable; unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */ unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ -#endif /* ENABLE_CXT_STATIC_QUIRKS */ -}; + unsigned int beep_amp; -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define set_beep_amp(spec, nid, idx, dir) \ - ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new cxt_beep_mixer[] = { - HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), - { } /* end */ + /* extra EAPD pins */ + unsigned int num_eapds; + hda_nid_t eapds[4]; }; -/* create beep controls if needed */ -static int add_beep_ctls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int err; - - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = cxt_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } - return 0; -} -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#define add_beep_ctls(codec) 0 -#endif - - -#ifdef ENABLE_CXT_STATIC_QUIRKS static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) @@ -344,6 +337,8 @@ static const struct hda_pcm_stream cx5051_pcm_analog_capture = { }, }; +static bool is_2_1_speaker(struct conexant_spec *spec); + static int conexant_build_pcms(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -358,6 +353,9 @@ static int conexant_build_pcms(struct hda_codec *codec) spec->multiout.max_channels; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + if (is_2_1_speaker(spec)) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; if (spec->capture_stream) info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->capture_stream; else { @@ -388,6 +386,8 @@ static int conexant_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } + if (spec->slave_dig_outs[0]) + codec->slave_dig_outs = spec->slave_dig_outs; } return 0; @@ -435,7 +435,7 @@ static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg, /* partial workaround for "azx_get_response timeout" */ if (power_state == AC_PWRST_D0) msleep(10); - snd_hda_codec_set_power_to_all(codec, fg, power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state, true); } static int conexant_init(struct hda_codec *codec) @@ -451,6 +451,7 @@ static int conexant_init(struct hda_codec *codec) static void conexant_free(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; + snd_hda_gen_free(&spec->gen); snd_hda_detach_beep_device(codec); kfree(spec); } @@ -466,6 +467,15 @@ static const struct snd_kcontrol_new cxt_capture_mixers[] = { {} }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* additional beep mixers; the actual parameters are overwritten at build */ +static const struct snd_kcontrol_new cxt_beep_mixer[] = { + HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), + { } /* end */ +}; +#endif + static const char * const slave_pfxs[] = { "Headphone", "Speaker", "Bass Speaker", "Front", "Surround", "CLFE", NULL @@ -514,9 +524,10 @@ static int conexant_build_controls(struct hda_codec *codec) } if (spec->vmaster_nid && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_pfxs, - "Playback Switch"); + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch", true, + &spec->vmaster_mute.sw_kctl); if (err < 0) return err; } @@ -527,9 +538,22 @@ static int conexant_build_controls(struct hda_codec *codec) return err; } - err = add_beep_ctls(codec); - if (err < 0) - return err; +#ifdef CONFIG_SND_HDA_INPUT_BEEP + /* create beep controls if needed */ + if (spec->beep_amp) { + const struct snd_kcontrol_new *knew; + for (knew = cxt_beep_mixer; knew->name; knew++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + } + } +#endif return 0; } @@ -542,6 +566,13 @@ static const struct hda_codec_ops conexant_patch_ops = { .set_power_state = conexant_set_power, }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +#define set_beep_amp(spec, nid, idx, dir) \ + ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif + static int patch_conexant_auto(struct hda_codec *codec); /* * EAPD control @@ -625,6 +656,8 @@ static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol, int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, spec->num_channel_mode, &spec->multiout.max_channels); + if (err >= 0 && spec->need_dac_fix) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; return err; } @@ -2463,6 +2496,10 @@ static void conexant_check_dig_outs(struct hda_codec *codec, continue; if (snd_hda_get_connections(codec, *dig_pins, nid_loc, 1) != 1) continue; + if (spec->slave_dig_outs[0]) + nid_loc++; + else + nid_loc = spec->slave_dig_outs; } } @@ -3104,152 +3141,1308 @@ static int patch_cxt5066(struct hda_codec *codec) return 0; } -#endif /* ENABLE_CXT_STATIC_QUIRKS */ - - /* * Automatic parser for CX20641 & co */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP -static void cx_auto_parse_beep(struct hda_codec *codec) +static int cx_auto_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t adc = spec->imux_info[spec->cur_mux[0]].adc; + if (spec->adc_switching) { + spec->cur_adc = adc; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + } + snd_hda_codec_setup_stream(codec, adc, stream_tag, 0, format); + return 0; +} - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { - set_beep_amp(spec, nid, 0, HDA_OUTPUT); +static int cx_auto_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + return 0; +} + +static const struct hda_pcm_stream cx_auto_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = cx_auto_capture_pcm_prepare, + .cleanup = cx_auto_capture_pcm_cleanup + }, +}; + +static const hda_nid_t cx_auto_adc_nids[] = { 0x14 }; + +#define get_connection_index(codec, mux, nid)\ + snd_hda_get_conn_index(codec, mux, nid, 0) + +/* get an unassigned DAC from the given list. + * Return the nid if found and reduce the DAC list, or return zero if + * not found + */ +static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t *dacs, int *num_dacs) +{ + int i, nums = *num_dacs; + hda_nid_t ret = 0; + + for (i = 0; i < nums; i++) { + if (get_connection_index(codec, pin, dacs[i]) >= 0) { + ret = dacs[i]; break; } + } + if (!ret) + return 0; + if (--nums > 0) + memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t)); + *num_dacs = nums; + return ret; } -#else -#define cx_auto_parse_beep(codec) -#endif -/* parse EAPDs */ -static void cx_auto_parse_eapd(struct hda_codec *codec) +#define MAX_AUTO_DACS 5 + +#define DAC_SLAVE_FLAG 0x8000 /* filled dac is a slave */ + +/* fill analog DAC list from the widget tree */ +static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs) { - struct conexant_spec *spec = codec->spec; hda_nid_t nid, end_nid; + int nums = 0; end_nid = codec->start_nid + codec->num_nodes; for (nid = codec->start_nid; nid < end_nid; nid++) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(wcaps); + if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) { + dacs[nums++] = nid; + if (nums >= MAX_AUTO_DACS) + break; + } + } + return nums; +} + +/* fill pin_dac_pair list from the pin and dac list */ +static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins, + int num_pins, hda_nid_t *dacs, int *rest, + struct pin_dac_pair *filled, int nums, + int type) +{ + int i, start = nums; + + for (i = 0; i < num_pins; i++, nums++) { + filled[nums].pin = pins[i]; + filled[nums].type = type; + filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest); + if (filled[nums].dac) continue; - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) + if (filled[start].dac && get_connection_index(codec, pins[i], filled[start].dac) >= 0) { + filled[nums].dac = filled[start].dac | DAC_SLAVE_FLAG; continue; - spec->eapds[spec->num_eapds++] = nid; - if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) + } + if (filled[0].dac && get_connection_index(codec, pins[i], filled[0].dac) >= 0) { + filled[nums].dac = filled[0].dac | DAC_SLAVE_FLAG; + continue; + } + snd_printdd("Failed to find a DAC for pin 0x%x", pins[i]); + } + return nums; +} + +/* parse analog output paths */ +static void cx_auto_parse_output(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t dacs[MAX_AUTO_DACS]; + int i, j, nums, rest; + + rest = fill_cx_auto_dacs(codec, dacs); + /* parse all analog output pins */ + nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs, + dacs, &rest, spec->dac_info, 0, + AUTO_PIN_LINE_OUT); + nums = fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs, + dacs, &rest, spec->dac_info, nums, + AUTO_PIN_HP_OUT); + nums = fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs, + dacs, &rest, spec->dac_info, nums, + AUTO_PIN_SPEAKER_OUT); + spec->dac_info_filled = nums; + /* fill multiout struct */ + for (i = 0; i < nums; i++) { + hda_nid_t dac = spec->dac_info[i].dac; + if (!dac || (dac & DAC_SLAVE_FLAG)) + continue; + switch (spec->dac_info[i].type) { + case AUTO_PIN_LINE_OUT: + spec->private_dac_nids[spec->multiout.num_dacs] = dac; + spec->multiout.num_dacs++; + break; + case AUTO_PIN_HP_OUT: + case AUTO_PIN_SPEAKER_OUT: + if (!spec->multiout.hp_nid) { + spec->multiout.hp_nid = dac; + break; + } + for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++) + if (!spec->multiout.extra_out_nid[j]) { + spec->multiout.extra_out_nid[j] = dac; + break; + } break; + } } + spec->multiout.dac_nids = spec->private_dac_nids; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; - /* NOTE: below is a wild guess; if we have more than two EAPDs, - * it's a new chip, where EAPDs are supposed to be associated to - * pins, and we can control EAPD per pin. - * OTOH, if only one or two EAPDs are found, it's an old chip, - * thus it might control over all pins. - */ - if (spec->num_eapds > 2) - spec->gen.own_eapd_ctl = 1; + for (i = 0; i < cfg->hp_outs; i++) { + if (is_jack_detectable(codec, cfg->hp_pins[i])) { + spec->auto_mute = 1; + break; + } + } + if (spec->auto_mute && + cfg->line_out_pins[0] && + cfg->line_out_type != AUTO_PIN_SPEAKER_OUT && + cfg->line_out_pins[0] != cfg->hp_pins[0] && + cfg->line_out_pins[0] != cfg->speaker_pins[0]) { + for (i = 0; i < cfg->line_outs; i++) { + if (is_jack_detectable(codec, cfg->line_out_pins[i])) { + spec->detect_line = 1; + break; + } + } + spec->automute_lines = spec->detect_line; + } + + spec->vmaster_nid = spec->private_dac_nids[0]; } static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on) + hda_nid_t *pins, bool on); + +static void do_automute(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, bool on) { + struct conexant_spec *spec = codec->spec; int i; + for (i = 0; i < num_pins; i++) + snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0); + if (spec->pin_eapd_ctrls) + cx_auto_turn_eapd(codec, num_pins, pins, on); +} + +static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) +{ + int i, present = 0; + for (i = 0; i < num_pins; i++) { - if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_EAPD_BTLENABLE, - on ? 0x02 : 0); + hda_nid_t nid = pins[i]; + if (!nid || !is_jack_detectable(codec, nid)) + break; + present |= snd_hda_jack_detect(codec, nid); } + return present; } -/* turn on/off EAPD according to Master switch */ -static void cx_auto_vmaster_hook(void *private_data, int enabled) +/* auto-mute/unmute speaker and line outs according to headphone jack */ +static void cx_auto_update_speakers(struct hda_codec *codec) { - struct hda_codec *codec = private_data; struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int on = 1; + + /* turn on HP EAPD when HP jacks are present */ + if (spec->pin_eapd_ctrls) { + if (spec->auto_mute) + on = spec->hp_present; + cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); + } - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); + /* mute speakers in auto-mode if HP or LO jacks are plugged */ + if (spec->auto_mute) + on = !(spec->hp_present || + (spec->detect_line && spec->line_present)); + do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, on); + + /* toggle line-out mutes if needed, too */ + /* if LO is a copy of either HP or Speaker, don't need to handle it */ + if (cfg->line_out_pins[0] == cfg->hp_pins[0] || + cfg->line_out_pins[0] == cfg->speaker_pins[0]) + return; + if (spec->auto_mute) { + /* mute LO in auto-mode when HP jack is present */ + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT || + spec->automute_lines) + on = !spec->hp_present; + else + on = 1; + } + do_automute(codec, cfg->line_outs, cfg->line_out_pins, on); } -static int cx_auto_build_controls(struct hda_codec *codec) +static void cx_auto_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { - int err; + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; + if (!spec->auto_mute) + return; + spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins); + cx_auto_update_speakers(codec); +} - err = add_beep_ctls(codec); - if (err < 0) - return err; +static void cx_auto_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; - return 0; + if (!spec->auto_mute || !spec->detect_line) + return; + spec->line_present = detect_jacks(codec, cfg->line_outs, + cfg->line_out_pins); + cx_auto_update_speakers(codec); } -static const struct hda_codec_ops cx_auto_patch_ops = { - .build_controls = cx_auto_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM - .check_power_status = snd_hda_gen_check_power_status, -#endif -}; +static int cx_automute_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + static const char * const texts3[] = { + "Disabled", "Speaker Only", "Line Out+Speaker" + }; -/* - * pin fix-up - */ -enum { - CXT_PINCFG_LENOVO_X200, - CXT_PINCFG_LENOVO_TP410, - CXT_PINCFG_LEMOTE_A1004, - CXT_PINCFG_LEMOTE_A1205, - CXT_FIXUP_STEREO_DMIC, - CXT_FIXUP_INC_MIC_BOOST, -}; + if (spec->automute_hp_lo) + return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); + return snd_hda_enum_bool_helper_info(kcontrol, uinfo); +} -static void cxt_fixup_stereo_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static int cx_automute_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - spec->gen.inv_dmic_split = 1; + unsigned int val; + if (!spec->auto_mute) + val = 0; + else if (!spec->automute_lines) + val = 1; + else + val = 2; + ucontrol->value.enumerated.item[0] = val; + return 0; } -static void cxt5066_increase_mic_boost(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static int cx_automute_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; - snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT, - (0x3 << AC_AMPCAP_OFFSET_SHIFT) | - (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); + switch (ucontrol->value.enumerated.item[0]) { + case 0: + if (!spec->auto_mute) + return 0; + spec->auto_mute = 0; + break; + case 1: + if (spec->auto_mute && !spec->automute_lines) + return 0; + spec->auto_mute = 1; + spec->automute_lines = 0; + break; + case 2: + if (!spec->automute_hp_lo) + return -EINVAL; + if (spec->auto_mute && spec->automute_lines) + return 0; + spec->auto_mute = 1; + spec->automute_lines = 1; + break; + default: + return -EINVAL; + } + cx_auto_update_speakers(codec); + return 1; } -/* ThinkPad X200 & co with cxt5051 */ -static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { - { 0x16, 0x042140ff }, /* HP (seq# overridden) */ - { 0x17, 0x21a11000 }, /* dock-mic */ - { 0x19, 0x2121103f }, /* dock-HP */ - { 0x1c, 0x21440100 }, /* dock SPDIF out */ - {} +static const struct snd_kcontrol_new cx_automute_mode_enum[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Auto-Mute Mode", + .info = cx_automute_mode_info, + .get = cx_automute_mode_get, + .put = cx_automute_mode_put, + }, + { } }; -/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */ -static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = { - { 0x19, 0x042110ff }, /* HP (seq# overridden) */ - { 0x1a, 0x21a190f0 }, /* dock-mic */ - { 0x1c, 0x212140ff }, /* dock-HP */ - {} -}; +static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + + return snd_hda_input_mux_info(&spec->private_imux, uinfo); +} + +static int cx_auto_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_mux[0]; + return 0; +} + +/* look for the route the given pin from mux and return the index; + * if do_select is set, actually select the route. + */ +static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t pin, hda_nid_t *srcp, + bool do_select, int depth) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int startidx, i, nums; + + switch (get_wcaps_type(get_wcaps(codec, mux))) { + case AC_WID_AUD_IN: + case AC_WID_AUD_SEL: + case AC_WID_AUD_MIX: + break; + default: + return -1; + } + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == pin) { + if (do_select) + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_CONNECT_SEL, i); + if (srcp) + *srcp = mux; + return i; + } + depth++; + if (depth == 2) + return -1; + + /* Try to rotate around connections to avoid one boost controlling + another input path as well */ + startidx = 0; + for (i = 0; i < spec->private_imux.num_items; i++) + if (spec->imux_info[i].pin == pin) { + startidx = i; + break; + } + + for (i = 0; i < nums; i++) { + int j = (i + startidx) % nums; + int ret = __select_input_connection(codec, conn[j], pin, srcp, + do_select, depth); + if (ret >= 0) { + if (do_select) + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_CONNECT_SEL, j); + return j; + } + } + return -1; +} + +static void select_input_connection(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t pin) +{ + __select_input_connection(codec, mux, pin, NULL, true, 0); +} + +static int get_input_connection(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t pin) +{ + return __select_input_connection(codec, mux, pin, NULL, false, 0); +} + +static int cx_auto_mux_enum_update(struct hda_codec *codec, + const struct hda_input_mux *imux, + unsigned int idx) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t adc; + int changed = 1; + + if (!imux->num_items) + return 0; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->cur_mux[0] == idx) + changed = 0; + adc = spec->imux_info[idx].adc; + select_input_connection(codec, spec->imux_info[idx].adc, + spec->imux_info[idx].pin); + if (spec->cur_adc && spec->cur_adc != adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = adc; + snd_hda_codec_setup_stream(codec, adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } + spec->cur_mux[0] = idx; + return changed; +} + +static int cx_auto_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + + return cx_auto_mux_enum_update(codec, &spec->private_imux, + ucontrol->value.enumerated.item[0]); +} + +static const struct snd_kcontrol_new cx_auto_capture_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = cx_auto_mux_enum_info, + .get = cx_auto_mux_enum_get, + .put = cx_auto_mux_enum_put + }, + {} +}; + +static bool select_automic(struct hda_codec *codec, int idx, bool detect) +{ + struct conexant_spec *spec = codec->spec; + if (idx < 0) + return false; + if (detect && !snd_hda_jack_detect(codec, spec->imux_info[idx].pin)) + return false; + cx_auto_mux_enum_update(codec, &spec->private_imux, idx); + return true; +} + +/* automatic switch internal and external mic */ +static void cx_auto_automic(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct conexant_spec *spec = codec->spec; + + if (!spec->auto_mic) + return; + if (!select_automic(codec, spec->auto_mic_ext, true)) + if (!select_automic(codec, spec->auto_mic_dock, true)) + select_automic(codec, spec->auto_mic_int, false); +} + +/* check whether the pin config is suitable for auto-mic switching; + * auto-mic is enabled only when one int-mic and one ext- and/or + * one dock-mic exist + */ +static void cx_auto_check_auto_mic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int pset[INPUT_PIN_ATTR_NORMAL + 1]; + int i; + + for (i = 0; i < ARRAY_SIZE(pset); i++) + pset[i] = -1; + for (i = 0; i < spec->private_imux.num_items; i++) { + hda_nid_t pin = spec->imux_info[i].pin; + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); + int type, attr; + attr = snd_hda_get_input_pin_attr(def_conf); + if (attr == INPUT_PIN_ATTR_UNUSED) + return; /* invalid entry */ + if (attr > INPUT_PIN_ATTR_NORMAL) + attr = INPUT_PIN_ATTR_NORMAL; + if (attr != INPUT_PIN_ATTR_INT && + !is_jack_detectable(codec, pin)) + return; /* non-detectable pin */ + type = get_defcfg_device(def_conf); + if (type != AC_JACK_MIC_IN && + (attr != INPUT_PIN_ATTR_DOCK || type != AC_JACK_LINE_IN)) + return; /* no valid input type */ + if (pset[attr] >= 0) + return; /* already occupied */ + pset[attr] = i; + } + if (pset[INPUT_PIN_ATTR_INT] < 0 || + (pset[INPUT_PIN_ATTR_NORMAL] < 0 && pset[INPUT_PIN_ATTR_DOCK])) + return; /* no input to switch*/ + spec->auto_mic = 1; + spec->auto_mic_ext = pset[INPUT_PIN_ATTR_NORMAL]; + spec->auto_mic_dock = pset[INPUT_PIN_ATTR_DOCK]; + spec->auto_mic_int = pset[INPUT_PIN_ATTR_INT]; +} + +static void cx_auto_parse_input(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux; + int i, j; + + imux = &spec->private_imux; + for (i = 0; i < cfg->num_inputs; i++) { + for (j = 0; j < spec->num_adc_nids; j++) { + hda_nid_t adc = spec->adc_nids[j]; + int idx = get_input_connection(codec, adc, + cfg->inputs[i].pin); + if (idx >= 0) { + const char *label; + label = hda_get_autocfg_input_label(codec, cfg, i); + spec->imux_info[imux->num_items].index = i; + spec->imux_info[imux->num_items].boost = 0; + spec->imux_info[imux->num_items].adc = adc; + spec->imux_info[imux->num_items].pin = + cfg->inputs[i].pin; + snd_hda_add_imux_item(imux, label, idx, NULL); + break; + } + } + } + if (imux->num_items >= 2 && cfg->num_inputs == imux->num_items) + cx_auto_check_auto_mic(codec); + if (imux->num_items > 1) { + for (i = 1; i < imux->num_items; i++) { + if (spec->imux_info[i].adc != spec->imux_info[0].adc) { + spec->adc_switching = 1; + break; + } + } + } +} + +/* get digital-input audio widget corresponding to the given pin */ +static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(wcaps); + if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) { + if (get_connection_index(codec, nid, pin) >= 0) + return nid; + } + } + return 0; +} + +static void cx_auto_parse_digital(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + + if (cfg->dig_outs && + snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1) + spec->multiout.dig_out_nid = nid; + if (cfg->dig_in_pin) + spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin); +} + +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static void cx_auto_parse_beep(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { + set_beep_amp(spec, nid, 0, HDA_OUTPUT); + break; + } +} +#else +#define cx_auto_parse_beep(codec) +#endif + +/* parse EAPDs */ +static void cx_auto_parse_eapd(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + continue; + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) + continue; + spec->eapds[spec->num_eapds++] = nid; + if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) + break; + } + + /* NOTE: below is a wild guess; if we have more than two EAPDs, + * it's a new chip, where EAPDs are supposed to be associated to + * pins, and we can control EAPD per pin. + * OTOH, if only one or two EAPDs are found, it's an old chip, + * thus it might control over all pins. + */ + spec->pin_eapd_ctrls = spec->num_eapds > 2; +} + +static int cx_auto_parse_auto_config(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + + cx_auto_parse_output(codec); + cx_auto_parse_input(codec); + cx_auto_parse_digital(codec); + cx_auto_parse_beep(codec); + cx_auto_parse_eapd(codec); + return 0; +} + +static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, bool on) +{ + int i; + for (i = 0; i < num_pins; i++) { + if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_EAPD_BTLENABLE, + on ? 0x02 : 0); + } +} + +static void select_connection(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t src) +{ + int idx = get_connection_index(codec, pin, src); + if (idx >= 0) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_CONNECT_SEL, idx); +} + +static void mute_outputs(struct hda_codec *codec, int num_nids, + const hda_nid_t *nids) +{ + int i, val; + + for (i = 0; i < num_nids; i++) { + hda_nid_t nid = nids[i]; + if (!(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + continue; + if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE) + val = AMP_OUT_MUTE; + else + val = AMP_OUT_ZERO; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } +} + +static void enable_unsol_pins(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, unsigned int action, + hda_jack_callback cb) +{ + int i; + for (i = 0; i < num_pins; i++) + snd_hda_jack_detect_enable_callback(codec, pins[i], action, cb); +} + +static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return true; + return false; +} + +/* is the given NID found in any of autocfg items? */ +static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid) +{ + int i; + + if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || + found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || + found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) || + found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs)) + return true; + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].pin == nid) + return true; + if (cfg->dig_in_pin == nid) + return true; + return false; +} + +/* clear unsol-event tags on unused pins; Conexant codecs seem to leave + * invalid unsol tags by some reason + */ +static void clear_unsol_on_unused_pins(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + if (!found_in_autocfg(cfg, pin->nid)) + snd_hda_codec_write(codec, pin->nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, 0); + } +} + +/* turn on/off EAPD according to Master switch */ +static void cx_auto_vmaster_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct conexant_spec *spec = codec->spec; + + if (enabled && spec->pin_eapd_ctrls) { + cx_auto_update_speakers(codec); + return; + } + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); +} + +static void cx_auto_init_output(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + int i; + + mute_outputs(codec, spec->multiout.num_dacs, spec->multiout.dac_nids); + for (i = 0; i < cfg->hp_outs; i++) { + unsigned int val = PIN_OUT; + if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) & + AC_PINCAP_HP_DRV) + val |= AC_PINCTL_HP_EN; + snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val); + } + mute_outputs(codec, cfg->hp_outs, cfg->hp_pins); + mute_outputs(codec, cfg->line_outs, cfg->line_out_pins); + mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins); + for (i = 0; i < spec->dac_info_filled; i++) { + nid = spec->dac_info[i].dac; + if (!nid) + nid = spec->multiout.dac_nids[0]; + else if (nid & DAC_SLAVE_FLAG) + nid &= ~DAC_SLAVE_FLAG; + select_connection(codec, spec->dac_info[i].pin, nid); + } + if (spec->auto_mute) { + enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins, + CONEXANT_HP_EVENT, cx_auto_hp_automute); + spec->hp_present = detect_jacks(codec, cfg->hp_outs, + cfg->hp_pins); + if (spec->detect_line) { + enable_unsol_pins(codec, cfg->line_outs, + cfg->line_out_pins, + CONEXANT_LINE_EVENT, + cx_auto_line_automute); + spec->line_present = + detect_jacks(codec, cfg->line_outs, + cfg->line_out_pins); + } + } + cx_auto_update_speakers(codec); + /* turn on all EAPDs if no individual EAPD control is available */ + if (!spec->pin_eapd_ctrls) + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); + clear_unsol_on_unused_pins(codec); +} + +static void cx_auto_init_input(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, val; + + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t nid = spec->adc_nids[i]; + if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + continue; + if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) + val = AMP_IN_MUTE(0); + else + val = AMP_IN_UNMUTE(0); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + val); + } + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + unsigned int type = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + type |= snd_hda_get_default_vref(codec, pin); + snd_hda_set_pin_ctl(codec, pin, type); + } + + if (spec->auto_mic) { + if (spec->auto_mic_ext >= 0) { + snd_hda_jack_detect_enable_callback(codec, + cfg->inputs[spec->auto_mic_ext].pin, + CONEXANT_MIC_EVENT, cx_auto_automic); + } + if (spec->auto_mic_dock >= 0) { + snd_hda_jack_detect_enable_callback(codec, + cfg->inputs[spec->auto_mic_dock].pin, + CONEXANT_MIC_EVENT, cx_auto_automic); + } + cx_auto_automic(codec, NULL); + } else { + select_input_connection(codec, spec->imux_info[0].adc, + spec->imux_info[0].pin); + } +} + +static void cx_auto_init_digital(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (spec->multiout.dig_out_nid) + snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT); + if (spec->dig_in_nid) + snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN); +} + +static int cx_auto_init(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + snd_hda_gen_apply_verbs(codec); + cx_auto_init_output(codec); + cx_auto_init_input(codec); + cx_auto_init_digital(codec); + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + return 0; +} + +static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, + const char *dir, int cidx, + hda_nid_t nid, int hda_dir, int amp_idx, int chs) +{ + static char name[44]; + static struct snd_kcontrol_new knew[] = { + HDA_CODEC_VOLUME(name, 0, 0, 0), + HDA_CODEC_MUTE(name, 0, 0, 0), + }; + static const char * const sfx[2] = { "Volume", "Switch" }; + int i, err; + + for (i = 0; i < 2; i++) { + struct snd_kcontrol *kctl; + knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx, + hda_dir); + knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; + knew[i].index = cidx; + snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]); + kctl = snd_ctl_new1(&knew[i], codec); + if (!kctl) + return -ENOMEM; + err = snd_hda_ctl_add(codec, nid, kctl); + if (err < 0) + return err; + if (!(query_amp_caps(codec, nid, hda_dir) & + (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))) + break; + } + return 0; +} + +#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \ + cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3) + +#define cx_auto_add_pb_volume(codec, nid, str, idx) \ + cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) + +static int try_add_pb_volume(struct hda_codec *codec, hda_nid_t dac, + hda_nid_t pin, const char *name, int idx) +{ + unsigned int caps; + if (dac && !(dac & DAC_SLAVE_FLAG)) { + caps = query_amp_caps(codec, dac, HDA_OUTPUT); + if (caps & AC_AMPCAP_NUM_STEPS) + return cx_auto_add_pb_volume(codec, dac, name, idx); + } + caps = query_amp_caps(codec, pin, HDA_OUTPUT); + if (caps & AC_AMPCAP_NUM_STEPS) + return cx_auto_add_pb_volume(codec, pin, name, idx); + return 0; +} + +static bool is_2_1_speaker(struct conexant_spec *spec) +{ + int i, type, num_spk = 0; + + for (i = 0; i < spec->dac_info_filled; i++) { + type = spec->dac_info[i].type; + if (type == AUTO_PIN_LINE_OUT) + type = spec->autocfg.line_out_type; + if (type == AUTO_PIN_SPEAKER_OUT) + num_spk++; + } + return (num_spk == 2 && spec->autocfg.line_out_type != AUTO_PIN_LINE_OUT); +} + +static int cx_auto_build_output_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int i, err; + int num_line = 0, num_hp = 0, num_spk = 0; + bool speaker_2_1; + static const char * const texts[3] = { "Front", "Surround", "CLFE" }; + + if (spec->dac_info_filled == 1) + return try_add_pb_volume(codec, spec->dac_info[0].dac, + spec->dac_info[0].pin, + "Master", 0); + + speaker_2_1 = is_2_1_speaker(spec); + + for (i = 0; i < spec->dac_info_filled; i++) { + const char *label; + int idx, type; + hda_nid_t dac = spec->dac_info[i].dac; + type = spec->dac_info[i].type; + if (type == AUTO_PIN_LINE_OUT) + type = spec->autocfg.line_out_type; + switch (type) { + case AUTO_PIN_LINE_OUT: + default: + label = texts[num_line++]; + idx = 0; + break; + case AUTO_PIN_HP_OUT: + label = "Headphone"; + idx = num_hp++; + break; + case AUTO_PIN_SPEAKER_OUT: + if (speaker_2_1) { + label = num_spk++ ? "Bass Speaker" : "Speaker"; + idx = 0; + } else { + label = "Speaker"; + idx = num_spk++; + } + break; + } + err = try_add_pb_volume(codec, dac, + spec->dac_info[i].pin, + label, idx); + if (err < 0) + return err; + } + + if (spec->auto_mute) { + err = snd_hda_add_new_ctls(codec, cx_automute_mode_enum); + if (err < 0) + return err; + } + + return 0; +} + +/* Returns zero if this is a normal stereo channel, and non-zero if it should + be split in two independent channels. + dest_label must be at least 44 characters. */ +static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label, + char *dest_label, int nid) +{ + struct conexant_spec *spec = codec->spec; + int i; + + if (!spec->fixup_stereo_dmic) + return 0; + + for (i = 0; i < AUTO_CFG_MAX_INS; i++) { + int def_conf; + if (spec->autocfg.inputs[i].pin != nid) + continue; + + if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC) + return 0; + def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) + return 0; + + /* Finally found the inverted internal mic! */ + snprintf(dest_label, 44, "Inverted %s", label); + return 1; + } + return 0; +} + +static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, + const char *label, const char *pfx, + int cidx) +{ + struct conexant_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_adc_nids; i++) { + char rightch_label[44]; + hda_nid_t adc_nid = spec->adc_nids[i]; + int idx = get_input_connection(codec, adc_nid, nid); + if (idx < 0) + continue; + if (codec->single_adc_amp) + idx = 0; + + if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { + /* Make two independent kcontrols for left and right */ + int err = cx_auto_add_volume_idx(codec, label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 1); + if (err < 0) + return err; + return cx_auto_add_volume_idx(codec, rightch_label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 2); + } + return cx_auto_add_volume_idx(codec, label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 3); + } + return 0; +} + +static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx, + const char *label, int cidx) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t mux, nid; + int i, con; + + nid = spec->imux_info[idx].pin; + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + char rightch_label[44]; + if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { + int err = cx_auto_add_volume_idx(codec, label, " Boost", + cidx, nid, HDA_INPUT, 0, 1); + if (err < 0) + return err; + return cx_auto_add_volume_idx(codec, rightch_label, " Boost", + cidx, nid, HDA_INPUT, 0, 2); + } + return cx_auto_add_volume(codec, label, " Boost", cidx, + nid, HDA_INPUT); + } + con = __select_input_connection(codec, spec->imux_info[idx].adc, nid, + &mux, false, 0); + if (con < 0) + return 0; + for (i = 0; i < idx; i++) { + if (spec->imux_info[i].boost == mux) + return 0; /* already present */ + } + + if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) { + spec->imux_info[idx].boost = mux; + return cx_auto_add_volume(codec, label, " Boost", cidx, + mux, HDA_OUTPUT); + } + return 0; +} + +static int cx_auto_build_input_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux; + const char *prev_label; + int input_conn[HDA_MAX_NUM_INPUTS]; + int i, j, err, cidx; + int multi_connection; + + if (!imux->num_items) + return 0; + + multi_connection = 0; + for (i = 0; i < imux->num_items; i++) { + cidx = get_input_connection(codec, spec->imux_info[i].adc, + spec->imux_info[i].pin); + if (cidx < 0) + continue; + input_conn[i] = spec->imux_info[i].adc; + if (!codec->single_adc_amp) + input_conn[i] |= cidx << 8; + if (i > 0 && input_conn[i] != input_conn[0]) + multi_connection = 1; + } + + prev_label = NULL; + cidx = 0; + for (i = 0; i < imux->num_items; i++) { + hda_nid_t nid = spec->imux_info[i].pin; + const char *label; + + label = hda_get_autocfg_input_label(codec, &spec->autocfg, + spec->imux_info[i].index); + if (label == prev_label) + cidx++; + else + cidx = 0; + prev_label = label; + + err = cx_auto_add_boost_volume(codec, i, label, cidx); + if (err < 0) + return err; + + if (!multi_connection) { + if (i > 0) + continue; + err = cx_auto_add_capture_volume(codec, nid, + "Capture", "", cidx); + } else { + bool dup_found = false; + for (j = 0; j < i; j++) { + if (input_conn[j] == input_conn[i]) { + dup_found = true; + break; + } + } + if (dup_found) + continue; + err = cx_auto_add_capture_volume(codec, nid, + label, " Capture", cidx); + } + if (err < 0) + return err; + } + + if (spec->private_imux.num_items > 1 && !spec->auto_mic) { + err = snd_hda_add_new_ctls(codec, cx_auto_capture_mixers); + if (err < 0) + return err; + } + + return 0; +} + +static int cx_auto_build_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int err; + + err = cx_auto_build_output_controls(codec); + if (err < 0) + return err; + err = cx_auto_build_input_controls(codec); + if (err < 0) + return err; + err = conexant_build_controls(codec); + if (err < 0) + return err; + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + if (spec->vmaster_mute.sw_kctl) { + spec->vmaster_mute.hook = cx_auto_vmaster_hook; + err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, + spec->vmaster_mute_led); + if (err < 0) + return err; + } + return 0; +} + +static int cx_auto_search_adcs(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int caps = get_wcaps(codec, nid); + if (get_wcaps_type(caps) != AC_WID_AUD_IN) + continue; + if (caps & AC_WCAP_DIGITAL) + continue; + if (snd_BUG_ON(spec->num_adc_nids >= + ARRAY_SIZE(spec->private_adc_nids))) + break; + spec->private_adc_nids[spec->num_adc_nids++] = nid; + } + spec->adc_nids = spec->private_adc_nids; + return 0; +} + +static const struct hda_codec_ops cx_auto_patch_ops = { + .build_controls = cx_auto_build_controls, + .build_pcms = conexant_build_pcms, + .init = cx_auto_init, + .free = conexant_free, + .unsol_event = snd_hda_jack_unsol_event, +}; + +/* + * pin fix-up + */ +enum { + CXT_PINCFG_LENOVO_X200, + CXT_PINCFG_LENOVO_TP410, + CXT_PINCFG_LEMOTE_A1004, + CXT_PINCFG_LEMOTE_A1205, + CXT_FIXUP_STEREO_DMIC, + CXT_FIXUP_INC_MIC_BOOST, +}; + +static void cxt_fixup_stereo_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + spec->fixup_stereo_dmic = 1; +} + +static void cxt5066_increase_mic_boost(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT, + (0x3 << AC_AMPCAP_OFFSET_SHIFT) | + (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); +} + +/* ThinkPad X200 & co with cxt5051 */ +static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { + { 0x16, 0x042140ff }, /* HP (seq# overridden) */ + { 0x17, 0x21a11000 }, /* dock-mic */ + { 0x19, 0x2121103f }, /* dock-HP */ + { 0x1c, 0x21440100 }, /* dock SPDIF out */ + {} +}; + +/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */ +static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = { + { 0x19, 0x042110ff }, /* HP (seq# overridden) */ + { 0x1a, 0x21a190f0 }, /* dock-mic */ + { 0x1c, 0x212140ff }, /* dock-HP */ + {} +}; /* Lemote A1004/A1205 with cxt5066 */ static const struct hda_pintbl cxt_pincfg_lemote[] = { @@ -3339,22 +4532,13 @@ static int patch_conexant_auto(struct hda_codec *codec) spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; - snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; - - cx_auto_parse_beep(codec); - cx_auto_parse_eapd(codec); - if (spec->gen.own_eapd_ctl) - spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; + snd_hda_gen_init(&spec->gen); switch (codec->vendor_id) { case 0x14f15045: codec->single_adc_amp = 1; break; - case 0x14f15047: - codec->pin_amp_workaround = 1; - spec->gen.mixer_nid = 0x19; - break; case 0x14f15051: add_cx5051_fake_mutes(codec); codec->pin_amp_workaround = 1; @@ -3366,6 +4550,8 @@ static int patch_conexant_auto(struct hda_codec *codec) break; } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + /* Show mute-led control only on HP laptops * This is a sort of white-list: on HP laptops, EAPD corresponds * only to the mute-LED without actualy amp function. Meanwhile, @@ -3374,20 +4560,20 @@ static int patch_conexant_auto(struct hda_codec *codec) */ switch (codec->subsystem_id >> 16) { case 0x103c: - spec->gen.vmaster_mute_enum = 1; + spec->vmaster_mute_led = 1; break; } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + err = cx_auto_search_adcs(codec); if (err < 0) - goto error; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - goto error; - + return err; + err = cx_auto_parse_auto_config(codec); + if (err < 0) { + kfree(codec->spec); + codec->spec = NULL; + return err; + } + spec->capture_stream = &cx_auto_pcm_analog_capture; codec->patch_ops = cx_auto_patch_ops; if (spec->beep_amp) snd_hda_attach_beep_device(codec, spec->beep_amp); @@ -3404,19 +4590,8 @@ static int patch_conexant_auto(struct hda_codec *codec) } return 0; - - error: - snd_hda_gen_free(codec); - return err; } -#ifndef ENABLE_CXT_STATIC_QUIRKS -#define patch_cxt5045 patch_conexant_auto -#define patch_cxt5047 patch_conexant_auto -#define patch_cxt5051 patch_conexant_auto -#define patch_cxt5066 patch_conexant_auto -#endif - /* */ diff --git a/trunk/sound/pci/hda/patch_hdmi.c b/trunk/sound/pci/hda/patch_hdmi.c index 1b3b4ee3e8a7..807a2aa1ff38 100644 --- a/trunk/sound/pci/hda/patch_hdmi.c +++ b/trunk/sound/pci/hda/patch_hdmi.c @@ -1100,12 +1100,8 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, if (!static_hdmi_pcm && eld->eld_valid) { snd_hdmi_eld_update_pcm_info(eld, hinfo); if (hinfo->channels_min > hinfo->channels_max || - !hinfo->rates || !hinfo->formats) { - per_cvt->assigned = 0; - hinfo->nid = 0; - snd_hda_spdif_ctls_unassign(codec, pin_idx); + !hinfo->rates || !hinfo->formats) return -ENODEV; - } } /* Store the updated parameters */ diff --git a/trunk/sound/pci/hda/patch_realtek.c b/trunk/sound/pci/hda/patch_realtek.c index dc75607fc05b..cf3886171109 100644 --- a/trunk/sound/pci/hda/patch_realtek.c +++ b/trunk/sound/pci/hda/patch_realtek.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -36,10 +35,12 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" /* unsol event tags */ -#define ALC_DCVOL_EVENT 0x08 +#define ALC_FRONT_EVENT 0x01 +#define ALC_DCVOL_EVENT 0x02 +#define ALC_HP_EVENT 0x04 +#define ALC_MIC_EVENT 0x08 /* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -66,42 +67,355 @@ struct alc_customize_define { unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ }; +struct alc_multi_io { + hda_nid_t pin; /* multi-io widget pin NID */ + hda_nid_t dac; /* DAC to be connected */ + unsigned int ctl_in; /* cached input-pin control value */ +}; + +enum { + ALC_AUTOMUTE_PIN, /* change the pin control */ + ALC_AUTOMUTE_AMP, /* mute/unmute the pin AMP */ + ALC_AUTOMUTE_MIXER, /* mute/unmute mixer widget AMP */ +}; + +#define MAX_VOL_NIDS 0x40 + +/* make compatible with old code */ +#define alc_apply_pincfgs snd_hda_apply_pincfgs +#define alc_apply_fixup snd_hda_apply_fixup +#define alc_pick_fixup snd_hda_pick_fixup +#define alc_fixup hda_fixup +#define alc_pincfg hda_pintbl +#define alc_model_fixup hda_model_fixup + +#define ALC_FIXUP_PINS HDA_FIXUP_PINS +#define ALC_FIXUP_VERBS HDA_FIXUP_VERBS +#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC + +#define ALC_FIXUP_ACT_PRE_PROBE HDA_FIXUP_ACT_PRE_PROBE +#define ALC_FIXUP_ACT_PROBE HDA_FIXUP_ACT_PROBE +#define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT +#define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD + + struct alc_spec { - struct hda_gen_spec gen; /* must be at head */ + struct hda_gen_spec gen; /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; + const struct snd_kcontrol_new *cap_mixer; /* capture mixer */ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ + char stream_name_analog[32]; /* analog PCM stream */ + const struct hda_pcm_stream *stream_analog_playback; + const struct hda_pcm_stream *stream_analog_capture; + const struct hda_pcm_stream *stream_analog_alt_playback; + const struct hda_pcm_stream *stream_analog_alt_capture; + + char stream_name_digital[32]; /* digital PCM stream */ + const struct hda_pcm_stream *stream_digital_playback; + const struct hda_pcm_stream *stream_digital_capture; + + /* playback */ + struct hda_multi_out multiout; /* playback set-up + * max_channels, dacs must be set + * dig_out_nid and hp_nid are optional + */ + hda_nid_t alt_dac_nid; + hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */ + int dig_out_type; + + /* capture */ + unsigned int num_adc_nids; + const hda_nid_t *adc_nids; + const hda_nid_t *capsrc_nids; + hda_nid_t dig_in_nid; /* digital-in NID; optional */ + hda_nid_t mixer_nid; /* analog-mixer NID */ + DECLARE_BITMAP(vol_ctls, MAX_VOL_NIDS << 1); + DECLARE_BITMAP(sw_ctls, MAX_VOL_NIDS << 1); + + /* capture setup for dynamic dual-adc switch */ + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + + /* capture source */ + unsigned int num_mux_defs; + const struct hda_input_mux *input_mux; + unsigned int cur_mux[3]; + hda_nid_t ext_mic_pin; + hda_nid_t dock_mic_pin; + hda_nid_t int_mic_pin; + + /* channel model */ + const struct hda_channel_mode *channel_mode; + int num_channel_mode; + int need_dac_fix; + int const_channel_count; /* min. channel count (for speakers) */ + int ext_channel_count; /* current channel count for multi-io */ + + /* PCM information */ + struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; struct alc_customize_define cdefine; - unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ - - /* inverted dmic fix */ - unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ - unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ + struct snd_array kctls; + struct hda_input_mux private_imux[3]; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; + unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; + int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; - /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */ - int mute_led_polarity; - hda_nid_t mute_led_nid; - /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM void (*power_hook)(struct hda_codec *codec); #endif void (*shutup)(struct hda_codec *codec); + void (*automute_hook)(struct hda_codec *codec); + + /* for pin sensing */ + unsigned int hp_jack_present:1; + unsigned int line_jack_present:1; + unsigned int master_mute:1; + unsigned int auto_mic:1; + unsigned int auto_mic_valid_imux:1; /* valid imux for auto-mic */ + unsigned int automute_speaker:1; /* automute speaker outputs */ + unsigned int automute_lo:1; /* automute LO outputs */ + unsigned int detect_hp:1; /* Headphone detection enabled */ + unsigned int detect_lo:1; /* Line-out detection enabled */ + unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ + unsigned int automute_lo_possible:1; /* there are line outs and HP */ + unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + + /* other flags */ + unsigned int no_analog :1; /* digital I/O only */ + unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int single_input_src:1; + unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ + unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ + unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ + unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ + unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ + unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ + + /* auto-mute control */ + int automute_mode; + hda_nid_t automute_mixer_nid[AUTO_CFG_MAX_OUTS]; int init_amp; int codec_variant; /* flag for other variants */ + /* for virtual master */ + hda_nid_t vmaster_nid; + struct hda_vmaster_mute_hook vmaster_mute; +#ifdef CONFIG_PM + struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[8]; +#endif + /* for PLL fix */ hda_nid_t pll_nid; unsigned int pll_coef_idx, pll_coef_bit; unsigned int coef0; + + /* multi-io */ + int multi_ios; + struct alc_multi_io multi_io[4]; + + /* bind volumes */ + struct snd_array bind_ctls; }; +static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, + int dir, unsigned int bits) +{ + if (!nid) + return false; + if (get_wcaps(codec, nid) & (1 << (dir + 1))) + if (query_amp_caps(codec, nid, dir) & bits) + return true; + return false; +} + +#define nid_has_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) +#define nid_has_volume(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) + +/* + * input MUX handling + */ +static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id); + if (mux_idx >= spec->num_mux_defs) + mux_idx = 0; + if (!spec->input_mux[mux_idx].num_items && mux_idx > 0) + mux_idx = 0; + return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo); +} + +static int alc_mux_enum_get(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); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + return 0; +} + +static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; + + if (spec->cur_adc && spec->cur_adc != new_adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = new_adc; + snd_hda_codec_setup_stream(codec, new_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + return true; + } + return false; +} + +static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx) +{ + return spec->capsrc_nids ? + spec->capsrc_nids[idx] : spec->adc_nids[idx]; +} + +static void call_update_outputs(struct hda_codec *codec); +static void alc_inv_dmic_sync(struct hda_codec *codec, bool force); + +/* for shared I/O, change the pin-control accordingly */ +static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) +{ + struct alc_spec *spec = codec->spec; + unsigned int val; + hda_nid_t pin = spec->autocfg.inputs[1].pin; + /* NOTE: this assumes that there are only two inputs, the + * first is the real internal mic and the second is HP/mic jack. + */ + + val = snd_hda_get_default_vref(codec, pin); + + /* This pin does not have vref caps - let's enable vref on pin 0x18 + instead, as suggested by Realtek */ + if (val == AC_PINCTL_VREF_HIZ) { + const hda_nid_t vref_pin = 0x18; + /* Sanity check pin 0x18 */ + if (get_wcaps_type(get_wcaps(codec, vref_pin)) == AC_WID_PIN && + get_defcfg_connect(snd_hda_codec_get_pincfg(codec, vref_pin)) == AC_JACK_PORT_NONE) { + unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); + if (vref_val != AC_PINCTL_VREF_HIZ) + snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); + } + } + + val = set_as_mic ? val | PIN_IN : PIN_HP; + snd_hda_set_pin_ctl(codec, pin, val); + + spec->automute_speaker = !set_as_mic; + call_update_outputs(codec); +} + +/* select the given imux item; either unmute exclusively or select the route */ +static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, + unsigned int idx, bool force) +{ + struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux; + unsigned int mux_idx; + int i, type, num_conns; + hda_nid_t nid; + + if (!spec->input_mux) + return 0; + + mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; + imux = &spec->input_mux[mux_idx]; + if (!imux->num_items && mux_idx > 0) + imux = &spec->input_mux[0]; + if (!imux->num_items) + return 0; + + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->cur_mux[adc_idx] == idx && !force) + return 0; + spec->cur_mux[adc_idx] = idx; + + if (spec->shared_mic_hp) + update_shared_mic_hp(codec, spec->cur_mux[adc_idx]); + + if (spec->dyn_adc_switch) { + alc_dyn_adc_pcm_resetup(codec, idx); + adc_idx = spec->dyn_adc_idx[idx]; + } + + nid = get_capsrc(spec, adc_idx); + + /* no selection? */ + num_conns = snd_hda_get_num_conns(codec, nid); + if (num_conns <= 1) + return 1; + + type = get_wcaps_type(get_wcaps(codec, nid)); + if (type == AC_WID_AUD_MIX) { + /* Matrix-mixer style (e.g. ALC882) */ + int active = imux->items[idx].index; + for (i = 0; i < num_conns; i++) { + unsigned int v = (i == active) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, i, + HDA_AMP_MUTE, v); + } + } else { + /* MUX style (e.g. ALC880) */ + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[idx].index); + } + alc_inv_dmic_sync(codec, true); + return 1; +} + +static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + return alc_mux_select(codec, adc_idx, + ucontrol->value.enumerated.item[0], false); +} + +/* + * set up the input pin config (depending on the given auto-pin type) + */ +static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, + int auto_pin_type) +{ + unsigned int val = PIN_IN; + if (auto_pin_type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, nid); + snd_hda_set_pin_ctl(codec, nid, val); +} + /* * Append the given mixer and verb elements for the later use * The mixer array is referred in build_controls(), and init_verbs are @@ -171,6 +485,171 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, alc_fix_pll(codec); } +/* + * Jack detections for HP auto-mute and mic-switch + */ + +/* check each pin in the given array; returns true if any of them is plugged */ +static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) +{ + int i, present = 0; + + for (i = 0; i < num_pins; i++) { + hda_nid_t nid = pins[i]; + if (!nid) + break; + present |= snd_hda_jack_detect(codec, nid); + } + return present; +} + +/* standard HP/line-out auto-mute helper */ +static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, + bool mute, bool hp_out) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute_bits = mute ? HDA_AMP_MUTE : 0; + unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); + int i; + + for (i = 0; i < num_pins; i++) { + hda_nid_t nid = pins[i]; + unsigned int val; + if (!nid) + break; + switch (spec->automute_mode) { + case ALC_AUTOMUTE_PIN: + /* don't reset VREF value in case it's controlling + * the amp (see alc861_fixup_asus_amp_vref_0f()) + */ + if (spec->keep_vref_in_automute) { + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val &= ~PIN_HP; + } else + val = 0; + val |= pin_bits; + snd_hda_set_pin_ctl(codec, nid, val); + break; + case ALC_AUTOMUTE_AMP: + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute_bits); + break; + case ALC_AUTOMUTE_MIXER: + nid = spec->automute_mixer_nid[i]; + if (!nid) + break; + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, + HDA_AMP_MUTE, mute_bits); + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1, + HDA_AMP_MUTE, mute_bits); + break; + } + } +} + +/* Toggle outputs muting */ +static void update_outputs(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int on; + + /* Control HP pins/amps depending on master_mute state; + * in general, HP pins/amps control should be enabled in all cases, + * but currently set only for master_mute, just to be safe + */ + if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */ + do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), + spec->autocfg.hp_pins, spec->master_mute, true); + + if (!spec->automute_speaker) + on = 0; + else + on = spec->hp_jack_present | spec->line_jack_present; + on |= spec->master_mute; + do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), + spec->autocfg.speaker_pins, on, false); + + /* toggle line-out mutes if needed, too */ + /* if LO is a copy of either HP or Speaker, don't need to handle it */ + if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || + spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) + return; + if (!spec->automute_lo) + on = 0; + else + on = spec->hp_jack_present; + on |= spec->master_mute; + do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), + spec->autocfg.line_out_pins, on, false); +} + +static void call_update_outputs(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + if (spec->automute_hook) + spec->automute_hook(codec); + else + update_outputs(codec); +} + +/* standard HP-automute helper */ +static void alc_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct alc_spec *spec = codec->spec; + + spec->hp_jack_present = + detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), + spec->autocfg.hp_pins); + if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) + return; + call_update_outputs(codec); +} + +/* standard line-out-automute helper */ +static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct alc_spec *spec = codec->spec; + + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) + return; + /* check LO jack only when it's different from HP */ + if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) + return; + + spec->line_jack_present = + detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), + spec->autocfg.line_out_pins); + if (!spec->automute_speaker || !spec->detect_lo) + return; + call_update_outputs(codec); +} + +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 0) + +/* standard mic auto-switch helper */ +static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t *pins = spec->imux_pins; + + if (!spec->auto_mic || !spec->auto_mic_valid_imux) + return; + if (snd_BUG_ON(!spec->adc_nids)) + return; + if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) + return; + + if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx])) + alc_mux_select(codec, 0, spec->ext_mic_idx, false); + else if (spec->dock_mic_idx >= 0 && + snd_hda_jack_detect(codec, pins[spec->dock_mic_idx])) + alc_mux_select(codec, 0, spec->dock_mic_idx, false); + else + alc_mux_select(codec, 0, spec->int_mic_idx, false); +} + /* update the master volume per volume-knob's unsol event */ static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack) { @@ -200,6 +679,14 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) snd_hda_jack_unsol_event(codec, res >> 2); } +/* call init functions of standard auto-mute helpers */ +static void alc_inithook(struct hda_codec *codec) +{ + alc_hp_automute(codec, NULL); + alc_line_automute(codec, NULL); + alc_mic_automute(codec, NULL); +} + /* additional initialization for ALC888 variants */ static void alc888_coef_init(struct hda_codec *codec) { @@ -320,88 +807,184 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) } } - /* - * Realtek SSID verification + * Auto-Mute mode mixer enum support */ +static int alc_automute_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + static const char * const texts3[] = { + "Disabled", "Speaker Only", "Line Out+Speaker" + }; -/* Could be any non-zero and even value. When used as fixup, tells - * the driver to ignore any present sku defines. - */ -#define ALC_FIXUP_SKU_IGNORE (2) + if (spec->automute_speaker_possible && spec->automute_lo_possible) + return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); + return snd_hda_enum_bool_helper_info(kcontrol, uinfo); +} -static void alc_fixup_sku_ignore(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static int alc_automute_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cdefine.fixup = 1; - spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; + unsigned int val = 0; + if (spec->automute_speaker) + val++; + if (spec->automute_lo) + val++; + + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int alc_automute_mode_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; + + switch (ucontrol->value.enumerated.item[0]) { + case 0: + if (!spec->automute_speaker && !spec->automute_lo) + return 0; + spec->automute_speaker = 0; + spec->automute_lo = 0; + break; + case 1: + if (spec->automute_speaker_possible) { + if (!spec->automute_lo && spec->automute_speaker) + return 0; + spec->automute_speaker = 1; + spec->automute_lo = 0; + } else if (spec->automute_lo_possible) { + if (spec->automute_lo) + return 0; + spec->automute_lo = 1; + } else + return -EINVAL; + break; + case 2: + if (!spec->automute_lo_possible || !spec->automute_speaker_possible) + return -EINVAL; + if (spec->automute_speaker && spec->automute_lo) + return 0; + spec->automute_speaker = 1; + spec->automute_lo = 1; + break; + default: + return -EINVAL; } + call_update_outputs(codec); + return 1; } -static int alc_auto_parse_customize_define(struct hda_codec *codec) +static const struct snd_kcontrol_new alc_automute_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Auto-Mute Mode", + .info = alc_automute_mode_info, + .get = alc_automute_mode_get, + .put = alc_automute_mode_put, +}; + +static struct snd_kcontrol_new * +alc_kcontrol_new(struct alc_spec *spec, const char *name, + const struct snd_kcontrol_new *temp) +{ + struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); + if (!knew) + return NULL; + *knew = *temp; + knew->name = kstrdup(name, GFP_KERNEL); + if (!knew->name) + return NULL; + return knew; +} + +static int alc_add_automute_mode_enum(struct hda_codec *codec) { - unsigned int ass, tmp, i; - unsigned nid = 0; struct alc_spec *spec = codec->spec; - spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ + if (!alc_kcontrol_new(spec, "Auto-Mute Mode", &alc_automute_mode_enum)) + return -ENOMEM; + return 0; +} - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return -1; - goto do_sku; +/* + * Check the availability of HP/line-out auto-mute; + * Set up appropriately if really supported + */ +static int alc_init_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int present = 0; + int i, err; + + if (cfg->hp_pins[0]) + present++; + if (cfg->line_out_pins[0]) + present++; + if (cfg->speaker_pins[0]) + present++; + if (present < 2) /* need two different output types */ + return 0; + + if (!cfg->speaker_pins[0] && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->speaker_outs = cfg->line_outs; } - ass = codec->subsystem_id & 0xffff; - if (ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; + if (!cfg->hp_pins[0] && + cfg->line_out_type == AUTO_PIN_HP_OUT) { + memcpy(cfg->hp_pins, cfg->line_out_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = cfg->line_outs; + } - nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); + spec->automute_mode = ALC_AUTOMUTE_PIN; - if (!(ass & 1)) { - printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n", - codec->chip_name, ass); - return -1; + for (i = 0; i < cfg->hp_outs; i++) { + hda_nid_t nid = cfg->hp_pins[i]; + if (!is_jack_detectable(codec, nid)) + continue; + snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", + nid); + snd_hda_jack_detect_enable_callback(codec, nid, ALC_HP_EVENT, + alc_hp_automute); + spec->detect_hp = 1; } - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; + if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { + if (cfg->speaker_outs) + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t nid = cfg->line_out_pins[i]; + if (!is_jack_detectable(codec, nid)) + continue; + snd_printdd("realtek: Enable Line-Out " + "auto-muting on NID 0x%x\n", nid); + snd_hda_jack_detect_enable_callback(codec, nid, ALC_FRONT_EVENT, + alc_line_automute); + spec->detect_lo = 1; + } + spec->automute_lo_possible = spec->detect_hp; } - if (((ass >> 16) & 0xf) != tmp) - return -1; - spec->cdefine.port_connectivity = ass >> 30; - spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; - spec->cdefine.check_sum = (ass >> 16) & 0xf; - spec->cdefine.customization = ass >> 8; -do_sku: - spec->cdefine.sku_cfg = ass; - spec->cdefine.external_amp = (ass & 0x38) >> 3; - spec->cdefine.platform_type = (ass & 0x4) >> 2; - spec->cdefine.swap = (ass & 0x2) >> 1; - spec->cdefine.override = ass & 0x1; + spec->automute_speaker_possible = cfg->speaker_outs && + (spec->detect_hp || spec->detect_lo); - snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n", - nid, spec->cdefine.sku_cfg); - snd_printd("SKU: port_connectivity=0x%x\n", - spec->cdefine.port_connectivity); - snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep); - snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum); - snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization); - snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp); - snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type); - snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap); - snd_printd("SKU: override=0x%x\n", spec->cdefine.override); + spec->automute_lo = spec->automute_lo_possible; + spec->automute_speaker = spec->automute_speaker_possible; + if (spec->automute_speaker_possible || spec->automute_lo_possible) { + /* create a control for automute mode */ + err = alc_add_automute_mode_enum(codec); + if (err < 0) + return err; + } return 0; } @@ -414,6 +997,261 @@ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) return i; return -1; } + +/* check whether dynamic ADC-switching is available */ +static bool alc_check_dyn_adc_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, n, idx; + hda_nid_t cap, pin; + + if (imux != spec->input_mux) /* no dynamic imux? */ + return false; + + for (n = 0; n < spec->num_adc_nids; n++) { + cap = spec->private_capsrc_nids[n]; + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + if (!pin) + return false; + if (get_connection_index(codec, cap, pin) < 0) + break; + } + if (i >= imux->num_items) + return true; /* no ADC-switch is needed */ + } + + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + for (n = 0; n < spec->num_adc_nids; n++) { + cap = spec->private_capsrc_nids[n]; + idx = get_connection_index(codec, cap, pin); + if (idx >= 0) { + imux->items[i].index = idx; + spec->dyn_adc_idx[i] = n; + break; + } + } + } + + snd_printdd("realtek: enabling ADC switching\n"); + spec->dyn_adc_switch = 1; + return true; +} + +/* check whether all auto-mic pins are valid; setup indices if OK */ +static bool alc_auto_mic_check_imux(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux; + + if (!spec->auto_mic) + return false; + if (spec->auto_mic_valid_imux) + return true; /* already checked */ + + /* fill up imux indices */ + if (!alc_check_dyn_adc_switch(codec)) { + spec->auto_mic = 0; + return false; + } + + imux = spec->input_mux; + spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, + spec->imux_pins, imux->num_items); + spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin, + spec->imux_pins, imux->num_items); + spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin, + spec->imux_pins, imux->num_items); + if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) { + spec->auto_mic = 0; + return false; /* no corresponding imux */ + } + + snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin, + ALC_MIC_EVENT, alc_mic_automute); + if (spec->dock_mic_pin) + snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin, + ALC_MIC_EVENT, + alc_mic_automute); + + spec->auto_mic_valid_imux = 1; + spec->auto_mic = 1; + return true; +} + +/* + * Check the availability of auto-mic switch; + * Set up if really supported + */ +static int alc_init_auto_mic(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t fixed, ext, dock; + int i; + + if (spec->shared_mic_hp) + return 0; /* no auto-mic for the shared I/O */ + + spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1; + + fixed = ext = dock = 0; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + unsigned int defcfg; + defcfg = snd_hda_codec_get_pincfg(codec, nid); + switch (snd_hda_get_input_pin_attr(defcfg)) { + case INPUT_PIN_ATTR_INT: + if (fixed) + return 0; /* already occupied */ + if (cfg->inputs[i].type != AUTO_PIN_MIC) + return 0; /* invalid type */ + fixed = nid; + break; + case INPUT_PIN_ATTR_UNUSED: + return 0; /* invalid entry */ + case INPUT_PIN_ATTR_DOCK: + if (dock) + return 0; /* already occupied */ + if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) + return 0; /* invalid type */ + dock = nid; + break; + default: + if (ext) + return 0; /* already occupied */ + if (cfg->inputs[i].type != AUTO_PIN_MIC) + return 0; /* invalid type */ + ext = nid; + break; + } + } + if (!ext && dock) { + ext = dock; + dock = 0; + } + if (!ext || !fixed) + return 0; + if (!is_jack_detectable(codec, ext)) + return 0; /* no unsol support */ + if (dock && !is_jack_detectable(codec, dock)) + return 0; /* no unsol support */ + + /* check imux indices */ + spec->ext_mic_pin = ext; + spec->int_mic_pin = fixed; + spec->dock_mic_pin = dock; + + spec->auto_mic = 1; + if (!alc_auto_mic_check_imux(codec)) + return 0; + + snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", + ext, fixed, dock); + + return 0; +} + +/* check the availabilities of auto-mute and auto-mic switches */ +static int alc_auto_check_switches(struct hda_codec *codec) +{ + int err; + + err = alc_init_automute(codec); + if (err < 0) + return err; + err = alc_init_auto_mic(codec); + if (err < 0) + return err; + return 0; +} + +/* + * Realtek SSID verification + */ + +/* Could be any non-zero and even value. When used as fixup, tells + * the driver to ignore any present sku defines. + */ +#define ALC_FIXUP_SKU_IGNORE (2) + +static void alc_fixup_sku_ignore(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->cdefine.fixup = 1; + spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; + } +} + +static int alc_auto_parse_customize_define(struct hda_codec *codec) +{ + unsigned int ass, tmp, i; + unsigned nid = 0; + struct alc_spec *spec = codec->spec; + + spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ + + if (spec->cdefine.fixup) { + ass = spec->cdefine.sku_cfg; + if (ass == ALC_FIXUP_SKU_IGNORE) + return -1; + goto do_sku; + } + + ass = codec->subsystem_id & 0xffff; + if (ass != codec->bus->pci->subsystem_device && (ass & 1)) + goto do_sku; + + nid = 0x1d; + if (codec->vendor_id == 0x10ec0260) + nid = 0x17; + ass = snd_hda_codec_get_pincfg(codec, nid); + + if (!(ass & 1)) { + printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n", + codec->chip_name, ass); + return -1; + } + + /* check sum */ + tmp = 0; + for (i = 1; i < 16; i++) { + if ((ass >> i) & 1) + tmp++; + } + if (((ass >> 16) & 0xf) != tmp) + return -1; + + spec->cdefine.port_connectivity = ass >> 30; + spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; + spec->cdefine.check_sum = (ass >> 16) & 0xf; + spec->cdefine.customization = ass >> 8; +do_sku: + spec->cdefine.sku_cfg = ass; + spec->cdefine.external_amp = (ass & 0x38) >> 3; + spec->cdefine.platform_type = (ass & 0x4) >> 2; + spec->cdefine.swap = (ass & 0x2) >> 1; + spec->cdefine.override = ass & 0x1; + + snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n", + nid, spec->cdefine.sku_cfg); + snd_printd("SKU: port_connectivity=0x%x\n", + spec->cdefine.port_connectivity); + snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep); + snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum); + snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization); + snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp); + snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type); + snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap); + snd_printd("SKU: override=0x%x\n", spec->cdefine.override); + + return 0; +} + /* return true if the given NID is found in the list */ static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) { @@ -516,9 +1354,9 @@ static int alc_subsystem_id(struct hda_codec *codec, * 15 : 1 --> enable the function "Mute internal speaker * when the external headphone out jack is plugged" */ - if (!spec->gen.autocfg.hp_pins[0] && - !(spec->gen.autocfg.line_out_pins[0] && - spec->gen.autocfg.line_out_type == AUTO_PIN_HP_OUT)) { + if (!spec->autocfg.hp_pins[0] && + !(spec->autocfg.line_out_pins[0] && + spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)) { hda_nid_t nid; tmp = (ass >> 11) & 0x3; /* HP to chassis */ if (tmp == 0) @@ -531,10 +1369,10 @@ static int alc_subsystem_id(struct hda_codec *codec, nid = porti; else return 1; - if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, - spec->gen.autocfg.line_outs)) + if (found_in_nid_list(nid, spec->autocfg.line_out_pins, + spec->autocfg.line_outs)) return 1; - spec->gen.autocfg.hp_pins[0] = nid; + spec->autocfg.hp_pins[0] = nid; } return 1; } @@ -584,54 +1422,252 @@ static unsigned int alc_get_coef0(struct hda_codec *codec) } /* + * Digital I/O handling */ -static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) +/* set right pin controls for digital I/O */ +static void alc_auto_init_digital(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; - if (spec->dyn_adc_switch) - adc_idx = spec->dyn_adc_idx[imux_idx]; - return spec->adc_nids[adc_idx]; + struct alc_spec *spec = codec->spec; + int i; + hda_nid_t pin, dac; + + for (i = 0; i < spec->autocfg.dig_outs; i++) { + pin = spec->autocfg.dig_out_pins[i]; + if (!pin) + continue; + snd_hda_set_pin_ctl(codec, pin, PIN_OUT); + if (!i) + dac = spec->multiout.dig_out_nid; + else + dac = spec->slave_dig_outs[i - 1]; + if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) + continue; + snd_hda_codec_write(codec, dac, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + } + pin = spec->autocfg.dig_in_pin; + if (pin) + snd_hda_set_pin_ctl(codec, pin, PIN_IN); } -static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) +/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */ +static void alc_auto_parse_digital(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->gen.input_mux; - struct nid_path *path; - hda_nid_t nid; - int i, dir, parm; - unsigned int val; + int i, err, nums; + hda_nid_t dig_nid; + + /* support multiple SPDIFs; the secondary is set up as a slave */ + nums = 0; + for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t conn[4]; + err = snd_hda_get_connections(codec, + spec->autocfg.dig_out_pins[i], + conn, ARRAY_SIZE(conn)); + if (err <= 0) + continue; + dig_nid = conn[0]; /* assume the first element is audio-out */ + if (!nums) { + spec->multiout.dig_out_nid = dig_nid; + spec->dig_out_type = spec->autocfg.dig_out_type[0]; + } else { + spec->multiout.slave_dig_outs = spec->slave_dig_outs; + if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) + break; + spec->slave_dig_outs[nums - 1] = dig_nid; + } + nums++; + } - for (i = 0; i < imux->num_items; i++) { - if (spec->gen.imux_pins[i] == spec->inv_dmic_pin) - break; + if (spec->autocfg.dig_in_pin) { + dig_nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + unsigned int wcaps = get_wcaps(codec, dig_nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (!(wcaps & AC_WCAP_DIGITAL)) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + err = get_connection_index(codec, dig_nid, + spec->autocfg.dig_in_pin); + if (err >= 0) { + spec->dig_in_nid = dig_nid; + break; + } + } } - if (i >= imux->num_items) - return; +} - path = snd_hda_get_nid_path(codec, spec->inv_dmic_pin, - get_adc_nid(codec, adc_idx, i)); - val = path->ctls[NID_PATH_MUTE_CTL]; - if (!val) - return; - nid = get_amp_nid_(val); - dir = get_amp_direction_(val); - parm = AC_AMP_SET_RIGHT | - (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT); +/* + * capture mixer elements + */ +static int alc_cap_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned long val; + int err; + + mutex_lock(&codec->control_mutex); + if (spec->vol_in_capsrc) + val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); + kcontrol->private_value = val; + err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); + mutex_unlock(&codec->control_mutex); + return err; +} - /* flush all cached amps at first */ - snd_hda_codec_flush_cache(codec); +static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned long val; + int err; - /* we care only right channel */ - val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); - if (val & 0x80) /* if already muted, we don't need to touch */ - return; - val |= 0x80; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - parm | val); + mutex_lock(&codec->control_mutex); + if (spec->vol_in_capsrc) + val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); + kcontrol->private_value = val; + err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); + mutex_unlock(&codec->control_mutex); + return err; +} + +typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + getput_call_t func, bool is_put) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int i, err = 0; + + mutex_lock(&codec->control_mutex); + if (is_put && spec->dyn_adc_switch) { + for (i = 0; i < spec->num_adc_nids; i++) { + kcontrol->private_value = + HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], + 3, 0, HDA_INPUT); + err = func(kcontrol, ucontrol); + if (err < 0) + goto error; + } + } else { + i = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + if (spec->vol_in_capsrc) + kcontrol->private_value = + HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[i], + 3, 0, HDA_OUTPUT); + else + kcontrol->private_value = + HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], + 3, 0, HDA_INPUT); + err = func(kcontrol, ucontrol); + } + if (err >= 0 && is_put) + alc_inv_dmic_sync(codec, false); + error: + mutex_unlock(&codec->control_mutex); + return err; +} + +static int alc_cap_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_get, false); +} + +static int alc_cap_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_put, true); +} + +/* capture mixer elements */ +#define alc_cap_sw_info snd_ctl_boolean_stereo_info + +static int alc_cap_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_get, false); +} + +static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_put, true); +} + +#define _DEFINE_CAPMIX(num) \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Capture Switch", \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .count = num, \ + .info = alc_cap_sw_info, \ + .get = alc_cap_sw_get, \ + .put = alc_cap_sw_put, \ + }, \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Capture Volume", \ + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \ + .count = num, \ + .info = alc_cap_vol_info, \ + .get = alc_cap_vol_get, \ + .put = alc_cap_vol_put, \ + .tlv = { .c = alc_cap_vol_tlv }, \ + } + +#define _DEFINE_CAPSRC(num) \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + /* .name = "Capture Source", */ \ + .name = "Input Source", \ + .count = num, \ + .info = alc_mux_enum_info, \ + .get = alc_mux_enum_get, \ + .put = alc_mux_enum_put, \ + } + +#define DEFINE_CAPMIX(num) \ +static const struct snd_kcontrol_new alc_capture_mixer ## num[] = { \ + _DEFINE_CAPMIX(num), \ + _DEFINE_CAPSRC(num), \ + { } /* end */ \ +} + +#define DEFINE_CAPMIX_NOSRC(num) \ +static const struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \ + _DEFINE_CAPMIX(num), \ + { } /* end */ \ } +/* up to three ADCs */ +DEFINE_CAPMIX(1); +DEFINE_CAPMIX(2); +DEFINE_CAPMIX(3); +DEFINE_CAPMIX_NOSRC(1); +DEFINE_CAPMIX_NOSRC(2); +DEFINE_CAPMIX_NOSRC(3); + /* * Inverted digital-mic handling * @@ -650,31 +1686,43 @@ static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) { struct alc_spec *spec = codec->spec; - int src, nums; + int i; if (!spec->inv_dmic_fixup) return; if (!spec->inv_dmic_muted && !force) return; - nums = spec->gen.dyn_adc_switch ? 1 : spec->gen.num_adc_nids; - for (src = 0; src < nums; src++) { + for (i = 0; i < spec->num_adc_nids; i++) { + int src = spec->dyn_adc_switch ? 0 : i; bool dmic_fixup = false; + hda_nid_t nid; + int parm, dir, v; if (spec->inv_dmic_muted && - spec->gen.imux_pins[spec->gen.cur_mux[src]] == spec->inv_dmic_pin) + spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin) dmic_fixup = true; if (!dmic_fixup && !force) continue; - alc_inv_dmic_sync_adc(codec, src); + if (spec->vol_in_capsrc) { + nid = spec->capsrc_nids[i]; + parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT; + dir = HDA_OUTPUT; + } else { + nid = spec->adc_nids[i]; + parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT; + dir = HDA_INPUT; + } + /* we care only right channel */ + v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); + if (v & 0x80) /* if already muted, we don't need to touch */ + continue; + if (dmic_fixup) /* add mute for d-mic */ + v |= 0x80; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + parm | v); } } -static void alc_inv_dmic_hook(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol) -{ - alc_inv_dmic_sync(codec, false); -} - static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -701,7 +1749,6 @@ static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new alc_inv_dmic_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Inverted Internal Mic Capture Switch", .info = snd_ctl_boolean_mono_info, .get = alc_inv_dmic_sw_get, .put = alc_inv_dmic_sw_put, @@ -711,23 +1758,51 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &alc_inv_dmic_sw)) + if (!alc_kcontrol_new(spec, "Inverted Internal Mic Capture Switch", + &alc_inv_dmic_sw)) return -ENOMEM; spec->inv_dmic_fixup = 1; spec->inv_dmic_muted = 0; spec->inv_dmic_pin = nid; - spec->gen.cap_sync_hook = alc_inv_dmic_hook; return 0; } /* typically the digital mic is put at node 0x12 */ static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PROBE) + if (action == ALC_FIXUP_ACT_PROBE) alc_add_inv_dmic_mixer(codec, 0x12); } +/* + * virtual master controls + */ + +/* + * slave controls for virtual master + */ +static const char * const alc_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", "Mono", "Line Out", + "CLFE", "Bass Speaker", "PCM", + NULL, +}; + +/* + * build control elements + */ + +#define NID_MAPPING (-1) + +#define SUBDEV_SPEAKER_ (0 << 6) +#define SUBDEV_HP_ (1 << 6) +#define SUBDEV_LINE_ (2 << 6) +#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f)) +#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f)) +#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f)) + +static void alc_free_kctls(struct hda_codec *codec); #ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ @@ -738,20 +1813,45 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = { }; #endif -static int alc_build_controls(struct hda_codec *codec) +static int __alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int i, err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; + struct snd_kcontrol *kctl = NULL; + const struct snd_kcontrol_new *knew; + int i, j, err; + unsigned int u; + hda_nid_t nid; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); if (err < 0) return err; } + if (spec->cap_mixer) { + err = snd_hda_add_new_ctls(codec, spec->cap_mixer); + if (err < 0) + return err; + } + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_dig_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid, + spec->pcm_rec[1].pcm_type); + if (err < 0) + return err; + if (!spec->no_analog) { + 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); + if (err < 0) + return err; + } #ifdef CONFIG_SND_HDA_INPUT_BEEP /* create beep controls if needed */ @@ -768,155 +1868,2369 @@ static int alc_build_controls(struct hda_codec *codec) return err; } } -#endif +#endif + + /* if we have no master control, let's create it */ + if (!spec->no_analog && + !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, vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + vmaster_tlv, alc_slave_pfxs, + "Playback Volume"); + if (err < 0) + return err; + } + if (!spec->no_analog && + !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, alc_slave_pfxs, + "Playback Switch", + true, &spec->vmaster_mute.sw_kctl); + if (err < 0) + return err; + } + + /* assign Capture Source enums to NID */ + if (spec->capsrc_nids || spec->adc_nids) { + kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); + if (!kctl) + kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nid(codec, kctl, i, + get_capsrc(spec, i)); + if (err < 0) + return err; + } + } + if (spec->cap_mixer && spec->adc_nids) { + const char *kname = kctl ? kctl->id.name : NULL; + for (knew = spec->cap_mixer; knew->name; knew++) { + if (kname && strcmp(knew->name, kname) == 0) + continue; + kctl = snd_hda_find_mixer_ctl(codec, knew->name); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nid(codec, kctl, i, + spec->adc_nids[i]); + if (err < 0) + return err; + } + } + } + + /* other nid->control mapping */ + for (i = 0; i < spec->num_mixers; i++) { + for (knew = spec->mixers[i]; knew->name; knew++) { + if (knew->iface != NID_MAPPING) + continue; + kctl = snd_hda_find_mixer_ctl(codec, knew->name); + if (kctl == NULL) + continue; + u = knew->subdevice; + for (j = 0; j < 4; j++, u >>= 8) { + nid = u & 0x3f; + if (nid == 0) + continue; + switch (u & 0xc0) { + case SUBDEV_SPEAKER_: + nid = spec->autocfg.speaker_pins[nid]; + break; + case SUBDEV_LINE_: + nid = spec->autocfg.line_out_pins[nid]; + break; + case SUBDEV_HP_: + nid = spec->autocfg.hp_pins[nid]; + break; + default: + continue; + } + err = snd_hda_add_nid(codec, kctl, 0, nid); + if (err < 0) + return err; + } + u = knew->private_value; + for (j = 0; j < 4; j++, u >>= 8) { + nid = u & 0xff; + if (nid == 0) + continue; + err = snd_hda_add_nid(codec, kctl, 0, nid); + if (err < 0) + return err; + } + } + } + + alc_free_kctls(codec); /* no longer needed */ + + return 0; +} + +static int alc_build_jacks(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->shared_mic_hp) { + int err; + int nid = spec->autocfg.inputs[1].pin; + err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0); + if (err < 0) + return err; + err = snd_hda_jack_detect_enable(codec, nid, 0); + if (err < 0) + return err; + } + + return snd_hda_jack_add_kctls(codec, &spec->autocfg); +} + +static int alc_build_controls(struct hda_codec *codec) +{ + int err = __alc_build_controls(codec); + if (err < 0) + return err; + + err = alc_build_jacks(codec); + if (err < 0) + return err; + alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); + return 0; +} + + +/* + * Common callbacks + */ + +static void alc_init_special_input_src(struct hda_codec *codec); +static void alc_auto_init_std(struct hda_codec *codec); + +static int alc_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->init_hook) + spec->init_hook(codec); + + alc_fix_pll(codec); + alc_auto_init_amp(codec, spec->init_amp); + + snd_hda_gen_apply_verbs(codec); + alc_init_special_input_src(codec); + alc_auto_init_std(codec); + + alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); + + hda_call_check_power_status(codec, 0x01); + return 0; +} + +#ifdef CONFIG_PM +static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); +} +#endif + +/* + * Analog playback callbacks + */ +static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} + +static int alc_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int alc_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); +} + +static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +/* + * Analog capture + */ +static int alc_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], + stream_tag, 0, format); + return 0; +} + +static int alc_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + + snd_hda_codec_cleanup_stream(codec, + spec->adc_nids[substream->number + 1]); + return 0; +} + +/* analog capture with dynamic dual-adc changes */ +static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + return 0; +} + +static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct alc_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + return 0; +} + +static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = dyn_adc_capture_pcm_prepare, + .cleanup = dyn_adc_capture_pcm_cleanup + }, +}; + +/* + */ +static const struct hda_pcm_stream alc_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + /* NID is set in alc_build_pcms */ + .ops = { + .open = alc_playback_pcm_open, + .prepare = alc_playback_pcm_prepare, + .cleanup = alc_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream alc_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ +}; + +static const struct hda_pcm_stream alc_pcm_analog_alt_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ +}; + +static const struct hda_pcm_stream alc_pcm_analog_alt_capture = { + .substreams = 2, /* can be overridden */ + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ + .ops = { + .prepare = alc_alt_capture_pcm_prepare, + .cleanup = alc_alt_capture_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream alc_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ + .ops = { + .open = alc_dig_playback_pcm_open, + .close = alc_dig_playback_pcm_close, + .prepare = alc_dig_playback_pcm_prepare, + .cleanup = alc_dig_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream alc_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ +}; + +/* Used by alc_build_pcms to flag that a PCM has no playback stream */ +static const struct hda_pcm_stream alc_pcm_null_stream = { + .substreams = 0, + .channels_min = 0, + .channels_max = 0, +}; + +static int alc_build_pcms(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + const struct hda_pcm_stream *p; + bool have_multi_adcs; + int i; + + codec->num_pcms = 1; + codec->pcm_info = info; + + if (spec->no_analog) + goto skip_analog; + + snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), + "%s Analog", codec->chip_name); + info->name = spec->stream_name_analog; + + if (spec->multiout.num_dacs > 0) { + p = spec->stream_analog_playback; + if (!p) + p = &alc_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && + spec->autocfg.line_outs == 2) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; + } + if (spec->adc_nids) { + p = spec->stream_analog_capture; + if (!p) { + if (spec->dyn_adc_switch) + p = &dyn_adc_pcm_analog_capture; + else + p = &alc_pcm_analog_capture; + } + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + } + + if (spec->channel_mode) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; + for (i = 0; i < spec->num_channel_mode; i++) { + if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels; + } + } + } + + skip_analog: + /* SPDIF for stream index #1 */ + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + snprintf(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + "%s Digital", codec->chip_name); + codec->num_pcms = 2; + codec->slave_dig_outs = spec->multiout.slave_dig_outs; + info = spec->pcm_rec + 1; + info->name = spec->stream_name_digital; + if (spec->dig_out_type) + info->pcm_type = spec->dig_out_type; + else + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->multiout.dig_out_nid) { + p = spec->stream_digital_playback; + if (!p) + p = &alc_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; + } + if (spec->dig_in_nid) { + p = spec->stream_digital_capture; + if (!p) + p = &alc_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; + } + /* FIXME: do we need this for all Realtek codec models? */ + codec->spdif_status_reset = 1; + } + + if (spec->no_analog) + return 0; + + /* If the use of more than one ADC is requested for the current + * model, configure a second analog capture-only PCM. + */ + have_multi_adcs = (spec->num_adc_nids > 1) && + !spec->dyn_adc_switch && !spec->auto_mic && + (!spec->input_mux || spec->input_mux->num_items > 1); + /* Additional Analaog capture for index #2 */ + if (spec->alt_dac_nid || have_multi_adcs) { + codec->num_pcms = 3; + info = spec->pcm_rec + 2; + info->name = spec->stream_name_analog; + if (spec->alt_dac_nid) { + p = spec->stream_analog_alt_playback; + if (!p) + p = &alc_pcm_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->alt_dac_nid; + } else { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + alc_pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; + } + if (have_multi_adcs) { + p = spec->stream_analog_alt_capture; + if (!p) + p = &alc_pcm_analog_alt_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nids[1]; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids - 1; + } else { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + alc_pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; + } + } + + return 0; +} + +static inline void alc_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec && spec->shutup) + spec->shutup(codec); + snd_hda_shutup_pins(codec); +} + +static void alc_free_kctls(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +} + +static void alc_free_bind_ctls(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + if (spec->bind_ctls.list) { + struct hda_bind_ctls **ctl = spec->bind_ctls.list; + int i; + for (i = 0; i < spec->bind_ctls.used; i++) + kfree(ctl[i]); + } + snd_array_free(&spec->bind_ctls); +} + +static void alc_free(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec) + return; + + alc_free_kctls(codec); + alc_free_bind_ctls(codec); + snd_hda_gen_free(&spec->gen); + kfree(spec); + snd_hda_detach_beep_device(codec); +} + +#ifdef CONFIG_PM +static void alc_power_eapd(struct hda_codec *codec) +{ + alc_auto_setup_eapd(codec, false); +} + +static int alc_suspend(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_shutup(codec); + if (spec && spec->power_hook) + spec->power_hook(codec); + return 0; +} +#endif + +#ifdef CONFIG_PM +static int alc_resume(struct hda_codec *codec) +{ + msleep(150); /* to avoid pop noise */ + codec->patch_ops.init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + alc_inv_dmic_sync(codec, true); + hda_call_check_power_status(codec, 0x01); + return 0; +} +#endif + +/* + */ +static const struct hda_codec_ops alc_patch_ops = { + .build_controls = alc_build_controls, + .build_pcms = alc_build_pcms, + .init = alc_init, + .free = alc_free, + .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM + .resume = alc_resume, +#endif +#ifdef CONFIG_PM + .suspend = alc_suspend, + .check_power_status = alc_check_power_status, +#endif + .reboot_notify = alc_shutup, +}; + + +/* replace the codec chip_name with the given string */ +static int alc_codec_rename(struct hda_codec *codec, const char *name) +{ + kfree(codec->chip_name); + codec->chip_name = kstrdup(name, GFP_KERNEL); + if (!codec->chip_name) { + alc_free(codec); + return -ENOMEM; + } + return 0; +} + +/* + * Rename codecs appropriately from COEF value + */ +struct alc_codec_rename_table { + unsigned int vendor_id; + unsigned short coef_mask; + unsigned short coef_bits; + const char *name; +}; + +static struct alc_codec_rename_table rename_tbl[] = { + { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, + { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, + { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, + { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, + { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, + { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, + { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, + { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, + { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, + { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, + { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, + { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, + { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, + { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, + { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, + { } /* terminator */ +}; + +static int alc_codec_rename_from_preset(struct hda_codec *codec) +{ + const struct alc_codec_rename_table *p; + + for (p = rename_tbl; p->vendor_id; p++) { + if (p->vendor_id != codec->vendor_id) + continue; + if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) + return alc_codec_rename(codec, p->name); + } + return 0; +} + +/* + * Automatic parse of I/O pins from the BIOS configuration + */ + +enum { + ALC_CTL_WIDGET_VOL, + ALC_CTL_WIDGET_MUTE, + ALC_CTL_BIND_MUTE, + ALC_CTL_BIND_VOL, + ALC_CTL_BIND_SW, +}; +static const struct snd_kcontrol_new alc_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_BIND_MUTE(NULL, 0, 0, 0), + HDA_BIND_VOL(NULL, 0), + HDA_BIND_SW(NULL, 0), +}; + +/* add dynamic controls */ +static int add_control(struct alc_spec *spec, int type, const char *name, + int cidx, unsigned long val) +{ + struct snd_kcontrol_new *knew; + + knew = alc_kcontrol_new(spec, name, &alc_control_templates[type]); + if (!knew) + return -ENOMEM; + knew->index = cidx; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + knew->private_value = val; + return 0; +} + +static int add_control_with_pfx(struct alc_spec *spec, int type, + const char *pfx, const char *dir, + const char *sfx, int cidx, unsigned long val) +{ + char name[32]; + snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + return add_control(spec, type, name, cidx, val); +} + +#define add_pb_vol_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) +#define add_pb_sw_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) +#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) +#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) + +static const char * const channel_name[4] = { + "Front", "Surround", "CLFE", "Side" +}; + +static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, + bool can_be_master, int *index) +{ + struct auto_pin_cfg *cfg = &spec->autocfg; + + *index = 0; + if (cfg->line_outs == 1 && !spec->multi_ios && + !cfg->hp_outs && !cfg->speaker_outs && can_be_master) + return "Master"; + + switch (cfg->line_out_type) { + case AUTO_PIN_SPEAKER_OUT: + if (cfg->line_outs == 1) + return "Speaker"; + if (cfg->line_outs == 2) + return ch ? "Bass Speaker" : "Speaker"; + break; + case AUTO_PIN_HP_OUT: + /* for multi-io case, only the primary out */ + if (ch && spec->multi_ios) + break; + *index = ch; + return "Headphone"; + default: + if (cfg->line_outs == 1 && !spec->multi_ios) + return "PCM"; + break; + } + if (ch >= ARRAY_SIZE(channel_name)) { + snd_BUG(); + return "PCM"; + } + + return channel_name[ch]; +} + +#ifdef CONFIG_PM +/* add the powersave loopback-list entry */ +static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) +{ + struct hda_amp_list *list; + + if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) + return; + list = spec->loopback_list + spec->num_loopbacks; + list->nid = mix; + list->dir = HDA_INPUT; + list->idx = idx; + spec->num_loopbacks++; + spec->loopback.amplist = spec->loopback_list; +} +#else +#define add_loopback_list(spec, mix, idx) /* NOP */ +#endif + +/* create input playback/capture controls for the given pin */ +static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, + const char *ctlname, int ctlidx, + int idx, hda_nid_t mix_nid) +{ + int err; + + err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + if (err < 0) + return err; + err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + if (err < 0) + return err; + add_loopback_list(spec, mix_nid, idx); + return 0; +} + +static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, nid); + return (pincap & AC_PINCAP_IN) != 0; +} + +/* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */ +static int alc_auto_fill_adc_caps(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid; + hda_nid_t *adc_nids = spec->private_adc_nids; + hda_nid_t *cap_nids = spec->private_capsrc_nids; + int max_nums = ARRAY_SIZE(spec->private_adc_nids); + int i, nums = 0; + + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + hda_nid_t src; + unsigned int caps = get_wcaps(codec, nid); + int type = get_wcaps_type(caps); + + if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) + continue; + adc_nids[nums] = nid; + cap_nids[nums] = nid; + src = nid; + for (;;) { + int n; + type = get_wcaps_type(get_wcaps(codec, src)); + if (type == AC_WID_PIN) + break; + if (type == AC_WID_AUD_SEL) { + cap_nids[nums] = src; + break; + } + n = snd_hda_get_num_conns(codec, src); + if (n > 1) { + cap_nids[nums] = src; + break; + } else if (n != 1) + break; + if (snd_hda_get_connections(codec, src, &src, 1) != 1) + break; + } + if (++nums >= max_nums) + break; + } + spec->adc_nids = spec->private_adc_nids; + spec->capsrc_nids = spec->private_capsrc_nids; + spec->num_adc_nids = nums; + return nums; +} + +/* create playback/capture controls for input pins */ +static int alc_auto_create_input_ctls(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t mixer = spec->mixer_nid; + struct hda_input_mux *imux = &spec->private_imux[0]; + int num_adcs; + int i, c, err, idx, type_idx = 0; + const char *prev_label = NULL; + + num_adcs = alc_auto_fill_adc_caps(codec); + if (num_adcs < 0) + return 0; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin; + const char *label; + + pin = cfg->inputs[i].pin; + if (!alc_is_input_pin(codec, pin)) + continue; + + label = hda_get_autocfg_input_label(codec, cfg, i); + if (spec->shared_mic_hp && !strcmp(label, "Misc")) + label = "Headphone Mic"; + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + + if (mixer) { + idx = get_connection_index(codec, mixer, pin); + if (idx >= 0) { + err = new_analog_input(spec, pin, + label, type_idx, + idx, mixer); + if (err < 0) + return err; + } + } + + for (c = 0; c < num_adcs; c++) { + hda_nid_t cap = get_capsrc(spec, c); + idx = get_connection_index(codec, cap, pin); + if (idx >= 0) { + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(imux, label, idx, NULL); + break; + } + } + } + + spec->num_mux_defs = 1; + spec->input_mux = imux; + + return 0; +} + +/* create a shared input with the headphone out */ +static int alc_auto_create_shared_input(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int defcfg; + hda_nid_t nid; + + /* only one internal input pin? */ + if (cfg->num_inputs != 1) + return 0; + defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); + if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) + return 0; + + if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */ + else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT) + nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */ + else + return 0; /* both not available */ + + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) + return 0; /* no input */ + + cfg->inputs[1].pin = nid; + cfg->inputs[1].type = AUTO_PIN_MIC; + cfg->num_inputs = 2; + spec->shared_mic_hp = 1; + snd_printdd("realtek: Enable shared I/O jack on NID 0x%x\n", nid); + return 0; +} + +static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, + unsigned int pin_type) +{ + snd_hda_set_pin_ctl(codec, nid, pin_type); + /* unmute pin */ + if (nid_has_mute(codec, nid, HDA_OUTPUT)) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); +} + +static int get_pin_type(int line_out_type) +{ + if (line_out_type == AUTO_PIN_HP_OUT) + return PIN_HP; + else + return PIN_OUT; +} + +static void alc_auto_init_analog_input(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + if (alc_is_input_pin(codec, nid)) { + alc_set_input_pin(codec, nid, cfg->inputs[i].type); + 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); + } + } + + /* mute all loopback inputs */ + if (spec->mixer_nid) { + int nums = snd_hda_get_num_conns(codec, spec->mixer_nid); + for (i = 0; i < nums; i++) + snd_hda_codec_write(codec, spec->mixer_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i)); + } +} + +/* convert from MIX nid to DAC */ +static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid) +{ + hda_nid_t list[5]; + int i, num; + + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT) + return nid; + num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list)); + for (i = 0; i < num; i++) { + if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT) + return list[i]; + } + return 0; +} + +/* go down to the selector widget before the mixer */ +static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t srcs[5]; + int num = snd_hda_get_connections(codec, pin, srcs, + ARRAY_SIZE(srcs)); + if (num != 1 || + get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL) + return pin; + return srcs[0]; +} + +/* get MIX nid connected to the given pin targeted to DAC */ +static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + hda_nid_t mix[5]; + int i, num; + + pin = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); + for (i = 0; i < num; i++) { + if (alc_auto_mix_to_dac(codec, mix[i]) == dac) + return mix[i]; + } + return 0; +} + +/* select the connection from pin to DAC if needed */ +static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + hda_nid_t mix[5]; + int i, num; + + pin = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); + if (num < 2) + return 0; + for (i = 0; i < num; i++) { + if (alc_auto_mix_to_dac(codec, mix[i]) == dac) { + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_SET_CONNECT_SEL, i); + return 0; + } + } + return 0; +} + +static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) +{ + struct alc_spec *spec = codec->spec; + int i; + if (found_in_nid_list(nid, spec->multiout.dac_nids, + ARRAY_SIZE(spec->private_dac_nids)) || + found_in_nid_list(nid, spec->multiout.hp_out_nid, + ARRAY_SIZE(spec->multiout.hp_out_nid)) || + found_in_nid_list(nid, spec->multiout.extra_out_nid, + ARRAY_SIZE(spec->multiout.extra_out_nid))) + return true; + for (i = 0; i < spec->multi_ios; i++) { + if (spec->multi_io[i].dac == nid) + return true; + } + return false; +} + +/* look for an empty DAC slot */ +static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t srcs[5]; + int i, num; + + pin = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); + for (i = 0; i < num; i++) { + hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); + if (!nid) + continue; + if (!alc_is_dac_already_used(codec, nid)) + return nid; + } + return 0; +} + +/* check whether the DAC is reachable from the pin */ +static bool alc_auto_is_dac_reachable(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac) +{ + hda_nid_t srcs[5]; + int i, num; + + if (!pin || !dac) + return false; + pin = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); + for (i = 0; i < num; i++) { + hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); + if (nid == dac) + return true; + } + return false; +} + +static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t sel = alc_go_down_to_selector(codec, pin); + hda_nid_t nid, nid_found, srcs[5]; + int i, num = snd_hda_get_connections(codec, sel, srcs, + ARRAY_SIZE(srcs)); + if (num == 1) + return alc_auto_look_for_dac(codec, pin); + nid_found = 0; + for (i = 0; i < num; i++) { + if (srcs[i] == spec->mixer_nid) + continue; + nid = alc_auto_mix_to_dac(codec, srcs[i]); + if (nid && !alc_is_dac_already_used(codec, nid)) { + if (nid_found) + return 0; + nid_found = nid; + } + } + return nid_found; +} + +/* mark up volume and mute control NIDs: used during badness parsing and + * at creating actual controls + */ +static inline unsigned int get_ctl_pos(unsigned int data) +{ + hda_nid_t nid = get_amp_nid_(data); + unsigned int dir; + if (snd_BUG_ON(nid >= MAX_VOL_NIDS)) + return 0; + dir = get_amp_direction_(data); + return (nid << 1) | dir; +} + +#define is_ctl_used(bits, data) \ + test_bit(get_ctl_pos(data), bits) +#define mark_ctl_usage(bits, data) \ + set_bit(get_ctl_pos(data), bits) + +static void clear_vol_marks(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls)); + memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls)); +} + +/* badness definition */ +enum { + /* No primary DAC is found for the main output */ + BAD_NO_PRIMARY_DAC = 0x10000, + /* No DAC is found for the extra output */ + BAD_NO_DAC = 0x4000, + /* No possible multi-ios */ + BAD_MULTI_IO = 0x103, + /* No individual DAC for extra output */ + BAD_NO_EXTRA_DAC = 0x102, + /* No individual DAC for extra surrounds */ + BAD_NO_EXTRA_SURR_DAC = 0x101, + /* Primary DAC shared with main surrounds */ + BAD_SHARED_SURROUND = 0x100, + /* Primary DAC shared with main CLFE */ + BAD_SHARED_CLFE = 0x10, + /* Primary DAC shared with extra surrounds */ + BAD_SHARED_EXTRA_SURROUND = 0x10, + /* Volume widget is shared */ + BAD_SHARED_VOL = 0x10, +}; + +static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac); +static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac); + +static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid; + unsigned int val; + int badness = 0; + + nid = alc_look_for_out_vol_nid(codec, pin, dac); + if (nid) { + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + if (is_ctl_used(spec->vol_ctls, nid)) + badness += BAD_SHARED_VOL; + else + mark_ctl_usage(spec->vol_ctls, val); + } else + badness += BAD_SHARED_VOL; + nid = alc_look_for_out_mute_nid(codec, pin, dac); + if (nid) { + unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); + if (is_ctl_used(spec->sw_ctls, val)) + badness += BAD_SHARED_VOL; + else + mark_ctl_usage(spec->sw_ctls, val); + } else + badness += BAD_SHARED_VOL; + return badness; +} + +struct badness_table { + int no_primary_dac; /* no primary DAC */ + int no_dac; /* no secondary DACs */ + int shared_primary; /* primary DAC is shared with main output */ + int shared_surr; /* secondary DAC shared with main or primary */ + int shared_clfe; /* third DAC shared with main or primary */ + int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ +}; + +static struct badness_table main_out_badness = { + .no_primary_dac = BAD_NO_PRIMARY_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_PRIMARY_DAC, + .shared_surr = BAD_SHARED_SURROUND, + .shared_clfe = BAD_SHARED_CLFE, + .shared_surr_main = BAD_SHARED_SURROUND, +}; + +static struct badness_table extra_out_badness = { + .no_primary_dac = BAD_NO_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_EXTRA_DAC, + .shared_surr = BAD_SHARED_EXTRA_SURROUND, + .shared_clfe = BAD_SHARED_EXTRA_SURROUND, + .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, +}; + +/* try to assign DACs to pins and return the resultant badness */ +static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, + const hda_nid_t *pins, hda_nid_t *dacs, + const struct badness_table *bad) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, j; + int badness = 0; + hda_nid_t dac; + + if (!num_outs) + return 0; + + for (i = 0; i < num_outs; i++) { + hda_nid_t pin = pins[i]; + if (!dacs[i]) + dacs[i] = alc_auto_look_for_dac(codec, pin); + if (!dacs[i] && !i) { + for (j = 1; j < num_outs; j++) { + if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) { + dacs[0] = dacs[j]; + dacs[j] = 0; + break; + } + } + } + dac = dacs[i]; + if (!dac) { + if (alc_auto_is_dac_reachable(codec, pin, dacs[0])) + dac = dacs[0]; + else if (cfg->line_outs > i && + alc_auto_is_dac_reachable(codec, pin, + spec->private_dac_nids[i])) + dac = spec->private_dac_nids[i]; + if (dac) { + if (!i) + badness += bad->shared_primary; + else if (i == 1) + badness += bad->shared_surr; + else + badness += bad->shared_clfe; + } else if (alc_auto_is_dac_reachable(codec, pin, + spec->private_dac_nids[0])) { + dac = spec->private_dac_nids[0]; + badness += bad->shared_surr_main; + } else if (!i) + badness += bad->no_primary_dac; + else + badness += bad->no_dac; + } + if (dac) + badness += eval_shared_vol_badness(codec, pin, dac); + } + + return badness; +} + +static int alc_auto_fill_multi_ios(struct hda_codec *codec, + hda_nid_t reference_pin, + bool hardwired, int offset); + +static bool alc_map_singles(struct hda_codec *codec, int outs, + const hda_nid_t *pins, hda_nid_t *dacs) +{ + int i; + bool found = false; + for (i = 0; i < outs; i++) { + if (dacs[i]) + continue; + dacs[i] = get_dac_if_single(codec, pins[i]); + if (dacs[i]) + found = true; + } + return found; +} + +/* fill in the dac_nids table from the parsed pin configuration */ +static int fill_and_eval_dacs(struct hda_codec *codec, + bool fill_hardwired, + bool fill_mio_first) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err, badness; + + /* set num_dacs once to full for alc_auto_look_for_dac() */ + spec->multiout.num_dacs = cfg->line_outs; + spec->multiout.dac_nids = spec->private_dac_nids; + memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); + memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); + memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); + spec->multi_ios = 0; + clear_vol_marks(codec); + badness = 0; + + /* fill hard-wired DACs first */ + if (fill_hardwired) { + bool mapped; + do { + mapped = alc_map_singles(codec, cfg->line_outs, + cfg->line_out_pins, + spec->private_dac_nids); + mapped |= alc_map_singles(codec, cfg->hp_outs, + cfg->hp_pins, + spec->multiout.hp_out_nid); + mapped |= alc_map_singles(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid); + if (fill_mio_first && cfg->line_outs == 1 && + cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); + if (!err) + mapped = true; + } + } while (mapped); + } + + badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins, + spec->private_dac_nids, + &main_out_badness); + + /* re-count num_dacs and squash invalid entries */ + spec->multiout.num_dacs = 0; + for (i = 0; i < cfg->line_outs; i++) { + if (spec->private_dac_nids[i]) + spec->multiout.num_dacs++; + else { + memmove(spec->private_dac_nids + i, + spec->private_dac_nids + i + 1, + sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); + spec->private_dac_nids[cfg->line_outs - 1] = 0; + } + } + + if (fill_mio_first && + cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + /* try to fill multi-io first */ + err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + if (err < 0) + return err; + /* we don't count badness at this stage yet */ + } + + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins, + spec->multiout.hp_out_nid, + &extra_out_badness); + if (err < 0) + return err; + badness += err; + } + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc_auto_fill_dacs(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid, + &extra_out_badness); + if (err < 0) + return err; + badness += err; + } + if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + if (err < 0) + return err; + badness += err; + } + if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + /* try multi-ios with HP + inputs */ + int offset = 0; + if (cfg->line_outs >= 3) + offset = 1; + err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false, + offset); + if (err < 0) + return err; + badness += err; + } + + if (spec->multi_ios == 2) { + for (i = 0; i < 2; i++) + spec->private_dac_nids[spec->multiout.num_dacs++] = + spec->multi_io[i].dac; + spec->ext_channel_count = 2; + } else if (spec->multi_ios) { + spec->multi_ios = 0; + badness += BAD_MULTI_IO; + } + + return badness; +} + +#define DEBUG_BADNESS + +#ifdef DEBUG_BADNESS +#define debug_badness snd_printdd +#else +#define debug_badness(...) +#endif + +static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) +{ + debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->line_out_pins[0], cfg->line_out_pins[1], + cfg->line_out_pins[2], cfg->line_out_pins[2], + spec->multiout.dac_nids[0], + spec->multiout.dac_nids[1], + spec->multiout.dac_nids[2], + spec->multiout.dac_nids[3]); + if (spec->multi_ios > 0) + debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", + spec->multi_ios, + spec->multi_io[0].pin, spec->multi_io[1].pin, + spec->multi_io[0].dac, spec->multi_io[1].dac); + debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->hp_pins[0], cfg->hp_pins[1], + cfg->hp_pins[2], cfg->hp_pins[2], + spec->multiout.hp_out_nid[0], + spec->multiout.hp_out_nid[1], + spec->multiout.hp_out_nid[2], + spec->multiout.hp_out_nid[3]); + debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->speaker_pins[0], cfg->speaker_pins[1], + cfg->speaker_pins[2], cfg->speaker_pins[3], + spec->multiout.extra_out_nid[0], + spec->multiout.extra_out_nid[1], + spec->multiout.extra_out_nid[2], + spec->multiout.extra_out_nid[3]); +} + +static int alc_auto_fill_dac_nids(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg *best_cfg; + int best_badness = INT_MAX; + int badness; + bool fill_hardwired = true, fill_mio_first = true; + bool best_wired = true, best_mio = true; + bool hp_spk_swapped = false; + + best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); + if (!best_cfg) + return -ENOMEM; + *best_cfg = *cfg; + + for (;;) { + badness = fill_and_eval_dacs(codec, fill_hardwired, + fill_mio_first); + if (badness < 0) { + kfree(best_cfg); + return badness; + } + debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", + cfg->line_out_type, fill_hardwired, fill_mio_first, + badness); + debug_show_configs(spec, cfg); + if (badness < best_badness) { + best_badness = badness; + *best_cfg = *cfg; + best_wired = fill_hardwired; + best_mio = fill_mio_first; + } + if (!badness) + break; + fill_mio_first = !fill_mio_first; + if (!fill_mio_first) + continue; + fill_hardwired = !fill_hardwired; + if (!fill_hardwired) + continue; + if (hp_spk_swapped) + break; + hp_spk_swapped = true; + if (cfg->speaker_outs > 0 && + cfg->line_out_type == AUTO_PIN_HP_OUT) { + cfg->hp_outs = cfg->line_outs; + memcpy(cfg->hp_pins, cfg->line_out_pins, + sizeof(cfg->hp_pins)); + cfg->line_outs = cfg->speaker_outs; + memcpy(cfg->line_out_pins, cfg->speaker_pins, + sizeof(cfg->speaker_pins)); + cfg->speaker_outs = 0; + memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); + cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; + fill_hardwired = true; + continue; + } + if (cfg->hp_outs > 0 && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + fill_hardwired = true; + continue; + } + break; + } + + if (badness) { + *cfg = *best_cfg; + fill_and_eval_dacs(codec, best_wired, best_mio); + } + debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", + cfg->line_out_type, best_wired, best_mio); + debug_show_configs(spec, cfg); + + if (cfg->line_out_pins[0]) + spec->vmaster_nid = + alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0], + spec->multiout.dac_nids[0]); + + /* clear the bitmap flags for creating controls */ + clear_vol_marks(codec); + kfree(best_cfg); + return 0; +} + +static int alc_auto_add_vol_ctl(struct hda_codec *codec, + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs) +{ + struct alc_spec *spec = codec->spec; + unsigned int val; + if (!nid) + return 0; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); + if (is_ctl_used(spec->vol_ctls, val) && chs != 2) /* exclude LFE */ + return 0; + mark_ctl_usage(spec->vol_ctls, val); + return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, + val); +} + +static int alc_auto_add_stereo_vol(struct hda_codec *codec, + const char *pfx, int cidx, + hda_nid_t nid) +{ + int chs = 1; + if (get_wcaps(codec, nid) & AC_WCAP_STEREO) + chs = 3; + return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs); +} + +/* create a mute-switch for the given mixer widget; + * if it has multiple sources (e.g. DAC and loopback), create a bind-mute + */ +static int alc_auto_add_sw_ctl(struct hda_codec *codec, + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs) +{ + struct alc_spec *spec = codec->spec; + int wid_type; + int type; + unsigned long val; + if (!nid) + return 0; + wid_type = get_wcaps_type(get_wcaps(codec, nid)); + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { + type = ALC_CTL_WIDGET_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); + } else if (snd_hda_get_num_conns(codec, nid) == 1) { + type = ALC_CTL_WIDGET_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); + } else { + type = ALC_CTL_BIND_MUTE; + val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); + } + if (is_ctl_used(spec->sw_ctls, val) && chs != 2) /* exclude LFE */ + return 0; + mark_ctl_usage(spec->sw_ctls, val); + return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); +} + +static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx, + int cidx, hda_nid_t nid) +{ + int chs = 1; + if (get_wcaps(codec, nid) & AC_WCAP_STEREO) + chs = 3; + return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs); +} + +static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac) +{ + hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); + if (nid_has_mute(codec, pin, HDA_OUTPUT)) + return pin; + else if (mix && nid_has_mute(codec, mix, HDA_INPUT)) + return mix; + else if (nid_has_mute(codec, dac, HDA_OUTPUT)) + return dac; + return 0; +} + +static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac) +{ + hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); + if (nid_has_volume(codec, dac, HDA_OUTPUT)) + return dac; + else if (nid_has_volume(codec, mix, HDA_OUTPUT)) + return mix; + else if (nid_has_volume(codec, pin, HDA_OUTPUT)) + return pin; + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + struct alc_spec *spec = codec->spec; + int i, err, noutputs; + + noutputs = cfg->line_outs; + if (spec->multi_ios > 0 && cfg->line_outs < 3) + noutputs += spec->multi_ios; + + for (i = 0; i < noutputs; i++) { + const char *name; + int index; + hda_nid_t dac, pin; + hda_nid_t sw, vol; + + dac = spec->multiout.dac_nids[i]; + if (!dac) + continue; + if (i >= cfg->line_outs) { + pin = spec->multi_io[i - 1].pin; + index = 0; + name = channel_name[i]; + } else { + pin = cfg->line_out_pins[i]; + name = alc_get_line_out_pfx(spec, i, true, &index); + } + + sw = alc_look_for_out_mute_nid(codec, pin, dac); + vol = alc_look_for_out_vol_nid(codec, pin, dac); + if (!name || !strcmp(name, "CLFE")) { + /* Center/LFE */ + err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1); + if (err < 0) + return err; + err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2); + if (err < 0) + return err; + err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1); + if (err < 0) + return err; + err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2); + if (err < 0) + return err; + } else { + err = alc_auto_add_stereo_vol(codec, name, index, vol); + if (err < 0) + return err; + err = alc_auto_add_stereo_sw(codec, name, index, sw); + if (err < 0) + return err; + } + } + return 0; +} + +static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac, const char *pfx, + int cidx) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t sw, vol; + int err; + + if (!dac) { + unsigned int val; + /* the corresponding DAC is already occupied */ + if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) + return 0; /* no way */ + /* create a switch only */ + val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT); + if (is_ctl_used(spec->sw_ctls, val)) + return 0; /* already created */ + mark_ctl_usage(spec->sw_ctls, val); + return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val); + } + + sw = alc_look_for_out_mute_nid(codec, pin, dac); + vol = alc_look_for_out_vol_nid(codec, pin, dac); + err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol); + if (err < 0) + return err; + err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw); + if (err < 0) + return err; + return 0; +} + +static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec, + unsigned int nums, + struct hda_ctl_ops *ops) +{ + struct alc_spec *spec = codec->spec; + struct hda_bind_ctls **ctlp, *ctl; + ctlp = snd_array_new(&spec->bind_ctls); + if (!ctlp) + return NULL; + ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL); + *ctlp = ctl; + if (ctl) + ctl->ops = ops; + return ctl; +} + +/* add playback controls for speaker and HP outputs */ +static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, + const hda_nid_t *pins, + const hda_nid_t *dacs, + const char *pfx) +{ + struct alc_spec *spec = codec->spec; + struct hda_bind_ctls *ctl; + char name[32]; + int i, n, err; + + if (!num_pins || !pins[0]) + return 0; + + if (num_pins == 1) { + hda_nid_t dac = *dacs; + if (!dac) + dac = spec->multiout.dac_nids[0]; + return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0); + } + + for (i = 0; i < num_pins; i++) { + hda_nid_t dac; + if (dacs[num_pins - 1]) + dac = dacs[i]; /* with individual volumes */ + else + dac = 0; + if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { + err = alc_auto_create_extra_out(codec, pins[i], dac, + "Bass Speaker", 0); + } else if (num_pins >= 3) { + snprintf(name, sizeof(name), "%s %s", + pfx, channel_name[i]); + err = alc_auto_create_extra_out(codec, pins[i], dac, + name, 0); + } else { + err = alc_auto_create_extra_out(codec, pins[i], dac, + pfx, i); + } + if (err < 0) + return err; + } + if (dacs[num_pins - 1]) + return 0; + + /* Let's create a bind-controls for volumes */ + ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); + if (!ctl) + return -ENOMEM; + n = 0; + for (i = 0; i < num_pins; i++) { + hda_nid_t vol; + if (!pins[i] || !dacs[i]) + continue; + vol = alc_look_for_out_vol_nid(codec, pins[i], dacs[i]); + if (vol) + ctl->values[n++] = + HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); + } + if (n) { + snprintf(name, sizeof(name), "%s Playback Volume", pfx); + err = add_control(spec, ALC_CTL_BIND_VOL, name, 0, (long)ctl); + if (err < 0) + return err; + } + return 0; +} + +static int alc_auto_create_hp_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + return alc_auto_create_extra_outs(codec, spec->autocfg.hp_outs, + spec->autocfg.hp_pins, + spec->multiout.hp_out_nid, + "Headphone"); +} + +static int alc_auto_create_speaker_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + return alc_auto_create_extra_outs(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + spec->multiout.extra_out_nid, + "Speaker"); +} + +static void alc_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t pin, int pin_type, + hda_nid_t dac) +{ + int i, num; + hda_nid_t nid, mix = 0; + hda_nid_t srcs[HDA_MAX_CONNECTIONS]; + + alc_set_pin_output(codec, pin, pin_type); + nid = alc_go_down_to_selector(codec, pin); + num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); + for (i = 0; i < num; i++) { + if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) + continue; + mix = srcs[i]; + break; + } + if (!mix) + return; + + /* need the manual connection? */ + if (num > 1) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); + /* unmute mixer widget inputs */ + if (nid_has_mute(codec, mix, HDA_INPUT)) { + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + } + /* initialize volume */ + nid = alc_look_for_out_vol_nid(codec, pin, dac); + if (nid) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); + + /* unmute DAC if it's not assigned to a mixer */ + nid = alc_look_for_out_mute_nid(codec, pin, dac); + if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT)) + snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); +} + +static void alc_auto_init_multi_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int pin_type = get_pin_type(spec->autocfg.line_out_type); + int i; + + for (i = 0; i <= HDA_SIDE; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + if (nid) + alc_auto_set_output_and_unmute(codec, nid, pin_type, + spec->multiout.dac_nids[i]); + } +} + +static void alc_auto_init_extra_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + hda_nid_t pin, dac; + + for (i = 0; i < spec->autocfg.hp_outs; i++) { + if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) + break; + pin = spec->autocfg.hp_pins[i]; + if (!pin) + break; + dac = spec->multiout.hp_out_nid[i]; + if (!dac) { + if (i > 0 && spec->multiout.hp_out_nid[0]) + dac = spec->multiout.hp_out_nid[0]; + else + dac = spec->multiout.dac_nids[0]; + } + alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + } + for (i = 0; i < spec->autocfg.speaker_outs; i++) { + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) + break; + pin = spec->autocfg.speaker_pins[i]; + if (!pin) + break; + dac = spec->multiout.extra_out_nid[i]; + if (!dac) { + if (i > 0 && spec->multiout.extra_out_nid[0]) + dac = spec->multiout.extra_out_nid[0]; + else + dac = spec->multiout.dac_nids[0]; + } + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + } +} + +/* check whether the given pin can be a multi-io pin */ +static bool can_be_multiio_pin(struct hda_codec *codec, + unsigned int location, hda_nid_t nid) +{ + unsigned int defcfg, caps; + + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) + return false; + if (location && get_defcfg_location(defcfg) != location) + return false; + caps = snd_hda_query_pin_caps(codec, nid); + if (!(caps & AC_PINCAP_OUT)) + return false; + return true; +} + +/* + * multi-io helper + * + * When hardwired is set, try to fill ony hardwired pins, and returns + * zero if any pins are filled, non-zero if nothing found. + * When hardwired is off, try to fill possible input pins, and returns + * the badness value. + */ +static int alc_auto_fill_multi_ios(struct hda_codec *codec, + hda_nid_t reference_pin, + bool hardwired, int offset) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int type, i, j, dacs, num_pins, old_pins; + unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); + unsigned int location = get_defcfg_location(defcfg); + int badness = 0; + + old_pins = spec->multi_ios; + if (old_pins >= 2) + goto end_fill; + + num_pins = 0; + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != type) + continue; + if (can_be_multiio_pin(codec, location, + cfg->inputs[i].pin)) + num_pins++; + } + } + if (num_pins < 2) + goto end_fill; + + dacs = spec->multiout.num_dacs; + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + hda_nid_t dac = 0; + + if (cfg->inputs[i].type != type) + continue; + if (!can_be_multiio_pin(codec, location, nid)) + continue; + for (j = 0; j < spec->multi_ios; j++) { + if (nid == spec->multi_io[j].pin) + break; + } + if (j < spec->multi_ios) + continue; + + if (offset && offset + spec->multi_ios < dacs) { + dac = spec->private_dac_nids[offset + spec->multi_ios]; + if (!alc_auto_is_dac_reachable(codec, nid, dac)) + dac = 0; + } + if (hardwired) + dac = get_dac_if_single(codec, nid); + else if (!dac) + dac = alc_auto_look_for_dac(codec, nid); + if (!dac) { + badness++; + continue; + } + spec->multi_io[spec->multi_ios].pin = nid; + spec->multi_io[spec->multi_ios].dac = dac; + spec->multi_ios++; + if (spec->multi_ios >= 2) + break; + } + } + end_fill: + if (badness) + badness = BAD_MULTI_IO; + if (old_pins == spec->multi_ios) { + if (hardwired) + return 1; /* nothing found */ + else + return badness; /* no badness if nothing found */ + } + if (!hardwired && spec->multi_ios < 2) { + spec->multi_ios = old_pins; + return badness; + } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); return 0; } +static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; -/* - * Common callbacks - */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->multi_ios + 1; + if (uinfo->value.enumerated.item > spec->multi_ios) + uinfo->value.enumerated.item = spec->multi_ios; + sprintf(uinfo->value.enumerated.name, "%dch", + (uinfo->value.enumerated.item + 1) * 2); + return 0; +} -static int alc_init(struct hda_codec *codec) +static int alc_auto_ch_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; + return 0; +} - if (spec->init_hook) - spec->init_hook(codec); +static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid = spec->multi_io[idx].pin; + + if (!spec->multi_io[idx].ctl_in) + spec->multi_io[idx].ctl_in = + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (output) { + snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, 0); + alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac); + } else { + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_set_pin_ctl_cache(codec, nid, + spec->multi_io[idx].ctl_in); + } + return 0; +} - alc_fix_pll(codec); - alc_auto_init_amp(codec, spec->init_amp); +static int alc_auto_ch_mode_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 i, ch; + + ch = ucontrol->value.enumerated.item[0]; + if (ch < 0 || ch > spec->multi_ios) + return -EINVAL; + if (ch == (spec->ext_channel_count - 1) / 2) + return 0; + spec->ext_channel_count = (ch + 1) * 2; + for (i = 0; i < spec->multi_ios; i++) + alc_set_multi_io(codec, i, i < ch); + spec->multiout.max_channels = max(spec->ext_channel_count, + spec->const_channel_count); + if (spec->need_dac_fix) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; + return 1; +} - snd_hda_gen_init(codec); +static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_auto_ch_mode_info, + .get = alc_auto_ch_mode_get, + .put = alc_auto_ch_mode_put, +}; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); +static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + if (spec->multi_ios > 0) { + if (!alc_kcontrol_new(spec, "Channel Mode", + &alc_auto_channel_mode_enum)) + return -ENOMEM; + } return 0; } -static inline void alc_shutup(struct hda_codec *codec) +/* filter out invalid adc_nids (and capsrc_nids) that don't give all + * active input pins + */ +static void alc_remove_invalid_adc_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux; + hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + int i, n, nums; - if (spec && spec->shutup) - spec->shutup(codec); - snd_hda_shutup_pins(codec); + imux = spec->input_mux; + if (!imux) + return; + if (spec->dyn_adc_switch) + return; + + again: + nums = 0; + for (n = 0; n < spec->num_adc_nids; n++) { + hda_nid_t cap = spec->private_capsrc_nids[n]; + int num_conns = snd_hda_get_num_conns(codec, cap); + for (i = 0; i < imux->num_items; i++) { + hda_nid_t pin = spec->imux_pins[i]; + if (pin) { + if (get_connection_index(codec, cap, pin) < 0) + break; + } else if (num_conns <= imux->items[i].index) + break; + } + if (i >= imux->num_items) { + adc_nids[nums] = spec->private_adc_nids[n]; + capsrc_nids[nums++] = cap; + } + } + if (!nums) { + /* check whether ADC-switch is possible */ + if (!alc_check_dyn_adc_switch(codec)) { + if (spec->shared_mic_hp) { + spec->shared_mic_hp = 0; + spec->private_imux[0].num_items = 1; + goto again; + } + printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" + " using fallback 0x%x\n", + codec->chip_name, spec->private_adc_nids[0]); + spec->num_adc_nids = 1; + spec->auto_mic = 0; + return; + } + } else if (nums != spec->num_adc_nids) { + memcpy(spec->private_adc_nids, adc_nids, + nums * sizeof(hda_nid_t)); + memcpy(spec->private_capsrc_nids, capsrc_nids, + nums * sizeof(hda_nid_t)); + spec->num_adc_nids = nums; + } + + if (spec->auto_mic) + alc_auto_mic_check_imux(codec); /* check auto-mic setups */ + else if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) + spec->num_adc_nids = 1; /* reduce to a single ADC */ } -static void alc_free(struct hda_codec *codec) +/* + * initialize ADC paths + */ +static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) { struct alc_spec *spec = codec->spec; + hda_nid_t nid; - if (!spec) + nid = spec->adc_nids[adc_idx]; + /* mute ADC */ + if (nid_has_mute(codec, nid, HDA_INPUT)) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); return; - - snd_hda_gen_spec_free(&spec->gen); - snd_hda_detach_beep_device(codec); - kfree(spec); + } + if (!spec->capsrc_nids) + return; + nid = spec->capsrc_nids[adc_idx]; + if (nid_has_mute(codec, nid, HDA_OUTPUT)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); } -#ifdef CONFIG_PM -static void alc_power_eapd(struct hda_codec *codec) +static void alc_auto_init_input_src(struct hda_codec *codec) { - alc_auto_setup_eapd(codec, false); + struct alc_spec *spec = codec->spec; + int c, nums; + + for (c = 0; c < spec->num_adc_nids; c++) + alc_auto_init_adc(codec, c); + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + for (c = 0; c < nums; c++) + alc_mux_select(codec, c, spec->cur_mux[c], true); } -static int alc_suspend(struct hda_codec *codec) +/* add mic boosts if needed */ +static int alc_auto_add_mic_boost(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc_shutup(codec); - if (spec && spec->power_hook) - spec->power_hook(codec); + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + int type_idx = 0; + hda_nid_t nid; + const char *prev_label = NULL; + + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type > AUTO_PIN_MIC) + break; + nid = cfg->inputs[i].pin; + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + const char *label; + char boost_label[32]; + + label = hda_get_autocfg_input_label(codec, cfg, i); + if (spec->shared_mic_hp && !strcmp(label, "Misc")) + label = "Headphone Mic"; + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + + snprintf(boost_label, sizeof(boost_label), + "%s Boost Volume", label); + err = add_control(spec, ALC_CTL_WIDGET_VOL, + boost_label, type_idx, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } + } return 0; } -#endif -#ifdef CONFIG_PM -static int alc_resume(struct hda_codec *codec) +/* select or unmute the given capsrc route */ +static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, + int idx) { - msleep(150); /* to avoid pop noise */ - codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); - alc_inv_dmic_sync(codec, true); - hda_call_check_power_status(codec, 0x01); - return 0; + if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { + snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, + HDA_AMP_MUTE, 0); + } else if (snd_hda_get_num_conns(codec, cap) > 1) { + snd_hda_codec_write_cache(codec, cap, 0, + AC_VERB_SET_CONNECT_SEL, idx); + } } -#endif -/* - */ -static const struct hda_codec_ops alc_patch_ops = { - .build_controls = alc_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = alc_init, - .free = alc_free, - .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM - .resume = alc_resume, - .suspend = alc_suspend, - .check_power_status = snd_hda_gen_check_power_status, -#endif - .reboot_notify = alc_shutup, -}; +/* set the default connection to that pin */ +static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin) +{ + struct alc_spec *spec = codec->spec; + int i; + if (!pin) + return 0; + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t cap = get_capsrc(spec, i); + int idx; -/* replace the codec chip_name with the given string */ -static int alc_codec_rename(struct hda_codec *codec, const char *name) -{ - kfree(codec->chip_name); - codec->chip_name = kstrdup(name, GFP_KERNEL); - if (!codec->chip_name) { - alc_free(codec); - return -ENOMEM; + idx = get_connection_index(codec, cap, pin); + if (idx < 0) + continue; + select_or_unmute_capsrc(codec, cap, idx); + return i; /* return the found index */ } - return 0; + return -1; /* not found */ } -/* - * Rename codecs appropriately from COEF value - */ -struct alc_codec_rename_table { - unsigned int vendor_id; - unsigned short coef_mask; - unsigned short coef_bits; - const char *name; -}; +/* initialize some special cases for input sources */ +static void alc_init_special_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; -static struct alc_codec_rename_table rename_tbl[] = { - { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, - { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, - { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, - { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, - { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, - { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, - { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, - { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, - { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, - { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, - { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, - { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, - { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, - { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, - { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, - { } /* terminator */ -}; + for (i = 0; i < spec->autocfg.num_inputs; i++) + init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin); +} -static int alc_codec_rename_from_preset(struct hda_codec *codec) +/* assign appropriate capture mixers */ +static void set_capture_mixer(struct hda_codec *codec) { - const struct alc_codec_rename_table *p; + struct alc_spec *spec = codec->spec; + static const struct snd_kcontrol_new *caps[2][3] = { + { alc_capture_mixer_nosrc1, + alc_capture_mixer_nosrc2, + alc_capture_mixer_nosrc3 }, + { alc_capture_mixer1, + alc_capture_mixer2, + alc_capture_mixer3 }, + }; - for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->vendor_id) - continue; - if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) - return alc_codec_rename(codec, p->name); + /* check whether either of ADC or MUX has a volume control */ + if (!nid_has_volume(codec, spec->adc_nids[0], HDA_INPUT)) { + if (!spec->capsrc_nids) + return; /* no volume */ + if (!nid_has_volume(codec, spec->capsrc_nids[0], HDA_OUTPUT)) + return; /* no volume in capsrc, too */ + spec->vol_in_capsrc = 1; + } + + if (spec->num_adc_nids > 0) { + int mux = 0; + int num_adcs = 0; + + if (spec->input_mux && spec->input_mux->num_items > 1) + mux = 1; + if (spec->auto_mic) { + num_adcs = 1; + mux = 0; + } else if (spec->dyn_adc_switch) + num_adcs = 1; + if (!num_adcs) { + if (spec->num_adc_nids > 3) + spec->num_adc_nids = 3; + else if (!spec->num_adc_nids) + return; + num_adcs = spec->num_adc_nids; + } + spec->cap_mixer = caps[mux][num_adcs - 1]; } - return 0; } +/* + * standard auto-parser initializations + */ +static void alc_auto_init_std(struct hda_codec *codec) +{ + alc_auto_init_multi_out(codec); + alc_auto_init_extra_out(codec); + alc_auto_init_analog_input(codec); + alc_auto_init_input_src(codec); + alc_auto_init_digital(codec); + alc_inithook(codec); +} /* * Digital-beep handlers @@ -959,21 +4273,94 @@ static int alc_parse_auto_config(struct hda_codec *codec, const hda_nid_t *ssid_nids) { struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; + struct auto_pin_cfg *cfg = &spec->autocfg; int err; err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, spec->parse_flags); if (err < 0) return err; + if (!cfg->line_outs) { + if (cfg->dig_outs || cfg->dig_in_pin) { + spec->multiout.max_channels = 2; + spec->no_analog = 1; + goto dig_only; + } + return 0; /* can't find valid BIOS pin config */ + } - if (ssid_nids) - alc_ssid_check(codec, ssid_nids); + if (!spec->no_primary_hp && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && + cfg->line_outs <= cfg->hp_outs) { + /* use HP as primary out */ + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + } - err = snd_hda_gen_parse_auto_config(codec, cfg); + err = alc_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = alc_auto_add_multi_channel_mode(codec); + if (err < 0) + return err; + err = alc_auto_create_multi_out_ctls(codec, cfg); + if (err < 0) + return err; + err = alc_auto_create_hp_out(codec); + if (err < 0) + return err; + err = alc_auto_create_speaker_out(codec); + if (err < 0) + return err; + err = alc_auto_create_shared_input(codec); + if (err < 0) + return err; + err = alc_auto_create_input_ctls(codec); if (err < 0) return err; + /* check the multiple speaker pins */ + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + spec->const_channel_count = cfg->line_outs * 2; + else + spec->const_channel_count = cfg->speaker_outs * 2; + + if (spec->multi_ios > 0) + spec->multiout.max_channels = max(spec->ext_channel_count, + spec->const_channel_count); + else + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + dig_only: + alc_auto_parse_digital(codec); + + if (!spec->no_analog) + alc_remove_invalid_adc_nids(codec); + + if (ssid_nids) + alc_ssid_check(codec, ssid_nids); + + if (!spec->no_analog) { + err = alc_auto_check_switches(codec); + if (err < 0) + return err; + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + } + + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); + + if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); + return 1; } @@ -986,12 +4373,11 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); - spec->gen.mixer_nid = mixer_nid; - spec->gen.own_eapd_ctl = 1; codec->single_adc_amp = 1; - /* FIXME: do we need this for all Realtek codec models? */ - codec->spdif_status_reset = 1; + spec->mixer_nid = mixer_nid; + snd_hda_gen_init(&spec->gen); + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); + snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); err = alc_codec_rename_from_preset(codec); if (err < 0) { @@ -1034,28 +4420,27 @@ enum { ALC880_FIXUP_6ST_BASE, ALC880_FIXUP_6ST, ALC880_FIXUP_6ST_DIG, - ALC880_FIXUP_6ST_AUTOMUTE, }; /* enable the volume-knob widget support on NID 0x21 */ static void alc880_fixup_vol_knob(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PROBE) + if (action == ALC_FIXUP_ACT_PROBE) snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master); } -static const struct hda_fixup alc880_fixups[] = { +static const struct alc_fixup alc880_fixups[] = { [ALC880_FIXUP_GPIO1] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC880_FIXUP_GPIO2] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio2_init_verbs, }, [ALC880_FIXUP_MEDION_RIM] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, @@ -1065,8 +4450,8 @@ static const struct hda_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_LG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { /* disable bogus unused pins */ { 0x16, 0x411111f0 }, { 0x18, 0x411111f0 }, @@ -1075,8 +4460,8 @@ static const struct hda_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_W810] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { } @@ -1085,7 +4470,7 @@ static const struct hda_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_EAPD_COEF] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1094,7 +4479,7 @@ static const struct hda_fixup alc880_fixups[] = { }, }, [ALC880_FIXUP_TCL_S700] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1105,13 +4490,13 @@ static const struct hda_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_VOL_KNOB] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc880_fixup_vol_knob, }, [ALC880_FIXUP_FUJITSU] = { /* override all pins as BIOS on old Amilo is broken */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ @@ -1130,8 +4515,8 @@ static const struct hda_fixup alc880_fixups[] = { }, [ALC880_FIXUP_F1734] = { /* almost compatible with FUJITSU, but no bass and SPDIF */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x411111f0 }, /* N/A */ @@ -1150,8 +4535,8 @@ static const struct hda_fixup alc880_fixups[] = { }, [ALC880_FIXUP_UNIWILL] = { /* need to fix HP and speaker pins to be parsed correctly */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ @@ -1159,8 +4544,8 @@ static const struct hda_fixup alc880_fixups[] = { }, }, [ALC880_FIXUP_UNIWILL_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { 0x19, 0x411111f0 }, @@ -1170,8 +4555,8 @@ static const struct hda_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_Z71V] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { /* set up the whole pins as BIOS is utterly broken */ { 0x14, 0x99030120 }, /* speaker */ { 0x15, 0x0121411f }, /* HP */ @@ -1188,8 +4573,8 @@ static const struct hda_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_3ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x01014010 }, /* line-out */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x411111f0 }, /* N/A */ @@ -1206,8 +4591,8 @@ static const struct hda_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -1215,8 +4600,8 @@ static const struct hda_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_3ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -1224,8 +4609,8 @@ static const struct hda_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_5ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x01011411 }, /* CLFE */ @@ -1242,8 +4627,8 @@ static const struct hda_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_5ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -1251,8 +4636,8 @@ static const struct hda_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_5ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -1260,8 +4645,8 @@ static const struct hda_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_6ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x01016412 }, /* surr */ { 0x16, 0x01011411 }, /* CLFE */ @@ -1278,8 +4663,8 @@ static const struct hda_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_6ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -1287,23 +4672,14 @@ static const struct hda_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_6ST_BASE, }, [ALC880_FIXUP_6ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, .chained = true, .chain_id = ALC880_FIXUP_6ST_BASE, }, - [ALC880_FIXUP_6ST_AUTOMUTE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x0121401f }, /* HP with jack detect */ - { } - }, - .chained_before = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, }; static const struct snd_pci_quirk alc880_fixup_tbl[] = { @@ -1318,7 +4694,6 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), - SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE), SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734), SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), @@ -1374,14 +4749,13 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { {} }; -static const struct hda_model_fixup alc880_fixup_models[] = { +static const struct alc_model_fixup alc880_fixup_models[] = { {.id = ALC880_FIXUP_3ST, .name = "3stack"}, {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, {.id = ALC880_FIXUP_5ST, .name = "5stack"}, {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, {.id = ALC880_FIXUP_6ST, .name = "6stack"}, {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, - {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"}, {} }; @@ -1399,18 +4773,18 @@ static int patch_alc880(struct hda_codec *codec) return err; spec = codec->spec; - spec->gen.need_dac_fix = 1; + spec->need_dac_fix = 1; - snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, + alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, alc880_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc880_parse_auto_config(codec); if (err < 0) goto error; - if (!spec->gen.no_analog) { + if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -1421,7 +4795,7 @@ static int patch_alc880(struct hda_codec *codec) codec->patch_ops.unsol_event = alc880_unsol_event; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -1453,39 +4827,38 @@ enum { ALC260_FIXUP_REPLACER, ALC260_FIXUP_HP_B1900, ALC260_FIXUP_KN1, - ALC260_FIXUP_FSC_S7020, }; static void alc260_gpio1_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gen.hp_jack_present); + spec->hp_jack_present); } static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { + if (action == ALC_FIXUP_ACT_PROBE) { /* although the machine has only one output pin, we need to * toggle GPIO1 according to the jack state */ - spec->gen.automute_hook = alc260_gpio1_automute; - spec->gen.detect_hp = 1; - spec->gen.automute_speaker = 1; - spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable_callback(codec, 0x0f, HDA_GEN_HP_EVENT, - snd_hda_gen_hp_automute); - snd_hda_add_verbs(codec, alc_gpio1_init_verbs); + spec->automute_hook = alc260_gpio1_automute; + spec->detect_hp = 1; + spec->automute_speaker = 1; + spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ + snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT, + alc_hp_automute); + snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs); } } static void alc260_fixup_kn1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { + static const struct alc_pincfg pincfgs[] = { { 0x0f, 0x02214000 }, /* HP/speaker */ { 0x12, 0x90a60160 }, /* int mic */ { 0x13, 0x02a19000 }, /* ext mic */ @@ -1502,41 +4875,32 @@ static void alc260_fixup_kn1(struct hda_codec *codec, }; switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); + case ALC_FIXUP_ACT_PRE_PROBE: + alc_apply_pincfgs(codec, pincfgs); break; - case HDA_FIXUP_ACT_PROBE: + case ALC_FIXUP_ACT_PROBE: spec->init_amp = ALC_INIT_NONE; break; } } -static void alc260_fixup_fsc_s7020(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gen.add_out_jack_modes = 1; -} - -static const struct hda_fixup alc260_fixups[] = { +static const struct alc_fixup alc260_fixups[] = { [ALC260_FIXUP_HP_DC5750] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x11, 0x90130110 }, /* speaker */ { } } }, [ALC260_FIXUP_HP_PIN_0F] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x0f, 0x01214000 }, /* HP */ { } } }, [ALC260_FIXUP_COEF] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3040 }, @@ -1546,17 +4910,17 @@ static const struct hda_fixup alc260_fixups[] = { .chain_id = ALC260_FIXUP_HP_PIN_0F, }, [ALC260_FIXUP_GPIO1] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC260_FIXUP_GPIO1_TOGGLE] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc260_fixup_gpio1_toggle, .chained = true, .chain_id = ALC260_FIXUP_HP_PIN_0F, }, [ALC260_FIXUP_REPLACER] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, @@ -1566,19 +4930,15 @@ static const struct hda_fixup alc260_fixups[] = { .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, }, [ALC260_FIXUP_HP_B1900] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc260_fixup_gpio1_toggle, .chained = true, .chain_id = ALC260_FIXUP_COEF, }, [ALC260_FIXUP_KN1] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc260_fixup_kn1, }, - [ALC260_FIXUP_FSC_S7020] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_fsc_s7020, - }, }; static const struct snd_pci_quirk alc260_fixup_tbl[] = { @@ -1587,7 +4947,6 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), - SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020), SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), @@ -1607,21 +4966,16 @@ static int patch_alc260(struct hda_codec *codec) return err; spec = codec->spec; - /* as quite a few machines require HP amp for speaker outputs, - * it's easier to enable it unconditionally; even if it's unneeded, - * it's almost harmless. - */ - spec->gen.prefer_hp_amp = 1; - snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc260_parse_auto_config(codec); if (err < 0) goto error; - if (!spec->gen.no_analog) { + if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -1631,7 +4985,7 @@ static int patch_alc260(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -1685,9 +5039,9 @@ enum { }; static void alc889_fixup_coef(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { - if (action != HDA_FIXUP_ACT_INIT) + if (action != ALC_FIXUP_ACT_INIT) return; alc889_coef_init(codec); } @@ -1727,9 +5081,9 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) /* set up GPIO at initialization */ static void alc885_fixup_macpro_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { - if (action != HDA_FIXUP_ACT_INIT) + if (action != ALC_FIXUP_ACT_INIT) return; alc882_gpio_mute(codec, 0, 0); alc882_gpio_mute(codec, 1, 0); @@ -1740,9 +5094,9 @@ static void alc885_fixup_macpro_gpio(struct hda_codec *codec, * work correctly (bko#42740) */ static void alc889_fixup_dac_route(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PRE_PROBE) { + if (action == ALC_FIXUP_ACT_PRE_PROBE) { /* fake the connections during parsing the tree */ hda_nid_t conn1[2] = { 0x0c, 0x0d }; hda_nid_t conn2[2] = { 0x0e, 0x0f }; @@ -1750,7 +5104,7 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, snd_hda_override_conn_list(codec, 0x15, 2, conn1); snd_hda_override_conn_list(codec, 0x18, 2, conn2); snd_hda_override_conn_list(codec, 0x1a, 2, conn2); - } else if (action == HDA_FIXUP_ACT_PROBE) { + } else if (action == ALC_FIXUP_ACT_PROBE) { /* restore the connections */ hda_nid_t conn[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; snd_hda_override_conn_list(codec, 0x14, 5, conn); @@ -1762,60 +5116,62 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, /* Set VREF on HP pin */ static void alc889_fixup_mbp_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; static hda_nid_t nids[2] = { 0x14, 0x15 }; int i; - if (action != HDA_FIXUP_ACT_INIT) + if (action != ALC_FIXUP_ACT_INIT) return; for (i = 0; i < ARRAY_SIZE(nids); i++) { unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); if (get_defcfg_device(val) != AC_JACK_HP_OUT) continue; - val = snd_hda_codec_get_pin_target(codec, nids[i]); + val = snd_hda_codec_read(codec, nids[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); val |= AC_PINCTL_VREF_80; snd_hda_set_pin_ctl(codec, nids[i], val); - spec->gen.keep_vref_in_automute = 1; + spec->keep_vref_in_automute = 1; break; } } /* Set VREF on speaker pins on imac91 */ static void alc889_fixup_imac91_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; static hda_nid_t nids[2] = { 0x18, 0x1a }; int i; - if (action != HDA_FIXUP_ACT_INIT) + if (action != ALC_FIXUP_ACT_INIT) return; for (i = 0; i < ARRAY_SIZE(nids); i++) { unsigned int val; - val = snd_hda_codec_get_pin_target(codec, nids[i]); + val = snd_hda_codec_read(codec, nids[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); val |= AC_PINCTL_VREF_50; snd_hda_set_pin_ctl(codec, nids[i], val); } - spec->gen.keep_vref_in_automute = 1; + spec->keep_vref_in_automute = 1; } /* Don't take HP output as primary * strangely, the speaker output doesn't work on VAIO Z through DAC 0x05 */ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gen.no_primary_hp = 1; + if (action == ALC_FIXUP_ACT_PRE_PROBE) + spec->no_primary_hp = 1; } -static const struct hda_fixup alc882_fixups[] = { +static const struct alc_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x15, 0x01080104 }, /* side */ { 0x16, 0x01011012 }, /* rear */ { 0x17, 0x01016011 }, /* clfe */ @@ -1823,47 +5179,47 @@ static const struct hda_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_LENOVO_Y530] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x15, 0x99130112 }, /* rear int speakers */ { 0x16, 0x99130111 }, /* subwoofer */ { } } }, [ALC882_FIXUP_PB_M5210] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, PIN_VREF50 }, + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, {} } }, [ALC882_FIXUP_ACER_ASPIRE_7736] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_sku_ignore, }, [ALC882_FIXUP_ASUS_W90V] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x16, 0x99130110 }, /* fix sequence for CLFE */ { } } }, [ALC889_FIXUP_CD] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x1c, 0x993301f0 }, /* CD */ { } } }, [ALC889_FIXUP_VAIO_TT] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x17, 0x90170111 }, /* hidden surround speaker */ { } } }, [ALC888_FIXUP_EEE1601] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 }, @@ -1871,7 +5227,7 @@ static const struct hda_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1880,7 +5236,7 @@ static const struct hda_fixup alc882_fixups[] = { } }, [ALC883_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1889,7 +5245,7 @@ static const struct hda_fixup alc882_fixups[] = { } }, [ALC883_FIXUP_ACER_EAPD] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* eanable EAPD on Acer laptops */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1898,30 +5254,30 @@ static const struct hda_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_GPIO1] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC882_FIXUP_GPIO2] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio2_init_verbs, }, [ALC882_FIXUP_GPIO3] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio3_init_verbs, }, [ALC882_FIXUP_ASUS_W2JC] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, .chained = true, .chain_id = ALC882_FIXUP_EAPD, }, [ALC889_FIXUP_COEF] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc889_fixup_coef, }, [ALC882_FIXUP_ACER_ASPIRE_4930G] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x16, 0x99130111 }, /* CLFE speaker */ { 0x17, 0x99130112 }, /* surround speaker */ { } @@ -1930,8 +5286,8 @@ static const struct hda_fixup alc882_fixups[] = { .chain_id = ALC882_FIXUP_GPIO1, }, [ALC882_FIXUP_ACER_ASPIRE_8930G] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x16, 0x99130111 }, /* CLFE speaker */ { 0x1b, 0x99130112 }, /* surround speaker */ { } @@ -1941,7 +5297,7 @@ static const struct hda_fixup alc882_fixups[] = { }, [ALC882_FIXUP_ASPIRE_8930G_VERBS] = { /* additional init verbs for Acer Aspire 8930G */ - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* Enable all DACs */ /* DAC DISABLE/MUTE 1? */ @@ -1975,31 +5331,31 @@ static const struct hda_fixup alc882_fixups[] = { .chain_id = ALC882_FIXUP_GPIO1, }, [ALC885_FIXUP_MACPRO_GPIO] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc885_fixup_macpro_gpio, }, [ALC889_FIXUP_DAC_ROUTE] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc889_fixup_dac_route, }, [ALC889_FIXUP_MBP_VREF] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc889_fixup_mbp_vref, .chained = true, .chain_id = ALC882_FIXUP_GPIO1, }, [ALC889_FIXUP_IMAC91_VREF] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc889_fixup_imac91_vref, .chained = true, .chain_id = ALC882_FIXUP_GPIO1, }, [ALC882_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC882_FIXUP_NO_PRIMARY_HP] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc882_fixup_no_primary_hp, }, }; @@ -2074,7 +5430,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { {} }; -static const struct hda_model_fixup alc882_fixup_models[] = { +static const struct alc_model_fixup alc882_fixup_models[] = { {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, @@ -2117,9 +5473,9 @@ static int patch_alc882(struct hda_codec *codec) break; } - snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, + alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, alc882_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -2128,7 +5484,7 @@ static int patch_alc882(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog && has_cdefine_beep(codec)) { + if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -2137,7 +5493,7 @@ static int patch_alc882(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -2162,7 +5518,6 @@ static int alc262_parse_auto_config(struct hda_codec *codec) */ enum { ALC262_FIXUP_FSC_H270, - ALC262_FIXUP_FSC_S7110, ALC262_FIXUP_HP_Z200, ALC262_FIXUP_TYAN, ALC262_FIXUP_LENOVO_3000, @@ -2171,50 +5526,41 @@ enum { ALC262_FIXUP_INV_DMIC, }; -static const struct hda_fixup alc262_fixups[] = { +static const struct alc_fixup alc262_fixups[] = { [ALC262_FIXUP_FSC_H270] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0221142f }, /* front HP */ { 0x1b, 0x0121141f }, /* rear HP */ { } } }, - [ALC262_FIXUP_FSC_S7110] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x90170110 }, /* speaker */ - { } - }, - .chained = true, - .chain_id = ALC262_FIXUP_BENQ, - }, [ALC262_FIXUP_HP_Z200] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x16, 0x99130120 }, /* internal speaker */ { } } }, [ALC262_FIXUP_TYAN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x1993e1f0 }, /* int AUX */ { } } }, [ALC262_FIXUP_LENOVO_3000] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, PIN_VREF50 }, + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, {} }, .chained = true, .chain_id = ALC262_FIXUP_BENQ, }, [ALC262_FIXUP_BENQ] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, @@ -2222,7 +5568,7 @@ static const struct hda_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_BENQ_T31] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, @@ -2230,14 +5576,14 @@ static const struct hda_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, }; static const struct snd_pci_quirk alc262_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), - SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), + SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FIXUP_BENQ), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), @@ -2247,7 +5593,7 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { {} }; -static const struct hda_model_fixup alc262_fixup_models[] = { +static const struct alc_model_fixup alc262_fixup_models[] = { {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, {} }; @@ -2264,7 +5610,6 @@ static int patch_alc262(struct hda_codec *codec) return err; spec = codec->spec; - spec->gen.shared_mic_vref_pin = 0x18; #if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is @@ -2280,9 +5625,9 @@ static int patch_alc262(struct hda_codec *codec) #endif alc_fix_pll_init(codec, 0x20, 0x0a, 10); - snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, + alc_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, alc262_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -2291,7 +5636,7 @@ static int patch_alc262(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog && has_cdefine_beep(codec)) { + if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -2301,7 +5646,7 @@ static int patch_alc262(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -2342,13 +5687,13 @@ enum { ALC268_FIXUP_HP_EAPD, }; -static const struct hda_fixup alc268_fixups[] = { +static const struct alc_fixup alc268_fixups[] = { [ALC268_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC268_FIXUP_HP_EAPD] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0}, {} @@ -2356,7 +5701,7 @@ static const struct hda_fixup alc268_fixups[] = { }, }; -static const struct hda_model_fixup alc268_fixup_models[] = { +static const struct alc_model_fixup alc268_fixup_models[] = { {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, {} @@ -2379,10 +5724,9 @@ static int alc268_parse_auto_config(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int err = alc_parse_auto_config(codec, NULL, alc268_ssids); if (err > 0) { - if (!spec->gen.no_analog && - spec->gen.autocfg.speaker_pins[0] != 0x1d) { + if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { add_mixer(spec, alc268_beep_mixer); - snd_hda_add_verbs(codec, alc268_beep_init_verbs); + snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs); } } return err; @@ -2402,8 +5746,8 @@ static int patch_alc268(struct hda_codec *codec) spec = codec->spec; - snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + alc_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc268_parse_auto_config(codec); @@ -2434,7 +5778,7 @@ static int patch_alc268(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -2446,35 +5790,6 @@ static int patch_alc268(struct hda_codec *codec) /* * ALC269 */ - -static int playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .substreams = 1, .channels_min = 2, @@ -2482,9 +5797,9 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ /* NID is set in alc_build_pcms */ .ops = { - .open = playback_pcm_open, - .prepare = playback_pcm_prepare, - .cleanup = playback_pcm_cleanup + .open = alc_playback_pcm_open, + .prepare = alc_playback_pcm_prepare, + .cleanup = alc_playback_pcm_cleanup }, }; @@ -2592,27 +5907,27 @@ static int alc269_resume(struct hda_codec *codec) #endif /* CONFIG_PM */ static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) + if (action == ALC_FIXUP_ACT_PRE_PROBE) spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; } static void alc269_fixup_hweq(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { int coef; - if (action != HDA_FIXUP_ACT_INIT) + if (action != ALC_FIXUP_ACT_INIT) return; coef = alc_read_coef_idx(codec, 0x1e); alc_write_coef_idx(codec, 0x1e, coef | 0x80); } static void alc271_fixup_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { static const struct hda_verb verbs[] = { {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, @@ -2629,26 +5944,26 @@ static void alc271_fixup_dmic(struct hda_codec *codec, } static void alc269_fixup_pcm_44k(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PROBE) + if (action != ALC_FIXUP_ACT_PROBE) return; /* Due to a hardware problem on Lenovo Ideadpad, we need to * fix the sample rate of analog I/O to 44.1kHz */ - spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback; - spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture; + spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; + spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; } static void alc269_fixup_stereo_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { int coef; - if (action != HDA_FIXUP_ACT_INIT) + if (action != ALC_FIXUP_ACT_INIT) return; /* The digital-mic unit sends PDM (differential signal) instead of * the standard PCM, thus you can't record a valid mono stream as is. @@ -2661,7 +5976,7 @@ static void alc269_fixup_stereo_dmic(struct hda_codec *codec, static void alc269_quanta_automute(struct hda_codec *codec) { - snd_hda_gen_update_outputs(codec); + update_outputs(codec); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x0c); @@ -2675,79 +5990,70 @@ static void alc269_quanta_automute(struct hda_codec *codec) } static void alc269_fixup_quanta_mute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PROBE) + if (action != ALC_FIXUP_ACT_PROBE) return; - spec->gen.automute_hook = alc269_quanta_automute; + spec->automute_hook = alc269_quanta_automute; } -/* update mute-LED according to the speaker mute state via mic VREF pin */ -static void alc269_fixup_mic_mute_hook(void *private_data, int enabled) +/* update mute-LED according to the speaker mute state via mic1 VREF pin */ +static void alc269_fixup_mic1_mute_hook(void *private_data, int enabled) { struct hda_codec *codec = private_data; - struct alc_spec *spec = codec->spec; - unsigned int pinval; - - if (spec->mute_led_polarity) - enabled = !enabled; - pinval = AC_PINCTL_IN_EN | - (enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80); - if (spec->mute_led_nid) - snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval); + unsigned int pinval = AC_PINCTL_IN_EN + (enabled ? + AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80); + snd_hda_set_pin_ctl_cache(codec, 0x18, pinval); } -static void alc269_fixup_hp_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_mic1_mute(struct hda_codec *codec, + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - const struct dmi_device *dev = NULL; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - int pol, pin; - if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2) - continue; - if (pin < 0x0a || pin >= 0x10) - break; - spec->mute_led_polarity = pol; - spec->mute_led_nid = pin - 0x0a + 0x18; - spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; - spec->gen.vmaster_mute_enum = 1; - snd_printd("Detected mute LED for %x:%d\n", spec->mute_led_nid, - spec->mute_led_polarity); + switch (action) { + case ALC_FIXUP_ACT_BUILD: + spec->vmaster_mute.hook = alc269_fixup_mic1_mute_hook; + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); + /* fallthru */ + case ALC_FIXUP_ACT_INIT: + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); break; } } -static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +/* update mute-LED according to the speaker mute state via mic2 VREF pin */ +static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + unsigned int pinval = enabled ? 0x20 : 0x24; + snd_hda_set_pin_ctl_cache(codec, 0x19, pinval); +} + +static void alc269_fixup_mic2_mute(struct hda_codec *codec, + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_nid = 0x19; - spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; - spec->gen.vmaster_mute_enum = 1; + switch (action) { + case ALC_FIXUP_ACT_BUILD: + spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook; + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); + /* fallthru */ + case ALC_FIXUP_ACT_INIT: + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + break; } } static void alc271_hp_gate_mic_jack(struct hda_codec *codec, - const struct hda_fixup *fix, + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { - if (snd_BUG_ON(!spec->gen.am_entry[1].pin || - !spec->gen.autocfg.hp_pins[0])) - return; - snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin, - spec->gen.autocfg.hp_pins[0]); - } + if (action == ALC_FIXUP_ACT_PROBE) + snd_hda_jack_set_gating_jack(codec, spec->ext_mic_pin, + spec->autocfg.hp_pins[0]); } enum { @@ -2767,8 +6073,8 @@ enum { ALC269_FIXUP_DMIC, ALC269VB_FIXUP_AMIC, ALC269VB_FIXUP_DMIC, - ALC269_FIXUP_HP_MUTE_LED, - ALC269_FIXUP_HP_MUTE_LED_MIC2, + ALC269_FIXUP_MIC1_MUTE_LED, + ALC269_FIXUP_MIC2_MUTE_LED, ALC269_FIXUP_INV_DMIC, ALC269_FIXUP_LENOVO_DOCK, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, @@ -2776,16 +6082,16 @@ enum { ALC271_FIXUP_HP_GATE_MIC_JACK, }; -static const struct hda_fixup alc269_fixups[] = { +static const struct alc_fixup alc269_fixups[] = { [ALC269_FIXUP_SONY_VAIO] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - {0x19, PIN_VREFGRD}, + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, {} } }, [ALC275_FIXUP_SONY_VAIO_GPIO2] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x01, AC_VERB_SET_GPIO_MASK, 0x04}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04}, @@ -2796,7 +6102,7 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_SONY_VAIO }, [ALC269_FIXUP_DELL_M101Z] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* Enables internal speaker */ {0x20, AC_VERB_SET_COEF_INDEX, 13}, @@ -2805,50 +6111,50 @@ static const struct hda_fixup alc269_fixups[] = { } }, [ALC269_FIXUP_SKU_IGNORE] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_sku_ignore, }, [ALC269_FIXUP_ASUS_G73JW] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x17, 0x99130111 }, /* subwoofer */ { } } }, [ALC269_FIXUP_LENOVO_EAPD] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, {} } }, [ALC275_FIXUP_SONY_HWEQ] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc269_fixup_hweq, .chained = true, .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 }, [ALC271_FIXUP_DMIC] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc271_fixup_dmic, }, [ALC269_FIXUP_PCM_44K] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc269_fixup_pcm_44k, .chained = true, .chain_id = ALC269_FIXUP_QUANTA_MUTE }, [ALC269_FIXUP_STEREO_DMIC] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc269_fixup_stereo_dmic, }, [ALC269_FIXUP_QUANTA_MUTE] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc269_fixup_quanta_mute, }, [ALC269_FIXUP_LIFEBOOK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x1a, 0x2101103f }, /* dock line-out */ { 0x1b, 0x23a11040 }, /* dock mic-in */ { } @@ -2857,8 +6163,8 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_QUANTA_MUTE }, [ALC269_FIXUP_AMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121401f }, /* HP out */ { 0x18, 0x01a19c20 }, /* mic */ @@ -2867,8 +6173,8 @@ static const struct hda_fixup alc269_fixups[] = { }, }, [ALC269_FIXUP_DMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x12, 0x99a3092f }, /* int-mic */ { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121401f }, /* HP out */ @@ -2877,8 +6183,8 @@ static const struct hda_fixup alc269_fixups[] = { }, }, [ALC269VB_FIXUP_AMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19c20 }, /* mic */ { 0x19, 0x99a3092f }, /* int-mic */ @@ -2887,8 +6193,8 @@ static const struct hda_fixup alc269_fixups[] = { }, }, [ALC269VB_FIXUP_DMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x12, 0x99a3092f }, /* int-mic */ { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19c20 }, /* mic */ @@ -2896,21 +6202,21 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, - [ALC269_FIXUP_HP_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led, + [ALC269_FIXUP_MIC1_MUTE_LED] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc269_fixup_mic1_mute, }, - [ALC269_FIXUP_HP_MUTE_LED_MIC2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led_mic2, + [ALC269_FIXUP_MIC2_MUTE_LED] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc269_fixup_mic2_mute, }, [ALC269_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC269_FIXUP_LENOVO_DOCK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x19, 0x23a11040 }, /* dock mic */ { 0x1b, 0x2121103f }, /* dock headphone */ { } @@ -2919,12 +6225,12 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT }, [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc269_fixup_pincfg_no_hp_to_lineout, }, [ALC271_FIXUP_AMIC_MIC2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x19, 0x01a19c20 }, /* mic */ { 0x1b, 0x99a7012f }, /* int-mic */ @@ -2933,7 +6239,7 @@ static const struct hda_fixup alc269_fixups[] = { }, }, [ALC271_FIXUP_HP_GATE_MIC_JACK] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc271_hp_gate_mic_jack, .chained = true, .chain_id = ALC271_FIXUP_AMIC_MIC2, @@ -2943,8 +6249,9 @@ static const struct hda_fixup alc269_fixups[] = { static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), - SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x1972, "HP Pavilion 17", ALC269_FIXUP_MIC1_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x1977, "HP Pavilion 14", ALC269_FIXUP_MIC1_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), @@ -3027,7 +6334,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { {} }; -static const struct hda_model_fixup alc269_fixup_models[] = { +static const struct alc_model_fixup alc269_fixup_models[] = { {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"}, {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"}, {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"}, @@ -3094,11 +6401,10 @@ static int patch_alc269(struct hda_codec *codec) return err; spec = codec->spec; - spec->gen.shared_mic_vref_pin = 0x18; - snd_hda_pick_fixup(codec, alc269_fixup_models, + alc_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -3149,7 +6455,7 @@ static int patch_alc269(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog && has_cdefine_beep(codec)) { + if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -3162,7 +6468,7 @@ static int patch_alc269(struct hda_codec *codec) #endif spec->shutup = alc269_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -3192,48 +6498,49 @@ enum { /* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; unsigned int val; - if (action != HDA_FIXUP_ACT_INIT) + if (action != ALC_FIXUP_ACT_INIT) return; - val = snd_hda_codec_get_pin_target(codec, 0x0f); + val = snd_hda_codec_read(codec, 0x0f, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) val |= AC_PINCTL_IN_EN; val |= AC_PINCTL_VREF_50; snd_hda_set_pin_ctl(codec, 0x0f, val); - spec->gen.keep_vref_in_automute = 1; + spec->keep_vref_in_automute = 1; } /* suppress the jack-detection */ static void alc_fixup_no_jack_detect(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PRE_PROBE) + if (action == ALC_FIXUP_ACT_PRE_PROBE) codec->no_jack_detect = 1; } -static const struct hda_fixup alc861_fixups[] = { +static const struct alc_fixup alc861_fixups[] = { [ALC861_FIXUP_FSC_AMILO_PI1505] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x0b, 0x0221101f }, /* HP */ { 0x0f, 0x90170310 }, /* speaker */ { } } }, [ALC861_FIXUP_AMP_VREF_0F] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc861_fixup_asus_amp_vref_0f, }, [ALC861_FIXUP_NO_JACK_DETECT] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_no_jack_detect, }, [ALC861_FIXUP_ASUS_A6RP] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc861_fixup_asus_amp_vref_0f, .chained = true, .chain_id = ALC861_FIXUP_NO_JACK_DETECT, @@ -3263,15 +6570,15 @@ static int patch_alc861(struct hda_codec *codec) spec = codec->spec; - snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc861_parse_auto_config(codec); if (err < 0) goto error; - if (!spec->gen.no_analog) { + if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) goto error; @@ -3283,7 +6590,7 @@ static int patch_alc861(struct hda_codec *codec) spec->power_hook = alc_power_eapd; #endif - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -3313,17 +6620,17 @@ enum { /* exclude VREF80 */ static void alc861vd_fixup_dallas(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PRE_PROBE) { + if (action == ALC_FIXUP_ACT_PRE_PROBE) { snd_hda_override_pin_caps(codec, 0x18, 0x00000734); snd_hda_override_pin_caps(codec, 0x19, 0x0000073c); } } -static const struct hda_fixup alc861vd_fixups[] = { +static const struct alc_fixup alc861vd_fixups[] = { [ALC660VD_FIX_ASUS_GPIO1] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* reset GPIO1 */ {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, @@ -3333,7 +6640,7 @@ static const struct hda_fixup alc861vd_fixups[] = { } }, [ALC861VD_FIX_DALLAS] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc861vd_fixup_dallas, }, }; @@ -3358,15 +6665,15 @@ static int patch_alc861vd(struct hda_codec *codec) spec = codec->spec; - snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc861vd_parse_auto_config(codec); if (err < 0) goto error; - if (!spec->gen.no_analog) { + if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) goto error; @@ -3377,7 +6684,7 @@ static int patch_alc861vd(struct hda_codec *codec) spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; @@ -3418,9 +6725,9 @@ static int alc662_parse_auto_config(struct hda_codec *codec) } static void alc272_fixup_mario(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + const struct alc_fixup *fix, int action) { - if (action != HDA_FIXUP_ACT_PRE_PROBE) + if (action != ALC_FIXUP_ACT_PROBE) return; if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, (0x3b << AC_AMPCAP_OFFSET_SHIFT) | @@ -3451,39 +6758,39 @@ enum { ALC662_FIXUP_INV_DMIC, }; -static const struct hda_fixup alc662_fixups[] = { +static const struct alc_fixup alc662_fixups[] = { [ALC662_FIXUP_ASPIRE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x15, 0x99130112 }, /* subwoofer */ { } } }, [ALC662_FIXUP_IDEAPAD] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x17, 0x99130112 }, /* subwoofer */ { } } }, [ALC272_FIXUP_MARIO] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc272_fixup_mario, }, [ALC662_FIXUP_CZC_P10T] = { - .type = HDA_FIXUP_VERBS, + .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, {} } }, [ALC662_FIXUP_SKU_IGNORE] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_sku_ignore, }, [ALC662_FIXUP_HP_RP5800] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x0221201f }, /* HP out */ { } }, @@ -3491,8 +6798,8 @@ static const struct hda_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE1] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19c20 }, /* mic */ { 0x19, 0x99a3092f }, /* int-mic */ @@ -3503,8 +6810,8 @@ static const struct hda_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19820 }, /* mic */ { 0x19, 0x99a3092f }, /* int-mic */ @@ -3515,8 +6822,8 @@ static const struct hda_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE3] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121441f }, /* HP */ { 0x18, 0x01a19840 }, /* mic */ @@ -3528,8 +6835,8 @@ static const struct hda_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE4] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x16, 0x99130111 }, /* speaker */ { 0x18, 0x01a19840 }, /* mic */ @@ -3541,8 +6848,8 @@ static const struct hda_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE5] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121441f }, /* HP */ { 0x16, 0x99130111 }, /* speaker */ @@ -3554,8 +6861,8 @@ static const struct hda_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE6] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x01211420 }, /* HP2 */ { 0x18, 0x01a19840 }, /* mic */ @@ -3567,8 +6874,8 @@ static const struct hda_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE7] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x17, 0x99130111 }, /* speaker */ { 0x18, 0x01a19840 }, /* mic */ @@ -3581,8 +6888,8 @@ static const struct hda_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE8] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x12, 0x99a30970 }, /* int-mic */ { 0x15, 0x01214020 }, /* HP */ @@ -3595,18 +6902,18 @@ static const struct hda_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_NO_JACK_DETECT] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_no_jack_detect, }, [ALC662_FIXUP_ZOTAC_Z68] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { { 0x1b, 0x02214020 }, /* Front HP */ { } } }, [ALC662_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, + .type = ALC_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, }; @@ -3686,7 +6993,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { {} }; -static const struct hda_model_fixup alc662_fixup_models[] = { +static const struct alc_model_fixup alc662_fixup_models[] = { {.id = ALC272_FIXUP_MARIO, .name = "mario"}, {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, @@ -3747,9 +7054,9 @@ static int patch_alc662(struct hda_codec *codec) spec->init_hook = alc662_fill_coef; alc662_fill_coef(codec); - snd_hda_pick_fixup(codec, alc662_fixup_models, + alc_pick_fixup(codec, alc662_fixup_models, alc662_fixup_tbl, alc662_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -3765,7 +7072,7 @@ static int patch_alc662(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog && has_cdefine_beep(codec)) { + if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -3787,7 +7094,7 @@ static int patch_alc662(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); return 0; diff --git a/trunk/sound/pci/hda/patch_sigmatel.c b/trunk/sound/pci/hda/patch_sigmatel.c index 617ac1f542fb..a86547ca17c8 100644 --- a/trunk/sound/pci/hda/patch_sigmatel.c +++ b/trunk/sound/pci/hda/patch_sigmatel.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include "hda_codec.h" @@ -38,14 +39,18 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" enum { - STAC_VREF_EVENT = 8, + STAC_VREF_EVENT = 1, + STAC_INSERT_EVENT, STAC_PWR_EVENT, + STAC_HP_EVENT, + STAC_LO_EVENT, + STAC_MIC_EVENT, }; enum { + STAC_AUTO, STAC_REF, STAC_9200_OQO, STAC_9200_DELL_D21, @@ -61,11 +66,11 @@ enum { STAC_9200_M4, STAC_9200_M4_2, STAC_9200_PANASONIC, - STAC_9200_EAPD_INIT, STAC_9200_MODELS }; enum { + STAC_9205_AUTO, STAC_9205_REF, STAC_9205_DELL_M42, STAC_9205_DELL_M43, @@ -75,6 +80,7 @@ enum { }; enum { + STAC_92HD73XX_AUTO, STAC_92HD73XX_NO_JD, /* no jack-detection */ STAC_92HD73XX_REF, STAC_92HD73XX_INTEL, @@ -87,6 +93,7 @@ enum { }; enum { + STAC_92HD83XXX_AUTO, STAC_92HD83XXX_REF, STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, @@ -98,12 +105,11 @@ enum { STAC_92HD83XXX_HP_INV_LED, STAC_92HD83XXX_HP_MIC_LED, STAC_92HD83XXX_HEADSET_JACK, - STAC_92HD83XXX_HP, - STAC_HP_ENVY_BASS, STAC_92HD83XXX_MODELS }; enum { + STAC_92HD71BXX_AUTO, STAC_92HD71BXX_REF, STAC_DELL_M4_1, STAC_DELL_M4_2, @@ -112,13 +118,12 @@ enum { STAC_HP_DV4, STAC_HP_DV5, STAC_HP_HDX, - STAC_92HD71BXX_HP, - STAC_92HD71BXX_NO_DMIC, - STAC_92HD71BXX_NO_SMUX, + STAC_HP_DV4_1222NR, STAC_92HD71BXX_MODELS }; enum { + STAC_925x_AUTO, STAC_925x_REF, STAC_M1, STAC_M1_2, @@ -131,6 +136,7 @@ enum { }; enum { + STAC_922X_AUTO, STAC_D945_REF, STAC_D945GTP3, STAC_D945GTP5, @@ -139,45 +145,67 @@ enum { STAC_INTEL_MAC_V3, STAC_INTEL_MAC_V4, STAC_INTEL_MAC_V5, - STAC_INTEL_MAC_AUTO, + STAC_INTEL_MAC_AUTO, /* This model is selected if no module parameter + * is given, one of the above models will be + * chosen according to the subsystem id. */ + /* for backward compatibility */ + STAC_MACMINI, + STAC_MACBOOK, + STAC_MACBOOK_PRO_V1, + STAC_MACBOOK_PRO_V2, + STAC_IMAC_INTEL, + STAC_IMAC_INTEL_20, STAC_ECS_202, STAC_922X_DELL_D81, STAC_922X_DELL_D82, STAC_922X_DELL_M81, STAC_922X_DELL_M82, - STAC_922X_INTEL_MAC_GPIO, STAC_922X_MODELS }; enum { + STAC_927X_AUTO, STAC_D965_REF_NO_JD, /* no jack-detection */ STAC_D965_REF, STAC_D965_3ST, STAC_D965_5ST, STAC_D965_5ST_NO_FP, - STAC_D965_VERBS, STAC_DELL_3ST, STAC_DELL_BIOS, - STAC_DELL_BIOS_SPDIF, - STAC_927X_DELL_DMIC, STAC_927X_VOLKNOB, STAC_927X_MODELS }; enum { + STAC_9872_AUTO, STAC_9872_VAIO, STAC_9872_MODELS }; +struct sigmatel_mic_route { + hda_nid_t pin; + signed char mux_idx; + signed char dmux_idx; +}; + +#define MAX_PINS_NUM 16 +#define MAX_ADCS_NUM 4 +#define MAX_DMICS_NUM 4 + struct sigmatel_spec { - struct hda_gen_spec gen; + struct snd_kcontrol_new *mixers[4]; + unsigned int num_mixers; + int board_config; unsigned int eapd_switch: 1; + unsigned int surr_switch: 1; + unsigned int alt_switch: 1; + unsigned int hp_detect: 1; + unsigned int spdif_mute: 1; + unsigned int check_volume_offset:1; + unsigned int auto_mic:1; unsigned int linear_tone_beep:1; unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */ - unsigned int volknob_init:1; /* special volume-knob initialization */ - unsigned int powerdown_adcs:1; - unsigned int have_spdif_mux:1; /* gpio lines */ unsigned int eapd_mask; @@ -189,7 +217,6 @@ struct sigmatel_spec { unsigned int gpio_led_polarity; unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */ unsigned int vref_led; - int default_polarity; unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */ bool mic_mute_led_on; /* current mic mute state */ @@ -199,7 +226,6 @@ struct sigmatel_spec { /* analog loopback */ const struct snd_kcontrol_new *aloopback_ctl; - unsigned int aloopback; unsigned char aloopback_mask; unsigned char aloopback_shift; @@ -207,141 +233,449 @@ struct sigmatel_spec { unsigned int power_map_bits; unsigned int num_pwrs; const hda_nid_t *pwr_nids; - unsigned int active_adcs; + const hda_nid_t *dac_list; + + /* playback */ + struct hda_input_mux *mono_mux; + unsigned int cur_mmux; + struct hda_multi_out multiout; + hda_nid_t dac_nids[5]; + hda_nid_t hp_dacs[5]; + hda_nid_t speaker_dacs[5]; + + int volume_offset; + + /* capture */ + const hda_nid_t *adc_nids; + unsigned int num_adcs; + const hda_nid_t *mux_nids; + unsigned int num_muxes; + const hda_nid_t *dmic_nids; + unsigned int num_dmics; + const hda_nid_t *dmux_nids; + unsigned int num_dmuxes; + const hda_nid_t *smux_nids; + unsigned int num_smuxes; + unsigned int num_analog_muxes; + + const unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */ + const unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */ + unsigned int num_caps; /* number of capture volume/switch elements */ + + struct sigmatel_mic_route ext_mic; + struct sigmatel_mic_route int_mic; + struct sigmatel_mic_route dock_mic; + + const char * const *spdif_labels; - /* beep widgets */ + hda_nid_t dig_in_nid; + hda_nid_t mono_nid; hda_nid_t anabeep_nid; hda_nid_t digbeep_nid; - /* SPDIF-out mux */ - const char * const *spdif_labels; - struct hda_input_mux spdif_mux; + /* pin widgets */ + const hda_nid_t *pin_nids; + unsigned int num_pins; + + /* codec specific stuff */ + const struct hda_verb *init; + const struct snd_kcontrol_new *mixer; + + /* capture source */ + struct hda_input_mux *dinput_mux; + unsigned int cur_dmux[2]; + struct hda_input_mux *input_mux; + unsigned int cur_mux[3]; + struct hda_input_mux *sinput_mux; unsigned int cur_smux[2]; + unsigned int cur_amux; + hda_nid_t *amp_nids; + unsigned int powerdown_adcs; + + /* i/o switches */ + unsigned int io_switch[2]; + unsigned int clfe_swap; + hda_nid_t line_switch; /* shared line-in for input and output */ + hda_nid_t mic_switch; /* shared mic-in for input and output */ + hda_nid_t hp_switch; /* NID of HP as line-out */ + unsigned int aloopback; + + struct hda_pcm pcm_rec[2]; /* PCM information */ + + /* dynamic controls and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + struct hda_input_mux private_dimux; + struct hda_input_mux private_imux; + struct hda_input_mux private_smux; + struct hda_input_mux private_mono_mux; + + /* auto spec */ + unsigned auto_pin_cnt; + hda_nid_t auto_pin_nids[MAX_PINS_NUM]; + unsigned auto_adc_cnt; + hda_nid_t auto_adc_nids[MAX_ADCS_NUM]; + hda_nid_t auto_mux_nids[MAX_ADCS_NUM]; + hda_nid_t auto_dmux_nids[MAX_ADCS_NUM]; + unsigned long auto_capvols[MAX_ADCS_NUM]; + unsigned auto_dmic_cnt; + hda_nid_t auto_dmic_nids[MAX_DMICS_NUM]; + + struct hda_vmaster_mute_hook vmaster_mute; }; #define AC_VERB_IDT_SET_POWER_MAP 0x7ec #define AC_VERB_IDT_GET_POWER_MAP 0xfec +static const hda_nid_t stac9200_adc_nids[1] = { + 0x03, +}; + +static const hda_nid_t stac9200_mux_nids[1] = { + 0x0c, +}; + +static const hda_nid_t stac9200_dac_nids[1] = { + 0x02, +}; + static const hda_nid_t stac92hd73xx_pwr_nids[8] = { 0x0a, 0x0b, 0x0c, 0xd, 0x0e, 0x0f, 0x10, 0x11 }; +static const hda_nid_t stac92hd73xx_slave_dig_outs[2] = { + 0x26, 0, +}; + +static const hda_nid_t stac92hd73xx_adc_nids[2] = { + 0x1a, 0x1b +}; + +#define STAC92HD73XX_NUM_DMICS 2 +static const hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { + 0x13, 0x14, 0 +}; + +#define STAC92HD73_DAC_COUNT 5 + +static const hda_nid_t stac92hd73xx_mux_nids[2] = { + 0x20, 0x21, +}; + +static const hda_nid_t stac92hd73xx_dmux_nids[2] = { + 0x20, 0x21, +}; + +static const hda_nid_t stac92hd73xx_smux_nids[2] = { + 0x22, 0x23, +}; + +#define STAC92HD73XX_NUM_CAPS 2 +static const unsigned long stac92hd73xx_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), +}; +#define stac92hd73xx_capsws stac92hd73xx_capvols + +#define STAC92HD83_DAC_COUNT 3 + static const hda_nid_t stac92hd83xxx_pwr_nids[7] = { 0x0a, 0x0b, 0x0c, 0xd, 0x0e, 0x0f, 0x10 }; +static const hda_nid_t stac92hd83xxx_slave_dig_outs[2] = { + 0x1e, 0, +}; + +static const hda_nid_t stac92hd83xxx_dmic_nids[] = { + 0x11, 0x20, +}; + static const hda_nid_t stac92hd71bxx_pwr_nids[3] = { 0x0a, 0x0d, 0x0f }; +static const hda_nid_t stac92hd71bxx_adc_nids[2] = { + 0x12, 0x13, +}; -/* - * PCM hooks - */ -static void stac_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct sigmatel_spec *spec = codec->spec; - if (action == HDA_GEN_PCM_ACT_OPEN && spec->stream_delay) - msleep(spec->stream_delay); -} +static const hda_nid_t stac92hd71bxx_mux_nids[2] = { + 0x1a, 0x1b +}; -static void stac_capture_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - struct sigmatel_spec *spec = codec->spec; - int i, idx = 0; +static const hda_nid_t stac92hd71bxx_dmux_nids[2] = { + 0x1c, 0x1d, +}; - if (!spec->powerdown_adcs) - return; +static const hda_nid_t stac92hd71bxx_smux_nids[2] = { + 0x24, 0x25, +}; - for (i = 0; i < spec->gen.num_all_adcs; i++) { - if (spec->gen.all_adcs[i] == hinfo->nid) { - idx = i; - break; - } - } +#define STAC92HD71BXX_NUM_DMICS 2 +static const hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = { + 0x18, 0x19, 0 +}; - switch (action) { - case HDA_GEN_PCM_ACT_OPEN: - msleep(40); - snd_hda_codec_write(codec, hinfo->nid, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - spec->active_adcs |= (1 << idx); - break; - case HDA_GEN_PCM_ACT_CLOSE: - snd_hda_codec_write(codec, hinfo->nid, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - spec->active_adcs &= ~(1 << idx); - break; - } -} +static const hda_nid_t stac92hd71bxx_dmic_5port_nids[STAC92HD71BXX_NUM_DMICS] = { + 0x18, 0 +}; -/* - * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a - * funky external mute control using GPIO pins. - */ +static const hda_nid_t stac92hd71bxx_slave_dig_outs[2] = { + 0x22, 0 +}; -static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, - unsigned int dir_mask, unsigned int data) -{ - unsigned int gpiostate, gpiomask, gpiodir; +#define STAC92HD71BXX_NUM_CAPS 2 +static const unsigned long stac92hd71bxx_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), +}; +#define stac92hd71bxx_capsws stac92hd71bxx_capvols - snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); +static const hda_nid_t stac925x_adc_nids[1] = { + 0x03, +}; - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); +static const hda_nid_t stac925x_mux_nids[1] = { + 0x0f, +}; - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= mask; +static const hda_nid_t stac925x_dac_nids[1] = { + 0x02, +}; - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= dir_mask; +#define STAC925X_NUM_DMICS 1 +static const hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = { + 0x15, 0 +}; - /* Configure GPIOx as CMOS */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); +static const hda_nid_t stac925x_dmux_nids[1] = { + 0x14, +}; - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ +static const unsigned long stac925x_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT), +}; +static const unsigned long stac925x_capsws[] = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), +}; - msleep(1); +static const hda_nid_t stac922x_adc_nids[2] = { + 0x06, 0x07, +}; - snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ +static const hda_nid_t stac922x_mux_nids[2] = { + 0x12, 0x13, +}; + +#define STAC922X_NUM_CAPS 2 +static const unsigned long stac922x_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT), +}; +#define stac922x_capsws stac922x_capvols + +static const hda_nid_t stac927x_slave_dig_outs[2] = { + 0x1f, 0, +}; + +static const hda_nid_t stac927x_adc_nids[3] = { + 0x07, 0x08, 0x09 +}; + +static const hda_nid_t stac927x_mux_nids[3] = { + 0x15, 0x16, 0x17 +}; + +static const hda_nid_t stac927x_smux_nids[1] = { + 0x21, +}; + +static const hda_nid_t stac927x_dac_nids[6] = { + 0x02, 0x03, 0x04, 0x05, 0x06, 0 +}; + +static const hda_nid_t stac927x_dmux_nids[1] = { + 0x1b, +}; + +#define STAC927X_NUM_DMICS 2 +static const hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = { + 0x13, 0x14, 0 +}; + +#define STAC927X_NUM_CAPS 3 +static const unsigned long stac927x_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT), +}; +static const unsigned long stac927x_capsws[] = { + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), +}; + +static const char * const stac927x_spdif_labels[5] = { + "Digital Playback", "ADAT", "Analog Mux 1", + "Analog Mux 2", "Analog Mux 3" +}; + +static const hda_nid_t stac9205_adc_nids[2] = { + 0x12, 0x13 +}; + +static const hda_nid_t stac9205_mux_nids[2] = { + 0x19, 0x1a +}; + +static const hda_nid_t stac9205_dmux_nids[1] = { + 0x1d, +}; + +static const hda_nid_t stac9205_smux_nids[1] = { + 0x21, +}; + +#define STAC9205_NUM_DMICS 2 +static const hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = { + 0x17, 0x18, 0 +}; + +#define STAC9205_NUM_CAPS 2 +static const unsigned long stac9205_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT), +}; +static const unsigned long stac9205_capsws[] = { + HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT), +}; + +static const hda_nid_t stac9200_pin_nids[8] = { + 0x08, 0x09, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, +}; + +static const hda_nid_t stac925x_pin_nids[8] = { + 0x07, 0x08, 0x0a, 0x0b, + 0x0c, 0x0d, 0x10, 0x11, +}; + +static const hda_nid_t stac922x_pin_nids[10] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x15, 0x1b, +}; + +static const hda_nid_t stac92hd73xx_pin_nids[13] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x22, 0x23 +}; + +#define STAC92HD71BXX_NUM_PINS 13 +static const hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x00, + 0x00, 0x14, 0x18, 0x19, 0x1e, + 0x1f, 0x20, 0x27 +}; +static const hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x14, 0x18, 0x19, 0x1e, + 0x1f, 0x20, 0x27 +}; + +static const hda_nid_t stac927x_pin_nids[14] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x21, 0x22, 0x23, +}; + +static const hda_nid_t stac9205_pin_nids[12] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x14, 0x16, 0x17, 0x18, + 0x21, 0x22, +}; + +static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->dinput_mux, uinfo); } -/* hook for controlling mic-mute LED GPIO */ -static void stac_capture_led_hook(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol) +static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - bool mute; + unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - if (!ucontrol) - return; + ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx]; + return 0; +} - mute = !(ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]); - if (spec->mic_mute_led_on != mute) { - spec->mic_mute_led_on = mute; - if (mute) - spec->gpio_data |= spec->mic_mute_led_gpio; +static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol, + spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]); +} + +static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->sinput_mux, uinfo); +} + +static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx]; + return 0; +} + +static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *smux = &spec->private_smux; + unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + int err, val; + hda_nid_t nid; + + err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol, + spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]); + if (err < 0) + return err; + + if (spec->spdif_mute) { + if (smux_idx == 0) + nid = spec->multiout.dig_out_nid; else - spec->gpio_data &= ~spec->mic_mute_led_gpio; - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); + nid = codec->slave_dig_outs[smux_idx - 1]; + if (spec->cur_smux[smux_idx] == smux->num_items - 1) + val = HDA_AMP_MUTE; + else + val = 0; + /* un/mute SPDIF out */ + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, val); } + return 0; } static int stac_vrefout_set(struct hda_codec *codec, @@ -367,228 +701,129 @@ static int stac_vrefout_set(struct hda_codec *codec, return 1; } -/* update mute-LED accoring to the master switch */ -static void stac_update_led_status(struct hda_codec *codec, int enabled) +static unsigned int stac92xx_vref_set(struct hda_codec *codec, + hda_nid_t nid, unsigned int new_vref) { - struct sigmatel_spec *spec = codec->spec; - int muted = !enabled; + int error; + unsigned int pincfg; + pincfg = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (!spec->gpio_led) - return; + pincfg &= 0xff; + pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + pincfg |= new_vref; - /* LED state is inverted on these systems */ - if (spec->gpio_led_polarity) - muted = !muted; + if (new_vref == AC_PINCTL_VREF_HIZ) + pincfg |= AC_PINCTL_OUT_EN; + else + pincfg |= AC_PINCTL_IN_EN; - if (!spec->vref_mute_led_nid) { - if (muted) - spec->gpio_data |= spec->gpio_led; - else - spec->gpio_data &= ~spec->gpio_led; - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - } else { - spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; - stac_vrefout_set(codec, spec->vref_mute_led_nid, - spec->vref_led); - } + error = snd_hda_set_pin_ctl_cache(codec, nid, pincfg); + if (error < 0) + return error; + else + return 1; } -/* vmaster hook to update mute LED */ -static void stac_vmaster_hook(void *private_data, int val) +static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid) { - stac_update_led_status(private_data, val); + unsigned int vref; + vref = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + vref &= AC_PINCTL_VREFEN; + return vref; } -/* automute hook to handle GPIO mute and EAPD updates */ -static void stac_update_outputs(struct hda_codec *codec) +static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->input_mux, uinfo); +} - if (spec->gpio_mute) - spec->gen.master_mute = - !(snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); +static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - snd_hda_gen_update_outputs(codec); + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + return 0; +} - if (spec->eapd_mask && spec->eapd_switch) { - unsigned int val = spec->gpio_data; - if (spec->gen.speaker_muted) - val &= ~spec->eapd_mask; - else - val |= spec->eapd_mask; - if (spec->gpio_data != val) - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, - val); +static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + const struct hda_input_mux *imux = spec->input_mux; + unsigned int idx, prev_idx, didx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + prev_idx = spec->cur_mux[adc_idx]; + if (prev_idx == idx) + return 0; + if (idx < spec->num_analog_muxes) { + snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[idx].index); + if (prev_idx >= spec->num_analog_muxes && + spec->mux_nids[adc_idx] != spec->dmux_nids[adc_idx]) { + imux = spec->dinput_mux; + /* 0 = analog */ + snd_hda_codec_write_cache(codec, + spec->dmux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[0].index); + } + } else { + imux = spec->dinput_mux; + /* first dimux item is hardcoded to select analog imux, + * so lets skip it + */ + didx = idx - spec->num_analog_muxes + 1; + snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[didx].index); } + spec->cur_mux[adc_idx] = idx; + return 1; } -static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, - bool enable, bool do_write) +static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - unsigned int idx, val; + return snd_hda_input_mux_info(spec->mono_mux, uinfo); +} - for (idx = 0; idx < spec->num_pwrs; idx++) { - if (spec->pwr_nids[idx] == nid) - break; - } - if (idx >= spec->num_pwrs) - return; +static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; - idx = 1 << idx; - - val = spec->power_map_bits; - if (enable) - val &= ~idx; - else - val |= idx; - - /* power down unused output ports */ - if (val != spec->power_map_bits) { - spec->power_map_bits = val; - if (do_write) - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_IDT_SET_POWER_MAP, val); - } -} - -/* update power bit per jack plug/unplug */ -static void jack_update_power(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - if (!spec->num_pwrs) - return; - - if (jack && jack->nid) { - stac_toggle_power_map(codec, jack->nid, - snd_hda_jack_detect(codec, jack->nid), - true); - return; - } - - /* update all jacks */ - for (i = 0; i < spec->num_pwrs; i++) { - hda_nid_t nid = spec->pwr_nids[i]; - jack = snd_hda_jack_tbl_get(codec, nid); - if (!jack || !jack->action) - continue; - if (jack->action == STAC_PWR_EVENT || - jack->action <= HDA_GEN_LAST_EVENT) - stac_toggle_power_map(codec, nid, - snd_hda_jack_detect(codec, nid), - false); - } - - snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_IDT_SET_POWER_MAP, - spec->power_map_bits); -} - -static void stac_hp_automute(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - snd_hda_gen_hp_automute(codec, jack); - jack_update_power(codec, jack); -} - -static void stac_line_automute(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - snd_hda_gen_line_automute(codec, jack); - jack_update_power(codec, jack); -} - -static void stac_mic_autoswitch(struct hda_codec *codec, - struct hda_jack_tbl *jack) -{ - snd_hda_gen_mic_autoswitch(codec, jack); - jack_update_power(codec, jack); -} - -static void stac_vref_event(struct hda_codec *codec, struct hda_jack_tbl *event) -{ - unsigned int data; - - data = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - /* toggle VREF state based on GPIOx status */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, - !!(data & (1 << event->private_data))); -} - -/* initialize the power map and enable the power event to jacks that - * haven't been assigned to automute - */ -static void stac_init_power_map(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_pwrs; i++) { - hda_nid_t nid = spec->pwr_nids[i]; - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - def_conf = get_defcfg_connect(def_conf); - if (snd_hda_jack_tbl_get(codec, nid)) - continue; - if (def_conf == AC_JACK_PORT_COMPLEX && - !(spec->vref_mute_led_nid == nid || - is_jack_detectable(codec, nid))) { - snd_hda_jack_detect_enable_callback(codec, nid, - STAC_PWR_EVENT, - jack_update_power); - } else { - if (def_conf == AC_JACK_PORT_NONE) - stac_toggle_power_map(codec, nid, false, false); - else - stac_toggle_power_map(codec, nid, true, false); - } - } -} - -/* - */ - -static inline bool get_int_hint(struct hda_codec *codec, const char *key, - int *valp) -{ - return !snd_hda_get_int_hint(codec, key, valp); + ucontrol->value.enumerated.item[0] = spec->cur_mmux; + return 0; } -/* override some hints from the hwdep entry */ -static void stac_store_hints(struct hda_codec *codec) +static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - int val; - if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) { - spec->eapd_mask = spec->gpio_dir = spec->gpio_data = - spec->gpio_mask; - } - if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir)) - spec->gpio_mask &= spec->gpio_mask; - if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) - spec->gpio_dir &= spec->gpio_mask; - if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask)) - spec->eapd_mask &= spec->gpio_mask; - if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute)) - spec->gpio_mute &= spec->gpio_mask; - val = snd_hda_get_bool_hint(codec, "eapd_switch"); - if (val >= 0) - spec->eapd_switch = val; + return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol, + spec->mono_nid, &spec->cur_mmux); } -/* - * loopback controls - */ - -#define stac_aloopback_info snd_ctl_boolean_mono_info +#define stac92xx_aloopback_info snd_ctl_boolean_mono_info -static int stac_aloopback_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); @@ -599,8 +834,8 @@ static int stac_aloopback_get(struct snd_kcontrol *kcontrol, return 0; } -static int stac_aloopback_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; @@ -639,815 +874,498 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, return 1; } +static const struct hda_verb stac9200_core_init[] = { + /* set dac0mux for dac converter */ + { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {} +}; + +static const struct hda_verb stac9200_eapd_init[] = { + /* set dac0mux for dac converter */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + {} +}; + +static const 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}, + {} +}; + +static const struct hda_verb stac92hd73xx_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + {} +}; + +static const struct hda_verb stac92hd83xxx_core_init[] = { + /* power state controls amps */ + { 0x01, AC_VERB_SET_EAPD, 1 << 2}, + {} +}; + +static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = { + { 0x22, 0x785, 0x43 }, + { 0x22, 0x782, 0xe0 }, + { 0x22, 0x795, 0x00 }, + {} +}; + +static const struct hda_verb stac92hd71bxx_core_init[] = { + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + {} +}; + +static const struct hda_verb stac92hd71bxx_unmute_core_init[] = { + /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */ + { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {} +}; + +static const struct hda_verb stac925x_core_init[] = { + /* set dac0mux for dac converter */ + { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* mute the master volume */ + { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + {} +}; + +static const struct hda_verb stac922x_core_init[] = { + /* set master volume and direct control */ + { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + {} +}; + +static const struct hda_verb d965_core_init[] = { + /* set master volume and direct control */ + { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* unmute node 0x1b */ + { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* select node 0x03 as DAC */ + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {} +}; + +static const struct hda_verb dell_3st_core_init[] = { + /* don't set delta bit */ + {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, + /* unmute node 0x1b */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* select node 0x03 as DAC */ + {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {} +}; + +static const struct hda_verb stac927x_core_init[] = { + /* set master volume and direct control */ + { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* enable analog pc beep path */ + { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, + {} +}; + +static const struct hda_verb stac927x_volknob_core_init[] = { + /* don't set delta bit */ + {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, + /* enable analog pc beep path */ + {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, + {} +}; + +static const struct hda_verb stac9205_core_init[] = { + /* set master volume and direct control */ + { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* enable analog pc beep path */ + { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, + {} +}; + +#define STAC_MONO_MUX \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Mono Mux", \ + .count = 1, \ + .info = stac92xx_mono_mux_enum_info, \ + .get = stac92xx_mono_mux_enum_get, \ + .put = stac92xx_mono_mux_enum_put, \ + } + #define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = "Analog Loopback", \ .count = cnt, \ - .info = stac_aloopback_info, \ - .get = stac_aloopback_get, \ - .put = stac_aloopback_put, \ + .info = stac92xx_aloopback_info, \ + .get = stac92xx_aloopback_get, \ + .put = stac92xx_aloopback_put, \ .private_value = verb_read | (verb_write << 16), \ } -/* - * Mute LED handling on HP laptops - */ +#define DC_BIAS(xname, idx, nid) \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = idx, \ + .info = stac92xx_dc_bias_info, \ + .get = stac92xx_dc_bias_get, \ + .put = stac92xx_dc_bias_put, \ + .private_value = nid, \ + } -/* check whether it's a HP laptop with a docking port */ -static bool hp_bnb2011_with_dock(struct hda_codec *codec) -{ - if (codec->vendor_id != 0x111d7605 && - codec->vendor_id != 0x111d76d1) - return false; +static const struct snd_kcontrol_new stac9200_mixer[] = { + HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xb, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0xb, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), + { } /* end */ +}; - switch (codec->subsystem_id) { - case 0x103c1618: - case 0x103c1619: - case 0x103c161a: - case 0x103c161b: - case 0x103c161c: - case 0x103c161d: - case 0x103c161e: - case 0x103c161f: +static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), + {} +}; - case 0x103c162a: - case 0x103c162b: +static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4), + {} +}; - case 0x103c1630: - case 0x103c1631: +static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5), + {} +}; - case 0x103c1633: - case 0x103c1634: - case 0x103c1635: - case 0x103c3587: - case 0x103c3588: - case 0x103c3589: - case 0x103c358a: +static const struct snd_kcontrol_new stac92hd71bxx_loopback[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2) +}; - case 0x103c3667: - case 0x103c3668: - case 0x103c3669: +static const struct snd_kcontrol_new stac925x_mixer[] = { + HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xe, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x0e, 0, HDA_OUTPUT), + { } /* end */ +}; - return true; - } - return false; +static const struct snd_kcontrol_new stac9205_loopback[] = { + STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1), + {} +}; + +static const struct snd_kcontrol_new stac927x_loopback[] = { + STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1), + {} +}; + +static struct snd_kcontrol_new stac_dmux_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Source", + /* count set later */ + .info = stac92xx_dmux_enum_info, + .get = stac92xx_dmux_enum_get, + .put = stac92xx_dmux_enum_put, +}; + +static struct snd_kcontrol_new stac_smux_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Playback Source", + /* count set later */ + .info = stac92xx_smux_enum_info, + .get = stac92xx_smux_enum_get, + .put = stac92xx_smux_enum_put, +}; + +static const char * const slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", "Bass Speaker", "IEC958", "PCM", + NULL +}; + +static void stac92xx_update_led_status(struct hda_codec *codec, int enabled); + +static void stac92xx_vmaster_hook(void *private_data, int val) +{ + stac92xx_update_led_status(private_data, val); } -static bool hp_blike_system(u32 subsystem_id) -{ - switch (subsystem_id) { - case 0x103c1520: - case 0x103c1521: - case 0x103c1523: - case 0x103c1524: - case 0x103c1525: - case 0x103c1722: - case 0x103c1723: - case 0x103c1724: - case 0x103c1725: - case 0x103c1726: - case 0x103c1727: - case 0x103c1728: - case 0x103c1729: - case 0x103c172a: - case 0x103c172b: - case 0x103c307e: - case 0x103c307f: - case 0x103c3080: - case 0x103c3081: - case 0x103c7007: - case 0x103c7008: - return true; - } - return false; -} - -static void set_hp_led_gpio(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int gpio; - - if (spec->gpio_led) - return; - - gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); - gpio &= AC_GPIO_IO_COUNT; - if (gpio > 3) - spec->gpio_led = 0x08; /* GPIO 3 */ - else - spec->gpio_led = 0x01; /* GPIO 0 */ -} - -/* - * This method searches for the mute LED GPIO configuration - * provided as OEM string in SMBIOS. The format of that string - * is HP_Mute_LED_P_G or HP_Mute_LED_P - * where P can be 0 or 1 and defines mute LED GPIO control state (low/high) - * that corresponds to the NOT muted state of the master volume - * and G is the index of the GPIO to use as the mute LED control (0..9) - * If _G portion is missing it is assigned based on the codec ID - * - * So, HP B-series like systems may have HP_Mute_LED_0 (current models) - * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings - * - * - * The dv-series laptops don't seem to have the HP_Mute_LED* strings in - * SMBIOS - at least the ones I have seen do not have them - which include - * my own system (HP Pavilion dv6-1110ax) and my cousin's - * HP Pavilion dv9500t CTO. - * Need more information on whether it is true across the entire series. - * -- kunal - */ -static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) +static void stac92xx_free_kctls(struct hda_codec *codec); + +static int stac92xx_build_controls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - const struct dmi_device *dev = NULL; + unsigned int vmaster_tlv[4]; + int err; + int i; - if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { - get_int_hint(codec, "gpio_led_polarity", - &spec->gpio_led_polarity); - return 1; + if (spec->mixer) { + err = snd_hda_add_new_ctls(codec, spec->mixer); + if (err < 0) + return err; } - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (sscanf(dev->name, "HP_Mute_LED_%d_%x", - &spec->gpio_led_polarity, - &spec->gpio_led) == 2) { - unsigned int max_gpio; - max_gpio = snd_hda_param_read(codec, codec->afg, - AC_PAR_GPIO_CAP); - max_gpio &= AC_GPIO_IO_COUNT; - if (spec->gpio_led < max_gpio) - spec->gpio_led = 1 << spec->gpio_led; - else - spec->vref_mute_led_nid = spec->gpio_led; - return 1; - } - if (sscanf(dev->name, "HP_Mute_LED_%d", - &spec->gpio_led_polarity) == 1) { - set_hp_led_gpio(codec); - return 1; - } - /* BIOS bug: unfilled OEM string */ - if (strstr(dev->name, "HP_Mute_LED_P_G")) { - set_hp_led_gpio(codec); - if (default_polarity >= 0) - spec->gpio_led_polarity = default_polarity; - else - spec->gpio_led_polarity = 1; - return 1; + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; + } + if (!spec->auto_mic && spec->num_dmuxes > 0 && + snd_hda_get_bool_hint(codec, "separate_dmux") == 1) { + stac_dmux_mixer.count = spec->num_dmuxes; + err = snd_hda_ctl_add(codec, 0, + snd_ctl_new1(&stac_dmux_mixer, codec)); + if (err < 0) + return err; + } + if (spec->num_smuxes > 0) { + int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid); + struct hda_input_mux *smux = &spec->private_smux; + /* check for mute support on SPDIF out */ + if (wcaps & AC_WCAP_OUT_AMP) { + snd_hda_add_imux_item(smux, "Off", 0, NULL); + spec->spdif_mute = 1; } + stac_smux_mixer.count = spec->num_smuxes; + err = snd_hda_ctl_add(codec, 0, + snd_ctl_new1(&stac_smux_mixer, codec)); + if (err < 0) + return err; } - /* - * Fallback case - if we don't find the DMI strings, - * we statically set the GPIO - if not a B-series system - * and default polarity is provided - */ - if (!hp_blike_system(codec->subsystem_id) && - (default_polarity == 0 || default_polarity == 1)) { - set_hp_led_gpio(codec); - spec->gpio_led_polarity = default_polarity; - return 1; + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_dig_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid, + spec->autocfg.dig_out_type[0]); + 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 && !(spec->gpio_dir & 0x01)) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); + if (err < 0) + return err; } - return 0; -} -/* - * PC beep controls - */ + /* if we have no master control, let's create it */ + snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], + HDA_OUTPUT, vmaster_tlv); + /* correct volume offset */ + vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset; + /* minimum value is actually mute */ + vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + vmaster_tlv, slave_pfxs, + "Playback Volume"); + if (err < 0) + return err; -/* create PC beep volume controls */ -static int stac_auto_create_beep_ctls(struct hda_codec *codec, - hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); - struct snd_kcontrol_new *knew; - static struct snd_kcontrol_new abeep_mute_ctl = - HDA_CODEC_MUTE(NULL, 0, 0, 0); - static struct snd_kcontrol_new dbeep_mute_ctl = - HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0); - static struct snd_kcontrol_new beep_vol_ctl = - HDA_CODEC_VOLUME(NULL, 0, 0, 0); + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch", true, + &spec->vmaster_mute.sw_kctl); + if (err < 0) + return err; - /* check for mute support for the the amp */ - if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { - const struct snd_kcontrol_new *temp; - if (spec->anabeep_nid == nid) - temp = &abeep_mute_ctl; - else - temp = &dbeep_mute_ctl; - knew = snd_hda_gen_add_kctl(&spec->gen, - "Beep Playback Switch", temp); - if (!knew) - return -ENOMEM; - knew->private_value = - HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT); + if (spec->gpio_led) { + spec->vmaster_mute.hook = stac92xx_vmaster_hook; + err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); + if (err < 0) + return err; } - /* check to see if there is volume support for the amp */ - if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { - knew = snd_hda_gen_add_kctl(&spec->gen, - "Beep Playback Volume", - &beep_vol_ctl); - if (!knew) - return -ENOMEM; - knew->private_value = - HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT); + if (spec->aloopback_ctl && + snd_hda_get_bool_hint(codec, "loopback") == 1) { + err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl); + if (err < 0) + return err; } - return 0; -} -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define stac_dig_beep_switch_info snd_ctl_boolean_mono_info + stac92xx_free_kctls(codec); /* no longer needed */ -static int stac_dig_beep_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = codec->beep->enabled; - return 0; -} + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; -static int stac_dig_beep_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); + return 0; } -static const struct snd_kcontrol_new stac_dig_beep_ctrl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Beep Playback Switch", - .info = stac_dig_beep_switch_info, - .get = stac_dig_beep_switch_get, - .put = stac_dig_beep_switch_put, +static const unsigned int ref9200_pin_configs[8] = { + 0x01c47010, 0x01447010, 0x0221401f, 0x01114010, + 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, }; -static int stac_beep_switch_ctl(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_dig_beep_ctrl)) - return -ENOMEM; - return 0; -} -#endif +static const unsigned int gateway9200_m4_pin_configs[8] = { + 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010, + 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3, +}; +static const unsigned int gateway9200_m4_2_pin_configs[8] = { + 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010, + 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3, +}; /* - * SPDIF-out mux controls - */ + STAC 9200 pin configs for + 102801A8 + 102801DE + 102801E8 +*/ +static const unsigned int dell9200_d21_pin_configs[8] = { + 0x400001f0, 0x400001f1, 0x02214030, 0x01014010, + 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, +}; -static int stac_smux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->spdif_mux, uinfo); -} +/* + STAC 9200 pin configs for + 102801C0 + 102801C1 +*/ +static const unsigned int dell9200_d22_pin_configs[8] = { + 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010, + 0x01813020, 0x02a19021, 0x90100140, 0x400001f2, +}; -static int stac_smux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); +/* + STAC 9200 pin configs for + 102801C4 (Dell Dimension E310) + 102801C5 + 102801C7 + 102801D9 + 102801DA + 102801E3 +*/ +static const unsigned int dell9200_d23_pin_configs[8] = { + 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010, + 0x01813020, 0x01a19021, 0x90100140, 0x400001f2, +}; - ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx]; - return 0; -} -static int stac_smux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); +/* + STAC 9200-32 pin configs for + 102801B5 (Dell Inspiron 630m) + 102801D8 (Dell Inspiron 640m) +*/ +static const unsigned int dell9200_m21_pin_configs[8] = { + 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310, + 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd, +}; - return snd_hda_input_mux_put(codec, &spec->spdif_mux, ucontrol, - spec->gen.autocfg.dig_out_pins[smux_idx], - &spec->cur_smux[smux_idx]); -} +/* + STAC 9200-32 pin configs for + 102801C2 (Dell Latitude D620) + 102801C8 + 102801CC (Dell Latitude D820) + 102801D4 + 102801D6 +*/ +static const unsigned int dell9200_m22_pin_configs[8] = { + 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310, + 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc, +}; -static struct snd_kcontrol_new stac_smux_mixer = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - /* count set later */ - .info = stac_smux_enum_info, - .get = stac_smux_enum_get, - .put = stac_smux_enum_put, +/* + STAC 9200-32 pin configs for + 102801CE (Dell XPS M1710) + 102801CF (Dell Precision M90) +*/ +static const unsigned int dell9200_m23_pin_configs[8] = { + 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310, + 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc, }; -static const char * const stac_spdif_labels[] = { - "Digital Playback", "Analog Mux 1", "Analog Mux 2", NULL +/* + STAC 9200-32 pin configs for + 102801C9 + 102801CA + 102801CB (Dell Latitude 120L) + 102801D3 +*/ +static const unsigned int dell9200_m24_pin_configs[8] = { + 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310, + 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe, }; -static int stac_create_spdif_mux_ctls(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - const char * const *labels = spec->spdif_labels; - struct snd_kcontrol_new *kctl; - int i, num_cons; +/* + STAC 9200-32 pin configs for + 102801BD (Dell Inspiron E1505n) + 102801EE + 102801EF +*/ +static const unsigned int dell9200_m25_pin_configs[8] = { + 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310, + 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd, +}; - if (cfg->dig_outs < 1) - return 0; +/* + STAC 9200-32 pin configs for + 102801F5 (Dell Inspiron 1501) + 102801F6 +*/ +static const unsigned int dell9200_m26_pin_configs[8] = { + 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310, + 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe, +}; - num_cons = snd_hda_get_num_conns(codec, cfg->dig_out_pins[0]); - if (num_cons <= 1) - return 0; +/* + STAC 9200-32 + 102801CD (Dell Inspiron E1705/9400) +*/ +static const unsigned int dell9200_m27_pin_configs[8] = { + 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310, + 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc, +}; - if (!labels) - labels = stac_spdif_labels; - for (i = 0; i < num_cons; i++) { - if (snd_BUG_ON(!labels[i])) - return -EINVAL; - snd_hda_add_imux_item(&spec->spdif_mux, labels[i], i, NULL); - } +static const unsigned int oqo9200_pin_configs[8] = { + 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210, + 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3, +}; - kctl = snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_smux_mixer); - if (!kctl) - return -ENOMEM; - kctl->count = cfg->dig_outs; - return 0; -} +static const 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, + [STAC_9200_DELL_M21] = dell9200_m21_pin_configs, + [STAC_9200_DELL_M22] = dell9200_m22_pin_configs, + [STAC_9200_DELL_M23] = dell9200_m23_pin_configs, + [STAC_9200_DELL_M24] = dell9200_m24_pin_configs, + [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_M4] = gateway9200_m4_pin_configs, + [STAC_9200_M4_2] = gateway9200_m4_2_pin_configs, + [STAC_9200_PANASONIC] = ref9200_pin_configs, +}; -/* - */ +static const char * const stac9200_models[STAC_9200_MODELS] = { + [STAC_AUTO] = "auto", + [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", + [STAC_9200_DELL_M21] = "dell-m21", + [STAC_9200_DELL_M22] = "dell-m22", + [STAC_9200_DELL_M23] = "dell-m23", + [STAC_9200_DELL_M24] = "dell-m24", + [STAC_9200_DELL_M25] = "dell-m25", + [STAC_9200_DELL_M26] = "dell-m26", + [STAC_9200_DELL_M27] = "dell-m27", + [STAC_9200_M4] = "gateway-m4", + [STAC_9200_M4_2] = "gateway-m4-2", + [STAC_9200_PANASONIC] = "panasonic", +}; -static const struct hda_verb stac9200_core_init[] = { - /* set dac0mux for dac converter */ - { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {} -}; - -static const struct hda_verb stac9200_eapd_init[] = { - /* set dac0mux for dac converter */ - {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {} -}; - -static const 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}, - {} -}; - -static const struct hda_verb stac92hd73xx_core_init[] = { - /* set master volume and direct control */ - { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb stac92hd83xxx_core_init[] = { - /* power state controls amps */ - { 0x01, AC_VERB_SET_EAPD, 1 << 2}, - {} -}; - -static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = { - { 0x22, 0x785, 0x43 }, - { 0x22, 0x782, 0xe0 }, - { 0x22, 0x795, 0x00 }, - {} -}; - -static const struct hda_verb stac92hd71bxx_core_init[] = { - /* set master volume and direct control */ - { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb stac92hd71bxx_unmute_core_init[] = { - /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */ - { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {} -}; - -static const struct hda_verb stac925x_core_init[] = { - /* set dac0mux for dac converter */ - { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* mute the master volume */ - { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - {} -}; - -static const struct hda_verb stac922x_core_init[] = { - /* set master volume and direct control */ - { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - {} -}; - -static const struct hda_verb d965_core_init[] = { - /* unmute node 0x1b */ - { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* select node 0x03 as DAC */ - { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {} -}; - -static const struct hda_verb dell_3st_core_init[] = { - /* don't set delta bit */ - {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, - /* unmute node 0x1b */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* select node 0x03 as DAC */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, - {} -}; - -static const struct hda_verb stac927x_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* enable analog pc beep path */ - { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct hda_verb stac927x_volknob_core_init[] = { - /* don't set delta bit */ - {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f}, - /* enable analog pc beep path */ - {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct hda_verb stac9205_core_init[] = { - /* set master volume and direct control */ - { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* enable analog pc beep path */ - { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5}, - {} -}; - -static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback = - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3); - -static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback = - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4); - -static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback = - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5); - -static const struct snd_kcontrol_new stac92hd71bxx_loopback = - STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2); - -static const struct snd_kcontrol_new stac9205_loopback = - STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1); - -static const struct snd_kcontrol_new stac927x_loopback = - STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1); - -static const struct hda_pintbl ref9200_pin_configs[] = { - { 0x08, 0x01c47010 }, - { 0x09, 0x01447010 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01114010 }, - { 0x0f, 0x02a19020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x01813122 }, - {} -}; - -static const struct hda_pintbl gateway9200_m4_pin_configs[] = { - { 0x08, 0x400000fe }, - { 0x09, 0x404500f4 }, - { 0x0d, 0x400100f0 }, - { 0x0e, 0x90110010 }, - { 0x0f, 0x400100f1 }, - { 0x10, 0x02a1902e }, - { 0x11, 0x500000f2 }, - { 0x12, 0x500000f3 }, - {} -}; - -static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = { - { 0x08, 0x400000fe }, - { 0x09, 0x404500f4 }, - { 0x0d, 0x400100f0 }, - { 0x0e, 0x90110010 }, - { 0x0f, 0x400100f1 }, - { 0x10, 0x02a1902e }, - { 0x11, 0x500000f2 }, - { 0x12, 0x500000f3 }, - {} -}; - -/* - STAC 9200 pin configs for - 102801A8 - 102801DE - 102801E8 -*/ -static const struct hda_pintbl dell9200_d21_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x02214030 }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x02a19020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x01813122 }, - {} -}; - -/* - STAC 9200 pin configs for - 102801C0 - 102801C1 -*/ -static const struct hda_pintbl dell9200_d22_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x01813020 }, - { 0x10, 0x02a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x400001f2 }, - {} -}; - -/* - STAC 9200 pin configs for - 102801C4 (Dell Dimension E310) - 102801C5 - 102801C7 - 102801D9 - 102801DA - 102801E3 -*/ -static const struct hda_pintbl dell9200_d23_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x01813020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x400001f2 }, - {} -}; - - -/* - STAC 9200-32 pin configs for - 102801B5 (Dell Inspiron 630m) - 102801D8 (Dell Inspiron 640m) -*/ -static const struct hda_pintbl dell9200_m21_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x03441340 }, - { 0x0d, 0x0321121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fb }, - { 0x10, 0x03a11020 }, - { 0x11, 0x401003fc }, - { 0x12, 0x403003fd }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801C2 (Dell Latitude D620) - 102801C8 - 102801CC (Dell Latitude D820) - 102801D4 - 102801D6 -*/ -static const struct hda_pintbl dell9200_m22_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x0144131f }, - { 0x0d, 0x0321121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x90a70321 }, - { 0x10, 0x03a11020 }, - { 0x11, 0x401003fb }, - { 0x12, 0x40f000fc }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801CE (Dell XPS M1710) - 102801CF (Dell Precision M90) -*/ -static const struct hda_pintbl dell9200_m23_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x01441340 }, - { 0x0d, 0x0421421f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fb }, - { 0x10, 0x04a1102e }, - { 0x11, 0x90170311 }, - { 0x12, 0x403003fc }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801C9 - 102801CA - 102801CB (Dell Latitude 120L) - 102801D3 -*/ -static const struct hda_pintbl dell9200_m24_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x404003fb }, - { 0x0d, 0x0321121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fc }, - { 0x10, 0x03a11020 }, - { 0x11, 0x401003fd }, - { 0x12, 0x403003fe }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801BD (Dell Inspiron E1505n) - 102801EE - 102801EF -*/ -static const struct hda_pintbl dell9200_m25_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x01441340 }, - { 0x0d, 0x0421121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fb }, - { 0x10, 0x04a11020 }, - { 0x11, 0x401003fc }, - { 0x12, 0x403003fd }, - {} -}; - -/* - STAC 9200-32 pin configs for - 102801F5 (Dell Inspiron 1501) - 102801F6 -*/ -static const struct hda_pintbl dell9200_m26_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x404003fb }, - { 0x0d, 0x0421121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fc }, - { 0x10, 0x04a11020 }, - { 0x11, 0x401003fd }, - { 0x12, 0x403003fe }, - {} -}; - -/* - STAC 9200-32 - 102801CD (Dell Inspiron E1705/9400) -*/ -static const struct hda_pintbl dell9200_m27_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x01441340 }, - { 0x0d, 0x0421121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x90170310 }, - { 0x10, 0x04a11020 }, - { 0x11, 0x90170310 }, - { 0x12, 0x40f003fc }, - {} -}; - -static const struct hda_pintbl oqo9200_pin_configs[] = { - { 0x08, 0x40c000f0 }, - { 0x09, 0x404000f1 }, - { 0x0d, 0x0221121f }, - { 0x0e, 0x02211210 }, - { 0x0f, 0x90170111 }, - { 0x10, 0x90a70120 }, - { 0x11, 0x400000f2 }, - { 0x12, 0x400000f3 }, - {} -}; - - -static void stac9200_fixup_panasonic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gpio_mask = spec->gpio_dir = 0x09; - spec->gpio_data = 0x00; - /* CF-74 has no headphone detection, and the driver should *NOT* - * do detection and HP/speaker toggle because the hardware does it. - */ - spec->gen.suppress_auto_mute = 1; - } -} - - -static const struct hda_fixup stac9200_fixups[] = { - [STAC_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref9200_pin_configs, - }, - [STAC_9200_OQO] = { - .type = HDA_FIXUP_PINS, - .v.pins = oqo9200_pin_configs, - .chained = true, - .chain_id = STAC_9200_EAPD_INIT, - }, - [STAC_9200_DELL_D21] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_d21_pin_configs, - }, - [STAC_9200_DELL_D22] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_d22_pin_configs, - }, - [STAC_9200_DELL_D23] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_d23_pin_configs, - }, - [STAC_9200_DELL_M21] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m21_pin_configs, - }, - [STAC_9200_DELL_M22] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m22_pin_configs, - }, - [STAC_9200_DELL_M23] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m23_pin_configs, - }, - [STAC_9200_DELL_M24] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m24_pin_configs, - }, - [STAC_9200_DELL_M25] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m25_pin_configs, - }, - [STAC_9200_DELL_M26] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m26_pin_configs, - }, - [STAC_9200_DELL_M27] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m27_pin_configs, - }, - [STAC_9200_M4] = { - .type = HDA_FIXUP_PINS, - .v.pins = gateway9200_m4_pin_configs, - .chained = true, - .chain_id = STAC_9200_EAPD_INIT, - }, - [STAC_9200_M4_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = gateway9200_m4_2_pin_configs, - .chained = true, - .chain_id = STAC_9200_EAPD_INIT, - }, - [STAC_9200_PANASONIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9200_fixup_panasonic, - }, - [STAC_9200_EAPD_INIT] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {} - }, - }, -}; - -static const struct hda_model_fixup stac9200_models[] = { - { .id = STAC_REF, .name = "ref" }, - { .id = STAC_9200_OQO, .name = "oqo" }, - { .id = STAC_9200_DELL_D21, .name = "dell-d21" }, - { .id = STAC_9200_DELL_D22, .name = "dell-d22" }, - { .id = STAC_9200_DELL_D23, .name = "dell-d23" }, - { .id = STAC_9200_DELL_M21, .name = "dell-m21" }, - { .id = STAC_9200_DELL_M22, .name = "dell-m22" }, - { .id = STAC_9200_DELL_M23, .name = "dell-m23" }, - { .id = STAC_9200_DELL_M24, .name = "dell-m24" }, - { .id = STAC_9200_DELL_M25, .name = "dell-m25" }, - { .id = STAC_9200_DELL_M26, .name = "dell-m26" }, - { .id = STAC_9200_DELL_M27, .name = "dell-m27" }, - { .id = STAC_9200_M4, .name = "gateway-m4" }, - { .id = STAC_9200_M4_2, .name = "gateway-m4-2" }, - { .id = STAC_9200_PANASONIC, .name = "panasonic" }, - {} -}; - -static const struct snd_pci_quirk stac9200_fixup_tbl[] = { +static const struct snd_pci_quirk stac9200_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), @@ -1523,159 +1441,70 @@ static const struct snd_pci_quirk stac9200_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref925x_pin_configs[] = { - { 0x07, 0x40c003f0 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x01813022 }, - { 0x0b, 0x02a19021 }, - { 0x0c, 0x90a70320 }, - { 0x0d, 0x02214210 }, - { 0x10, 0x01019020 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int ref925x_pin_configs[8] = { + 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, + 0x90a70320, 0x02214210, 0x01019020, 0x9033032e, }; -static const struct hda_pintbl stac925xM1_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM1_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM1_2_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM1_2_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM2_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM2_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM2_2_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM2_2_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM3_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x503303f3 }, - {} +static const unsigned int stac925xM3_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x503303f3, }; -static const struct hda_pintbl stac925xM5_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM5_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM6_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x90330320 }, - {} +static const unsigned int stac925xM6_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x90330320, }; -static const struct hda_fixup stac925x_fixups[] = { - [STAC_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref925x_pin_configs, - }, - [STAC_M1] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM1_pin_configs, - }, - [STAC_M1_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM1_2_pin_configs, - }, - [STAC_M2] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM2_pin_configs, - }, - [STAC_M2_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM2_2_pin_configs, - }, - [STAC_M3] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM3_pin_configs, - }, - [STAC_M5] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM5_pin_configs, - }, - [STAC_M6] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM6_pin_configs, - }, +static const unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = { + [STAC_REF] = ref925x_pin_configs, + [STAC_M1] = stac925xM1_pin_configs, + [STAC_M1_2] = stac925xM1_2_pin_configs, + [STAC_M2] = stac925xM2_pin_configs, + [STAC_M2_2] = stac925xM2_2_pin_configs, + [STAC_M3] = stac925xM3_pin_configs, + [STAC_M5] = stac925xM5_pin_configs, + [STAC_M6] = stac925xM6_pin_configs, }; -static const struct hda_model_fixup stac925x_models[] = { - { .id = STAC_REF, .name = "ref" }, - { .id = STAC_M1, .name = "m1" }, - { .id = STAC_M1_2, .name = "m1-2" }, - { .id = STAC_M2, .name = "m2" }, - { .id = STAC_M2_2, .name = "m2-2" }, - { .id = STAC_M3, .name = "m3" }, - { .id = STAC_M5, .name = "m5" }, - { .id = STAC_M6, .name = "m6" }, - {} +static const char * const stac925x_models[STAC_925x_MODELS] = { + [STAC_925x_AUTO] = "auto", + [STAC_REF] = "ref", + [STAC_M1] = "m1", + [STAC_M1_2] = "m1-2", + [STAC_M2] = "m2", + [STAC_M2_2] = "m2-2", + [STAC_M3] = "m3", + [STAC_M5] = "m5", + [STAC_M6] = "m6", }; -static const struct snd_pci_quirk stac925x_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), - - /* Default table for unknown ID */ - SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2), - - /* gateway machines are checked via codec ssid */ +static const struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = { SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2), SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5), SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1), @@ -1689,202 +1518,67 @@ static const struct snd_pci_quirk stac925x_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref92hd73xx_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x02a19040 }, - { 0x0c, 0x01a19020 }, - { 0x0d, 0x02214030 }, - { 0x0e, 0x0181302e }, - { 0x0f, 0x01014010 }, - { 0x10, 0x01014020 }, - { 0x11, 0x01014030 }, - { 0x12, 0x02319040 }, - { 0x13, 0x90a000f0 }, - { 0x14, 0x90a000f0 }, - { 0x22, 0x01452050 }, - { 0x23, 0x01452050 }, - {} -}; +static const struct snd_pci_quirk stac925x_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), + SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), -static const struct hda_pintbl dell_m6_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x4f00000f }, - { 0x0c, 0x4f0000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x03a11020 }, - { 0x0f, 0x0321101f }, - { 0x10, 0x4f0000f0 }, - { 0x11, 0x4f0000f0 }, - { 0x12, 0x4f0000f0 }, - { 0x13, 0x90a60160 }, - { 0x14, 0x4f0000f0 }, - { 0x22, 0x4f0000f0 }, - { 0x23, 0x4f0000f0 }, - {} -}; + /* Default table for unknown ID */ + SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2), -static const struct hda_pintbl alienware_m17x_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x0321101f }, - { 0x0c, 0x03a11020 }, - { 0x0d, 0x03014020 }, - { 0x0e, 0x90170110 }, - { 0x0f, 0x4f0000f0 }, - { 0x10, 0x4f0000f0 }, - { 0x11, 0x4f0000f0 }, - { 0x12, 0x4f0000f0 }, - { 0x13, 0x90a60160 }, - { 0x14, 0x4f0000f0 }, - { 0x22, 0x4f0000f0 }, - { 0x23, 0x904601b0 }, - {} + {} /* terminator */ }; -static const struct hda_pintbl intel_dg45id_pin_configs[] = { - { 0x0a, 0x02214230 }, - { 0x0b, 0x02A19240 }, - { 0x0c, 0x01013214 }, - { 0x0d, 0x01014210 }, - { 0x0e, 0x01A19250 }, - { 0x0f, 0x01011212 }, - { 0x10, 0x01016211 }, - {} +static const unsigned int ref92hd73xx_pin_configs[13] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, + 0x0181302e, 0x01014010, 0x01014020, 0x01014030, + 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, + 0x01452050, }; -static void stac92hd73xx_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, ref92hd73xx_pin_configs); - spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0; -} - -static void stac92hd73xx_fixup_dell(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - snd_hda_apply_pincfgs(codec, dell_m6_pin_configs); - spec->eapd_switch = 0; -} - -static void stac92hd73xx_fixup_dell_eq(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_add_verbs(codec, dell_eq_core_init); - spec->volknob_init = 1; -} - -/* Analog Mics */ -static void stac92hd73xx_fixup_dell_m6_amic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); -} - -/* Digital Mics */ -static void stac92hd73xx_fixup_dell_m6_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); -} - -/* Both */ -static void stac92hd73xx_fixup_dell_m6_both(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); -} - -static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; +static const unsigned int dell_m6_pin_configs[13] = { + 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110, + 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, +}; - snd_hda_apply_pincfgs(codec, alienware_m17x_pin_configs); - spec->eapd_switch = 0; -} +static const unsigned int alienware_m17x_pin_configs[13] = { + 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020, + 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, + 0x904601b0, +}; -static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - codec->no_jack_detect = 1; -} +static const unsigned int intel_dg45id_pin_configs[13] = { + 0x02214230, 0x02A19240, 0x01013214, 0x01014210, + 0x01A19250, 0x01011212, 0x01016211 +}; -static const struct hda_fixup stac92hd73xx_fixups[] = { - [STAC_92HD73XX_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_ref, - }, - [STAC_DELL_M6_AMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_m6_amic, - }, - [STAC_DELL_M6_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_m6_dmic, - }, - [STAC_DELL_M6_BOTH] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_m6_both, - }, - [STAC_DELL_EQ] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_eq, - }, - [STAC_ALIENWARE_M17X] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_alienware_m17x, - }, - [STAC_92HD73XX_INTEL] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_dg45id_pin_configs, - }, - [STAC_92HD73XX_NO_JD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_no_jd, - } +static const unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, + [STAC_DELL_M6_AMIC] = dell_m6_pin_configs, + [STAC_DELL_M6_DMIC] = dell_m6_pin_configs, + [STAC_DELL_M6_BOTH] = dell_m6_pin_configs, + [STAC_DELL_EQ] = dell_m6_pin_configs, + [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs, + [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs, }; -static const struct hda_model_fixup stac92hd73xx_models[] = { - { .id = STAC_92HD73XX_NO_JD, .name = "no-jd" }, - { .id = STAC_92HD73XX_REF, .name = "ref" }, - { .id = STAC_92HD73XX_INTEL, .name = "intel" }, - { .id = STAC_DELL_M6_AMIC, .name = "dell-m6-amic" }, - { .id = STAC_DELL_M6_DMIC, .name = "dell-m6-dmic" }, - { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" }, - { .id = STAC_DELL_EQ, .name = "dell-eq" }, - { .id = STAC_ALIENWARE_M17X, .name = "alienware" }, - {} +static const char * const stac92hd73xx_models[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_AUTO] = "auto", + [STAC_92HD73XX_NO_JD] = "no-jd", + [STAC_92HD73XX_REF] = "ref", + [STAC_92HD73XX_INTEL] = "intel", + [STAC_DELL_M6_AMIC] = "dell-m6-amic", + [STAC_DELL_M6_DMIC] = "dell-m6-dmic", + [STAC_DELL_M6_BOTH] = "dell-m6", + [STAC_DELL_EQ] = "dell-eq", + [STAC_ALIENWARE_M17X] = "alienware", }; -static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { +static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD73XX_REF), @@ -1922,7 +1616,10 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { "Dell Studio XPS 1645", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413, "Dell Studio 1558", STAC_DELL_M6_DMIC), - /* codec SSID matching */ + {} /* terminator */ +}; + +static const struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1, "Alienware M17x", STAC_ALIENWARE_M17X), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a, @@ -1932,240 +1629,68 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref92hd83xxx_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x02211010 }, - { 0x0c, 0x02a19020 }, - { 0x0d, 0x02170130 }, - { 0x0e, 0x01014050 }, - { 0x0f, 0x01819040 }, - { 0x10, 0x01014020 }, - { 0x11, 0x90a3014e }, - { 0x1f, 0x01451160 }, - { 0x20, 0x98560170 }, - {} +static const unsigned int ref92hd83xxx_pin_configs[10] = { + 0x02214030, 0x02211010, 0x02a19020, 0x02170130, + 0x01014050, 0x01819040, 0x01014020, 0x90a3014e, + 0x01451160, 0x98560170, }; -static const struct hda_pintbl dell_s14_pin_configs[] = { - { 0x0a, 0x0221403f }, - { 0x0b, 0x0221101f }, - { 0x0c, 0x02a19020 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x40f000f0 }, - { 0x10, 0x40f000f0 }, - { 0x11, 0x90a60160 }, - { 0x1f, 0x40f000f0 }, - { 0x20, 0x40f000f0 }, - {} +static const unsigned int dell_s14_pin_configs[10] = { + 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110, + 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160, + 0x40f000f0, 0x40f000f0, }; -static const struct hda_pintbl dell_vostro_3500_pin_configs[] = { - { 0x0a, 0x02a11020 }, - { 0x0b, 0x0221101f }, - { 0x0c, 0x400000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x400000f1 }, - { 0x0f, 0x400000f2 }, - { 0x10, 0x400000f3 }, - { 0x11, 0x90a60160 }, - { 0x1f, 0x400000f4 }, - { 0x20, 0x400000f5 }, - {} +static const unsigned int dell_vostro_3500_pin_configs[10] = { + 0x02a11020, 0x0221101f, 0x400000f0, 0x90170110, + 0x400000f1, 0x400000f2, 0x400000f3, 0x90a60160, + 0x400000f4, 0x400000f5, }; -static const struct hda_pintbl hp_dv7_4000_pin_configs[] = { - { 0x0a, 0x03a12050 }, - { 0x0b, 0x0321201f }, - { 0x0c, 0x40f000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x40f000f0 }, - { 0x10, 0x90170110 }, - { 0x11, 0xd5a30140 }, - { 0x1f, 0x40f000f0 }, - { 0x20, 0x40f000f0 }, - {} +static const unsigned int hp_dv7_4000_pin_configs[10] = { + 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110, + 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140, + 0x40f000f0, 0x40f000f0, }; -static const struct hda_pintbl hp_zephyr_pin_configs[] = { - { 0x0a, 0x01813050 }, - { 0x0b, 0x0421201f }, - { 0x0c, 0x04a1205e }, - { 0x0d, 0x96130310 }, - { 0x0e, 0x96130310 }, - { 0x0f, 0x0101401f }, - { 0x10, 0x1111611f }, - { 0x11, 0xd5a30130 }, - {} +static const unsigned int hp_zephyr_pin_configs[10] = { + 0x01813050, 0x0421201f, 0x04a1205e, 0x96130310, + 0x96130310, 0x0101401f, 0x1111611f, 0xd5a30130, + 0, 0, }; -static const struct hda_pintbl hp_cNB11_intquad_pin_configs[] = { - { 0x0a, 0x40f000f0 }, - { 0x0b, 0x0221101f }, - { 0x0c, 0x02a11020 }, - { 0x0d, 0x92170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x92170110 }, - { 0x10, 0x40f000f0 }, - { 0x11, 0xd5a30130 }, - { 0x1f, 0x40f000f0 }, - { 0x20, 0x40f000f0 }, - {} +static const unsigned int hp_cNB11_intquad_pin_configs[10] = { + 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110, + 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130, + 0x40f000f0, 0x40f000f0, }; -static void stac92hd83xxx_fixup_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - if (hp_bnb2011_with_dock(codec)) { - snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); - snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); - } - - if (find_mute_led_cfg(codec, spec->default_polarity)) - snd_printd("mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); -} - -static void stac92hd83xxx_fixup_hp_zephyr(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, hp_zephyr_pin_configs); - snd_hda_add_verbs(codec, stac92hd83xxx_hp_zephyr_init); -} - -static void stac92hd83xxx_fixup_hp_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->default_polarity = 0; -} - -static void stac92hd83xxx_fixup_hp_inv_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->default_polarity = 1; -} - -static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ -} - -static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->headset_jack = 1; -} - -static const struct hda_fixup stac92hd83xxx_fixups[] = { - [STAC_92HD83XXX_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref92hd83xxx_pin_configs, - }, - [STAC_92HD83XXX_PWR_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref92hd83xxx_pin_configs, - }, - [STAC_DELL_S14] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_s14_pin_configs, - }, - [STAC_DELL_VOSTRO_3500] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_vostro_3500_pin_configs, - }, - [STAC_92HD83XXX_HP_cNB11_INTQUAD] = { - .type = HDA_FIXUP_PINS, - .v.pins = hp_cNB11_intquad_pin_configs, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp, - }, - [STAC_HP_DV7_4000] = { - .type = HDA_FIXUP_PINS, - .v.pins = hp_dv7_4000_pin_configs, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_HP_ZEPHYR] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_zephyr, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_led, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP_INV_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_inv_led, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP_MIC_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_mic_led, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HEADSET_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_headset_jack, - }, - [STAC_HP_ENVY_BASS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x0f, 0x90170111 }, - {} - }, - }, +static const unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { + [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, + [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs, + [STAC_DELL_S14] = dell_s14_pin_configs, + [STAC_DELL_VOSTRO_3500] = dell_vostro_3500_pin_configs, + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs, + [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs, + [STAC_HP_ZEPHYR] = hp_zephyr_pin_configs, }; -static const struct hda_model_fixup stac92hd83xxx_models[] = { - { .id = STAC_92HD83XXX_REF, .name = "ref" }, - { .id = STAC_92HD83XXX_PWR_REF, .name = "mic-ref" }, - { .id = STAC_DELL_S14, .name = "dell-s14" }, - { .id = STAC_DELL_VOSTRO_3500, .name = "dell-vostro-3500" }, - { .id = STAC_92HD83XXX_HP_cNB11_INTQUAD, .name = "hp_cNB11_intquad" }, - { .id = STAC_HP_DV7_4000, .name = "hp-dv7-4000" }, - { .id = STAC_HP_ZEPHYR, .name = "hp-zephyr" }, - { .id = STAC_92HD83XXX_HP_LED, .name = "hp-led" }, - { .id = STAC_92HD83XXX_HP_INV_LED, .name = "hp-inv-led" }, - { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" }, - { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" }, - { .id = STAC_HP_ENVY_BASS, .name = "hp-envy-bass" }, - {} +static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { + [STAC_92HD83XXX_AUTO] = "auto", + [STAC_92HD83XXX_REF] = "ref", + [STAC_92HD83XXX_PWR_REF] = "mic-ref", + [STAC_DELL_S14] = "dell-s14", + [STAC_DELL_VOSTRO_3500] = "dell-vostro-3500", + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad", + [STAC_HP_DV7_4000] = "hp-dv7-4000", + [STAC_HP_ZEPHYR] = "hp-zephyr", + [STAC_92HD83XXX_HP_LED] = "hp-led", + [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led", + [STAC_92HD83XXX_HP_MIC_LED] = "hp-mic-led", + [STAC_92HD83XXX_HEADSET_JACK] = "headset-jack", }; -static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { +static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD83XXX_REF), @@ -2205,8 +1730,6 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B, "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1888, - "HP Envy Spectre", STAC_HP_ENVY_BASS), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df, "HP Folio", STAC_92HD83XXX_HP_MIC_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, @@ -2243,360 +1766,130 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { "HP Mini", STAC_92HD83XXX_HP_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x144E, "HP Pavilion dv5", STAC_92HD83XXX_HP_INV_LED), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x148a, - "HP Mini", STAC_92HD83XXX_HP_LED), - SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP), {} /* terminator */ }; -/* HP dv7 bass switch - GPIO5 */ -#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info -static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20); - return 0; -} - -static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct sigmatel_spec *spec = codec->spec; - unsigned int gpio_data; - - gpio_data = (spec->gpio_data & ~0x20) | - (ucontrol->value.integer.value[0] ? 0x20 : 0); - if (gpio_data == spec->gpio_data) - return 0; - spec->gpio_data = gpio_data; - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); - return 1; -} - -static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .info = stac_hp_bass_gpio_info, - .get = stac_hp_bass_gpio_get, - .put = stac_hp_bass_gpio_put, +static const struct snd_pci_quirk stac92hd83xxx_codec_id_cfg_tbl[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561, + "HP", STAC_HP_ZEPHYR), + {} /* terminator */ }; -static int stac_add_hp_bass_switch(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (!snd_hda_gen_add_kctl(&spec->gen, "Bass Speaker Playback Switch", - &stac_hp_bass_sw_ctrl)) - return -ENOMEM; +static const unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, + 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0, + 0x90a000f0, 0x01452050, 0x01452050, 0x00000000, + 0x00000000 +}; - spec->gpio_mask |= 0x20; - spec->gpio_dir |= 0x20; - spec->gpio_data |= 0x20; - return 0; -} +static const unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, + 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000, + 0x00000000 +}; -static const struct hda_pintbl ref92hd71bxx_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x02a19040 }, - { 0x0c, 0x01a19020 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x0181302e }, - { 0x0f, 0x01014010 }, - { 0x14, 0x01019020 }, - { 0x18, 0x90a000f0 }, - { 0x19, 0x90a000f0 }, - { 0x1e, 0x01452050 }, - { 0x1f, 0x01452050 }, - {} +static const unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0, + 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, + 0x00000000 }; -static const struct hda_pintbl dell_m4_1_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11221 }, - { 0x0c, 0x40f000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x23a1902e }, - { 0x0f, 0x23014250 }, - { 0x14, 0x40f000f0 }, - { 0x18, 0x90a000f0 }, - { 0x19, 0x40f000f0 }, - { 0x1e, 0x4f0000f0 }, - { 0x1f, 0x4f0000f0 }, - {} +static const unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, + 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0, + 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, + 0x00000000 }; -static const struct hda_pintbl dell_m4_2_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11221 }, - { 0x0c, 0x90a70330 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x23a1902e }, - { 0x0f, 0x23014250 }, - { 0x14, 0x40f000f0 }, - { 0x18, 0x40f000f0 }, - { 0x19, 0x40f000f0 }, - { 0x1e, 0x044413b0 }, - { 0x1f, 0x044413b0 }, - {} +static const 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, + [STAC_DELL_M4_3] = dell_m4_3_pin_configs, + [STAC_HP_M4] = NULL, + [STAC_HP_DV4] = NULL, + [STAC_HP_DV5] = NULL, + [STAC_HP_HDX] = NULL, + [STAC_HP_DV4_1222NR] = NULL, }; -static const struct hda_pintbl dell_m4_3_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11221 }, - { 0x0c, 0x90a70330 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x40f000f0 }, - { 0x14, 0x40f000f0 }, - { 0x18, 0x90a000f0 }, - { 0x19, 0x40f000f0 }, - { 0x1e, 0x044413b0 }, - { 0x1f, 0x044413b0 }, - {} +static const char * const stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_AUTO] = "auto", + [STAC_92HD71BXX_REF] = "ref", + [STAC_DELL_M4_1] = "dell-m4-1", + [STAC_DELL_M4_2] = "dell-m4-2", + [STAC_DELL_M4_3] = "dell-m4-3", + [STAC_HP_M4] = "hp-m4", + [STAC_HP_DV4] = "hp-dv4", + [STAC_HP_DV5] = "hp-dv5", + [STAC_HP_HDX] = "hp-hdx", + [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr", }; -static void stac92hd71bxx_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; +static const 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_DFI, 0x3101, + "DFI LanParty", STAC_92HD71BXX_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb, + "HP dv4-1222nr", STAC_HP_DV4_1222NR), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720, + "HP", STAC_HP_DV5), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080, + "HP", STAC_HP_DV5), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0, + "HP dv4-7", STAC_HP_DV4), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600, + "HP dv4-7", STAC_HP_DV5), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610, + "HP HDX", STAC_HP_HDX), /* HDX18 */ + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a, + "HP mini 1000", STAC_HP_M4), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b, + "HP HDX", STAC_HP_HDX), /* HDX16 */ + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620, + "HP dv6", STAC_HP_DV5), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061, + "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */ + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e, + "HP DV6", STAC_HP_DV5), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, + "HP", STAC_HP_DV5), + 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), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa, + "unknown Dell", STAC_DELL_M4_3), + {} /* terminator */ +}; - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, ref92hd71bxx_pin_configs); - spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0; -} - -static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - struct hda_jack_tbl *jack; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - /* Enable VREF power saving on GPIO1 detect */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - snd_hda_jack_detect_enable_callback(codec, codec->afg, - STAC_VREF_EVENT, - stac_vref_event); - jack = snd_hda_jack_tbl_get(codec, codec->afg); - if (jack) - jack->private_data = 0x02; - - spec->gpio_mask |= 0x02; - - /* enable internal microphone */ - snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040); -} - -static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->gpio_led = 0x01; -} - -static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - unsigned int cap; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); - break; - - case HDA_FIXUP_ACT_PROBE: - /* enable bass on HP dv7 */ - cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); - cap &= AC_GPIO_IO_COUNT; - if (cap >= 6) - stac_add_hp_bass_switch(codec); - break; - } -} - -static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->gpio_led = 0x08; -} - - -static void stac92hd71bxx_fixup_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - if (hp_blike_system(codec->subsystem_id)) { - unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); - if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || - get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || - get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) { - /* It was changed in the BIOS to just satisfy MS DTM. - * Lets turn it back into slaved HP - */ - pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) - | (AC_JACK_HP_OUT << - AC_DEFCFG_DEVICE_SHIFT); - pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC - | AC_DEFCFG_SEQUENCE))) - | 0x1f; - snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg); - } - } - - if (find_mute_led_cfg(codec, 1)) - snd_printd("mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); - -} - -static const struct hda_fixup stac92hd71bxx_fixups[] = { - [STAC_92HD71BXX_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_ref, - }, - [STAC_DELL_M4_1] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_m4_1_pin_configs, - }, - [STAC_DELL_M4_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_m4_2_pin_configs, - }, - [STAC_DELL_M4_3] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_m4_3_pin_configs, - }, - [STAC_HP_M4] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_m4, - .chained = true, - .chain_id = STAC_92HD71BXX_HP, - }, - [STAC_HP_DV4] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_dv4, - .chained = true, - .chain_id = STAC_HP_DV5, - }, - [STAC_HP_DV5] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_dv5, - .chained = true, - .chain_id = STAC_92HD71BXX_HP, - }, - [STAC_HP_HDX] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_hdx, - .chained = true, - .chain_id = STAC_92HD71BXX_HP, - }, - [STAC_92HD71BXX_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp, - }, -}; - -static const struct hda_model_fixup stac92hd71bxx_models[] = { - { .id = STAC_92HD71BXX_REF, .name = "ref" }, - { .id = STAC_DELL_M4_1, .name = "dell-m4-1" }, - { .id = STAC_DELL_M4_2, .name = "dell-m4-2" }, - { .id = STAC_DELL_M4_3, .name = "dell-m4-3" }, - { .id = STAC_HP_M4, .name = "hp-m4" }, - { .id = STAC_HP_DV4, .name = "hp-dv4" }, - { .id = STAC_HP_DV5, .name = "hp-dv5" }, - { .id = STAC_HP_HDX, .name = "hp-hdx" }, - { .id = STAC_HP_DV4, .name = "hp-dv4-1222nr" }, - {} -}; - -static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_92HD71BXX_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_92HD71BXX_REF), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720, - "HP", STAC_HP_DV5), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080, - "HP", STAC_HP_DV5), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0, - "HP dv4-7", STAC_HP_DV4), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600, - "HP dv4-7", STAC_HP_DV5), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610, - "HP HDX", STAC_HP_HDX), /* HDX18 */ - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a, - "HP mini 1000", STAC_HP_M4), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b, - "HP HDX", STAC_HP_HDX), /* HDX16 */ - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620, - "HP dv6", STAC_HP_DV5), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061, - "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */ - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e, - "HP DV6", STAC_HP_DV5), - SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, - "HP", STAC_HP_DV5), - SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD71BXX_HP), - 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), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa, - "unknown Dell", STAC_DELL_M4_3), - {} /* terminator */ -}; - -static const struct hda_pintbl ref922x_pin_configs[] = { - { 0x0a, 0x01014010 }, - { 0x0b, 0x01016011 }, - { 0x0c, 0x01012012 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01813122 }, - { 0x0f, 0x01011014 }, - { 0x10, 0x01441030 }, - { 0x11, 0x01c41030 }, - { 0x15, 0x40000100 }, - { 0x1b, 0x40000100 }, - {} -}; +static const unsigned int ref922x_pin_configs[10] = { + 0x01014010, 0x01016011, 0x01012012, 0x0221401f, + 0x01813122, 0x01011014, 0x01441030, 0x01c41030, + 0x40000100, 0x40000100, +}; /* STAC 922X pin configs for @@ -2606,18 +1899,10 @@ static const struct hda_pintbl ref922x_pin_configs[] = { 102801D1 102801D2 */ -static const struct hda_pintbl dell_922x_d81_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x01a19021 }, - { 0x0c, 0x01111012 }, - { 0x0d, 0x01114010 }, - { 0x0e, 0x02a19020 }, - { 0x0f, 0x01117011 }, - { 0x10, 0x400001f0 }, - { 0x11, 0x400001f1 }, - { 0x15, 0x01813122 }, - { 0x1b, 0x400001f2 }, - {} +static const unsigned int dell_922x_d81_pin_configs[10] = { + 0x02214030, 0x01a19021, 0x01111012, 0x01114010, + 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1, + 0x01813122, 0x400001f2, }; /* @@ -2625,311 +1910,130 @@ static const struct hda_pintbl dell_922x_d81_pin_configs[] = { 102801AC 102801D0 */ -static const struct hda_pintbl dell_922x_d82_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x01a19021 }, - { 0x0c, 0x01111012 }, - { 0x0d, 0x01114010 }, - { 0x0e, 0x02a19020 }, - { 0x0f, 0x01117011 }, - { 0x10, 0x01451140 }, - { 0x11, 0x400001f0 }, - { 0x15, 0x01813122 }, - { 0x1b, 0x400001f1 }, - {} +static const unsigned int dell_922x_d82_pin_configs[10] = { + 0x02214030, 0x01a19021, 0x01111012, 0x01114010, + 0x02a19020, 0x01117011, 0x01451140, 0x400001f0, + 0x01813122, 0x400001f1, }; /* STAC 922X pin configs for 102801BF */ -static const struct hda_pintbl dell_922x_m81_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x01112024 }, - { 0x0c, 0x01111222 }, - { 0x0d, 0x91174220 }, - { 0x0e, 0x03a11050 }, - { 0x0f, 0x01116221 }, - { 0x10, 0x90a70330 }, - { 0x11, 0x01452340 }, - { 0x15, 0x40C003f1 }, - { 0x1b, 0x405003f0 }, - {} +static const unsigned int dell_922x_m81_pin_configs[10] = { + 0x0321101f, 0x01112024, 0x01111222, 0x91174220, + 0x03a11050, 0x01116221, 0x90a70330, 0x01452340, + 0x40C003f1, 0x405003f0, }; /* STAC 9221 A1 pin configs for 102801D7 (Dell XPS M1210) */ -static const struct hda_pintbl dell_922x_m82_pin_configs[] = { - { 0x0a, 0x02211211 }, - { 0x0b, 0x408103ff }, - { 0x0c, 0x02a1123e }, - { 0x0d, 0x90100310 }, - { 0x0e, 0x408003f1 }, - { 0x0f, 0x0221121f }, - { 0x10, 0x03451340 }, - { 0x11, 0x40c003f2 }, - { 0x15, 0x508003f3 }, - { 0x1b, 0x405003f4 }, - {} -}; - -static const struct hda_pintbl d945gtp3_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x01a19022 }, - { 0x0c, 0x01813021 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x40000100 }, - { 0x0f, 0x40000100 }, - { 0x10, 0x40000100 }, - { 0x11, 0x40000100 }, - { 0x15, 0x02a19120 }, - { 0x1b, 0x40000100 }, - {} +static const unsigned int dell_922x_m82_pin_configs[10] = { + 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, + 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, + 0x508003f3, 0x405003f4, }; -static const struct hda_pintbl d945gtp5_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x01011012 }, - { 0x0c, 0x01813024 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19021 }, - { 0x0f, 0x01016011 }, - { 0x10, 0x01452130 }, - { 0x11, 0x40000100 }, - { 0x15, 0x02a19320 }, - { 0x1b, 0x40000100 }, - {} +static const unsigned int d945gtp3_pin_configs[10] = { + 0x0221401f, 0x01a19022, 0x01813021, 0x01014010, + 0x40000100, 0x40000100, 0x40000100, 0x40000100, + 0x02a19120, 0x40000100, }; -static const struct hda_pintbl intel_mac_v1_pin_configs[] = { - { 0x0a, 0x0121e21f }, - { 0x0b, 0x400000ff }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x400000fd }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0181e020 }, - { 0x10, 0x1145e030 }, - { 0x11, 0x11c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int d945gtp5_pin_configs[10] = { + 0x0221401f, 0x01011012, 0x01813024, 0x01014010, + 0x01a19021, 0x01016011, 0x01452130, 0x40000100, + 0x02a19320, 0x40000100, }; -static const struct hda_pintbl intel_mac_v2_pin_configs[] = { - { 0x0a, 0x0121e21f }, - { 0x0b, 0x90a7012e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x400000fd }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0181e020 }, - { 0x10, 0x1145e230 }, - { 0x11, 0x500000fa }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int intel_mac_v1_pin_configs[10] = { + 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd, + 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240, + 0x400000fc, 0x400000fb, }; -static const struct hda_pintbl intel_mac_v3_pin_configs[] = { - { 0x0a, 0x0121e21f }, - { 0x0b, 0x90a7012e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x400000fd }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0181e020 }, - { 0x10, 0x1145e230 }, - { 0x11, 0x11c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int intel_mac_v2_pin_configs[10] = { + 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, + 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa, + 0x400000fc, 0x400000fb, }; -static const struct hda_pintbl intel_mac_v4_pin_configs[] = { - { 0x0a, 0x0321e21f }, - { 0x0b, 0x03a1e02e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x9017e11f }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0381e020 }, - { 0x10, 0x1345e230 }, - { 0x11, 0x13c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int intel_mac_v3_pin_configs[10] = { + 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, + 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240, + 0x400000fc, 0x400000fb, }; -static const struct hda_pintbl intel_mac_v5_pin_configs[] = { - { 0x0a, 0x0321e21f }, - { 0x0b, 0x03a1e02e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x9017e11f }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0381e020 }, - { 0x10, 0x1345e230 }, - { 0x11, 0x13c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int intel_mac_v4_pin_configs[10] = { + 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, + 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, + 0x400000fc, 0x400000fb, }; -static const struct hda_pintbl ecs202_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x01a19020 }, - { 0x0d, 0x01114010 }, - { 0x0e, 0x408000f0 }, - { 0x0f, 0x01813022 }, - { 0x10, 0x074510a0 }, - { 0x11, 0x40c400f1 }, - { 0x15, 0x9037012e }, - { 0x1b, 0x40e000f2 }, - {} +static const unsigned int intel_mac_v5_pin_configs[10] = { + 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, + 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, + 0x400000fc, 0x400000fb, }; -/* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */ -static const struct snd_pci_quirk stac922x_intel_mac_fixup_tbl[] = { - SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1), - SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2), - SND_PCI_QUIRK(0x106b, 0x0700, "Mac", STAC_INTEL_MAC_V2), - SND_PCI_QUIRK(0x106b, 0x0e00, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x0f00, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1600, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1700, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x0200, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1e00, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1a00, "Mac", STAC_INTEL_MAC_V4), - SND_PCI_QUIRK(0x106b, 0x0a00, "Mac", STAC_INTEL_MAC_V5), - SND_PCI_QUIRK(0x106b, 0x2200, "Mac", STAC_INTEL_MAC_V5), - {} +static const unsigned int ecs202_pin_configs[10] = { + 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010, + 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1, + 0x9037012e, 0x40e000f2, }; -static const struct hda_fixup stac922x_fixups[]; - -/* remap the fixup from codec SSID and apply it */ -static void stac922x_fixup_intel_mac_auto(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - snd_hda_pick_fixup(codec, NULL, stac922x_intel_mac_fixup_tbl, - stac922x_fixups); - if (codec->fixup_id != STAC_INTEL_MAC_AUTO) - snd_hda_apply_fixup(codec, action); -} - -static void stac922x_fixup_intel_mac_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gpio_mask = spec->gpio_dir = 0x03; - spec->gpio_data = 0x03; - } -} - -static const struct hda_fixup stac922x_fixups[] = { - [STAC_D945_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref922x_pin_configs, - }, - [STAC_D945GTP3] = { - .type = HDA_FIXUP_PINS, - .v.pins = d945gtp3_pin_configs, - }, - [STAC_D945GTP5] = { - .type = HDA_FIXUP_PINS, - .v.pins = d945gtp5_pin_configs, - }, - [STAC_INTEL_MAC_AUTO] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac922x_fixup_intel_mac_auto, - }, - [STAC_INTEL_MAC_V1] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v1_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V2] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v2_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V3] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v3_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V4] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v4_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V5] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v5_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_922X_INTEL_MAC_GPIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac922x_fixup_intel_mac_gpio, - }, - [STAC_ECS_202] = { - .type = HDA_FIXUP_PINS, - .v.pins = ecs202_pin_configs, - }, - [STAC_922X_DELL_D81] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_d81_pin_configs, - }, - [STAC_922X_DELL_D82] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_d82_pin_configs, - }, - [STAC_922X_DELL_M81] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_m81_pin_configs, - }, - [STAC_922X_DELL_M82] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_m82_pin_configs, - }, +static const unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { + [STAC_D945_REF] = ref922x_pin_configs, + [STAC_D945GTP3] = d945gtp3_pin_configs, + [STAC_D945GTP5] = d945gtp5_pin_configs, + [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs, + [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs, + [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs, + [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs, + [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs, + [STAC_INTEL_MAC_AUTO] = intel_mac_v3_pin_configs, + /* for backward compatibility */ + [STAC_MACMINI] = intel_mac_v3_pin_configs, + [STAC_MACBOOK] = intel_mac_v5_pin_configs, + [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs, + [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs, + [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs, + [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs, + [STAC_ECS_202] = ecs202_pin_configs, + [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs, + [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs, + [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs, + [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs, }; -static const struct hda_model_fixup stac922x_models[] = { - { .id = STAC_D945_REF, .name = "ref" }, - { .id = STAC_D945GTP5, .name = "5stack" }, - { .id = STAC_D945GTP3, .name = "3stack" }, - { .id = STAC_INTEL_MAC_V1, .name = "intel-mac-v1" }, - { .id = STAC_INTEL_MAC_V2, .name = "intel-mac-v2" }, - { .id = STAC_INTEL_MAC_V3, .name = "intel-mac-v3" }, - { .id = STAC_INTEL_MAC_V4, .name = "intel-mac-v4" }, - { .id = STAC_INTEL_MAC_V5, .name = "intel-mac-v5" }, - { .id = STAC_INTEL_MAC_AUTO, .name = "intel-mac-auto" }, - { .id = STAC_ECS_202, .name = "ecs202" }, - { .id = STAC_922X_DELL_D81, .name = "dell-d81" }, - { .id = STAC_922X_DELL_D82, .name = "dell-d82" }, - { .id = STAC_922X_DELL_M81, .name = "dell-m81" }, - { .id = STAC_922X_DELL_M82, .name = "dell-m82" }, +static const char * const stac922x_models[STAC_922X_MODELS] = { + [STAC_922X_AUTO] = "auto", + [STAC_D945_REF] = "ref", + [STAC_D945GTP5] = "5stack", + [STAC_D945GTP3] = "3stack", + [STAC_INTEL_MAC_V1] = "intel-mac-v1", + [STAC_INTEL_MAC_V2] = "intel-mac-v2", + [STAC_INTEL_MAC_V3] = "intel-mac-v3", + [STAC_INTEL_MAC_V4] = "intel-mac-v4", + [STAC_INTEL_MAC_V5] = "intel-mac-v5", + [STAC_INTEL_MAC_AUTO] = "intel-mac-auto", /* for backward compatibility */ - { .id = STAC_INTEL_MAC_V3, .name = "macmini" }, - { .id = STAC_INTEL_MAC_V5, .name = "macbook" }, - { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro-v1" }, - { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro" }, - { .id = STAC_INTEL_MAC_V2, .name = "imac-intel" }, - { .id = STAC_INTEL_MAC_V3, .name = "imac-intel-20" }, - {} + [STAC_MACMINI] = "macmini", + [STAC_MACBOOK] = "macbook", + [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1", + [STAC_MACBOOK_PRO_V2] = "macbook-pro", + [STAC_IMAC_INTEL] = "imac-intel", + [STAC_IMAC_INTEL_20] = "imac-intel-20", + [STAC_ECS_202] = "ecs202", + [STAC_922X_DELL_D81] = "dell-d81", + [STAC_922X_DELL_D82] = "dell-d82", + [STAC_922X_DELL_M81] = "dell-m81", + [STAC_922X_DELL_M82] = "dell-m82", }; -static const struct snd_pci_quirk stac922x_fixup_tbl[] = { +static const struct snd_pci_quirk stac922x_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D945_REF), @@ -2992,10 +2096,9 @@ static const struct snd_pci_quirk stac922x_fixup_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204, "Intel D945", STAC_D945_REF), /* other systems */ - /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */ - SND_PCI_QUIRK(0x8384, 0x7680, "Mac", STAC_INTEL_MAC_AUTO), - + SND_PCI_QUIRK(0x8384, 0x7680, + "Mac", STAC_INTEL_MAC_AUTO), /* Dell systems */ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7, "unknown Dell", STAC_922X_DELL_D81), @@ -3021,229 +2124,65 @@ static const struct snd_pci_quirk stac922x_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref927x_pin_configs[] = { - { 0x0a, 0x02214020 }, - { 0x0b, 0x02a19080 }, - { 0x0c, 0x0181304e }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19040 }, - { 0x0f, 0x01011012 }, - { 0x10, 0x01016011 }, - { 0x11, 0x0101201f }, - { 0x12, 0x183301f0 }, - { 0x13, 0x18a001f0 }, - { 0x14, 0x18a001f0 }, - { 0x21, 0x01442070 }, - { 0x22, 0x01c42190 }, - { 0x23, 0x40000100 }, - {} +static const unsigned int ref927x_pin_configs[14] = { + 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, + 0x01a19040, 0x01011012, 0x01016011, 0x0101201f, + 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070, + 0x01c42190, 0x40000100, }; -static const struct hda_pintbl d965_3st_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x02a19120 }, - { 0x0c, 0x40000100 }, - { 0x0d, 0x01014011 }, - { 0x0e, 0x01a19021 }, - { 0x0f, 0x01813024 }, - { 0x10, 0x40000100 }, - { 0x11, 0x40000100 }, - { 0x12, 0x40000100 }, - { 0x13, 0x40000100 }, - { 0x14, 0x40000100 }, - { 0x21, 0x40000100 }, - { 0x22, 0x40000100 }, - { 0x23, 0x40000100 }, - {} +static const unsigned int d965_3st_pin_configs[14] = { + 0x0221401f, 0x02a19120, 0x40000100, 0x01014011, + 0x01a19021, 0x01813024, 0x40000100, 0x40000100, + 0x40000100, 0x40000100, 0x40000100, 0x40000100, + 0x40000100, 0x40000100 }; -static const struct hda_pintbl d965_5st_pin_configs[] = { - { 0x0a, 0x02214020 }, - { 0x0b, 0x02a19080 }, - { 0x0c, 0x0181304e }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19040 }, - { 0x0f, 0x01011012 }, - { 0x10, 0x01016011 }, - { 0x11, 0x40000100 }, - { 0x12, 0x40000100 }, - { 0x13, 0x40000100 }, - { 0x14, 0x40000100 }, - { 0x21, 0x01442070 }, - { 0x22, 0x40000100 }, - { 0x23, 0x40000100 }, - {} +static const unsigned int d965_5st_pin_configs[14] = { + 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, + 0x01a19040, 0x01011012, 0x01016011, 0x40000100, + 0x40000100, 0x40000100, 0x40000100, 0x01442070, + 0x40000100, 0x40000100 }; -static const struct hda_pintbl d965_5st_no_fp_pin_configs[] = { - { 0x0a, 0x40000100 }, - { 0x0b, 0x40000100 }, - { 0x0c, 0x0181304e }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19040 }, - { 0x0f, 0x01011012 }, - { 0x10, 0x01016011 }, - { 0x11, 0x40000100 }, - { 0x12, 0x40000100 }, - { 0x13, 0x40000100 }, - { 0x14, 0x40000100 }, - { 0x21, 0x01442070 }, - { 0x22, 0x40000100 }, - { 0x23, 0x40000100 }, - {} +static const unsigned int d965_5st_no_fp_pin_configs[14] = { + 0x40000100, 0x40000100, 0x0181304e, 0x01014010, + 0x01a19040, 0x01011012, 0x01016011, 0x40000100, + 0x40000100, 0x40000100, 0x40000100, 0x01442070, + 0x40000100, 0x40000100 }; -static const struct hda_pintbl dell_3st_pin_configs[] = { - { 0x0a, 0x02211230 }, - { 0x0b, 0x02a11220 }, - { 0x0c, 0x01a19040 }, - { 0x0d, 0x01114210 }, - { 0x0e, 0x01111212 }, - { 0x0f, 0x01116211 }, - { 0x10, 0x01813050 }, - { 0x11, 0x01112214 }, - { 0x12, 0x403003fa }, - { 0x13, 0x90a60040 }, - { 0x14, 0x90a60040 }, - { 0x21, 0x404003fb }, - { 0x22, 0x40c003fc }, - { 0x23, 0x40000100 }, - {} +static const unsigned int dell_3st_pin_configs[14] = { + 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, + 0x01111212, 0x01116211, 0x01813050, 0x01112214, + 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb, + 0x40c003fc, 0x40000100 }; -static void stac927x_fixup_ref_no_jd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* no jack detecion for ref-no-jd model */ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - codec->no_jack_detect = 1; -} - -static void stac927x_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_apply_pincfgs(codec, ref927x_pin_configs); - spec->eapd_mask = spec->gpio_mask = 0; - spec->gpio_dir = spec->gpio_data = 0; - } -} - -static void stac927x_fixup_dell_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - if (codec->subsystem_id != 0x1028022f) { - /* GPIO2 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x04; - spec->gpio_dir = spec->gpio_data = 0x04; - } - - snd_hda_add_verbs(codec, dell_3st_core_init); - spec->volknob_init = 1; -} - -static void stac927x_fixup_volknob(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_add_verbs(codec, stac927x_volknob_core_init); - spec->volknob_init = 1; - } -} - -static const struct hda_fixup stac927x_fixups[] = { - [STAC_D965_REF_NO_JD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_ref_no_jd, - .chained = true, - .chain_id = STAC_D965_REF, - }, - [STAC_D965_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_ref, - }, - [STAC_D965_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = d965_3st_pin_configs, - .chained = true, - .chain_id = STAC_D965_VERBS, - }, - [STAC_D965_5ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = d965_5st_pin_configs, - .chained = true, - .chain_id = STAC_D965_VERBS, - }, - [STAC_D965_VERBS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = d965_core_init, - }, - [STAC_D965_5ST_NO_FP] = { - .type = HDA_FIXUP_PINS, - .v.pins = d965_5st_no_fp_pin_configs, - }, - [STAC_DELL_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_3st_pin_configs, - .chained = true, - .chain_id = STAC_927X_DELL_DMIC, - }, - [STAC_DELL_BIOS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* configure the analog microphone on some laptops */ - { 0x0c, 0x90a79130 }, - /* correct the front output jack as a hp out */ - { 0x0f, 0x0227011f }, - /* correct the front input jack as a mic */ - { 0x0e, 0x02a79130 }, - {} - }, - .chained = true, - .chain_id = STAC_927X_DELL_DMIC, - }, - [STAC_DELL_BIOS_SPDIF] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* correct the device field to SPDIF out */ - { 0x21, 0x01442070 }, - {} - }, - .chained = true, - .chain_id = STAC_DELL_BIOS, - }, - [STAC_927X_DELL_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_dell_dmic, - }, - [STAC_927X_VOLKNOB] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_volknob, - }, +static const unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { + [STAC_D965_REF_NO_JD] = ref927x_pin_configs, + [STAC_D965_REF] = ref927x_pin_configs, + [STAC_D965_3ST] = d965_3st_pin_configs, + [STAC_D965_5ST] = d965_5st_pin_configs, + [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs, + [STAC_DELL_3ST] = dell_3st_pin_configs, + [STAC_DELL_BIOS] = NULL, + [STAC_927X_VOLKNOB] = NULL, }; -static const struct hda_model_fixup stac927x_models[] = { - { .id = STAC_D965_REF_NO_JD, .name = "ref-no-jd" }, - { .id = STAC_D965_REF, .name = "ref" }, - { .id = STAC_D965_3ST, .name = "3stack" }, - { .id = STAC_D965_5ST, .name = "5stack" }, - { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" }, - { .id = STAC_DELL_3ST, .name = "dell-3stack" }, - { .id = STAC_DELL_BIOS, .name = "dell-bios" }, - { .id = STAC_927X_VOLKNOB, .name = "volknob" }, - {} +static const char * const stac927x_models[STAC_927X_MODELS] = { + [STAC_927X_AUTO] = "auto", + [STAC_D965_REF_NO_JD] = "ref-no-jd", + [STAC_D965_REF] = "ref", + [STAC_D965_3ST] = "3stack", + [STAC_D965_5ST] = "5stack", + [STAC_D965_5ST_NO_FP] = "5stack-no-fp", + [STAC_DELL_3ST] = "dell-3stack", + [STAC_DELL_BIOS] = "dell-bios", + [STAC_927X_VOLKNOB] = "volknob", }; -static const struct snd_pci_quirk stac927x_fixup_tbl[] = { +static const struct snd_pci_quirk stac927x_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D965_REF), @@ -3265,12 +2204,12 @@ static const struct snd_pci_quirk stac927x_fixup_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS_SPDIF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS_SPDIF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS), /* 965 based 5 stack systems */ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300, "Intel D965", STAC_D965_5ST), @@ -3281,20 +2220,10 @@ static const struct snd_pci_quirk stac927x_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref9205_pin_configs[] = { - { 0x0a, 0x40000100 }, - { 0x0b, 0x40000100 }, - { 0x0c, 0x01016011 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01813122 }, - { 0x0f, 0x01a19021 }, - { 0x14, 0x01019020 }, - { 0x16, 0x40000100 }, - { 0x17, 0x90a000f0 }, - { 0x18, 0x90a000f0 }, - { 0x21, 0x01441030 }, - { 0x22, 0x01c41030 }, - {} +static const unsigned int ref9205_pin_configs[12] = { + 0x40000100, 0x40000100, 0x01016011, 0x01014010, + 0x01813122, 0x01a19021, 0x01019020, 0x40000100, + 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 }; /* @@ -3308,340 +2237,2795 @@ static const struct hda_pintbl ref9205_pin_configs[] = { 10280228 (Dell Vostro 1500) 10280229 (Dell Vostro 1700) */ -static const struct hda_pintbl dell_9205_m42_pin_configs[] = { - { 0x0a, 0x0321101F }, - { 0x0b, 0x03A11020 }, - { 0x0c, 0x400003FA }, - { 0x0d, 0x90170310 }, - { 0x0e, 0x400003FB }, - { 0x0f, 0x400003FC }, - { 0x14, 0x400003FD }, - { 0x16, 0x40F000F9 }, - { 0x17, 0x90A60330 }, - { 0x18, 0x400003FF }, - { 0x21, 0x0144131F }, - { 0x22, 0x40C003FE }, - {} +static const unsigned int dell_9205_m42_pin_configs[12] = { + 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310, + 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9, + 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE, +}; + +/* + STAC 9205 pin configs for + 102801F9 + 102801FA + 102801FE + 102801FF (Dell Precision M4300) + 10280206 + 10280200 + 10280201 +*/ +static const unsigned int dell_9205_m43_pin_configs[12] = { + 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310, + 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9, + 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8, +}; + +static const unsigned int dell_9205_m44_pin_configs[12] = { + 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310, + 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9, + 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe, +}; + +static const unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { + [STAC_9205_REF] = ref9205_pin_configs, + [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs, + [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs, + [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs, + [STAC_9205_EAPD] = NULL, +}; + +static const char * const stac9205_models[STAC_9205_MODELS] = { + [STAC_9205_AUTO] = "auto", + [STAC_9205_REF] = "ref", + [STAC_9205_DELL_M42] = "dell-m42", + [STAC_9205_DELL_M43] = "dell-m43", + [STAC_9205_DELL_M44] = "dell-m44", + [STAC_9205_EAPD] = "eapd", }; -/* - STAC 9205 pin configs for - 102801F9 - 102801FA - 102801FE - 102801FF (Dell Precision M4300) - 10280206 - 10280200 - 10280201 -*/ -static const struct hda_pintbl dell_9205_m43_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x03a11020 }, - { 0x0c, 0x90a70330 }, - { 0x0d, 0x90170310 }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x400000ff }, - { 0x14, 0x400000fd }, - { 0x16, 0x40f000f9 }, - { 0x17, 0x400000fa }, - { 0x18, 0x400000fc }, - { 0x21, 0x0144131f }, - { 0x22, 0x40c003f8 }, - /* Enable SPDIF in/out */ - { 0x1f, 0x01441030 }, - { 0x20, 0x1c410030 }, - {} -}; +static const struct snd_pci_quirk stac9205_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_9205_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30, + "SigmaTel", STAC_9205_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, + "DFI LanParty", STAC_9205_REF), + /* Dell */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1, + "unknown Dell", STAC_9205_DELL_M42), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2, + "unknown Dell", STAC_9205_DELL_M42), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc, + "unknown Dell", STAC_9205_DELL_M42), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd, + "unknown Dell", STAC_9205_DELL_M42), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff, + "Dell Precision M4300", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204, + "unknown Dell", STAC_9205_DELL_M42), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, + "Dell Inspiron", STAC_9205_DELL_M44), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, + "Dell Vostro 1500", STAC_9205_DELL_M42), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229, + "Dell Vostro 1700", STAC_9205_DELL_M42), + /* Gateway */ + SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD), + SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD), + {} /* terminator */ +}; + +static void stac92xx_set_config_regs(struct hda_codec *codec, + const unsigned int *pincfgs) +{ + int i; + struct sigmatel_spec *spec = codec->spec; + + if (!pincfgs) + return; + + for (i = 0; i < spec->num_pins; i++) + if (spec->pin_nids[i] && pincfgs[i]) + snd_hda_codec_set_pincfg(codec, spec->pin_nids[i], + pincfgs[i]); +} + +/* + * Analog playback callbacks + */ +static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct sigmatel_spec *spec = codec->spec; + if (spec->stream_delay) + msleep(spec->stream_delay); + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} + +static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream); +} + +static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital playback callbacks + */ +static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int stac92xx_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct sigmatel_spec *spec = codec->spec; + return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); +} + + +/* + * Analog capture callbacks + */ +static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = spec->adc_nids[substream->number]; + + if (spec->powerdown_adcs) { + msleep(40); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + } + snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); + return 0; +} + +static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = spec->adc_nids[substream->number]; + + snd_hda_codec_cleanup_stream(codec, nid); + if (spec->powerdown_adcs) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + return 0; +} + +static const struct hda_pcm_stream stac92xx_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in stac92xx_build_pcms */ + .ops = { + .open = stac92xx_dig_playback_pcm_open, + .close = stac92xx_dig_playback_pcm_close, + .prepare = stac92xx_dig_playback_pcm_prepare, + .cleanup = stac92xx_dig_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream stac92xx_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in stac92xx_build_pcms */ +}; + +static const struct hda_pcm_stream stac92xx_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = 0x02, /* NID to query formats and rates */ + .ops = { + .open = stac92xx_playback_pcm_open, + .prepare = stac92xx_playback_pcm_prepare, + .cleanup = stac92xx_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0x06, /* NID to query formats and rates */ + .ops = { + .open = stac92xx_playback_pcm_open, + .prepare = stac92xx_playback_pcm_prepare, + .cleanup = stac92xx_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream stac92xx_pcm_analog_capture = { + .channels_min = 2, + .channels_max = 2, + /* NID + .substreams is set in stac92xx_build_pcms */ + .ops = { + .prepare = stac92xx_capture_pcm_prepare, + .cleanup = stac92xx_capture_pcm_cleanup + }, +}; + +static int stac92xx_build_pcms(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "STAC92xx Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dac_nids[0]; + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && + spec->autocfg.line_outs == 2) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; + + info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs; + + if (spec->alt_switch) { + codec->num_pcms++; + info++; + info->name = "STAC92xx Analog Alt"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback; + } + + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + codec->num_pcms++; + info++; + info->name = "STAC92xx Digital"; + info->pcm_type = spec->autocfg.dig_out_type[0]; + 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; + } + if (spec->dig_in_nid) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; + } + } + + return 0; +} + +static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type) + +{ + snd_hda_set_pin_ctl_cache(codec, nid, pin_type); +} + +#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info + +static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + ucontrol->value.integer.value[0] = !!spec->hp_switch; + return 0; +} + +static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid); + +static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + int nid = kcontrol->private_value; + + spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0; + + /* check to be sure that the ports are up to date with + * switch changes + */ + stac_issue_unsol_event(codec, nid); + + return 1; +} + +static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int i; + static const char * const texts[] = { + "Mic In", "Line In", "Line Out" + }; + + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = kcontrol->private_value; + + if (nid == spec->mic_switch || nid == spec->line_switch) + i = 3; + else + i = 2; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = i; + uinfo->count = 1; + if (uinfo->value.enumerated.item >= i) + uinfo->value.enumerated.item = i-1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref = stac92xx_vref_get(codec, nid); + + if (vref == snd_hda_get_default_vref(codec, nid)) + ucontrol->value.enumerated.item[0] = 0; + else if (vref == AC_PINCTL_VREF_GRD) + ucontrol->value.enumerated.item[0] = 1; + else if (vref == AC_PINCTL_VREF_HIZ) + ucontrol->value.enumerated.item[0] = 2; + + return 0; +} + +static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int new_vref = 0; + int error; + hda_nid_t nid = kcontrol->private_value; + + if (ucontrol->value.enumerated.item[0] == 0) + new_vref = snd_hda_get_default_vref(codec, nid); + else if (ucontrol->value.enumerated.item[0] == 1) + new_vref = AC_PINCTL_VREF_GRD; + else if (ucontrol->value.enumerated.item[0] == 2) + new_vref = AC_PINCTL_VREF_HIZ; + else + return 0; + + if (new_vref != stac92xx_vref_get(codec, nid)) { + error = stac92xx_vref_set(codec, nid, new_vref); + return error; + } + + return 0; +} + +static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + char *texts[2]; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + if (kcontrol->private_value == spec->line_switch) + texts[0] = "Line In"; + else + texts[0] = "Mic In"; + texts[1] = "Line Out"; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = 2; + uinfo->count = 1; + + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = kcontrol->private_value; + int io_idx = (nid == spec->mic_switch) ? 1 : 0; + + ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx]; + return 0; +} + +static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = kcontrol->private_value; + int io_idx = (nid == spec->mic_switch) ? 1 : 0; + unsigned short val = !!ucontrol->value.enumerated.item[0]; + + spec->io_switch[io_idx] = val; + + if (val) + stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); + else { + unsigned int pinctl = AC_PINCTL_IN_EN; + if (io_idx) /* set VREF for mic */ + pinctl |= snd_hda_get_default_vref(codec, nid); + stac92xx_auto_set_pinctl(codec, nid, pinctl); + } + + /* check the auto-mute again: we need to mute/unmute the speaker + * appropriately according to the pin direction + */ + if (spec->hp_detect) + stac_issue_unsol_event(codec, nid); + + return 1; +} + +#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info + +static int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + ucontrol->value.integer.value[0] = spec->clfe_swap; + return 0; +} + +static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = kcontrol->private_value & 0xff; + unsigned int val = !!ucontrol->value.integer.value[0]; + + if (spec->clfe_swap == val) + return 0; + + spec->clfe_swap = val; + + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, + spec->clfe_swap ? 0x4 : 0x0); + + return 1; +} + +#define STAC_CODEC_HP_SWITCH(xname) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = 0, \ + .info = stac92xx_hp_switch_info, \ + .get = stac92xx_hp_switch_get, \ + .put = stac92xx_hp_switch_put, \ + } + +#define STAC_CODEC_IO_SWITCH(xname, xpval) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = 0, \ + .info = stac92xx_io_switch_info, \ + .get = stac92xx_io_switch_get, \ + .put = stac92xx_io_switch_put, \ + .private_value = xpval, \ + } + +#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = 0, \ + .info = stac92xx_clfe_switch_info, \ + .get = stac92xx_clfe_switch_get, \ + .put = stac92xx_clfe_switch_put, \ + .private_value = xpval, \ + } + +enum { + STAC_CTL_WIDGET_VOL, + STAC_CTL_WIDGET_MUTE, + STAC_CTL_WIDGET_MUTE_BEEP, + STAC_CTL_WIDGET_MONO_MUX, + STAC_CTL_WIDGET_HP_SWITCH, + STAC_CTL_WIDGET_IO_SWITCH, + STAC_CTL_WIDGET_CLFE_SWITCH, + STAC_CTL_WIDGET_DC_BIAS +}; + +static const struct snd_kcontrol_new stac92xx_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0), + STAC_MONO_MUX, + STAC_CODEC_HP_SWITCH(NULL), + STAC_CODEC_IO_SWITCH(NULL, 0), + STAC_CODEC_CLFE_SWITCH(NULL, 0), + DC_BIAS(NULL, 0, 0), +}; + +/* add dynamic controls */ +static struct snd_kcontrol_new * +stac_control_new(struct sigmatel_spec *spec, + const struct snd_kcontrol_new *ktemp, + const char *name, + unsigned int subdev) +{ + struct snd_kcontrol_new *knew; + + knew = snd_array_new(&spec->kctls); + if (!knew) + return NULL; + *knew = *ktemp; + knew->name = kstrdup(name, GFP_KERNEL); + if (!knew->name) { + /* roolback */ + memset(knew, 0, sizeof(*knew)); + spec->kctls.alloced--; + return NULL; + } + knew->subdevice = subdev; + return knew; +} + +static struct snd_kcontrol_new * +add_control_temp(struct sigmatel_spec *spec, + const struct snd_kcontrol_new *ktemp, + int idx, const char *name, + unsigned long val) +{ + struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, + HDA_SUBDEV_AMP_FLAG); + if (!knew) + return NULL; + knew->index = idx; + knew->private_value = val; + return knew; +} + +static int stac92xx_add_control_temp(struct sigmatel_spec *spec, + const struct snd_kcontrol_new *ktemp, + int idx, const char *name, + unsigned long val) +{ + return add_control_temp(spec, ktemp, idx, name, val) ? 0 : -ENOMEM; +} + +static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec, + int type, int idx, const char *name, + unsigned long val) +{ + return stac92xx_add_control_temp(spec, + &stac92xx_control_templates[type], + idx, name, val); +} + + +/* add dynamic controls */ +static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type, + const char *name, unsigned long val) +{ + return stac92xx_add_control_idx(spec, type, 0, name, val); +} + +static const struct snd_kcontrol_new stac_input_src_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .info = stac92xx_mux_enum_info, + .get = stac92xx_mux_enum_get, + .put = stac92xx_mux_enum_put, +}; + +static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, + hda_nid_t nid, int idx) +{ + int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int control = 0; + struct sigmatel_spec *spec = codec->spec; + char name[22]; + + if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { + if (spec->headset_jack && snd_hda_get_input_pin_attr(def_conf) + != INPUT_PIN_ATTR_DOCK) + return 0; + if (snd_hda_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD + && nid == spec->line_switch) + control = STAC_CTL_WIDGET_IO_SWITCH; + else if (snd_hda_query_pin_caps(codec, nid) + & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT)) + control = STAC_CTL_WIDGET_DC_BIAS; + else if (nid == spec->mic_switch) + control = STAC_CTL_WIDGET_IO_SWITCH; + } + + if (control) { + snd_hda_get_pin_label(codec, nid, &spec->autocfg, + name, sizeof(name), NULL); + return stac92xx_add_control(codec->spec, control, + strcat(name, " Jack Mode"), nid); + } + + return 0; +} + +static int stac92xx_add_input_source(struct sigmatel_spec *spec) +{ + struct snd_kcontrol_new *knew; + struct hda_input_mux *imux = &spec->private_imux; + + if (spec->auto_mic) + return 0; /* no need for input source */ + if (!spec->num_adcs || imux->num_items <= 1) + return 0; /* no need for input source control */ + knew = stac_control_new(spec, &stac_input_src_temp, + stac_input_src_temp.name, 0); + if (!knew) + return -ENOMEM; + knew->count = spec->num_adcs; + return 0; +} + +/* check whether the line-input can be used as line-out */ +static hda_nid_t check_line_out_switch(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + unsigned int pincap; + int i; + + if (cfg->line_out_type != AUTO_PIN_LINE_OUT) + return 0; + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) { + nid = cfg->inputs[i].pin; + pincap = snd_hda_query_pin_caps(codec, nid); + if (pincap & AC_PINCAP_OUT) + return nid; + } + } + return 0; +} + +static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid); + +/* check whether the mic-input can be used as line-out */ +static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int def_conf, pincap; + int i; + + *dac = 0; + if (cfg->line_out_type != AUTO_PIN_LINE_OUT) + return 0; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + def_conf = snd_hda_codec_get_pincfg(codec, nid); + /* some laptops have an internal analog microphone + * which can't be used as a output */ + if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { + pincap = snd_hda_query_pin_caps(codec, nid); + if (pincap & AC_PINCAP_OUT) { + *dac = get_unassigned_dac(codec, nid); + if (*dac) + return nid; + } + } + } + return 0; +} + +static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) +{ + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) { + if (spec->multiout.dac_nids[i] == nid) + return 1; + } + + return 0; +} + +static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) +{ + int i; + if (is_in_dac_nids(spec, nid)) + return 1; + for (i = 0; i < spec->autocfg.hp_outs; i++) + if (spec->hp_dacs[i] == nid) + return 1; + for (i = 0; i < spec->autocfg.speaker_outs; i++) + if (spec->speaker_dacs[i] == nid) + return 1; + return 0; +} + +static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int j, conn_len; + hda_nid_t conn[HDA_MAX_CONNECTIONS], fallback_dac; + unsigned int wcaps, wtype; + + conn_len = snd_hda_get_connections(codec, nid, conn, + HDA_MAX_CONNECTIONS); + /* 92HD88: trace back up the link of nids to find the DAC */ + while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0])) + != AC_WID_AUD_OUT)) { + nid = conn[0]; + conn_len = snd_hda_get_connections(codec, nid, conn, + HDA_MAX_CONNECTIONS); + } + for (j = 0; j < conn_len; j++) { + wcaps = get_wcaps(codec, conn[j]); + wtype = get_wcaps_type(wcaps); + /* we check only analog outputs */ + if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL)) + continue; + /* if this route has a free DAC, assign it */ + if (!check_all_dac_nids(spec, conn[j])) { + if (conn_len > 1) { + /* select this DAC in the pin's input mux */ + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, j); + } + return conn[j]; + } + } + + /* if all DACs are already assigned, connect to the primary DAC, + unless we're assigning a secondary headphone */ + fallback_dac = spec->multiout.dac_nids[0]; + if (spec->multiout.hp_nid) { + for (j = 0; j < cfg->hp_outs; j++) + if (cfg->hp_pins[j] == nid) { + fallback_dac = spec->multiout.hp_nid; + break; + } + } + + if (conn_len > 1) { + for (j = 0; j < conn_len; j++) { + if (conn[j] == fallback_dac) { + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, j); + break; + } + } + } + return 0; +} + +static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid); +static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid); + +/* + * Fill in the dac_nids table from the parsed pin configuration + * This function only works when every pin in line_out_pins[] + * contains atleast one DAC in its connection list. Some 92xx + * codecs are not connected directly to a DAC, such as the 9200 + * and 9202/925x. For those, dac_nids[] must be hard-coded. + */ +static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + hda_nid_t nid, dac; + + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + dac = get_unassigned_dac(codec, nid); + if (!dac) { + if (spec->multiout.num_dacs > 0) { + /* we have already working output pins, + * so let's drop the broken ones again + */ + cfg->line_outs = spec->multiout.num_dacs; + break; + } + /* error out, no available DAC found */ + snd_printk(KERN_ERR + "%s: No available DAC for pin 0x%x\n", + __func__, nid); + return -ENODEV; + } + add_spec_dacs(spec, dac); + } + + for (i = 0; i < cfg->hp_outs; i++) { + nid = cfg->hp_pins[i]; + dac = get_unassigned_dac(codec, nid); + if (dac) { + if (!spec->multiout.hp_nid) + spec->multiout.hp_nid = dac; + else + add_spec_extra_dacs(spec, dac); + } + spec->hp_dacs[i] = dac; + } + + for (i = 0; i < cfg->speaker_outs; i++) { + nid = cfg->speaker_pins[i]; + dac = get_unassigned_dac(codec, nid); + if (dac) + add_spec_extra_dacs(spec, dac); + spec->speaker_dacs[i] = dac; + } + + /* add line-in as output */ + nid = check_line_out_switch(codec); + if (nid) { + dac = get_unassigned_dac(codec, nid); + if (dac) { + snd_printdd("STAC: Add line-in 0x%x as output %d\n", + nid, cfg->line_outs); + cfg->line_out_pins[cfg->line_outs] = nid; + cfg->line_outs++; + spec->line_switch = nid; + add_spec_dacs(spec, dac); + } + } + /* add mic as output */ + nid = check_mic_out_switch(codec, &dac); + if (nid && dac) { + snd_printdd("STAC: Add mic-in 0x%x as output %d\n", + nid, cfg->line_outs); + cfg->line_out_pins[cfg->line_outs] = nid; + cfg->line_outs++; + spec->mic_switch = nid; + add_spec_dacs(spec, dac); + } + + snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", + spec->multiout.num_dacs, + spec->multiout.dac_nids[0], + spec->multiout.dac_nids[1], + spec->multiout.dac_nids[2], + spec->multiout.dac_nids[3], + spec->multiout.dac_nids[4]); + + return 0; +} + +/* create volume control/switch for the given prefx type */ +static int create_controls_idx(struct hda_codec *codec, const char *pfx, + int idx, hda_nid_t nid, int chs) +{ + struct sigmatel_spec *spec = codec->spec; + char name[32]; + int err; + + if (!spec->check_volume_offset) { + unsigned int caps, step, nums, db_scale; + caps = query_amp_caps(codec, nid, HDA_OUTPUT); + step = (caps & AC_AMPCAP_STEP_SIZE) >> + AC_AMPCAP_STEP_SIZE_SHIFT; + step = (step + 1) * 25; /* in .01dB unit */ + nums = (caps & AC_AMPCAP_NUM_STEPS) >> + AC_AMPCAP_NUM_STEPS_SHIFT; + db_scale = nums * step; + /* if dB scale is over -64dB, and finer enough, + * let's reduce it to half + */ + if (db_scale > 6400 && nums >= 0x1f) + spec->volume_offset = nums / 2; + spec->check_volume_offset = 1; + } + + sprintf(name, "%s Playback Volume", pfx); + err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name, + HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT, + spec->volume_offset)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", pfx); + err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; + return 0; +} + +#define create_controls(codec, pfx, nid, chs) \ + create_controls_idx(codec, pfx, 0, nid, chs) + +static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) +{ + if (spec->multiout.num_dacs > 4) { + printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); + return 1; + } else { + snd_BUG_ON(spec->multiout.dac_nids != spec->dac_nids); + spec->dac_nids[spec->multiout.num_dacs] = nid; + spec->multiout.num_dacs++; + } + return 0; +} + +static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid) +{ + int i; + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) { + if (!spec->multiout.extra_out_nid[i]) { + spec->multiout.extra_out_nid[i] = nid; + return 0; + } + } + printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid); + return 1; +} + +/* Create output controls + * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT) + */ +static int create_multi_out_ctls(struct hda_codec *codec, int num_outs, + const hda_nid_t *pins, + const hda_nid_t *dac_nids, + int type) +{ + struct sigmatel_spec *spec = codec->spec; + static const char * const chname[4] = { + "Front", "Surround", NULL /*CLFE*/, "Side" + }; + hda_nid_t nid; + int i, err; + unsigned int wid_caps; + + for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) { + if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) { + if (is_jack_detectable(codec, pins[i])) + spec->hp_detect = 1; + } + nid = dac_nids[i]; + if (!nid) + continue; + if (type != AUTO_PIN_HP_OUT && i == 2) { + /* Center/LFE */ + err = create_controls(codec, "Center", nid, 1); + if (err < 0) + return err; + err = create_controls(codec, "LFE", nid, 2); + if (err < 0) + return err; + + wid_caps = get_wcaps(codec, nid); + + if (wid_caps & AC_WCAP_LR_SWAP) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_CLFE_SWITCH, + "Swap Center/LFE Playback Switch", nid); + + if (err < 0) + return err; + } + + } else { + const char *name; + int idx; + switch (type) { + case AUTO_PIN_HP_OUT: + name = "Headphone"; + idx = i; + break; + case AUTO_PIN_SPEAKER_OUT: + if (num_outs <= 2) { + name = i ? "Bass Speaker" : "Speaker"; + idx = 0; + break; + } + /* Fall through in case of multi speaker outs */ + default: + name = chname[i]; + idx = 0; + break; + } + err = create_controls_idx(codec, name, idx, nid, 3); + if (err < 0) + return err; + } + } + return 0; +} + +static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, + unsigned int dir_mask, unsigned int data); + +/* hook for controlling mic-mute LED GPIO */ +static int stac92xx_capture_sw_put_led(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + int err; + bool mute; + + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (err <= 0) + return err; + mute = !(ucontrol->value.integer.value[0] && + ucontrol->value.integer.value[1]); + if (spec->mic_mute_led_on != mute) { + spec->mic_mute_led_on = mute; + if (mute) + spec->gpio_data |= spec->mic_mute_led_gpio; + else + spec->gpio_data &= ~spec->mic_mute_led_gpio; + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); + } + return err; +} + +static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol, + unsigned long sw, int idx) +{ + struct sigmatel_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + int err; + + err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, + "Capture Volume", vol); + if (err < 0) + return err; + + knew = add_control_temp(spec, + &stac92xx_control_templates[STAC_CTL_WIDGET_MUTE], + idx, "Capture Switch", sw); + if (!knew) + return -ENOMEM; + /* add a LED hook for some HP laptops */ + if (spec->mic_mute_led_gpio) + knew->put = stac92xx_capture_sw_put_led; + + 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) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid; + int err; + int idx; + + err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins, + spec->multiout.dac_nids, + cfg->line_out_type); + if (err < 0) + return err; + + if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_HP_SWITCH, + "Headphone as Line Out Switch", + cfg->hp_pins[cfg->hp_outs - 1]); + if (err < 0) + return err; + } + + for (idx = 0; idx < cfg->num_inputs; idx++) { + if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN) + break; + nid = cfg->inputs[idx].pin; + err = stac92xx_add_jack_mode_control(codec, nid, idx); + if (err < 0) + return err; + } + + 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) +{ + struct sigmatel_spec *spec = codec->spec; + int err; + + err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins, + spec->hp_dacs, AUTO_PIN_HP_OUT); + if (err < 0) + return err; + + err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, + spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT); + if (err < 0) + return err; + + return 0; +} + +/* labels for mono mux outputs */ +static const char * const stac92xx_mono_labels[4] = { + "DAC0", "DAC1", "Mixer", "DAC2" +}; + +/* create mono mux for mono out on capable codecs */ +static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *mono_mux = &spec->private_mono_mux; + int i, num_cons; + hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)]; + + num_cons = snd_hda_get_connections(codec, + spec->mono_nid, + con_lst, + HDA_MAX_NUM_INPUTS); + if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) + return -EINVAL; + + for (i = 0; i < num_cons; i++) + snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i, + NULL); + + return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX, + "Mono Mux", spec->mono_nid); +} + +/* create PC beep volume controls */ +static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, + hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); + int err, type = STAC_CTL_WIDGET_MUTE_BEEP; + + if (spec->anabeep_nid == nid) + type = STAC_CTL_WIDGET_MUTE; + + /* check for mute support for the the amp */ + if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { + err = stac92xx_add_control(spec, type, + "Beep Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + + /* check to see if there is volume support for the amp */ + if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, + "Beep Playback Volume", + HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + return 0; +} + +#ifdef CONFIG_SND_HDA_INPUT_BEEP +#define stac92xx_dig_beep_switch_info snd_ctl_boolean_mono_info + +static int stac92xx_dig_beep_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = codec->beep->enabled; + return 0; +} + +static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); +} + +static const struct snd_kcontrol_new stac92xx_dig_beep_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = stac92xx_dig_beep_switch_info, + .get = stac92xx_dig_beep_switch_get, + .put = stac92xx_dig_beep_switch_put, +}; + +static int stac92xx_beep_switch_ctl(struct hda_codec *codec) +{ + return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl, + 0, "Beep Playback Switch", 0); +} +#endif + +static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i, j, err = 0; + + for (i = 0; i < spec->num_muxes; i++) { + hda_nid_t nid; + unsigned int wcaps; + unsigned long val; + + nid = spec->mux_nids[i]; + wcaps = get_wcaps(codec, nid); + if (!(wcaps & AC_WCAP_OUT_AMP)) + continue; + + /* check whether already the same control was created as + * normal Capture Volume. + */ + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + for (j = 0; j < spec->num_caps; j++) { + if (spec->capvols[j] == val) + break; + } + if (j < spec->num_caps) + continue; + + err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i, + "Mux Capture Volume", val); + if (err < 0) + return err; + } + return 0; +}; + +static const char * const stac92xx_spdif_labels[3] = { + "Digital Playback", "Analog Mux 1", "Analog Mux 2", +}; + +static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *spdif_mux = &spec->private_smux; + const char * const *labels = spec->spdif_labels; + int i, num_cons; + hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; + + num_cons = snd_hda_get_connections(codec, + spec->smux_nids[0], + con_lst, + HDA_MAX_NUM_INPUTS); + if (num_cons <= 0) + return -EINVAL; + + if (!labels) + labels = stac92xx_spdif_labels; + + for (i = 0; i < num_cons; i++) + snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL); + + return 0; +} + +/* labels for dmic mux inputs */ +static const char * const stac92xx_dmic_labels[5] = { + "Analog Inputs", "Digital Mic 1", "Digital Mic 2", + "Digital Mic 3", "Digital Mic 4" +}; + +static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux, + int idx) +{ + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int nums; + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + if (idx >= 0 && idx < nums) + return conn[idx]; + return 0; +} + +/* look for NID recursively */ +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 1) + +/* create a volume assigned to the given pin (only if supported) */ +/* return 1 if the volume control is created */ +static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid, + const char *label, int idx, int direction) +{ + unsigned int caps, nums; + char name[32]; + int err; + + if (direction == HDA_OUTPUT) + caps = AC_WCAP_OUT_AMP; + else + caps = AC_WCAP_IN_AMP; + if (!(get_wcaps(codec, nid) & caps)) + return 0; + caps = query_amp_caps(codec, nid, direction); + nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + if (!nums) + return 0; + snprintf(name, sizeof(name), "%s Capture Volume", label); + err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction)); + if (err < 0) + return err; + return 1; +} + +/* create playback/capture controls for input pins on dmic capable codecs */ +static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux; + struct hda_input_mux *dimux = &spec->private_dimux; + int err, i; + unsigned int def_conf; + + snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL); + + for (i = 0; i < spec->num_dmics; i++) { + hda_nid_t nid; + int index, type_idx; + char label[32]; + + nid = spec->dmic_nids[i]; + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + continue; + def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + continue; + + index = get_connection_index(codec, spec->dmux_nids[0], nid); + if (index < 0) + continue; + + snd_hda_get_pin_label(codec, nid, &spec->autocfg, + label, sizeof(label), NULL); + snd_hda_add_imux_item(dimux, label, index, &type_idx); + if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) + snd_hda_add_imux_item(imux, label, index, &type_idx); + + err = create_elem_capture_vol(codec, nid, label, type_idx, + HDA_INPUT); + if (err < 0) + return err; + if (!err) { + err = create_elem_capture_vol(codec, nid, label, + type_idx, HDA_OUTPUT); + if (err < 0) + return err; + if (!err) { + nid = get_connected_node(codec, + spec->dmux_nids[0], index); + if (nid) + err = create_elem_capture_vol(codec, + nid, label, + type_idx, HDA_INPUT); + if (err < 0) + return err; + } + } + } + + return 0; +} + +static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock) +{ + unsigned int cfg; + unsigned int type; + + if (!nid) + return 0; + cfg = snd_hda_codec_get_pincfg(codec, nid); + type = get_defcfg_device(cfg); + switch (snd_hda_get_input_pin_attr(cfg)) { + case INPUT_PIN_ATTR_INT: + if (*fixed) + return 1; /* already occupied */ + if (type != AC_JACK_MIC_IN) + return 1; /* invalid type */ + *fixed = nid; + break; + case INPUT_PIN_ATTR_UNUSED: + break; + case INPUT_PIN_ATTR_DOCK: + if (*dock) + return 1; /* already occupied */ + if (type != AC_JACK_MIC_IN && type != AC_JACK_LINE_IN) + return 1; /* invalid type */ + *dock = nid; + break; + default: + if (*ext) + return 1; /* already occupied */ + if (type != AC_JACK_MIC_IN) + return 1; /* invalid type */ + *ext = nid; + break; + } + return 0; +} + +static int set_mic_route(struct hda_codec *codec, + struct sigmatel_mic_route *mic, + hda_nid_t pin) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + mic->pin = pin; + if (pin == 0) + return 0; + for (i = 0; i < cfg->num_inputs; i++) { + if (pin == cfg->inputs[i].pin) + break; + } + if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) { + /* analog pin */ + i = get_connection_index(codec, spec->mux_nids[0], pin); + if (i < 0) + return -1; + mic->mux_idx = i; + mic->dmux_idx = -1; + if (spec->dmux_nids) + mic->dmux_idx = get_connection_index(codec, + spec->dmux_nids[0], + spec->mux_nids[0]); + } else if (spec->dmux_nids) { + /* digital pin */ + i = get_connection_index(codec, spec->dmux_nids[0], pin); + if (i < 0) + return -1; + mic->dmux_idx = i; + mic->mux_idx = -1; + if (spec->mux_nids) + mic->mux_idx = get_connection_index(codec, + spec->mux_nids[0], + spec->dmux_nids[0]); + } + return 0; +} + +/* return non-zero if the device is for automatic mic switch */ +static int stac_check_auto_mic(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t fixed, ext, dock; + int i; + + fixed = ext = dock = 0; + for (i = 0; i < cfg->num_inputs; i++) + if (check_mic_pin(codec, cfg->inputs[i].pin, + &fixed, &ext, &dock)) + return 0; + for (i = 0; i < spec->num_dmics; i++) + if (check_mic_pin(codec, spec->dmic_nids[i], + &fixed, &ext, &dock)) + return 0; + if (!fixed || (!ext && !dock)) + return 0; /* no input to switch */ + if (!is_jack_detectable(codec, ext)) + return 0; /* no unsol support */ + if (set_mic_route(codec, &spec->ext_mic, ext) || + set_mic_route(codec, &spec->int_mic, fixed) || + set_mic_route(codec, &spec->dock_mic, dock)) + return 0; /* something is wrong */ + return 1; +} + +/* create playback/capture controls for input pins */ +static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux; + int i, j; + const char *label; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + int index, err, type_idx; + + index = -1; + for (j = 0; j < spec->num_muxes; j++) { + index = get_connection_index(codec, spec->mux_nids[j], + nid); + if (index >= 0) + break; + } + if (index < 0) + continue; + + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, index, &type_idx); + + err = create_elem_capture_vol(codec, nid, + label, type_idx, + HDA_INPUT); + if (err < 0) + return err; + } + spec->num_analog_muxes = imux->num_items; + + if (imux->num_items) { + /* + * Set the current input for the muxes. + * The STAC9221 has two input muxes with identical source + * NID lists. Hopefully this won't get confused. + */ + for (i = 0; i < spec->num_muxes; i++) { + snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[0].index); + } + } + + return 0; +} + +static void stac92xx_auto_init_multi_out(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->autocfg.line_outs; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); + } +} + +static void stac92xx_auto_init_hp_out(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->autocfg.hp_outs; i++) { + hda_nid_t pin; + pin = spec->autocfg.hp_pins[i]; + if (pin) /* connect to front */ + stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); + } + for (i = 0; i < spec->autocfg.speaker_outs; i++) { + hda_nid_t pin; + pin = spec->autocfg.speaker_pins[i]; + if (pin) /* connect to front */ + stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN); + } +} + +static int is_dual_headphones(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i, valid_hps; + + if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT || + spec->autocfg.hp_outs <= 1) + return 0; + valid_hps = 0; + for (i = 0; i < spec->autocfg.hp_outs; i++) { + hda_nid_t nid = spec->autocfg.hp_pins[i]; + unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE) + continue; + valid_hps++; + } + return (valid_hps > 1); +} + + +static int stac92xx_parse_auto_config(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t dig_out = 0, dig_in = 0; + int hp_swap = 0; + int i, err; + + if ((err = snd_hda_parse_pin_def_config(codec, + &spec->autocfg, + spec->dmic_nids)) < 0) + return err; + if (! spec->autocfg.line_outs) + return 0; /* can't find valid pin config */ + + /* If we have no real line-out pin and multiple hp-outs, HPs should + * be set up as multi-channel outputs. + */ + if (is_dual_headphones(codec)) { + /* Copy hp_outs to line_outs, backup line_outs in + * speaker_outs so that the following routines can handle + * HP pins as primary outputs. + */ + snd_printdd("stac92xx: Enabling multi-HPs workaround\n"); + memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins, + sizeof(spec->autocfg.line_out_pins)); + spec->autocfg.speaker_outs = spec->autocfg.line_outs; + memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins, + sizeof(spec->autocfg.hp_pins)); + spec->autocfg.line_outs = spec->autocfg.hp_outs; + spec->autocfg.line_out_type = AUTO_PIN_HP_OUT; + spec->autocfg.hp_outs = 0; + hp_swap = 1; + } + if (spec->autocfg.mono_out_pin) { + int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) & + (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP); + u32 caps = query_amp_caps(codec, + spec->autocfg.mono_out_pin, dir); + hda_nid_t conn_list[1]; + + /* get the mixer node and then the mono mux if it exists */ + if (snd_hda_get_connections(codec, + spec->autocfg.mono_out_pin, conn_list, 1) && + snd_hda_get_connections(codec, conn_list[0], + conn_list, 1) > 0) { + + int wcaps = get_wcaps(codec, conn_list[0]); + int wid_type = get_wcaps_type(wcaps); + /* LR swap check, some stac925x have a mux that + * changes the DACs output path instead of the + * mono-mux path. + */ + if (wid_type == AC_WID_AUD_SEL && + !(wcaps & AC_WCAP_LR_SWAP)) + spec->mono_nid = conn_list[0]; + } + if (dir) { + hda_nid_t nid = spec->autocfg.mono_out_pin; + + /* most mono outs have a least a mute/unmute switch */ + dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT; + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, + "Mono Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir)); + if (err < 0) + return err; + /* check for volume support for the amp */ + if ((caps & AC_AMPCAP_NUM_STEPS) + >> AC_AMPCAP_NUM_STEPS_SHIFT) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_VOL, + "Mono Playback Volume", + HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir)); + if (err < 0) + return err; + } + } + + stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin, + AC_PINCTL_OUT_EN); + } + + if (!spec->multiout.num_dacs) { + err = stac92xx_auto_fill_dac_nids(codec); + if (err < 0) + return err; + err = stac92xx_auto_create_multi_out_ctls(codec, + &spec->autocfg); + if (err < 0) + return err; + } + + /* setup analog beep controls */ + if (spec->anabeep_nid > 0) { + err = stac92xx_auto_create_beep_ctls(codec, + spec->anabeep_nid); + if (err < 0) + return err; + } + + /* setup digital beep controls and input device */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP + if (spec->digbeep_nid > 0) { + hda_nid_t nid = spec->digbeep_nid; + unsigned int caps; + + err = stac92xx_auto_create_beep_ctls(codec, nid); + if (err < 0) + return err; + err = snd_hda_attach_beep_device(codec, nid); + if (err < 0) + return err; + if (codec->beep) { + /* IDT/STAC codecs have linear beep tone parameter */ + codec->beep->linear_tone = spec->linear_tone_beep; + /* if no beep switch is available, make its own one */ + caps = query_amp_caps(codec, nid, HDA_OUTPUT); + if (!(caps & AC_AMPCAP_MUTE)) { + err = stac92xx_beep_switch_ctl(codec); + if (err < 0) + return err; + } + } + } +#endif + + err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + + /* All output parsing done, now restore the swapped hp pins */ + if (hp_swap) { + memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, + sizeof(spec->autocfg.hp_pins)); + spec->autocfg.hp_outs = spec->autocfg.line_outs; + spec->autocfg.line_out_type = AUTO_PIN_HP_OUT; + spec->autocfg.line_outs = 0; + } + + if (stac_check_auto_mic(codec)) { + spec->auto_mic = 1; + /* only one capture for auto-mic */ + spec->num_adcs = 1; + spec->num_caps = 1; + spec->num_muxes = 1; + } + + for (i = 0; i < spec->num_caps; i++) { + err = stac92xx_add_capvol_ctls(codec, spec->capvols[i], + spec->capsws[i], i); + if (err < 0) + return err; + } + + err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg); + if (err < 0) + return err; + + if (spec->mono_nid > 0) { + err = stac92xx_auto_create_mono_output_ctls(codec); + if (err < 0) + return err; + } + if (spec->num_dmics > 0 && !spec->dinput_mux) + if ((err = stac92xx_auto_create_dmic_input_ctls(codec, + &spec->autocfg)) < 0) + return err; + if (spec->num_muxes > 0) { + err = stac92xx_auto_create_mux_input_ctls(codec); + if (err < 0) + return err; + } + if (spec->num_smuxes > 0) { + err = stac92xx_auto_create_spdif_mux_ctls(codec); + if (err < 0) + return err; + } + + err = stac92xx_add_input_source(spec); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + if (spec->multiout.max_channels > 2) + spec->surr_switch = 1; + + /* find digital out and in converters */ + for (i = codec->start_nid; i < codec->start_nid + codec->num_nodes; i++) { + unsigned int wid_caps = get_wcaps(codec, i); + if (wid_caps & AC_WCAP_DIGITAL) { + switch (get_wcaps_type(wid_caps)) { + case AC_WID_AUD_OUT: + if (!dig_out) + dig_out = i; + break; + case AC_WID_AUD_IN: + if (!dig_in) + dig_in = i; + break; + } + } + } + if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = dig_out; + if (dig_in && spec->autocfg.dig_in_pin) + spec->dig_in_nid = dig_in; + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux; + if (!spec->dinput_mux) + spec->dinput_mux = &spec->private_dimux; + spec->sinput_mux = &spec->private_smux; + spec->mono_mux = &spec->private_mono_mux; + return 1; +} + +/* add playback controls for HP output */ +static int stac9200_auto_create_hp_ctls(struct hda_codec *codec, + struct auto_pin_cfg *cfg) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t pin = cfg->hp_pins[0]; + + if (! pin) + return 0; + + if (is_jack_detectable(codec, pin)) + spec->hp_detect = 1; + + return 0; +} + +/* add playback controls for LFE output */ +static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec, + struct auto_pin_cfg *cfg) +{ + struct sigmatel_spec *spec = codec->spec; + int err; + hda_nid_t lfe_pin = 0x0; + int i; + + /* + * search speaker outs and line outs for a mono speaker pin + * with an amp. If one is found, add LFE controls + * for it. + */ + for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) { + hda_nid_t pin = spec->autocfg.speaker_pins[i]; + unsigned 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 */ + lfe_pin = pin; + } + + /* if speaker_outs is 0, then speakers may be in line_outs */ + if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) { + for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) { + hda_nid_t pin = spec->autocfg.line_out_pins[i]; + unsigned int defcfg; + defcfg = snd_hda_codec_get_pincfg(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, + must be lfe */ + lfe_pin = pin; + } + } + } + + if (lfe_pin) { + err = create_controls(codec, "LFE", lfe_pin, 1); + if (err < 0) + return err; + } + + return 0; +} + +static int stac9200_parse_auto_config(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int err; + + if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0) + return err; + + if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) + return err; + + if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0) + return err; + + if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0) + return err; + + if (spec->num_muxes > 0) { + err = stac92xx_auto_create_mux_input_ctls(codec); + if (err < 0) + return err; + } + + err = stac92xx_add_input_source(spec); + if (err < 0) + return err; + + if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = 0x05; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = 0x04; + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux; + spec->dinput_mux = &spec->private_dimux; + + return 1; +} + +/* + * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a + * funky external mute control using GPIO pins. + */ + +static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, + unsigned int dir_mask, unsigned int data) +{ + unsigned int gpiostate, gpiomask, gpiodir; + + snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); + + gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); + + gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_MASK, 0); + gpiomask |= mask; + + gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + gpiodir |= dir_mask; + + /* Configure GPIOx as CMOS */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_MASK, gpiomask); + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ + + msleep(1); + + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ +} + +static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, + unsigned char type, int data) +{ + struct hda_jack_tbl *event; + + event = snd_hda_jack_tbl_new(codec, nid); + if (!event) + return -ENOMEM; + event->action = type; + event->private_data = data; + + return 0; +} + +static void handle_unsol_event(struct hda_codec *codec, + struct hda_jack_tbl *event); + +/* check if given nid is a valid pin and no other events are assigned + * to it. If OK, assign the event, set the unsol flag, and returns 1. + * Otherwise, returns zero. + */ +static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, + unsigned int type) +{ + struct hda_jack_tbl *event; + + if (!is_jack_detectable(codec, nid)) + return 0; + event = snd_hda_jack_tbl_new(codec, nid); + if (!event) + return -ENOMEM; + if (event->action && event->action != type) + return 0; + event->action = type; + event->callback = handle_unsol_event; + snd_hda_jack_detect_enable(codec, nid, 0); + return 1; +} + +static int is_nid_out_jack_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) +{ + int i; + for (i = 0; i < cfg->hp_outs; i++) + if (cfg->hp_pins[i] == nid) + return 1; /* nid is a HP-Out */ + for (i = 0; i < cfg->line_outs; i++) + if (cfg->line_out_pins[i] == nid) + return 1; /* nid is a line-Out */ + 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 */ + const hda_nid_t *dac; + for (dac = spec->dac_list; *dac; dac++) + if (!check_all_dac_nids(spec, *dac)) + snd_hda_codec_write(codec, *dac, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); +} + +static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, + int enable); + +static inline int get_int_hint(struct hda_codec *codec, const char *key, + int *valp) +{ + const char *p; + p = snd_hda_get_hint(codec, key); + if (p) { + unsigned long val; + if (!strict_strtoul(p, 0, &val)) { + *valp = val; + return 1; + } + } + return 0; +} + +/* override some hints from the hwdep entry */ +static void stac_store_hints(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int val; + + val = snd_hda_get_bool_hint(codec, "hp_detect"); + if (val >= 0) + spec->hp_detect = val; + if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) { + spec->eapd_mask = spec->gpio_dir = spec->gpio_data = + spec->gpio_mask; + } + if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir)) + spec->gpio_mask &= spec->gpio_mask; + if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) + spec->gpio_dir &= spec->gpio_mask; + if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask)) + spec->eapd_mask &= spec->gpio_mask; + if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute)) + spec->gpio_mute &= spec->gpio_mask; + val = snd_hda_get_bool_hint(codec, "eapd_switch"); + if (val >= 0) + spec->eapd_switch = val; +} + +static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins, + const hda_nid_t *pins) +{ + while (num_pins--) + stac_issue_unsol_event(codec, *pins++); +} + +/* fake event to set up pins */ +static void stac_fake_hp_events(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + if (spec->autocfg.hp_outs) + stac_issue_unsol_events(codec, spec->autocfg.hp_outs, + spec->autocfg.hp_pins); + if (spec->autocfg.line_outs && + spec->autocfg.line_out_pins[0] != spec->autocfg.hp_pins[0]) + stac_issue_unsol_events(codec, spec->autocfg.line_outs, + spec->autocfg.line_out_pins); +} + +static int stac92xx_init(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int gpio; + int i; + + if (spec->init) + snd_hda_sequence_write(codec, spec->init); + + /* power down adcs initially */ + if (spec->powerdown_adcs) + for (i = 0; i < spec->num_adcs; i++) + snd_hda_codec_write(codec, + spec->adc_nids[i], 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + /* override some hints */ + stac_store_hints(codec); + + /* set up GPIO */ + gpio = spec->gpio_data; + /* turn on EAPD statically when spec->eapd_switch isn't set. + * otherwise, unsol event will turn it on/off dynamically + */ + if (!spec->eapd_switch) + gpio |= spec->eapd_mask; + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio); + + /* set up pins */ + if (spec->hp_detect) { + /* Enable unsolicited responses on the HP widget */ + for (i = 0; i < cfg->hp_outs; i++) { + hda_nid_t nid = cfg->hp_pins[i]; + enable_pin_detect(codec, nid, STAC_HP_EVENT); + } + if (cfg->line_out_type == AUTO_PIN_LINE_OUT && + cfg->speaker_outs > 0) { + /* enable pin-detect for line-outs as well */ + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t nid = cfg->line_out_pins[i]; + enable_pin_detect(codec, nid, STAC_LO_EVENT); + } + } + + /* force to enable the first line-out; the others are set up + * in unsol_event + */ + stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0], + AC_PINCTL_OUT_EN); + /* fake event to set up pins */ + stac_fake_hp_events(codec); + } else { + stac92xx_auto_init_multi_out(codec); + stac92xx_auto_init_hp_out(codec); + for (i = 0; i < cfg->hp_outs; i++) + stac_toggle_power_map(codec, cfg->hp_pins[i], 1); + } + if (spec->auto_mic) { + /* initialize connection to analog input */ + if (spec->dmux_nids) + snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, 0); + if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT)) + stac_issue_unsol_event(codec, spec->ext_mic.pin); + if (enable_pin_detect(codec, spec->dock_mic.pin, + STAC_MIC_EVENT)) + stac_issue_unsol_event(codec, spec->dock_mic.pin); + } + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + int type = cfg->inputs[i].type; + unsigned int pinctl, conf; + if (type == AUTO_PIN_MIC) { + /* for mic pins, force to initialize */ + pinctl = snd_hda_get_default_vref(codec, nid); + pinctl |= AC_PINCTL_IN_EN; + stac92xx_auto_set_pinctl(codec, nid, pinctl); + } else { + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + /* if PINCTL already set then skip */ + /* Also, if both INPUT and OUTPUT are set, + * it must be a BIOS bug; need to override, too + */ + if (!(pinctl & AC_PINCTL_IN_EN) || + (pinctl & AC_PINCTL_OUT_EN)) { + pinctl &= ~AC_PINCTL_OUT_EN; + pinctl |= AC_PINCTL_IN_EN; + stac92xx_auto_set_pinctl(codec, nid, pinctl); + } + } + conf = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { + if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT)) + stac_issue_unsol_event(codec, nid); + } + } + for (i = 0; i < spec->num_dmics; i++) + stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], + AC_PINCTL_IN_EN); + if (cfg->dig_out_pins[0]) + stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0], + AC_PINCTL_OUT_EN); + if (cfg->dig_in_pin) + stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, + AC_PINCTL_IN_EN); + for (i = 0; i < spec->num_pwrs; i++) { + hda_nid_t nid = spec->pwr_nids[i]; + unsigned int pinctl, def_conf; + + def_conf = snd_hda_codec_get_pincfg(codec, nid); + def_conf = get_defcfg_connect(def_conf); + if (def_conf == AC_JACK_PORT_NONE) { + /* power off unused ports */ + stac_toggle_power_map(codec, nid, 0); + continue; + } + if (def_conf == AC_JACK_PORT_FIXED) { + /* no need for jack detection for fixed pins */ + stac_toggle_power_map(codec, nid, 1); + continue; + } + /* power on when no jack detection is available */ + /* or when the VREF is used for controlling LED */ + if (!spec->hp_detect || + spec->vref_mute_led_nid == nid || + !is_jack_detectable(codec, nid)) { + stac_toggle_power_map(codec, nid, 1); + continue; + } + + if (is_nid_out_jack_pin(cfg, nid)) + continue; /* already has an unsol event */ + + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 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) { + stac_toggle_power_map(codec, nid, 1); + continue; + } + if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) { + stac_issue_unsol_event(codec, nid); + continue; + } + /* none of the above, turn the port OFF */ + stac_toggle_power_map(codec, nid, 0); + } + + /* sync mute LED */ + if (spec->gpio_led) { + if (spec->vmaster_mute.hook) + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + else /* the very first init call doesn't have vmaster yet */ + stac92xx_update_led_status(codec, false); + } -static const struct hda_pintbl dell_9205_m44_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11020 }, - { 0x0c, 0x400003fa }, - { 0x0d, 0x90170310 }, - { 0x0e, 0x400003fb }, - { 0x0f, 0x400003fc }, - { 0x14, 0x400003fd }, - { 0x16, 0x400003f9 }, - { 0x17, 0x90a60330 }, - { 0x18, 0x400003ff }, - { 0x21, 0x01441340 }, - { 0x22, 0x40c003fe }, - {} -}; + /* sync the power-map */ + if (spec->num_pwrs) + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_IDT_SET_POWER_MAP, + spec->power_map_bits); + if (spec->dac_list) + stac92xx_power_down(codec); + return 0; +} -static void stac9205_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void stac92xx_free_kctls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_apply_pincfgs(codec, ref9205_pin_configs); - /* SPDIF-In enabled */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0; + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); } + snd_array_free(&spec->kctls); } -static void stac9205_fixup_dell_m43(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void stac92xx_shutup_pins(struct hda_codec *codec) { - struct sigmatel_spec *spec = codec->spec; - struct hda_jack_tbl *jack; + unsigned int i, def_conf; + + if (codec->bus->shutdown) + return; + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + def_conf = snd_hda_codec_get_pincfg(codec, pin->nid); + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) + snd_hda_set_pin_ctl(codec, pin->nid, 0); + } +} - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs); +static void stac92xx_shutup(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; - /* Enable unsol response for GPIO4/Dock HP connection */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - snd_hda_jack_detect_enable_callback(codec, codec->afg, - STAC_VREF_EVENT, - stac_vref_event); - jack = snd_hda_jack_tbl_get(codec, codec->afg); - if (jack) - jack->private_data = 0x01; + stac92xx_shutup_pins(codec); - spec->gpio_dir = 0x0b; - spec->eapd_mask = 0x01; - spec->gpio_mask = 0x1b; - spec->gpio_mute = 0x10; - /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, - * GPIO3 Low = DRM - */ - spec->gpio_data = 0x01; - } + if (spec->eapd_mask) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data & + ~spec->eapd_mask); } -static void stac9205_fixup_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void stac92xx_free(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->eapd_switch = 0; + if (! spec) + return; + + kfree(spec); + snd_hda_detach_beep_device(codec); } -static const struct hda_fixup stac9205_fixups[] = { - [STAC_9205_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9205_fixup_ref, - }, - [STAC_9205_DELL_M42] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_9205_m42_pin_configs, - }, - [STAC_9205_DELL_M43] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9205_fixup_dell_m43, - }, - [STAC_9205_DELL_M44] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_9205_m44_pin_configs, - }, - [STAC_9205_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9205_fixup_eapd, - }, - {} -}; +static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, + unsigned int flag) +{ + unsigned int old_ctl, pin_ctl; -static const struct hda_model_fixup stac9205_models[] = { - { .id = STAC_9205_REF, .name = "ref" }, - { .id = STAC_9205_DELL_M42, .name = "dell-m42" }, - { .id = STAC_9205_DELL_M43, .name = "dell-m43" }, - { .id = STAC_9205_DELL_M44, .name = "dell-m44" }, - { .id = STAC_9205_EAPD, .name = "eapd" }, - {} -}; + pin_ctl = snd_hda_codec_read(codec, nid, + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); -static const struct snd_pci_quirk stac9205_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, - "DFI LanParty", STAC_9205_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30, - "SigmaTel", STAC_9205_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, - "DFI LanParty", STAC_9205_REF), - /* Dell */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff, - "Dell Precision M4300", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204, - "unknown Dell", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c, - "Dell Precision", STAC_9205_DELL_M43), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, - "Dell Inspiron", STAC_9205_DELL_M44), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, - "Dell Vostro 1500", STAC_9205_DELL_M42), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229, - "Dell Vostro 1700", STAC_9205_DELL_M42), - /* Gateway */ - SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD), - SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD), - {} /* terminator */ -}; + if (pin_ctl & AC_PINCTL_IN_EN) { + /* + * we need to check the current set-up direction of + * shared input pins since they can be switched via + * "xxx as Output" mixer switch + */ + struct sigmatel_spec *spec = codec->spec; + if (nid == spec->line_switch || nid == spec->mic_switch) + return; + } + + old_ctl = pin_ctl; + /* if setting pin direction bits, clear the current + direction bits first */ + if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)) + pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + + pin_ctl |= flag; + if (old_ctl != pin_ctl) + snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl); +} + +static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, + unsigned int flag) +{ + unsigned int pin_ctl = snd_hda_codec_read(codec, nid, + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); + if (pin_ctl & flag) + snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl & ~flag); +} + +static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) +{ + if (!nid) + return 0; + return snd_hda_jack_detect(codec, nid); +} -static int stac_parse_auto_config(struct hda_codec *codec) +static void stac92xx_line_out_detect(struct hda_codec *codec, + int presence) { struct sigmatel_spec *spec = codec->spec; - int err; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); - if (err < 0) - return err; + if (cfg->speaker_outs == 0) + return; - /* add hooks */ - spec->gen.pcm_playback_hook = stac_playback_pcm_hook; - spec->gen.pcm_capture_hook = stac_capture_pcm_hook; + for (i = 0; i < cfg->line_outs; i++) { + if (presence) + break; + presence = get_pin_presence(codec, cfg->line_out_pins[i]); + if (presence) { + unsigned int pinctl; + pinctl = snd_hda_codec_read(codec, + cfg->line_out_pins[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (pinctl & AC_PINCTL_IN_EN) + presence = 0; /* mic- or line-input */ + } + } - spec->gen.automute_hook = stac_update_outputs; - spec->gen.hp_automute_hook = stac_hp_automute; - spec->gen.line_automute_hook = stac_line_automute; - spec->gen.mic_autoswitch_hook = stac_mic_autoswitch; + if (presence) { + /* disable speakers */ + for (i = 0; i < cfg->speaker_outs; i++) + stac92xx_reset_pinctl(codec, cfg->speaker_pins[i], + AC_PINCTL_OUT_EN); + if (spec->eapd_mask && spec->eapd_switch) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data & + ~spec->eapd_mask); + } else { + /* enable speakers */ + for (i = 0; i < cfg->speaker_outs; i++) + stac92xx_set_pinctl(codec, cfg->speaker_pins[i], + AC_PINCTL_OUT_EN); + if (spec->eapd_mask && spec->eapd_switch) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data | + spec->eapd_mask); + } +} - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; +/* return non-zero if the hp-pin of the given array index isn't + * a jack-detection target + */ +static int no_hp_sensing(struct sigmatel_spec *spec, int i) +{ + struct auto_pin_cfg *cfg = &spec->autocfg; - /* minimum value is actually mute */ - spec->gen.vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; + /* ignore sensing of shared line and mic jacks */ + if (cfg->hp_pins[i] == spec->line_switch) + return 1; + if (cfg->hp_pins[i] == spec->mic_switch) + return 1; + /* ignore if the pin is set as line-out */ + if (cfg->hp_pins[i] == spec->hp_switch) + return 1; + return 0; +} - /* setup analog beep controls */ - if (spec->anabeep_nid > 0) { - err = stac_auto_create_beep_ctls(codec, - spec->anabeep_nid); - if (err < 0) - return err; - } +static void stac92xx_hp_detect(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, presence; - /* setup digital beep controls and input device */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP - if (spec->digbeep_nid > 0) { - hda_nid_t nid = spec->digbeep_nid; - unsigned int caps; + presence = 0; + if (spec->gpio_mute) + presence = !(snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); - err = stac_auto_create_beep_ctls(codec, nid); - if (err < 0) - return err; - err = snd_hda_attach_beep_device(codec, nid); - if (err < 0) - return err; - if (codec->beep) { - /* IDT/STAC codecs have linear beep tone parameter */ - codec->beep->linear_tone = spec->linear_tone_beep; - /* if no beep switch is available, make its own one */ - caps = query_amp_caps(codec, nid, HDA_OUTPUT); - if (!(caps & AC_AMPCAP_MUTE)) { - err = stac_beep_switch_ctl(codec); - if (err < 0) - return err; - } + for (i = 0; i < cfg->hp_outs; i++) { + if (presence) + break; + if (no_hp_sensing(spec, i)) + continue; + presence = get_pin_presence(codec, cfg->hp_pins[i]); + if (presence) { + unsigned int pinctl; + pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (pinctl & AC_PINCTL_IN_EN) + presence = 0; /* mic- or line-input */ } } -#endif - - if (spec->gpio_led) - spec->gen.vmaster_mute.hook = stac_vmaster_hook; - if (spec->aloopback_ctl && - snd_hda_get_bool_hint(codec, "loopback") == 1) { - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl)) - return -ENOMEM; + if (presence) { + /* disable lineouts */ + if (spec->hp_switch) + stac92xx_reset_pinctl(codec, spec->hp_switch, + AC_PINCTL_OUT_EN); + for (i = 0; i < cfg->line_outs; i++) + stac92xx_reset_pinctl(codec, cfg->line_out_pins[i], + AC_PINCTL_OUT_EN); + } else { + /* enable lineouts */ + if (spec->hp_switch) + stac92xx_set_pinctl(codec, spec->hp_switch, + AC_PINCTL_OUT_EN); + for (i = 0; i < cfg->line_outs; i++) + stac92xx_set_pinctl(codec, cfg->line_out_pins[i], + AC_PINCTL_OUT_EN); + } + stac92xx_line_out_detect(codec, presence); + /* toggle hp outs */ + for (i = 0; i < cfg->hp_outs; i++) { + unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN; + if (no_hp_sensing(spec, i)) + continue; + if (1 /*presence*/) + stac92xx_set_pinctl(codec, cfg->hp_pins[i], val); +#if 0 /* FIXME */ +/* Resetting the pinctl like below may lead to (a sort of) regressions + * on some devices since they use the HP pin actually for line/speaker + * outs although the default pin config shows a different pin (that is + * wrong and useless). + * + * So, it's basically a problem of default pin configs, likely a BIOS issue. + * But, disabling the code below just works around it, and I'm too tired of + * bug reports with such devices... + */ + else + stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val); +#endif /* FIXME */ } +} - if (spec->have_spdif_mux) { - err = stac_create_spdif_mux_ctls(codec); - if (err < 0) - return err; +static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, + int enable) +{ + struct sigmatel_spec *spec = codec->spec; + unsigned int idx, val; + + for (idx = 0; idx < spec->num_pwrs; idx++) { + if (spec->pwr_nids[idx] == nid) + break; } + if (idx >= spec->num_pwrs) + return; + + idx = 1 << idx; - stac_init_power_map(codec); + val = spec->power_map_bits; + if (enable) + val &= ~idx; + else + val |= idx; - return 0; + /* power down unused output ports */ + if (val != spec->power_map_bits) { + spec->power_map_bits = val; + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_IDT_SET_POWER_MAP, val); + } } +static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid) +{ + stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid)); +} -static int stac_init(struct hda_codec *codec) +/* get the pin connection (fixed, none, etc) */ +static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx) { struct sigmatel_spec *spec = codec->spec; - unsigned int gpio; - int i; + unsigned int cfg; - /* override some hints */ - stac_store_hints(codec); + cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]); + return get_defcfg_connect(cfg); +} - /* set up GPIO */ - gpio = spec->gpio_data; - /* turn on EAPD statically when spec->eapd_switch isn't set. - * otherwise, unsol event will turn it on/off dynamically - */ - if (!spec->eapd_switch) - gpio |= spec->eapd_mask; - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio); +static int stac92xx_connected_ports(struct hda_codec *codec, + const hda_nid_t *nids, int num_nids) +{ + struct sigmatel_spec *spec = codec->spec; + int idx, num; + unsigned int def_conf; + + for (num = 0; num < num_nids; num++) { + for (idx = 0; idx < spec->num_pins; idx++) + if (spec->pin_nids[idx] == nids[num]) + break; + if (idx >= spec->num_pins) + break; + def_conf = stac_get_defcfg_connect(codec, idx); + if (def_conf == AC_JACK_PORT_NONE) + break; + } + return num; +} - snd_hda_gen_init(codec); +static void stac92xx_mic_detect(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct sigmatel_mic_route *mic; - /* sync the power-map */ - if (spec->num_pwrs) - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_IDT_SET_POWER_MAP, - spec->power_map_bits); + if (get_pin_presence(codec, spec->ext_mic.pin)) + mic = &spec->ext_mic; + else if (get_pin_presence(codec, spec->dock_mic.pin)) + mic = &spec->dock_mic; + else + mic = &spec->int_mic; + if (mic->dmux_idx >= 0) + snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, + mic->dmux_idx); + if (mic->mux_idx >= 0) + snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0, + AC_VERB_SET_CONNECT_SEL, + mic->mux_idx); +} - /* power down inactive ADCs */ - if (spec->powerdown_adcs) { - for (i = 0; i < spec->gen.num_all_adcs; i++) { - if (spec->active_adcs & (1 << i)) - continue; - snd_hda_codec_write(codec, spec->gen.all_adcs[i], 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); +static void handle_unsol_event(struct hda_codec *codec, + struct hda_jack_tbl *event) +{ + struct sigmatel_spec *spec = codec->spec; + int data; + + switch (event->action) { + case STAC_HP_EVENT: + case STAC_LO_EVENT: + stac92xx_hp_detect(codec); + break; + case STAC_MIC_EVENT: + stac92xx_mic_detect(codec); + break; + } + + switch (event->action) { + case STAC_HP_EVENT: + case STAC_LO_EVENT: + case STAC_MIC_EVENT: + case STAC_INSERT_EVENT: + case STAC_PWR_EVENT: + if (spec->num_pwrs > 0) + stac92xx_pin_sense(codec, event->nid); + + switch (codec->subsystem_id) { + case 0x103c308f: + if (event->nid == 0xb) { + int pin = AC_PINCTL_IN_EN; + + if (get_pin_presence(codec, 0xa) + && get_pin_presence(codec, 0xb)) + pin |= AC_PINCTL_VREF_80; + if (!get_pin_presence(codec, 0xb)) + pin |= AC_PINCTL_VREF_80; + + /* toggle VREF state based on mic + hp pin + * status + */ + stac92xx_auto_set_pinctl(codec, 0x0a, pin); + } } + break; + case STAC_VREF_EVENT: + data = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + /* toggle VREF state based on GPIOx status */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, + !!(data & (1 << event->private_data))); + break; } +} - return 0; +static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_jack_tbl *event = snd_hda_jack_tbl_get(codec, nid); + if (!event) + return; + handle_unsol_event(codec, event); } -static void stac_shutup(struct hda_codec *codec) +static int hp_blike_system(u32 subsystem_id); + +static void set_hp_led_gpio(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; + unsigned int gpio; - snd_hda_shutup_pins(codec); + if (spec->gpio_led) + return; - if (spec->eapd_mask) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data & - ~spec->eapd_mask); + gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + gpio &= AC_GPIO_IO_COUNT; + if (gpio > 3) + spec->gpio_led = 0x08; /* GPIO 3 */ + else + spec->gpio_led = 0x01; /* GPIO 0 */ } -static void stac_free(struct hda_codec *codec) +/* + * This method searches for the mute LED GPIO configuration + * provided as OEM string in SMBIOS. The format of that string + * is HP_Mute_LED_P_G or HP_Mute_LED_P + * where P can be 0 or 1 and defines mute LED GPIO control state (low/high) + * that corresponds to the NOT muted state of the master volume + * and G is the index of the GPIO to use as the mute LED control (0..9) + * If _G portion is missing it is assigned based on the codec ID + * + * So, HP B-series like systems may have HP_Mute_LED_0 (current models) + * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings + * + * + * The dv-series laptops don't seem to have the HP_Mute_LED* strings in + * SMBIOS - at least the ones I have seen do not have them - which include + * my own system (HP Pavilion dv6-1110ax) and my cousin's + * HP Pavilion dv9500t CTO. + * Need more information on whether it is true across the entire series. + * -- kunal + */ +static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) { struct sigmatel_spec *spec = codec->spec; + const struct dmi_device *dev = NULL; - if (!spec) - return; + if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { + get_int_hint(codec, "gpio_led_polarity", + &spec->gpio_led_polarity); + return 1; + } + if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) { + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, + NULL, dev))) { + if (sscanf(dev->name, "HP_Mute_LED_%d_%x", + &spec->gpio_led_polarity, + &spec->gpio_led) == 2) { + unsigned int max_gpio; + max_gpio = snd_hda_param_read(codec, codec->afg, + AC_PAR_GPIO_CAP); + max_gpio &= AC_GPIO_IO_COUNT; + if (spec->gpio_led < max_gpio) + spec->gpio_led = 1 << spec->gpio_led; + else + spec->vref_mute_led_nid = spec->gpio_led; + return 1; + } + if (sscanf(dev->name, "HP_Mute_LED_%d", + &spec->gpio_led_polarity) == 1) { + set_hp_led_gpio(codec); + return 1; + } + /* BIOS bug: unfilled OEM string */ + if (strstr(dev->name, "HP_Mute_LED_P_G")) { + set_hp_led_gpio(codec); + switch (codec->subsystem_id) { + case 0x103c148a: + spec->gpio_led_polarity = 0; + break; + default: + spec->gpio_led_polarity = 1; + break; + } + return 1; + } + } - snd_hda_gen_spec_free(&spec->gen); - kfree(spec); - snd_hda_detach_beep_device(codec); + /* + * Fallback case - if we don't find the DMI strings, + * we statically set the GPIO - if not a B-series system + * and default polarity is provided + */ + if (!hp_blike_system(codec->subsystem_id) && + (default_polarity == 0 || default_polarity == 1)) { + set_hp_led_gpio(codec); + spec->gpio_led_polarity = default_polarity; + return 1; + } + } + return 0; +} + +static int hp_blike_system(u32 subsystem_id) +{ + switch (subsystem_id) { + case 0x103c1520: + case 0x103c1521: + case 0x103c1523: + case 0x103c1524: + case 0x103c1525: + case 0x103c1722: + case 0x103c1723: + case 0x103c1724: + case 0x103c1725: + case 0x103c1726: + case 0x103c1727: + case 0x103c1728: + case 0x103c1729: + case 0x103c172a: + case 0x103c172b: + case 0x103c307e: + case 0x103c307f: + case 0x103c3080: + case 0x103c3081: + case 0x103c7007: + case 0x103c7008: + return 1; + } + return 0; } #ifdef CONFIG_PROC_FS @@ -3692,22 +5076,24 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer, #endif #ifdef CONFIG_PM -static int stac_resume(struct hda_codec *codec) +static int stac92xx_resume(struct hda_codec *codec) { - codec->patch_ops.init(codec); + stac92xx_init(codec); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); + /* fake event to set up pins again to override cached values */ + stac_fake_hp_events(codec); return 0; } -static int stac_suspend(struct hda_codec *codec) +static int stac92xx_suspend(struct hda_codec *codec) { - stac_shutup(codec); + stac92xx_shutup(codec); return 0; } -static void stac_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) +static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) { unsigned int afg_power_state = power_state; struct sigmatel_spec *spec = codec->spec; @@ -3724,37 +5110,67 @@ static void stac_set_power_state(struct hda_codec *codec, hda_nid_t fg, } snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, afg_power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state, true); } #else -#define stac_suspend NULL -#define stac_resume NULL -#define stac_set_power_state NULL +#define stac92xx_suspend NULL +#define stac92xx_resume NULL +#define stac92xx_set_power_state NULL #endif /* CONFIG_PM */ -static const struct hda_codec_ops stac_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = stac_init, - .free = stac_free, +/* update mute-LED accoring to the master switch */ +static void stac92xx_update_led_status(struct hda_codec *codec, int enabled) +{ + struct sigmatel_spec *spec = codec->spec; + int muted = !enabled; + + if (!spec->gpio_led) + return; + + /* LED state is inverted on these systems */ + if (spec->gpio_led_polarity) + muted = !muted; + + if (!spec->vref_mute_led_nid) { + if (muted) + spec->gpio_data |= spec->gpio_led; + else + spec->gpio_data &= ~spec->gpio_led; + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); + } else { + spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD; + stac_vrefout_set(codec, spec->vref_mute_led_nid, + spec->vref_led); + } +} + +static const struct hda_codec_ops stac92xx_patch_ops = { + .build_controls = stac92xx_build_controls, + .build_pcms = stac92xx_build_pcms, + .init = stac92xx_init, + .free = stac92xx_free, .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM - .suspend = stac_suspend, - .resume = stac_resume, + .suspend = stac92xx_suspend, + .resume = stac92xx_resume, #endif - .reboot_notify = stac_shutup, + .reboot_notify = stac92xx_shutup, }; -static int alloc_stac_spec(struct hda_codec *codec) +static int alloc_stac_spec(struct hda_codec *codec, int num_pins, + const hda_nid_t *pin_nids) { struct sigmatel_spec *spec; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; - snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */ + spec->num_pins = num_pins; + spec->pin_nids = pin_nids; + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); return 0; } @@ -3763,29 +5179,59 @@ static int patch_stac9200(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); + err = alloc_stac_spec(codec, ARRAY_SIZE(stac9200_pin_nids), + stac9200_pin_nids); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - - codec->patch_ops = stac_patch_ops; - - snd_hda_add_verbs(codec, stac9200_eapd_init); + spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS, + stac9200_models, + stac9200_cfg_tbl); + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac9200_brd_tbl[spec->board_config]); + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = stac9200_dac_nids; + spec->adc_nids = stac9200_adc_nids; + spec->mux_nids = stac9200_mux_nids; + spec->num_muxes = 1; + spec->num_dmics = 0; + spec->num_adcs = 1; + spec->num_pwrs = 0; + + if (spec->board_config == STAC_9200_M4 || + spec->board_config == STAC_9200_M4_2 || + spec->board_config == STAC_9200_OQO) + spec->init = stac9200_eapd_init; + else + spec->init = stac9200_core_init; + spec->mixer = stac9200_mixer; - snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl, - stac9200_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + if (spec->board_config == STAC_9200_PANASONIC) { + spec->gpio_mask = spec->gpio_dir = 0x09; + spec->gpio_data = 0x00; + } - err = stac_parse_auto_config(codec); + err = stac9200_parse_auto_config(codec); if (err < 0) { - stac_free(codec); + stac92xx_free(codec); return err; } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + /* CF-74 has no headphone detection, and the driver should *NOT* + * do detection and HP/speaker toggle because the hardware does it. + */ + if (spec->board_config == STAC_9200_PANASONIC) + spec->hp_detect = 0; + + codec->patch_ops = stac92xx_patch_ops; return 0; } @@ -3795,29 +5241,79 @@ static int patch_stac925x(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); + err = alloc_stac_spec(codec, ARRAY_SIZE(stac925x_pin_nids), + stac925x_pin_nids); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - codec->patch_ops = stac_patch_ops; - - snd_hda_add_verbs(codec, stac925x_core_init); - - snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl, - stac925x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + /* Check first for codec ID */ + spec->board_config = snd_hda_check_board_codec_sid_config(codec, + STAC_925x_MODELS, + stac925x_models, + stac925x_codec_id_cfg_tbl); + + /* Now checks for PCI ID, if codec ID is not found */ + if (spec->board_config < 0) + spec->board_config = snd_hda_check_board_config(codec, + STAC_925x_MODELS, + stac925x_models, + stac925x_cfg_tbl); + again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac925x_brd_tbl[spec->board_config]); + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = stac925x_dac_nids; + spec->adc_nids = stac925x_adc_nids; + spec->mux_nids = stac925x_mux_nids; + spec->num_muxes = 1; + spec->num_adcs = 1; + spec->num_pwrs = 0; + switch (codec->vendor_id) { + case 0x83847632: /* STAC9202 */ + case 0x83847633: /* STAC9202D */ + case 0x83847636: /* STAC9251 */ + case 0x83847637: /* STAC9251D */ + spec->num_dmics = STAC925X_NUM_DMICS; + spec->dmic_nids = stac925x_dmic_nids; + spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids); + spec->dmux_nids = stac925x_dmux_nids; + break; + default: + spec->num_dmics = 0; + break; + } - err = stac_parse_auto_config(codec); + spec->init = stac925x_core_init; + spec->mixer = stac925x_mixer; + spec->num_caps = 1; + spec->capvols = stac925x_capvols; + spec->capsws = stac925x_capsws; + + err = stac92xx_parse_auto_config(codec); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_925x_REF; + goto again; + } + err = -EINVAL; + } if (err < 0) { - stac_free(codec); + stac92xx_free(codec); return err; } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + codec->patch_ops = stac92xx_patch_ops; return 0; } @@ -3825,79 +5321,357 @@ static int patch_stac925x(struct hda_codec *codec) static int patch_stac92hd73xx(struct hda_codec *codec) { struct sigmatel_spec *spec; + hda_nid_t conn[STAC92HD73_DAC_COUNT + 2]; int err; int num_dacs; - err = alloc_stac_spec(codec); + err = alloc_stac_spec(codec, ARRAY_SIZE(stac92hd73xx_pin_nids), + stac92hd73xx_pin_nids); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 0; - spec->gen.mixer_nid = 0x1d; - spec->have_spdif_mux = 1; + codec->slave_dig_outs = stac92hd73xx_slave_dig_outs; + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD73XX_MODELS, + stac92hd73xx_models, + stac92hd73xx_cfg_tbl); + /* check codec subsystem id if not found */ + if (spec->board_config < 0) + spec->board_config = + snd_hda_check_board_codec_sid_config(codec, + STAC_92HD73XX_MODELS, stac92hd73xx_models, + stac92hd73xx_codec_id_cfg_tbl); +again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac92hd73xx_brd_tbl[spec->board_config]); + + num_dacs = snd_hda_get_connections(codec, 0x0a, + conn, STAC92HD73_DAC_COUNT + 2) - 1; - num_dacs = snd_hda_get_num_conns(codec, 0x0a) - 1; if (num_dacs < 3 || num_dacs > 5) { printk(KERN_WARNING "hda_codec: Could not determine " "number of channels defaulting to DAC count\n"); - num_dacs = 5; + num_dacs = STAC92HD73_DAC_COUNT; } - + spec->init = stac92hd73xx_core_init; switch (num_dacs) { case 0x3: /* 6 Channel */ - spec->aloopback_ctl = &stac92hd73xx_6ch_loopback; + spec->aloopback_ctl = stac92hd73xx_6ch_loopback; break; case 0x4: /* 8 Channel */ - spec->aloopback_ctl = &stac92hd73xx_8ch_loopback; + spec->aloopback_ctl = stac92hd73xx_8ch_loopback; break; case 0x5: /* 10 Channel */ - spec->aloopback_ctl = &stac92hd73xx_10ch_loopback; + spec->aloopback_ctl = stac92hd73xx_10ch_loopback; break; } + spec->multiout.dac_nids = spec->dac_nids; spec->aloopback_mask = 0x01; spec->aloopback_shift = 8; spec->digbeep_nid = 0x1c; + spec->mux_nids = stac92hd73xx_mux_nids; + spec->adc_nids = stac92hd73xx_adc_nids; + spec->dmic_nids = stac92hd73xx_dmic_nids; + spec->dmux_nids = stac92hd73xx_dmux_nids; + spec->smux_nids = stac92hd73xx_smux_nids; + + spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); + spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); + + spec->num_caps = STAC92HD73XX_NUM_CAPS; + spec->capvols = stac92hd73xx_capvols; + spec->capsws = stac92hd73xx_capsws; + + switch (spec->board_config) { + case STAC_DELL_EQ: + spec->init = dell_eq_core_init; + /* fallthru */ + case STAC_DELL_M6_AMIC: + case STAC_DELL_M6_DMIC: + case STAC_DELL_M6_BOTH: + spec->num_smuxes = 0; + spec->eapd_switch = 0; - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; - - spec->eapd_switch = 1; + switch (spec->board_config) { + case STAC_DELL_M6_AMIC: /* Analog Mics */ + snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); + spec->num_dmics = 0; + break; + case STAC_DELL_M6_DMIC: /* Digital Mics */ + snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + break; + case STAC_DELL_M6_BOTH: /* Both */ + snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); + snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + break; + } + break; + case STAC_ALIENWARE_M17X: + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); + spec->eapd_switch = 0; + break; + default: + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); + spec->eapd_switch = 1; + break; + } + if (spec->board_config != STAC_92HD73XX_REF) { + /* GPIO0 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; + } spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); spec->pwr_nids = stac92hd73xx_pwr_nids; - spec->gen.own_eapd_ctl = 1; - spec->gen.power_down_unused = 1; - - codec->patch_ops = stac_patch_ops; - - snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl, - stac92hd73xx_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + err = stac92xx_parse_auto_config(codec); - if (!spec->volknob_init) - snd_hda_add_verbs(codec, stac92hd73xx_core_init); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD73XX_REF; + goto again; + } + err = -EINVAL; + } - err = stac_parse_auto_config(codec); if (err < 0) { - stac_free(codec); + stac92xx_free(codec); return err; } + if (spec->board_config == STAC_92HD73XX_NO_JD) + spec->hp_detect = 0; + + codec->patch_ops = stac92xx_patch_ops; + codec->proc_widget_hook = stac92hd7x_proc_hook; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; +} + +static int hp_bnb2011_with_dock(struct hda_codec *codec) +{ + if (codec->vendor_id != 0x111d7605 && + codec->vendor_id != 0x111d76d1) + return 0; + + switch (codec->subsystem_id) { + case 0x103c1618: + case 0x103c1619: + case 0x103c161a: + case 0x103c161b: + case 0x103c161c: + case 0x103c161d: + case 0x103c161e: + case 0x103c161f: + + case 0x103c162a: + case 0x103c162b: + + case 0x103c1630: + case 0x103c1631: + + case 0x103c1633: + case 0x103c1634: + case 0x103c1635: + + case 0x103c3587: + case 0x103c3588: + case 0x103c3589: + case 0x103c358a: + case 0x103c3667: + case 0x103c3668: + case 0x103c3669: + + return 1; + } return 0; } -static void stac_setup_gpio(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; +static void stac92hd8x_add_pin(struct hda_codec *codec, hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int i; + + spec->auto_pin_nids[spec->auto_pin_cnt] = nid; + spec->auto_pin_cnt++; + + if (get_defcfg_device(def_conf) == AC_JACK_MIC_IN && + get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) { + for (i = 0; i < ARRAY_SIZE(stac92hd83xxx_dmic_nids); i++) { + if (nid == stac92hd83xxx_dmic_nids[i]) { + spec->auto_dmic_nids[spec->auto_dmic_cnt] = nid; + spec->auto_dmic_cnt++; + } + } + } +} + +static void stac92hd8x_add_adc(struct hda_codec *codec, hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + + spec->auto_adc_nids[spec->auto_adc_cnt] = nid; + spec->auto_adc_cnt++; +} + +static void stac92hd8x_add_mux(struct hda_codec *codec, hda_nid_t nid) +{ + int i, j; + struct sigmatel_spec *spec = codec->spec; + + for (i = 0; i < spec->auto_adc_cnt; i++) { + if (get_connection_index(codec, + spec->auto_adc_nids[i], nid) >= 0) { + /* mux and volume for adc_nids[i] */ + if (!spec->auto_mux_nids[i]) { + spec->auto_mux_nids[i] = nid; + /* 92hd codecs capture volume is in mux */ + spec->auto_capvols[i] = HDA_COMPOSE_AMP_VAL(nid, + 3, 0, HDA_OUTPUT); + } + for (j = 0; j < spec->auto_dmic_cnt; j++) { + if (get_connection_index(codec, nid, + spec->auto_dmic_nids[j]) >= 0) { + /* dmux for adc_nids[i] */ + if (!spec->auto_dmux_nids[i]) + spec->auto_dmux_nids[i] = nid; + break; + } + } + break; + } + } +} + +static void stac92hd8x_fill_auto_spec(struct hda_codec *codec) +{ + hda_nid_t nid, end_nid; + unsigned int wid_caps, wid_type; + struct sigmatel_spec *spec = codec->spec; + + end_nid = codec->start_nid + codec->num_nodes; + + for (nid = codec->start_nid; nid < end_nid; nid++) { + wid_caps = get_wcaps(codec, nid); + wid_type = get_wcaps_type(wid_caps); + + if (wid_type == AC_WID_PIN) + stac92hd8x_add_pin(codec, nid); + + if (wid_type == AC_WID_AUD_IN && !(wid_caps & AC_WCAP_DIGITAL)) + stac92hd8x_add_adc(codec, nid); + } + + for (nid = codec->start_nid; nid < end_nid; nid++) { + wid_caps = get_wcaps(codec, nid); + wid_type = get_wcaps_type(wid_caps); + + if (wid_type == AC_WID_AUD_SEL) + stac92hd8x_add_mux(codec, nid); + } + + spec->pin_nids = spec->auto_pin_nids; + spec->num_pins = spec->auto_pin_cnt; + spec->adc_nids = spec->auto_adc_nids; + spec->num_adcs = spec->auto_adc_cnt; + spec->capvols = spec->auto_capvols; + spec->capsws = spec->auto_capvols; + spec->num_caps = spec->auto_adc_cnt; + spec->mux_nids = spec->auto_mux_nids; + spec->num_muxes = spec->auto_adc_cnt; + spec->dmux_nids = spec->auto_dmux_nids; + spec->num_dmuxes = spec->auto_adc_cnt; + spec->dmic_nids = spec->auto_dmic_nids; + spec->num_dmics = spec->auto_dmic_cnt; +} + +static int patch_stac92hd83xxx(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + int default_polarity = -1; /* no default cfg */ + int err; + + err = alloc_stac_spec(codec, 0, NULL); /* pins filled later */ + if (err < 0) + return err; + + if (hp_bnb2011_with_dock(codec)) { + snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); + snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); + } + + codec->epss = 0; /* longer delay needed for D3 */ + stac92hd8x_fill_auto_spec(codec); + + spec = codec->spec; + spec->linear_tone_beep = 0; + codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs; + spec->digbeep_nid = 0x21; + spec->pwr_nids = stac92hd83xxx_pwr_nids; + spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); + spec->multiout.dac_nids = spec->dac_nids; + spec->init = stac92hd83xxx_core_init; + + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD83XXX_MODELS, + stac92hd83xxx_models, + stac92hd83xxx_cfg_tbl); + /* check codec subsystem id if not found */ + if (spec->board_config < 0) + spec->board_config = + snd_hda_check_board_codec_sid_config(codec, + STAC_92HD83XXX_MODELS, stac92hd83xxx_models, + stac92hd83xxx_codec_id_cfg_tbl); +again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac92hd83xxx_brd_tbl[spec->board_config]); + + codec->patch_ops = stac92xx_patch_ops; + + switch (spec->board_config) { + case STAC_HP_ZEPHYR: + spec->init = stac92hd83xxx_hp_zephyr_init; + break; + case STAC_92HD83XXX_HP_LED: + default_polarity = 0; + break; + case STAC_92HD83XXX_HP_INV_LED: + default_polarity = 1; + break; + case STAC_92HD83XXX_HP_MIC_LED: + spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ + break; + case STAC_92HD83XXX_HEADSET_JACK: + spec->headset_jack = 1; + break; + } + + if (find_mute_led_cfg(codec, default_polarity)) + snd_printd("mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); if (spec->gpio_led) { if (!spec->vref_mute_led_nid) { @@ -3906,7 +5680,7 @@ static void stac_setup_gpio(struct hda_codec *codec) spec->gpio_data |= spec->gpio_led; } else { codec->patch_ops.set_power_state = - stac_set_power_state; + stac92xx_set_power_state; } } @@ -3915,91 +5689,99 @@ static void stac_setup_gpio(struct hda_codec *codec) spec->gpio_dir |= spec->mic_mute_led_gpio; spec->mic_mute_led_on = true; spec->gpio_data |= spec->mic_mute_led_gpio; - - spec->gen.cap_sync_hook = stac_capture_led_hook; } -} - -static int patch_stac92hd83xxx(struct hda_codec *codec) -{ - struct sigmatel_spec *spec; - int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - - codec->epss = 0; /* longer delay needed for D3 */ - - spec = codec->spec; - spec->linear_tone_beep = 0; - spec->gen.own_eapd_ctl = 1; - spec->gen.power_down_unused = 1; - spec->gen.mixer_nid = 0x1b; - - spec->digbeep_nid = 0x21; - spec->pwr_nids = stac92hd83xxx_pwr_nids; - spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); - spec->default_polarity = -1; /* no default cfg */ - - codec->patch_ops = stac_patch_ops; - - snd_hda_add_verbs(codec, stac92hd83xxx_core_init); - - snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl, - stac92hd83xxx_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - stac_setup_gpio(codec); + err = stac92xx_parse_auto_config(codec); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD83XXX_REF; + goto again; + } + err = -EINVAL; + } - err = stac_parse_auto_config(codec); if (err < 0) { - stac_free(codec); + stac92xx_free(codec); return err; } codec->proc_widget_hook = stac92hd_proc_hook; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - return 0; } -static const hda_nid_t stac92hd95_pwr_nids[] = { - 0x0a, 0x0b, 0x0c, 0x0d -}; - -static int patch_stac92hd95(struct hda_codec *codec) +static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec, + hda_nid_t dig0pin) { - struct sigmatel_spec *spec; - int err; + struct sigmatel_spec *spec = codec->spec; + int idx; - err = alloc_stac_spec(codec); - if (err < 0) - return err; + for (idx = 0; idx < spec->num_pins; idx++) + if (spec->pin_nids[idx] == dig0pin) + break; + if ((idx + 2) >= spec->num_pins) + return 0; - codec->epss = 0; /* longer delay needed for D3 */ + /* dig1pin case */ + if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE) + return 2; - spec = codec->spec; - spec->linear_tone_beep = 0; - spec->gen.own_eapd_ctl = 1; - spec->gen.power_down_unused = 1; + /* dig0pin + dig2pin case */ + if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE) + return 2; + if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE) + return 1; + else + return 0; +} + +/* HP dv7 bass switch - GPIO5 */ +#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info +static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20); + return 0; +} + +static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + unsigned int gpio_data; - spec->digbeep_nid = 0x19; - spec->pwr_nids = stac92hd95_pwr_nids; - spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids); - spec->default_polarity = -1; /* no default cfg */ + gpio_data = (spec->gpio_data & ~0x20) | + (ucontrol->value.integer.value[0] ? 0x20 : 0); + if (gpio_data == spec->gpio_data) + return 0; + spec->gpio_data = gpio_data; + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); + return 1; +} - codec->patch_ops = stac_patch_ops; +static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = stac_hp_bass_gpio_info, + .get = stac_hp_bass_gpio_get, + .put = stac_hp_bass_gpio_put, +}; - err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return err; - } +static int stac_add_hp_bass_switch(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; - codec->proc_widget_hook = stac92hd_proc_hook; + if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl, + "Bass Speaker Playback Switch", 0)) + return -ENOMEM; + spec->gpio_mask |= 0x20; + spec->gpio_dir |= 0x20; + spec->gpio_data |= 0x20; return 0; } @@ -4007,32 +5789,82 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init; + unsigned int pin_cfg; int err; - err = alloc_stac_spec(codec); + err = alloc_stac_spec(codec, STAC92HD71BXX_NUM_PINS, + stac92hd71bxx_pin_nids_4port); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 0; - spec->gen.own_eapd_ctl = 1; - spec->gen.power_down_unused = 1; - spec->gen.mixer_nid = 0x17; - spec->have_spdif_mux = 1; + codec->patch_ops = stac92xx_patch_ops; + switch (codec->vendor_id) { + case 0x111d76b6: + case 0x111d76b7: + break; + case 0x111d7603: + case 0x111d7608: + /* On 92HD75Bx 0x27 isn't a pin nid */ + spec->num_pins--; + /* fallthrough */ + default: + spec->pin_nids = stac92hd71bxx_pin_nids_6port; + } + spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD71BXX_MODELS, + stac92hd71bxx_models, + stac92hd71bxx_cfg_tbl); +again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac92hd71bxx_brd_tbl[spec->board_config]); + + if (spec->board_config != STAC_92HD71BXX_REF) { + /* GPIO0 = EAPD */ + spec->gpio_mask = 0x01; + spec->gpio_dir = 0x01; + spec->gpio_data = 0x01; + } - codec->patch_ops = stac_patch_ops; + spec->dmic_nids = stac92hd71bxx_dmic_nids; + spec->dmux_nids = stac92hd71bxx_dmux_nids; - /* GPIO0 = EAPD */ - spec->gpio_mask = 0x01; - spec->gpio_dir = 0x01; - spec->gpio_data = 0x01; + spec->num_caps = STAC92HD71BXX_NUM_CAPS; + spec->capvols = stac92hd71bxx_capvols; + spec->capsws = stac92hd71bxx_capsws; switch (codec->vendor_id) { case 0x111d76b6: /* 4 Port without Analog Mixer */ case 0x111d76b7: unmute_init++; + /* fallthru */ + case 0x111d76b4: /* 6 Port without Analog Mixer */ + case 0x111d76b5: + codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; + spec->num_dmics = stac92xx_connected_ports(codec, + stac92hd71bxx_dmic_nids, + STAC92HD71BXX_NUM_DMICS); break; case 0x111d7608: /* 5 Port with Analog Mixer */ + switch (spec->board_config) { + case STAC_HP_M4: + /* Enable VREF power saving on GPIO1 detect */ + err = stac_add_event(codec, codec->afg, + STAC_VREF_EVENT, 0x02); + if (err < 0) + return err; + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); + snd_hda_jack_detect_enable(codec, codec->afg, 0); + spec->gpio_mask |= 0x02; + break; + } if ((codec->revision_id & 0xf) == 0 || (codec->revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ @@ -4041,44 +5873,157 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) unmute_init++; snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); + spec->dmic_nids = stac92hd71bxx_dmic_5port_nids; + spec->num_dmics = stac92xx_connected_ports(codec, + stac92hd71bxx_dmic_5port_nids, + STAC92HD71BXX_NUM_DMICS - 1); break; case 0x111d7603: /* 6 Port with Analog Mixer */ if ((codec->revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ + /* fallthru */ + default: + codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; + spec->num_dmics = stac92xx_connected_ports(codec, + stac92hd71bxx_dmic_nids, + STAC92HD71BXX_NUM_DMICS); break; } if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB) - snd_hda_add_verbs(codec, stac92hd71bxx_core_init); + spec->init = stac92hd71bxx_core_init; if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) snd_hda_sequence_write_cache(codec, unmute_init); - spec->aloopback_ctl = &stac92hd71bxx_loopback; + spec->aloopback_ctl = stac92hd71bxx_loopback; spec->aloopback_mask = 0x50; spec->aloopback_shift = 0; spec->powerdown_adcs = 1; spec->digbeep_nid = 0x26; - spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); + spec->mux_nids = stac92hd71bxx_mux_nids; + spec->adc_nids = stac92hd71bxx_adc_nids; + spec->smux_nids = stac92hd71bxx_smux_nids; spec->pwr_nids = stac92hd71bxx_pwr_nids; - snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl, - stac92hd71bxx_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); + spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); + spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e); + + snd_printdd("Found board config: %d\n", spec->board_config); + + switch (spec->board_config) { + case STAC_HP_M4: + /* enable internal microphone */ + snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040); + stac92xx_auto_set_pinctl(codec, 0x0e, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_80); + /* fallthru */ + case STAC_DELL_M4_2: + spec->num_dmics = 0; + spec->num_smuxes = 0; + spec->num_dmuxes = 0; + break; + case STAC_DELL_M4_1: + case STAC_DELL_M4_3: + spec->num_dmics = 1; + spec->num_smuxes = 0; + spec->num_dmuxes = 1; + break; + case STAC_HP_DV4_1222NR: + spec->num_dmics = 1; + /* I don't know if it needs 1 or 2 smuxes - will wait for + * bug reports to fix if needed + */ + spec->num_smuxes = 1; + spec->num_dmuxes = 1; + /* fallthrough */ + case STAC_HP_DV4: + spec->gpio_led = 0x01; + /* fallthrough */ + case STAC_HP_DV5: + snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); + stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); + /* HP dv6 gives the headphone pin as a line-out. Thus we + * need to set hp_detect flag here to force to enable HP + * detection. + */ + spec->hp_detect = 1; + break; + case STAC_HP_HDX: + spec->num_dmics = 1; + spec->num_dmuxes = 1; + spec->num_smuxes = 1; + spec->gpio_led = 0x08; + break; + } + + if (hp_blike_system(codec->subsystem_id)) { + pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); + if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || + get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || + get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) { + /* It was changed in the BIOS to just satisfy MS DTM. + * Lets turn it back into slaved HP + */ + pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) + | (AC_JACK_HP_OUT << + AC_DEFCFG_DEVICE_SHIFT); + pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC + | AC_DEFCFG_SEQUENCE))) + | 0x1f; + snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg); + } + } + + if (find_mute_led_cfg(codec, 1)) + snd_printd("mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); + + if (spec->gpio_led) { + if (!spec->vref_mute_led_nid) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; + } else { + codec->patch_ops.set_power_state = + stac92xx_set_power_state; + } + } + + spec->multiout.dac_nids = spec->dac_nids; - stac_setup_gpio(codec); + err = stac92xx_parse_auto_config(codec); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD71BXX_REF; + goto again; + } + err = -EINVAL; + } - err = stac_parse_auto_config(codec); if (err < 0) { - stac_free(codec); + stac92xx_free(codec); return err; } - codec->proc_widget_hook = stac92hd7x_proc_hook; + /* enable bass on HP dv7 */ + if (spec->board_config == STAC_HP_DV4 || + spec->board_config == STAC_HP_DV5) { + unsigned int cap; + cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); + cap &= AC_GPIO_IO_COUNT; + if (cap >= 6) + stac_add_hp_bass_switch(codec); + } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + codec->proc_widget_hook = stac92hd7x_proc_hook; return 0; } @@ -4088,86 +6033,217 @@ static int patch_stac922x(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); + err = alloc_stac_spec(codec, ARRAY_SIZE(stac922x_pin_nids), + stac922x_pin_nids); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; + spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS, + stac922x_models, + stac922x_cfg_tbl); + if (spec->board_config == STAC_INTEL_MAC_AUTO) { + spec->gpio_mask = spec->gpio_dir = 0x03; + spec->gpio_data = 0x03; + /* Intel Macs have all same PCI SSID, so we need to check + * codec SSID to distinguish the exact models + */ + printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id); + switch (codec->subsystem_id) { + + case 0x106b0800: + spec->board_config = STAC_INTEL_MAC_V1; + break; + case 0x106b0600: + case 0x106b0700: + spec->board_config = STAC_INTEL_MAC_V2; + break; + case 0x106b0e00: + case 0x106b0f00: + case 0x106b1600: + case 0x106b1700: + case 0x106b0200: + case 0x106b1e00: + spec->board_config = STAC_INTEL_MAC_V3; + break; + case 0x106b1a00: + case 0x00000100: + spec->board_config = STAC_INTEL_MAC_V4; + break; + case 0x106b0a00: + case 0x106b2200: + spec->board_config = STAC_INTEL_MAC_V5; + break; + default: + spec->board_config = STAC_INTEL_MAC_V3; + break; + } + } - codec->patch_ops = stac_patch_ops; + again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac922x_brd_tbl[spec->board_config]); - snd_hda_add_verbs(codec, stac922x_core_init); + spec->adc_nids = stac922x_adc_nids; + spec->mux_nids = stac922x_mux_nids; + spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids); + spec->num_dmics = 0; + spec->num_pwrs = 0; - /* Fix Mux capture level; max to 2 */ - snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT, - (0 << AC_AMPCAP_OFFSET_SHIFT) | - (2 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); + spec->init = stac922x_core_init; - snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl, - stac922x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + spec->num_caps = STAC922X_NUM_CAPS; + spec->capvols = stac922x_capvols; + spec->capsws = stac922x_capsws; - err = stac_parse_auto_config(codec); + spec->multiout.dac_nids = spec->dac_nids; + + err = stac92xx_parse_auto_config(codec); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_D945_REF; + goto again; + } + err = -EINVAL; + } if (err < 0) { - stac_free(codec); + stac92xx_free(codec); return err; } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + codec->patch_ops = stac92xx_patch_ops; + + /* Fix Mux capture level; max to 2 */ + snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT, + (0 << AC_AMPCAP_OFFSET_SHIFT) | + (2 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); return 0; } -static const char * const stac927x_spdif_labels[] = { - "Digital Playback", "ADAT", "Analog Mux 1", - "Analog Mux 2", "Analog Mux 3", NULL -}; - static int patch_stac927x(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); + err = alloc_stac_spec(codec, ARRAY_SIZE(stac927x_pin_nids), + stac927x_pin_nids); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - spec->have_spdif_mux = 1; - spec->spdif_labels = stac927x_spdif_labels; + codec->slave_dig_outs = stac927x_slave_dig_outs; + spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS, + stac927x_models, + stac927x_cfg_tbl); + again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac927x_brd_tbl[spec->board_config]); spec->digbeep_nid = 0x23; + spec->adc_nids = stac927x_adc_nids; + spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + spec->mux_nids = stac927x_mux_nids; + spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); + spec->smux_nids = stac927x_smux_nids; + spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids); + spec->spdif_labels = stac927x_spdif_labels; + spec->dac_list = stac927x_dac_nids; + spec->multiout.dac_nids = spec->dac_nids; - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x01; - spec->gpio_dir = spec->gpio_data = 0x01; + if (spec->board_config != STAC_D965_REF) { + /* GPIO0 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = 0x01; + spec->gpio_dir = spec->gpio_data = 0x01; + } - spec->aloopback_ctl = &stac927x_loopback; - spec->aloopback_mask = 0x40; - spec->aloopback_shift = 0; - spec->eapd_switch = 1; + switch (spec->board_config) { + case STAC_D965_3ST: + case STAC_D965_5ST: + /* GPIO0 High = Enable EAPD */ + spec->num_dmics = 0; + spec->init = d965_core_init; + break; + case STAC_DELL_BIOS: + switch (codec->subsystem_id) { + case 0x10280209: + case 0x1028022e: + /* correct the device field to SPDIF out */ + snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070); + break; + } + /* configure the analog microphone on some laptops */ + snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130); + /* correct the front output jack as a hp out */ + snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f); + /* correct the front input jack as a mic */ + snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130); + /* fallthru */ + case STAC_DELL_3ST: + if (codec->subsystem_id != 0x1028022f) { + /* GPIO2 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = 0x04; + spec->gpio_dir = spec->gpio_data = 0x04; + } + spec->dmic_nids = stac927x_dmic_nids; + spec->num_dmics = STAC927X_NUM_DMICS; - codec->patch_ops = stac_patch_ops; + spec->init = dell_3st_core_init; + spec->dmux_nids = stac927x_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); + break; + case STAC_927X_VOLKNOB: + spec->num_dmics = 0; + spec->init = stac927x_volknob_core_init; + break; + default: + spec->num_dmics = 0; + spec->init = stac927x_core_init; + break; + } - snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl, - stac927x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + spec->num_caps = STAC927X_NUM_CAPS; + spec->capvols = stac927x_capvols; + spec->capsws = stac927x_capsws; - if (!spec->volknob_init) - snd_hda_add_verbs(codec, stac927x_core_init); + spec->num_pwrs = 0; + spec->aloopback_ctl = stac927x_loopback; + spec->aloopback_mask = 0x40; + spec->aloopback_shift = 0; + spec->eapd_switch = 1; - err = stac_parse_auto_config(codec); + err = stac92xx_parse_auto_config(codec); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_D965_REF; + goto again; + } + err = -EINVAL; + } if (err < 0) { - stac_free(codec); + stac92xx_free(codec); return err; } + codec->patch_ops = stac92xx_patch_ops; + codec->proc_widget_hook = stac927x_proc_hook; /* @@ -4182,7 +6258,9 @@ static int patch_stac927x(struct hda_codec *codec) */ codec->bus->needs_damn_long_delay = 1; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + /* no jack detecion for ref-no-jd model */ + if (spec->board_config == STAC_D965_REF_NO_JD) + spec->hp_detect = 0; return 0; } @@ -4192,45 +6270,102 @@ static int patch_stac9205(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); + err = alloc_stac_spec(codec, ARRAY_SIZE(stac9205_pin_nids), + stac9205_pin_nids); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - spec->have_spdif_mux = 1; + spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS, + stac9205_models, + stac9205_cfg_tbl); + again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac9205_brd_tbl[spec->board_config]); spec->digbeep_nid = 0x23; - - snd_hda_add_verbs(codec, stac9205_core_init); - spec->aloopback_ctl = &stac9205_loopback; + spec->adc_nids = stac9205_adc_nids; + spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids); + spec->mux_nids = stac9205_mux_nids; + spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids); + spec->smux_nids = stac9205_smux_nids; + spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids); + spec->dmic_nids = stac9205_dmic_nids; + spec->num_dmics = STAC9205_NUM_DMICS; + spec->dmux_nids = stac9205_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids); + spec->num_pwrs = 0; + + spec->init = stac9205_core_init; + spec->aloopback_ctl = stac9205_loopback; + + spec->num_caps = STAC9205_NUM_CAPS; + spec->capvols = stac9205_capvols; + spec->capsws = stac9205_capsws; spec->aloopback_mask = 0x40; spec->aloopback_shift = 0; - - /* GPIO0 High = EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; - /* Turn on/off EAPD per HP plugging */ - spec->eapd_switch = 1; + if (spec->board_config != STAC_9205_EAPD) + spec->eapd_switch = 1; + spec->multiout.dac_nids = spec->dac_nids; + + switch (spec->board_config){ + case STAC_9205_DELL_M43: + /* Enable SPDIF in/out */ + snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030); + snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030); - codec->patch_ops = stac_patch_ops; + /* Enable unsol response for GPIO4/Dock HP connection */ + err = stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x01); + if (err < 0) + return err; + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); + snd_hda_jack_detect_enable(codec, codec->afg, 0); - snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl, - stac9205_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + spec->gpio_dir = 0x0b; + spec->eapd_mask = 0x01; + spec->gpio_mask = 0x1b; + spec->gpio_mute = 0x10; + /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, + * GPIO3 Low = DRM + */ + spec->gpio_data = 0x01; + break; + case STAC_9205_REF: + /* SPDIF-In enabled */ + break; + default: + /* GPIO0 High = EAPD */ + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; + break; + } - err = stac_parse_auto_config(codec); + err = stac92xx_parse_auto_config(codec); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_9205_REF; + goto again; + } + err = -EINVAL; + } if (err < 0) { - stac_free(codec); + stac92xx_free(codec); return err; } - codec->proc_widget_hook = stac9205_proc_hook; + codec->patch_ops = stac92xx_patch_ops; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + codec->proc_widget_hook = stac9205_proc_hook; return 0; } @@ -4245,32 +6380,40 @@ static const struct hda_verb stac9872_core_init[] = { {} }; -static const struct hda_pintbl stac9872_vaio_pin_configs[] = { - { 0x0a, 0x03211020 }, - { 0x0b, 0x411111f0 }, - { 0x0c, 0x411111f0 }, - { 0x0d, 0x03a15030 }, - { 0x0e, 0x411111f0 }, - { 0x0f, 0x90170110 }, - { 0x11, 0x411111f0 }, - { 0x13, 0x411111f0 }, - { 0x14, 0x90a7013e }, - {} +static const hda_nid_t stac9872_pin_nids[] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x11, 0x13, 0x14, }; -static const struct hda_model_fixup stac9872_models[] = { - { .id = STAC_9872_VAIO, .name = "vaio" }, - {} +static const hda_nid_t stac9872_adc_nids[] = { + 0x8 /*,0x6*/ }; -static const struct hda_fixup stac9872_fixups[] = { - [STAC_9872_VAIO] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac9872_vaio_pin_configs, - }, +static const hda_nid_t stac9872_mux_nids[] = { + 0x15 +}; + +static const unsigned long stac9872_capvols[] = { + HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), +}; +#define stac9872_capsws stac9872_capvols + +static const unsigned int stac9872_vaio_pin_configs[9] = { + 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030, + 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0, + 0x90a7013e +}; + +static const char * const stac9872_models[STAC_9872_MODELS] = { + [STAC_9872_AUTO] = "auto", + [STAC_9872_VAIO] = "vaio", }; -static const struct snd_pci_quirk stac9872_fixup_tbl[] = { +static const unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = { + [STAC_9872_VAIO] = stac9872_vaio_pin_configs, +}; + +static const struct snd_pci_quirk stac9872_cfg_tbl[] = { SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0, "Sony VAIO F/S", STAC_9872_VAIO), {} /* terminator */ @@ -4281,30 +6424,41 @@ static int patch_stac9872(struct hda_codec *codec) struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); + err = alloc_stac_spec(codec, ARRAY_SIZE(stac9872_pin_nids), + stac9872_pin_nids); if (err < 0) return err; spec = codec->spec; spec->linear_tone_beep = 1; - spec->gen.own_eapd_ctl = 1; - - codec->patch_ops = stac_patch_ops; - - snd_hda_add_verbs(codec, stac9872_core_init); - snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl, - stac9872_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = stac_parse_auto_config(codec); + spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS, + stac9872_models, + stac9872_cfg_tbl); + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac9872_brd_tbl[spec->board_config]); + + spec->multiout.dac_nids = spec->dac_nids; + spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids); + spec->adc_nids = stac9872_adc_nids; + spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids); + spec->mux_nids = stac9872_mux_nids; + spec->init = stac9872_core_init; + spec->num_caps = 1; + spec->capvols = stac9872_capvols; + spec->capsws = stac9872_capsws; + + err = stac92xx_parse_auto_config(codec); if (err < 0) { - stac_free(codec); + stac92xx_free(codec); return -EINVAL; } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - + spec->input_mux = &spec->private_imux; + codec->patch_ops = stac92xx_patch_ops; return 0; } @@ -4375,7 +6529,6 @@ static const struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx }, - { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 }, { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, diff --git a/trunk/sound/pci/hda/patch_via.c b/trunk/sound/pci/hda/patch_via.c index ca7d962a08a6..09bb64996d72 100644 --- a/trunk/sound/pci/hda/patch_via.c +++ b/trunk/sound/pci/hda/patch_via.c @@ -56,7 +56,6 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" /* Pin Widget NID */ #define VT1708_HP_PIN_NID 0x20 @@ -87,6 +86,39 @@ enum VIA_HDA_CODEC { (spec)->codec_type == VT1812 ||\ (spec)->codec_type == VT1802) +#define MAX_NID_PATH_DEPTH 5 + +/* output-path: DAC -> ... -> pin + * idx[] contains the source index number of the next widget; + * e.g. idx[0] is the index of the DAC selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ +struct nid_path { + int depth; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int vol_ctl; + unsigned int mute_ctl; +}; + +/* input-path */ +struct via_input { + hda_nid_t pin; /* input-pin or aa-mix */ + int adc_idx; /* ADC index to be used */ + int mux_idx; /* MUX index (if any) */ + const char *label; /* input-source label */ +}; + +#define VIA_MAX_ADCS 3 + +enum { + STREAM_MULTI_OUT = (1 << 0), + STREAM_INDEP_HP = (1 << 1), +}; + struct via_spec { struct hda_gen_spec gen; @@ -97,7 +129,77 @@ struct via_spec { const struct hda_verb *init_verbs[5]; unsigned int num_iverbs; + char stream_name_analog[32]; + char stream_name_hp[32]; + const struct hda_pcm_stream *stream_analog_playback; + const struct hda_pcm_stream *stream_analog_capture; + + char stream_name_digital[32]; + const struct hda_pcm_stream *stream_digital_playback; + const struct hda_pcm_stream *stream_digital_capture; + + /* playback */ + struct hda_multi_out multiout; + hda_nid_t slave_dig_outs[2]; + hda_nid_t hp_dac_nid; + hda_nid_t speaker_dac_nid; + int hp_indep_shared; /* indep HP-DAC is shared with side ch */ + int opened_streams; /* STREAM_* bits */ + int active_streams; /* STREAM_* bits */ + int aamix_mode; /* loopback is enabled for output-path? */ + + /* Output-paths: + * There are different output-paths depending on the setup. + * out_path, hp_path and speaker_path are primary paths. If both + * direct DAC and aa-loopback routes are available, these contain + * the former paths. Meanwhile *_mix_path contain the paths with + * loopback mixer. (Since the loopback is only for front channel, + * no out_mix_path for surround channels.) + * The HP output has another path, hp_indep_path, which is used in + * the independent-HP mode. + */ + struct nid_path out_path[HDA_SIDE + 1]; + struct nid_path out_mix_path; + struct nid_path hp_path; + struct nid_path hp_mix_path; + struct nid_path hp_indep_path; + struct nid_path speaker_path; + struct nid_path speaker_mix_path; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t adc_nids[VIA_MAX_ADCS]; + hda_nid_t mux_nids[VIA_MAX_ADCS]; + hda_nid_t aa_mix_nid; + hda_nid_t dig_in_nid; + + /* capture source */ + bool dyn_adc_switch; + int num_inputs; + struct via_input inputs[AUTO_CFG_MAX_INS + 1]; + unsigned int cur_mux[VIA_MAX_ADCS]; + + /* dynamic DAC switching */ + unsigned int cur_dac_stream_tag; + unsigned int cur_dac_format; + unsigned int cur_hp_stream_tag; + unsigned int cur_hp_format; + + /* dynamic ADC switching */ + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + + /* PCM information */ + struct hda_pcm pcm_rec[3]; + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + /* HP mode source */ + unsigned int hp_independent_mode; unsigned int dmic_enabled; unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; @@ -105,22 +207,36 @@ struct via_spec { /* analog low-power control */ bool alc_mode; + /* smart51 setup */ + unsigned int smart51_nums; + hda_nid_t smart51_pins[2]; + int smart51_idxs[2]; + const char *smart51_labels[2]; + unsigned int smart51_enabled; + /* work to check hp jack state */ + struct hda_codec *codec; + struct delayed_work vt1708_hp_work; int hp_work_active; int vt1708_jack_detect; + int vt1708_hp_present; void (*set_widgets_power_state)(struct hda_codec *codec); unsigned int dac_stream_tag[4]; + + struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[8]; + + /* bind capture-volume */ + struct hda_bind_ctls *bind_cap_vol; + struct hda_bind_ctls *bind_cap_sw; + + struct mutex config_mutex; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); -static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action); -static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl); - -static struct via_spec *via_new_spec(struct hda_codec *codec) +static struct via_spec * via_new_spec(struct hda_codec *codec) { struct via_spec *spec; @@ -128,15 +244,15 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) if (spec == NULL) return NULL; + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); + mutex_init(&spec->config_mutex); codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); + spec->codec = codec; spec->codec_type = get_codec_type(codec); /* VT1708BCE & VT1708S are almost same */ if (spec->codec_type == VT1708BCE) spec->codec_type = VT1708S; - spec->no_pin_power_ctl = 1; - spec->gen.indep_hp = 1; - spec->gen.pcm_playback_hook = via_playback_pcm_hook; + snd_hda_gen_init(&spec->gen); return spec; } @@ -192,6 +308,16 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) return codec_type; }; +#define VIA_JACK_EVENT 0x20 +#define VIA_HP_EVENT 0x01 +#define VIA_LINE_EVENT 0x03 + +enum { + VIA_CTL_WIDGET_VOL, + VIA_CTL_WIDGET_MUTE, + VIA_CTL_WIDGET_ANALOG_MUTE, +}; + static void analog_low_current_mode(struct hda_codec *codec); static bool is_aa_path_mute(struct hda_codec *codec); @@ -199,34 +325,31 @@ static bool is_aa_path_mute(struct hda_codec *codec); (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ !is_aa_path_mute(codec)) -static void vt1708_stop_hp_work(struct hda_codec *codec) +static void vt1708_stop_hp_work(struct via_spec *spec) { - struct via_spec *spec = codec->spec; - if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) return; if (spec->hp_work_active) { - snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); - cancel_delayed_work_sync(&codec->jackpoll_work); - spec->hp_work_active = false; - codec->jackpoll_interval = 0; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1); + cancel_delayed_work_sync(&spec->vt1708_hp_work); + spec->hp_work_active = 0; } } -static void vt1708_update_hp_work(struct hda_codec *codec) +static void vt1708_update_hp_work(struct via_spec *spec) { - struct via_spec *spec = codec->spec; - if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) return; - if (spec->vt1708_jack_detect) { + if (spec->vt1708_jack_detect && + (spec->active_streams || hp_detect_with_aa(spec->codec))) { if (!spec->hp_work_active) { - codec->jackpoll_interval = msecs_to_jiffies(100); - snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); - queue_delayed_work(codec->bus->workq, - &codec->jackpoll_work, 0); - spec->hp_work_active = true; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0); + schedule_delayed_work(&spec->vt1708_hp_work, + msecs_to_jiffies(100)); + spec->hp_work_active = 1; } - } else if (!hp_detect_with_aa(codec)) - vt1708_stop_hp_work(codec); + } else if (!hp_detect_with_aa(spec->codec)) + vt1708_stop_hp_work(spec); } static void set_widgets_power_state(struct hda_codec *codec) @@ -236,10 +359,361 @@ static void set_widgets_power_state(struct hda_codec *codec) spec->set_widgets_power_state(codec); } +static int analog_input_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + set_widgets_power_state(codec); + analog_low_current_mode(snd_kcontrol_chip(kcontrol)); + vt1708_update_hp_work(codec->spec); + return change; +} + +/* modify .put = snd_hda_mixer_amp_switch_put */ +#define ANALOG_INPUT_MUTE \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = NULL, \ + .index = 0, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = analog_input_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } + +static const struct snd_kcontrol_new via_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + ANALOG_INPUT_MUTE, +}; + + +/* add dynamic controls */ +static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, + const struct snd_kcontrol_new *tmpl, + const char *name) +{ + struct snd_kcontrol_new *knew; + + knew = snd_array_new(&spec->kctls); + if (!knew) + return NULL; + *knew = *tmpl; + if (!name) + name = tmpl->name; + if (name) { + knew->name = kstrdup(name, GFP_KERNEL); + if (!knew->name) + return NULL; + } + return knew; +} + +static int __via_add_control(struct via_spec *spec, int type, const char *name, + int idx, unsigned long val) +{ + struct snd_kcontrol_new *knew; + + knew = __via_clone_ctl(spec, &via_control_templates[type], name); + if (!knew) + return -ENOMEM; + knew->index = idx; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + knew->private_value = val; + return 0; +} + +#define via_add_control(spec, type, name, val) \ + __via_add_control(spec, type, name, 0, val) + +#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) + +static void via_free_kctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +} + +/* create input playback/capture controls for the given pin */ +static int via_new_analog_input(struct via_spec *spec, const char *ctlname, + int type_idx, int idx, int mix_nid) +{ + char name[32]; + int err; + + sprintf(name, "%s Playback Volume", ctlname); + err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", ctlname); + err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + if (err < 0) + return err; + return 0; +} + +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 0) + +static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int mask) +{ + unsigned int caps; + if (!nid) + return false; + caps = get_wcaps(codec, nid); + if (dir == HDA_INPUT) + caps &= AC_WCAP_IN_AMP; + else + caps &= AC_WCAP_OUT_AMP; + if (!caps) + return false; + if (query_amp_caps(codec, nid, dir) & mask) + return true; + return false; +} + +#define have_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) + +/* enable/disable the output-route mixers */ +static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, + hda_nid_t mix_nid, int idx, bool enable) +{ + int i, num, val; + + if (!path) + return; + num = snd_hda_get_num_conns(codec, mix_nid); + for (i = 0; i < num; i++) { + if (i == idx) + val = AMP_IN_UNMUTE(i); + else + val = AMP_IN_MUTE(i); + snd_hda_codec_write(codec, mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } +} + +/* enable/disable the output-route */ +static void activate_output_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool force) +{ + struct via_spec *spec = codec->spec; + int i; + for (i = 0; i < path->depth; i++) { + hda_nid_t src, dst; + int idx = path->idx[i]; + src = path->path[i]; + if (i < path->depth - 1) + dst = path->path[i + 1]; + else + dst = 0; + if (enable && path->multi[i]) + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_CONNECT_SEL, idx); + if (!force && (dst == spec->aa_mix_nid)) + continue; + if (have_mute(codec, dst, HDA_INPUT)) + activate_output_mix(codec, path, dst, idx, enable); + if (!force && (src == path->vol_ctl || src == path->mute_ctl)) + continue; + if (have_mute(codec, src, HDA_OUTPUT)) { + int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; + snd_hda_codec_write(codec, src, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } + } +} + +/* set the given pin as output */ +static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, + int pin_type) +{ + if (!pin) + return; + snd_hda_set_pin_ctl(codec, pin, pin_type); + if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); +} + +static void via_auto_init_output(struct hda_codec *codec, + struct nid_path *path, int pin_type) +{ + unsigned int caps; + hda_nid_t pin; + + if (!path->depth) + return; + pin = path->path[path->depth - 1]; + + init_output_pin(codec, pin, pin_type); + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + caps = query_amp_caps(codec, pin, HDA_OUTPUT); + else + caps = 0; + if (caps & AC_AMPCAP_MUTE) { + unsigned int val; + val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE | val); + } + activate_output_path(codec, path, true, true); /* force on */ +} + +static void via_auto_init_multi_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct nid_path *path; + int i; + + for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) { + path = &spec->out_path[i]; + if (!i && spec->aamix_mode && spec->out_mix_path.depth) + path = &spec->out_mix_path; + via_auto_init_output(codec, path, PIN_OUT); + } +} + +/* deactivate the inactive headphone-paths */ +static void deactivate_hp_paths(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int shared = spec->hp_indep_shared; + + if (spec->hp_independent_mode) { + activate_output_path(codec, &spec->hp_path, false, false); + activate_output_path(codec, &spec->hp_mix_path, false, false); + if (shared) + activate_output_path(codec, &spec->out_path[shared], + false, false); + } else if (spec->aamix_mode || !spec->hp_path.depth) { + activate_output_path(codec, &spec->hp_indep_path, false, false); + activate_output_path(codec, &spec->hp_path, false, false); + } else { + activate_output_path(codec, &spec->hp_indep_path, false, false); + activate_output_path(codec, &spec->hp_mix_path, false, false); + } +} + +static void via_auto_init_hp_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->hp_path.depth) { + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); + return; + } + deactivate_hp_paths(codec); + if (spec->hp_independent_mode) + via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP); + else if (spec->aamix_mode) + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); + else + via_auto_init_output(codec, &spec->hp_path, PIN_HP); +} + +static void via_auto_init_speaker_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->autocfg.speaker_outs) + return; + if (!spec->speaker_path.depth) { + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); + return; + } + if (!spec->aamix_mode) { + activate_output_path(codec, &spec->speaker_mix_path, + false, false); + via_auto_init_output(codec, &spec->speaker_path, PIN_OUT); + } else { + activate_output_path(codec, &spec->speaker_path, false, false); + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); + } +} + +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); +static void via_hp_automute(struct hda_codec *codec); + +static void via_auto_init_analog_input(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t conn[HDA_MAX_CONNECTIONS]; + unsigned int ctl; + int i, num_conns; + + /* init ADCs */ + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t nid = spec->adc_nids[i]; + if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) || + !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)) + continue; + snd_hda_codec_write(codec, spec->adc_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + + /* init pins */ + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + if (spec->smart51_enabled && is_smart51_pins(codec, nid)) + ctl = PIN_OUT; + else { + ctl = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + ctl |= snd_hda_get_default_vref(codec, nid); + } + snd_hda_set_pin_ctl(codec, nid, ctl); + } + + /* init input-src */ + for (i = 0; i < spec->num_adc_nids; i++) { + int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; + /* secondary ADCs must have the unique MUX */ + if (i > 0 && !spec->mux_nids[i]) + break; + if (spec->mux_nids[adc_idx]) { + int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; + snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + } + if (spec->dyn_adc_switch) + break; /* only one input-src */ + } + + /* init aa-mixer */ + if (!spec->aa_mix_nid) + return; + num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, + ARRAY_SIZE(conn)); + for (i = 0; i < num_conns; i++) { + unsigned int caps = get_wcaps(codec, conn[i]); + if (get_wcaps_type(caps) == AC_WID_PIN) + snd_hda_codec_write(codec, spec->aa_mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i)); + } +} + static void update_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int parm) { - if (snd_hda_check_power_state(codec, nid, parm)) + if (snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0) == parm) return; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); } @@ -249,8 +723,8 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, { struct via_spec *spec = codec->spec; unsigned int format; - - if (snd_hda_check_power_state(codec, nid, parm)) + if (snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0) == parm) return; format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); if (format && (spec->dac_stream_tag[index] != format)) @@ -266,23 +740,6 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, } } -static bool smart51_enabled(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - return spec->gen.ext_channel_count > 2; -} - -static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->gen.multi_ios; i++) - if (spec->gen.multi_io[i].pin == pin) - return true; - return false; -} - static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int *affected_parm) { @@ -297,7 +754,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, no_presence |= spec->no_pin_power_ctl; if (!no_presence) present = snd_hda_jack_detect(codec, nid); - if ((smart51_enabled(codec) && is_smart51_pins(codec, nid)) + if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) || ((no_presence || present) && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { *affected_parm = AC_PWRST_D0; /* if it's connected */ @@ -338,185 +795,1802 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, return 1; } -static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { - { +static const struct snd_kcontrol_new via_pin_power_ctl_enum = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Dynamic Power-Control", .info = via_pin_power_ctl_info, .get = via_pin_power_ctl_get, .put = via_pin_power_ctl_put, - }, - {} /* terminator */ }; -/* check AA path's mute status */ -static bool is_aa_path_mute(struct hda_codec *codec) +static int via_independent_hp_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { "OFF", "ON" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int via_independent_hp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - const struct hda_amp_list *p; - int i, ch, v; - for (i = 0; i < spec->gen.num_loopbacks; i++) { - p = &spec->gen.loopback_list[i]; - for (ch = 0; ch < 2; ch++) { - v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, - p->idx); - if (!(v & HDA_AMP_MUTE) && v > 0) - return false; - } + ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; + return 0; +} + +/* adjust spec->multiout setup according to the current flags */ +static void setup_playback_multi_pcm(struct via_spec *spec) +{ + const struct auto_pin_cfg *cfg = &spec->autocfg; + spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; + spec->multiout.hp_nid = 0; + if (!spec->hp_independent_mode) { + if (!spec->hp_indep_shared) + spec->multiout.hp_nid = spec->hp_dac_nid; + } else { + if (spec->hp_indep_shared) + spec->multiout.num_dacs = cfg->line_outs - 1; } - return true; } -/* enter/exit analog low-current mode */ -static void __analog_low_current_mode(struct hda_codec *codec, bool force) +/* update DAC setups according to indep-HP switch; + * this function is called only when indep-HP is modified + */ +static void switch_indep_hp_dacs(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - bool enable; - unsigned int verb, parm; + int shared = spec->hp_indep_shared; + hda_nid_t shared_dac, hp_dac; - if (spec->no_pin_power_ctl) - enable = false; - else - enable = is_aa_path_mute(codec) && !spec->gen.active_streams; - if (enable == spec->alc_mode && !force) + if (!spec->opened_streams) return; - spec->alc_mode = enable; - /* decide low current mode's verb & parameter */ - switch (spec->codec_type) { - case VT1708B_8CH: - case VT1708B_4CH: - verb = 0xf70; - parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ - break; - case VT1708S: - case VT1718S: - case VT1716S: - verb = 0xf73; - parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ - break; - case VT1702: - verb = 0xf73; - parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ - break; - case VT2002P: - case VT1812: - case VT1802: - verb = 0xf93; - parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ - break; - case VT1705CF: - case VT1808: - verb = 0xf82; - parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ - break; - default: - return; /* other codecs are not supported */ + shared_dac = shared ? spec->multiout.dac_nids[shared] : 0; + hp_dac = spec->hp_dac_nid; + if (spec->hp_independent_mode) { + /* switch to indep-HP mode */ + if (spec->active_streams & STREAM_MULTI_OUT) { + __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); + __snd_hda_codec_cleanup_stream(codec, shared_dac, 1); + } + if (spec->active_streams & STREAM_INDEP_HP) + snd_hda_codec_setup_stream(codec, hp_dac, + spec->cur_hp_stream_tag, 0, + spec->cur_hp_format); + } else { + /* back to HP or shared-DAC */ + if (spec->active_streams & STREAM_INDEP_HP) + __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); + if (spec->active_streams & STREAM_MULTI_OUT) { + hda_nid_t dac; + int ch; + if (shared_dac) { /* reset mutli-ch DAC */ + dac = shared_dac; + ch = shared * 2; + } else { /* reset HP DAC */ + dac = hp_dac; + ch = 0; + } + snd_hda_codec_setup_stream(codec, dac, + spec->cur_dac_stream_tag, ch, + spec->cur_dac_format); + } + } + setup_playback_multi_pcm(spec); +} + +static int via_independent_hp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int cur, shared; + + mutex_lock(&spec->config_mutex); + cur = !!ucontrol->value.enumerated.item[0]; + if (spec->hp_independent_mode == cur) { + mutex_unlock(&spec->config_mutex); + return 0; + } + spec->hp_independent_mode = cur; + shared = spec->hp_indep_shared; + deactivate_hp_paths(codec); + if (cur) + activate_output_path(codec, &spec->hp_indep_path, true, false); + else { + if (shared) + activate_output_path(codec, &spec->out_path[shared], + true, false); + if (spec->aamix_mode || !spec->hp_path.depth) + activate_output_path(codec, &spec->hp_mix_path, + true, false); + else + activate_output_path(codec, &spec->hp_path, + true, false); + } + + switch_indep_hp_dacs(codec); + mutex_unlock(&spec->config_mutex); + + /* update jack power state */ + set_widgets_power_state(codec); + via_hp_automute(codec); + return 1; +} + +static const struct snd_kcontrol_new via_hp_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Independent HP", + .info = via_independent_hp_info, + .get = via_independent_hp_get, + .put = via_independent_hp_put, +}; + +static int via_hp_build(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + hda_nid_t nid; + + nid = spec->autocfg.hp_pins[0]; + knew = via_clone_control(spec, &via_hp_mixer); + if (knew == NULL) + return -ENOMEM; + + knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; + + return 0; +} + +static void notify_aa_path_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->smart51_nums; i++) { + struct snd_kcontrol *ctl; + struct snd_ctl_elem_id id; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); + ctl = snd_hda_find_mixer_ctl(codec, id.name); + if (ctl) + snd_ctl_notify(codec->bus->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &ctl->id); + } +} + +static void mute_aa_path(struct hda_codec *codec, int mute) +{ + struct via_spec *spec = codec->spec; + int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; + int i; + + /* check AA path's mute status */ + for (i = 0; i < spec->smart51_nums; i++) { + if (spec->smart51_idxs[i] < 0) + continue; + snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, + HDA_INPUT, spec->smart51_idxs[i], + HDA_AMP_MUTE, val); + } +} + +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->smart51_nums; i++) + if (spec->smart51_pins[i] == pin) + return true; + return false; +} + +static int via_smart51_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + + *ucontrol->value.integer.value = spec->smart51_enabled; + return 0; +} + +static int via_smart51_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int out_in = *ucontrol->value.integer.value + ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; + int i; + + for (i = 0; i < spec->smart51_nums; i++) { + hda_nid_t nid = spec->smart51_pins[i]; + unsigned int parm; + + parm = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + parm |= out_in; + snd_hda_set_pin_ctl(codec, nid, parm); + if (out_in == AC_PINCTL_OUT_EN) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + } + spec->smart51_enabled = *ucontrol->value.integer.value; + set_widgets_power_state(codec); + return 1; +} + +static const struct snd_kcontrol_new via_smart51_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Smart 5.1", + .count = 1, + .info = snd_ctl_boolean_mono_info, + .get = via_smart51_get, + .put = via_smart51_put, +}; + +static int via_smart51_build(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->smart51_nums) + return 0; + if (!via_clone_control(spec, &via_smart51_mixer)) + return -ENOMEM; + return 0; +} + +/* check AA path's mute status */ +static bool is_aa_path_mute(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct hda_amp_list *p; + int i, ch, v; + + for (i = 0; i < spec->num_loopbacks; i++) { + p = &spec->loopback_list[i]; + for (ch = 0; ch < 2; ch++) { + v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, + p->idx); + if (!(v & HDA_AMP_MUTE) && v > 0) + return false; + } + } + return true; +} + +/* enter/exit analog low-current mode */ +static void __analog_low_current_mode(struct hda_codec *codec, bool force) +{ + struct via_spec *spec = codec->spec; + bool enable; + unsigned int verb, parm; + + if (spec->no_pin_power_ctl) + enable = false; + else + enable = is_aa_path_mute(codec) && !spec->opened_streams; + if (enable == spec->alc_mode && !force) + return; + spec->alc_mode = enable; + + /* decide low current mode's verb & parameter */ + switch (spec->codec_type) { + case VT1708B_8CH: + case VT1708B_4CH: + verb = 0xf70; + parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ + break; + case VT1708S: + case VT1718S: + case VT1716S: + verb = 0xf73; + parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ + break; + case VT1702: + verb = 0xf73; + parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ + break; + case VT2002P: + case VT1812: + case VT1802: + verb = 0xf93; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; + case VT1705CF: + case VT1808: + verb = 0xf82; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; + default: + return; /* other codecs are not supported */ + } + /* send verb */ + snd_hda_codec_write(codec, codec->afg, 0, verb, parm); +} + +static void analog_low_current_mode(struct hda_codec *codec) +{ + return __analog_low_current_mode(codec, false); +} + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb vt1708_init_verbs[] = { + /* power down jack detect function */ + {0x1, 0xf81, 0x1}, + { } +}; + +static void set_stream_open(struct hda_codec *codec, int bit, bool active) +{ + struct via_spec *spec = codec->spec; + + if (active) + spec->opened_streams |= bit; + else + spec->opened_streams &= ~bit; + analog_low_current_mode(codec); +} + +static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int err; + + spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + set_stream_open(codec, STREAM_MULTI_OUT, true); + err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); + if (err < 0) { + set_stream_open(codec, STREAM_MULTI_OUT, false); + return err; + } + return 0; +} + +static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_open(codec, STREAM_MULTI_OUT, false); + return 0; +} + +static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + if (snd_BUG_ON(!spec->hp_dac_nid)) + return -EINVAL; + set_stream_open(codec, STREAM_INDEP_HP, true); + return 0; +} + +static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_open(codec, STREAM_INDEP_HP, false); + return 0; +} + +static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + setup_playback_multi_pcm(spec); + snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, + format, substream); + /* remember for dynamic DAC switch with indep-HP */ + spec->active_streams |= STREAM_MULTI_OUT; + spec->cur_dac_stream_tag = stream_tag; + spec->cur_dac_format = format; + mutex_unlock(&spec->config_mutex); + vt1708_update_hp_work(spec); + return 0; +} + +static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + if (spec->hp_independent_mode) + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, + stream_tag, 0, format); + spec->active_streams |= STREAM_INDEP_HP; + spec->cur_hp_stream_tag = stream_tag; + spec->cur_hp_format = format; + mutex_unlock(&spec->config_mutex); + vt1708_update_hp_work(spec); + return 0; +} + +static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + spec->active_streams &= ~STREAM_MULTI_OUT; + mutex_unlock(&spec->config_mutex); + vt1708_update_hp_work(spec); + return 0; +} + +static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + if (spec->hp_independent_mode) + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); + spec->active_streams &= ~STREAM_INDEP_HP; + mutex_unlock(&spec->config_mutex); + vt1708_update_hp_work(spec); + return 0; +} + +/* + * Digital out + */ +static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); + return 0; +} + +/* + * Analog capture + */ +static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + stream_tag, 0, format); + return 0; +} + +static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); + return 0; +} + +/* analog capture with dynamic ADC switching */ +static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; + + mutex_lock(&spec->config_mutex); + spec->cur_adc = spec->adc_nids[adc_idx]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + mutex_unlock(&spec->config_mutex); + return 0; +} + +static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + mutex_unlock(&spec->config_mutex); + return 0; +} + +/* re-setup the stream if running; called from input-src put */ +static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) +{ + struct via_spec *spec = codec->spec; + int adc_idx = spec->inputs[cur].adc_idx; + hda_nid_t adc = spec->adc_nids[adc_idx]; + bool ret = false; + + mutex_lock(&spec->config_mutex); + if (spec->cur_adc && spec->cur_adc != adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = adc; + snd_hda_codec_setup_stream(codec, adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + ret = true; + } + mutex_unlock(&spec->config_mutex); + return ret; +} + +static const struct hda_pcm_stream via_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_playback_multi_pcm_open, + .close = via_playback_multi_pcm_close, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream via_pcm_hp_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_playback_hp_pcm_open, + .close = via_playback_hp_pcm_close, + .prepare = via_playback_hp_pcm_prepare, + .cleanup = via_playback_hp_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + /* NID is set in via_build_pcms */ + /* We got noisy outputs on the right channel on VT1708 when + * 24bit samples are used. Until any workaround is found, + * disable the 24bit format, so far. + */ + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .ops = { + .open = via_playback_multi_pcm_open, + .close = via_playback_multi_pcm_close, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream via_pcm_analog_capture = { + .substreams = 1, /* will be changed in via_build_pcms() */ + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .prepare = via_dyn_adc_capture_pcm_prepare, + .cleanup = via_dyn_adc_capture_pcm_cleanup, + }, +}; + +static const struct hda_pcm_stream via_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream via_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +/* + * slave controls for virtual master + */ +static const char * const via_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", "Bass Speaker", + NULL, +}; + +static int via_build_controls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol *kctl; + int err, i; + + spec->no_pin_power_ctl = 1; + if (spec->set_widgets_power_state) + if (!via_clone_control(spec, &via_pin_power_ctl_enum)) + return -ENOMEM; + + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; + } + + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, + 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); + if (err < 0) + return err; + } + + /* 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, vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + vmaster_tlv, via_slave_pfxs, + "Playback Volume"); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, via_slave_pfxs, + "Playback Switch"); + if (err < 0) + return err; + } + + /* assign Capture Source enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); + for (i = 0; kctl && i < kctl->count; i++) { + if (!spec->mux_nids[i]) + continue; + err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); + if (err < 0) + return err; + } + + via_free_kctls(codec); /* no longer needed */ + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + + return 0; +} + +static int via_build_pcms(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->num_pcms = 0; + codec->pcm_info = info; + + if (spec->multiout.num_dacs || spec->num_adc_nids) { + snprintf(spec->stream_name_analog, + sizeof(spec->stream_name_analog), + "%s Analog", codec->chip_name); + info->name = spec->stream_name_analog; + + if (spec->multiout.num_dacs) { + if (!spec->stream_analog_playback) + spec->stream_analog_playback = + &via_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *spec->stream_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT + && spec->autocfg.line_outs == 2) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; + } + + if (!spec->stream_analog_capture) { + if (spec->dyn_adc_switch) + spec->stream_analog_capture = + &via_pcm_dyn_adc_analog_capture; + else + spec->stream_analog_capture = + &via_pcm_analog_capture; + } + if (spec->num_adc_nids) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + *spec->stream_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nids[0]; + if (!spec->dyn_adc_switch) + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids; + } + codec->num_pcms++; + info++; + } + + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + snprintf(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + "%s Digital", codec->chip_name); + info->name = spec->stream_name_digital; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->multiout.dig_out_nid) { + if (!spec->stream_digital_playback) + spec->stream_digital_playback = + &via_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *spec->stream_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dig_out_nid; + } + if (spec->dig_in_nid) { + if (!spec->stream_digital_capture) + spec->stream_digital_capture = + &via_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + *spec->stream_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->dig_in_nid; + } + codec->num_pcms++; + info++; + } + + if (spec->hp_dac_nid) { + snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), + "%s HP", codec->chip_name); + info->name = spec->stream_name_hp; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->hp_dac_nid; + codec->num_pcms++; + info++; + } + return 0; +} + +static void via_free(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec) + return; + + via_free_kctls(codec); + vt1708_stop_hp_work(spec); + kfree(spec->bind_cap_vol); + kfree(spec->bind_cap_sw); + snd_hda_gen_free(&spec->gen); + kfree(spec); +} + +/* mute/unmute outputs */ +static void toggle_output_mutes(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, bool mute) +{ + int i; + for (i = 0; i < num_pins; i++) { + unsigned int parm = snd_hda_codec_read(codec, pins[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (parm & AC_PINCTL_IN_EN) + continue; + if (mute) + parm &= ~AC_PINCTL_OUT_EN; + else + parm |= AC_PINCTL_OUT_EN; + snd_hda_set_pin_ctl(codec, pins[i], parm); + } +} + +/* mute internal speaker if line-out is plugged */ +static void via_line_automute(struct hda_codec *codec, int present) +{ + struct via_spec *spec = codec->spec; + + if (!spec->autocfg.speaker_outs) + return; + if (!present) + present = snd_hda_jack_detect(codec, + spec->autocfg.line_out_pins[0]); + toggle_output_mutes(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + present); +} + +/* mute internal speaker if HP is plugged */ +static void via_hp_automute(struct hda_codec *codec) +{ + int present = 0; + int nums; + struct via_spec *spec = codec->spec; + + if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] && + (spec->codec_type != VT1708 || spec->vt1708_jack_detect) && + is_jack_detectable(codec, spec->autocfg.hp_pins[0])) + present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); + + if (spec->smart51_enabled) + nums = spec->autocfg.line_outs + spec->smart51_nums; + else + nums = spec->autocfg.line_outs; + toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present); + + via_line_automute(codec, present); +} + +#ifdef CONFIG_PM +static int via_suspend(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + vt1708_stop_hp_work(spec); + + if (spec->codec_type == VT1802) { + /* Fix pop noise on headphones */ + int i; + for (i = 0; i < spec->autocfg.hp_outs; i++) + snd_hda_set_pin_ctl(codec, spec->autocfg.hp_pins[i], 0); + } + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + struct via_spec *spec = codec->spec; + return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); +} +#endif + +/* + */ + +static int via_init(struct hda_codec *codec); + +static const struct hda_codec_ops via_patch_ops = { + .build_controls = via_build_controls, + .build_pcms = via_build_pcms, + .init = via_init, + .free = via_free, + .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM + .suspend = via_suspend, + .check_power_status = via_check_power_status, +#endif +}; + +static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) { + if (spec->multiout.dac_nids[i] == dac) + return false; + } + if (spec->hp_dac_nid == dac) + return false; + return true; +} + +static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path, int depth) +{ + struct via_spec *spec = codec->spec; + hda_nid_t conn[8]; + int i, nums; + + if (nid == spec->aa_mix_nid) { + if (!with_aa_mix) + return false; + with_aa_mix = 2; /* mark aa-mix is included */ + } + + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) { + if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) + continue; + if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { + /* aa-mix is requested but not included? */ + if (!(spec->aa_mix_nid && with_aa_mix == 1)) + goto found; + } + } + if (depth >= MAX_NID_PATH_DEPTH) + return false; + for (i = 0; i < nums; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, conn[i])); + if (type == AC_WID_AUD_OUT) + continue; + if (__parse_output_path(codec, conn[i], target_dac, + with_aa_mix, path, depth + 1)) + goto found; + } + return false; + + found: + path->path[path->depth] = conn[i]; + path->idx[path->depth] = i; + if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + path->multi[path->depth] = 1; + path->depth++; + return true; +} + +static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path) +{ + if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { + path->path[path->depth] = nid; + path->depth++; + snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + path->depth, path->path[0], path->path[1], + path->path[2], path->path[3], path->path[4]); + return true; + } + return false; +} + +static int via_auto_fill_dac_nids(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + hda_nid_t nid; + + spec->multiout.num_dacs = 0; + spec->multiout.dac_nids = spec->private_dac_nids; + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t dac = 0; + nid = cfg->line_out_pins[i]; + if (!nid) + continue; + if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i])) + dac = spec->out_path[i].path[0]; + if (!i && parse_output_path(codec, nid, dac, 1, + &spec->out_mix_path)) + dac = spec->out_mix_path.path[0]; + if (dac) + spec->private_dac_nids[spec->multiout.num_dacs++] = dac; + } + if (!spec->out_path[0].depth && spec->out_mix_path.depth) { + spec->out_path[0] = spec->out_mix_path; + spec->out_mix_path.depth = 0; + } + return 0; +} + +static int create_ch_ctls(struct hda_codec *codec, const char *pfx, + int chs, bool check_dac, struct nid_path *path) +{ + struct via_spec *spec = codec->spec; + char name[32]; + hda_nid_t dac, pin, sel, nid; + int err; + + dac = check_dac ? path->path[0] : 0; + pin = path->path[path->depth - 1]; + sel = path->depth > 1 ? path->path[1] : 0; + + if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = dac; + else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = pin; + else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = sel; + else + nid = 0; + if (nid) { + sprintf(name, "%s Playback Volume", pfx); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; + path->vol_ctl = nid; + } + + if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = dac; + else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = pin; + else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = sel; + else + nid = 0; + if (nid) { + sprintf(name, "%s Playback Switch", pfx); + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; + path->mute_ctl = nid; + } + return 0; +} + +static void mangle_smart51(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg_item *ins = cfg->inputs; + int i, j, nums, attr; + int pins[AUTO_CFG_MAX_INS]; + + for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { + nums = 0; + for (i = 0; i < cfg->num_inputs; i++) { + unsigned int def; + if (ins[i].type > AUTO_PIN_LINE_IN) + continue; + def = snd_hda_codec_get_pincfg(codec, ins[i].pin); + if (snd_hda_get_input_pin_attr(def) != attr) + continue; + for (j = 0; j < nums; j++) + if (ins[pins[j]].type < ins[i].type) { + memmove(pins + j + 1, pins + j, + (nums - j) * sizeof(int)); + break; + } + pins[j] = i; + nums++; + } + if (cfg->line_outs + nums < 3) + continue; + for (i = 0; i < nums; i++) { + hda_nid_t pin = ins[pins[i]].pin; + spec->smart51_pins[spec->smart51_nums++] = pin; + cfg->line_out_pins[cfg->line_outs++] = pin; + if (cfg->line_outs == 3) + break; + } + return; + } +} + +static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src) +{ + dst->vol_ctl = src->vol_ctl; + dst->mute_ctl = src->mute_ctl; +} + +/* add playback controls from the parsed DAC table */ +static int via_auto_create_multi_out_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct nid_path *path; + static const char * const chname[4] = { + "Front", "Surround", NULL /* "CLFE" */, "Side" + }; + int i, idx, err; + int old_line_outs; + + /* check smart51 */ + old_line_outs = cfg->line_outs; + if (cfg->line_outs == 1) + mangle_smart51(codec); + + err = via_auto_fill_dac_nids(codec); + if (err < 0) + return err; + + if (spec->multiout.num_dacs < 3) { + spec->smart51_nums = 0; + cfg->line_outs = old_line_outs; + } + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t pin, dac; + pin = cfg->line_out_pins[i]; + dac = spec->multiout.dac_nids[i]; + if (!pin || !dac) + continue; + path = spec->out_path + i; + if (i == HDA_CLFE) { + err = create_ch_ctls(codec, "Center", 1, true, path); + if (err < 0) + return err; + err = create_ch_ctls(codec, "LFE", 2, true, path); + if (err < 0) + return err; + } else { + const char *pfx = chname[i]; + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && + cfg->line_outs <= 2) + pfx = i ? "Bass Speaker" : "Speaker"; + err = create_ch_ctls(codec, pfx, 3, true, path); + if (err < 0) + return err; + } + if (path != spec->out_path + i) + copy_path_mixer_ctls(&spec->out_path[i], path); + if (path == spec->out_path && spec->out_mix_path.depth) + copy_path_mixer_ctls(&spec->out_mix_path, path); + } + + idx = get_connection_index(codec, spec->aa_mix_nid, + spec->multiout.dac_nids[0]); + if (idx >= 0) { + /* add control to mixer */ + const char *name; + name = spec->out_mix_path.depth ? + "PCM Loopback Playback Volume" : "PCM Playback Volume"; + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, + idx, HDA_INPUT)); + if (err < 0) + return err; + name = spec->out_mix_path.depth ? + "PCM Loopback Playback Switch" : "PCM Playback Switch"; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, + idx, HDA_INPUT)); + if (err < 0) + return err; + } + + cfg->line_outs = old_line_outs; + + return 0; +} + +static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) +{ + struct via_spec *spec = codec->spec; + struct nid_path *path; + bool check_dac; + int i, err; + + if (!pin) + return 0; + + if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) { + for (i = HDA_SIDE; i >= HDA_CLFE; i--) { + if (i < spec->multiout.num_dacs && + parse_output_path(codec, pin, + spec->multiout.dac_nids[i], 0, + &spec->hp_indep_path)) { + spec->hp_indep_shared = i; + break; + } + } + } + if (spec->hp_indep_path.depth) { + spec->hp_dac_nid = spec->hp_indep_path.path[0]; + if (!spec->hp_indep_shared) + spec->hp_path = spec->hp_indep_path; + } + /* optionally check front-path w/o AA-mix */ + if (!spec->hp_path.depth) + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_FRONT], 0, + &spec->hp_path); + + if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + 1, &spec->hp_mix_path) && !spec->hp_path.depth) + return 0; + + if (spec->hp_path.depth) { + path = &spec->hp_path; + check_dac = true; + } else { + path = &spec->hp_mix_path; + check_dac = false; + } + err = create_ch_ctls(codec, "Headphone", 3, check_dac, path); + if (err < 0) + return err; + if (check_dac) + copy_path_mixer_ctls(&spec->hp_mix_path, path); + else + copy_path_mixer_ctls(&spec->hp_path, path); + if (spec->hp_indep_path.depth) + copy_path_mixer_ctls(&spec->hp_indep_path, path); + return 0; +} + +static int via_auto_create_speaker_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct nid_path *path; + bool check_dac; + hda_nid_t pin, dac = 0; + int err; + + pin = spec->autocfg.speaker_pins[0]; + if (!spec->autocfg.speaker_outs || !pin) + return 0; + + if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path)) + dac = spec->speaker_path.path[0]; + if (!dac) + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_FRONT], 0, + &spec->speaker_path); + if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + 1, &spec->speaker_mix_path) && !dac) + return 0; + + /* no AA-path for front? */ + if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth) + dac = 0; + + spec->speaker_dac_nid = dac; + spec->multiout.extra_out_nid[0] = dac; + if (dac) { + path = &spec->speaker_path; + check_dac = true; + } else { + path = &spec->speaker_mix_path; + check_dac = false; + } + err = create_ch_ctls(codec, "Speaker", 3, check_dac, path); + if (err < 0) + return err; + if (check_dac) + copy_path_mixer_ctls(&spec->speaker_mix_path, path); + else + copy_path_mixer_ctls(&spec->speaker_path, path); + return 0; +} + +#define via_aamix_ctl_info via_pin_power_ctl_info + +static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->aamix_mode; + return 0; +} + +static void update_aamix_paths(struct hda_codec *codec, int do_mix, + struct nid_path *nomix, struct nid_path *mix) +{ + if (do_mix) { + activate_output_path(codec, nomix, false, false); + activate_output_path(codec, mix, true, false); + } else { + activate_output_path(codec, mix, false, false); + activate_output_path(codec, nomix, true, false); + } +} + +static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int val = ucontrol->value.enumerated.item[0]; + + if (val == spec->aamix_mode) + return 0; + spec->aamix_mode = val; + /* update front path */ + update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path); + /* update HP path */ + if (!spec->hp_independent_mode) { + update_aamix_paths(codec, val, &spec->hp_path, + &spec->hp_mix_path); + } + /* update speaker path */ + update_aamix_paths(codec, val, &spec->speaker_path, + &spec->speaker_mix_path); + return 1; +} + +static const struct snd_kcontrol_new via_aamix_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Loopback Mixing", + .info = via_aamix_ctl_info, + .get = via_aamix_ctl_get, + .put = via_aamix_ctl_put, +}; + +static int via_auto_create_loopback_switch(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->aa_mix_nid) + return 0; /* no loopback switching available */ + if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth || + spec->speaker_path.depth)) + return 0; /* no loopback switching available */ + if (!via_clone_control(spec, &via_aamix_ctl_enum)) + return -ENOMEM; + return 0; +} + +/* look for ADCs */ +static int via_fill_adcs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid = codec->start_nid; + int i; + + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (wcaps & AC_WCAP_DIGITAL) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) + return -ENOMEM; + spec->adc_nids[spec->num_adc_nids++] = nid; + } + return 0; +} + +/* input-src control */ +static int via_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->num_inputs; + if (uinfo->value.enumerated.item >= spec->num_inputs) + uinfo->value.enumerated.item = spec->num_inputs - 1; + strcpy(uinfo->value.enumerated.name, + spec->inputs[uinfo->value.enumerated.item].label); + return 0; +} + +static int via_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[idx]; + return 0; +} + +static int via_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + hda_nid_t mux; + int cur; + + cur = ucontrol->value.enumerated.item[0]; + if (cur < 0 || cur >= spec->num_inputs) + return -EINVAL; + if (spec->cur_mux[idx] == cur) + return 0; + spec->cur_mux[idx] = cur; + if (spec->dyn_adc_switch) { + int adc_idx = spec->inputs[cur].adc_idx; + mux = spec->mux_nids[adc_idx]; + via_dyn_adc_pcm_resetup(codec, cur); + } else { + mux = spec->mux_nids[idx]; + if (snd_BUG_ON(!mux)) + return -EINVAL; + } + + if (mux) { + /* switch to D0 beofre change index */ + update_power_state(codec, mux, AC_PWRST_D0); + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_CONNECT_SEL, + spec->inputs[cur].mux_idx); + } + + /* update jack power state */ + set_widgets_power_state(codec); + return 0; +} + +static const struct snd_kcontrol_new via_input_src_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, +}; + +static int create_input_src_ctls(struct hda_codec *codec, int count) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + + if (spec->num_inputs <= 1 || !count) + return 0; /* no need for single src */ + + knew = via_clone_control(spec, &via_input_src_ctl); + if (!knew) + return -ENOMEM; + knew->count = count; + return 0; +} + +/* add the powersave loopback-list entry */ +static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) +{ + struct hda_amp_list *list; + + if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) + return; + list = spec->loopback_list + spec->num_loopbacks; + list->nid = mix; + list->dir = HDA_INPUT; + list->idx = idx; + spec->num_loopbacks++; + spec->loopback.amplist = spec->loopback_list; +} + +static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src, + hda_nid_t dst) +{ + return snd_hda_get_conn_index(codec, src, dst, 1) >= 0; +} + +/* add the input-route to the given pin */ +static bool add_input_route(struct hda_codec *codec, hda_nid_t pin) +{ + struct via_spec *spec = codec->spec; + int c, idx; + + spec->inputs[spec->num_inputs].adc_idx = -1; + spec->inputs[spec->num_inputs].pin = pin; + for (c = 0; c < spec->num_adc_nids; c++) { + if (spec->mux_nids[c]) { + idx = get_connection_index(codec, spec->mux_nids[c], + pin); + if (idx < 0) + continue; + spec->inputs[spec->num_inputs].mux_idx = idx; + } else { + if (!is_reachable_nid(codec, spec->adc_nids[c], pin)) + continue; + } + spec->inputs[spec->num_inputs].adc_idx = c; + /* Can primary ADC satisfy all inputs? */ + if (!spec->dyn_adc_switch && + spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) { + snd_printd(KERN_INFO + "via: dynamic ADC switching enabled\n"); + spec->dyn_adc_switch = 1; + } + return true; } - /* send verb */ - snd_hda_codec_write(codec, codec->afg, 0, verb, parm); + return false; } -static void analog_low_current_mode(struct hda_codec *codec) -{ - return __analog_low_current_mode(codec, false); -} +static int get_mux_nids(struct hda_codec *codec); -static int via_build_controls(struct hda_codec *codec) +/* parse input-routes; fill ADCs, MUXs and input-src entries */ +static int parse_analog_inputs(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err, i; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; - err = snd_hda_gen_build_controls(codec); + err = via_fill_adcs(codec); + if (err < 0) + return err; + err = get_mux_nids(codec); if (err < 0) return err; - if (spec->set_widgets_power_state) - spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; + /* fill all input-routes */ + for (i = 0; i < cfg->num_inputs; i++) { + if (add_input_route(codec, cfg->inputs[i].pin)) + spec->inputs[spec->num_inputs++].label = + hda_get_autocfg_input_label(codec, cfg, i); } + /* check for internal loopback recording */ + if (spec->aa_mix_nid && + add_input_route(codec, spec->aa_mix_nid)) + spec->inputs[spec->num_inputs++].label = "Stereo Mixer"; + return 0; } -static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) +/* create analog-loopback volume/switch controls */ +static int create_loopback_ctls(struct hda_codec *codec) { - analog_low_current_mode(codec); - vt1708_update_hp_work(codec); + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + const char *prev_label = NULL; + int type_idx = 0; + int i, j, err, idx; + + if (!spec->aa_mix_nid) + return 0; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + const char *label = hda_get_autocfg_input_label(codec, cfg, i); + + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + idx = get_connection_index(codec, spec->aa_mix_nid, pin); + if (idx >= 0) { + err = via_new_analog_input(spec, label, type_idx, + idx, spec->aa_mix_nid); + if (err < 0) + return err; + add_loopback_list(spec, spec->aa_mix_nid, idx); + } + + /* remember the label for smart51 control */ + for (j = 0; j < spec->smart51_nums; j++) { + if (spec->smart51_pins[j] == pin) { + spec->smart51_idxs[j] = idx; + spec->smart51_labels[j] = label; + break; + } + } + } + return 0; } -static void via_free(struct hda_codec *codec) +/* create mic-boost controls (if present) */ +static int create_mic_boost_ctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + const char *prev_label = NULL; + int type_idx = 0; + int i, err; - if (!spec) - return; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + unsigned int caps; + const char *label; + char name[32]; - vt1708_stop_hp_work(codec); - snd_hda_gen_spec_free(&spec->gen); - kfree(spec); + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + caps = query_amp_caps(codec, pin, HDA_INPUT); + if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) + continue; + label = hda_get_autocfg_input_label(codec, cfg, i); + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + snprintf(name, sizeof(name), "%s Boost Volume", label); + err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } + return 0; } -#ifdef CONFIG_PM -static int via_suspend(struct hda_codec *codec) +/* create capture and input-src controls for multiple streams */ +static int create_multi_adc_ctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - vt1708_stop_hp_work(codec); + int i, err; - if (spec->codec_type == VT1802) { - /* Fix pop noise on headphones */ - int i; - for (i = 0; i < spec->gen.autocfg.hp_outs; i++) - snd_hda_set_pin_ctl(codec, spec->gen.autocfg.hp_pins[i], 0); + /* create capture mixer elements */ + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t adc = spec->adc_nids[i]; + err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Capture Volume", i, + HDA_COMPOSE_AMP_VAL(adc, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; + err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Capture Switch", i, + HDA_COMPOSE_AMP_VAL(adc, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; } + /* input-source control */ + for (i = 0; i < spec->num_adc_nids; i++) + if (!spec->mux_nids[i]) + break; + err = create_input_src_ctls(codec, i); + if (err < 0) + return err; return 0; } -#endif -#ifdef CONFIG_PM -static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) +/* bind capture volume/switch */ +static struct snd_kcontrol_new via_bind_cap_vol_ctl = + HDA_BIND_VOL("Capture Volume", 0); +static struct snd_kcontrol_new via_bind_cap_sw_ctl = + HDA_BIND_SW("Capture Switch", 0); + +static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret, + struct hda_ctl_ops *ops) { - struct via_spec *spec = codec->spec; - set_widgets_power_state(codec); - analog_low_current_mode(codec); - vt1708_update_hp_work(codec); - return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); + struct hda_bind_ctls *ctl; + int i; + + ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL); + if (!ctl) + return -ENOMEM; + ctl->ops = ops; + for (i = 0; i < spec->num_adc_nids; i++) + ctl->values[i] = + HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT); + *ctl_ret = ctl; + return 0; } -#endif -/* - */ +/* create capture and input-src controls for dynamic ADC-switch case */ +static int create_dyn_adc_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + int err; -static int via_init(struct hda_codec *codec); + /* set up the bind capture ctls */ + err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol); + if (err < 0) + return err; + err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw); + if (err < 0) + return err; -static const struct hda_codec_ops via_patch_ops = { - .build_controls = via_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = via_init, - .free = via_free, - .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM - .suspend = via_suspend, - .check_power_status = via_check_power_status, -#endif -}; + /* create capture mixer elements */ + knew = via_clone_control(spec, &via_bind_cap_vol_ctl); + if (!knew) + return -ENOMEM; + knew->private_value = (long)spec->bind_cap_vol; + knew = via_clone_control(spec, &via_bind_cap_sw_ctl); + if (!knew) + return -ENOMEM; + knew->private_value = (long)spec->bind_cap_sw; + + /* input-source control */ + err = create_input_src_ctls(codec, 1); + if (err < 0) + return err; + return 0; +} + +/* parse and create capture-related stuff */ +static int via_auto_create_analog_input_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = parse_analog_inputs(codec); + if (err < 0) + return err; + if (spec->dyn_adc_switch) + err = create_dyn_adc_ctls(codec); + else + err = create_multi_adc_ctls(codec); + if (err < 0) + return err; + err = create_loopback_ctls(codec); + if (err < 0) + return err; + err = create_mic_boost_ctls(codec); + if (err < 0) + return err; + return 0; +} -static const struct hda_verb vt1708_init_verbs[] = { - /* power down jack detect function */ - {0x1, 0xf81, 0x1}, - { } -}; static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) { unsigned int def_conf; @@ -559,32 +2633,102 @@ static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, if (spec->vt1708_jack_detect == val) return 0; spec->vt1708_jack_detect = val; - vt1708_update_hp_work(codec); + if (spec->vt1708_jack_detect && + snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + via_hp_automute(codec); + vt1708_update_hp_work(spec); return 1; } -static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { - { +static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Jack Detect", .count = 1, .info = snd_ctl_boolean_mono_info, .get = vt1708_jack_detect_get, .put = vt1708_jack_detect_put, - }, - {} /* terminator */ }; -static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) +static void fill_dig_outs(struct hda_codec *codec); +static void fill_dig_in(struct hda_codec *codec); + +static int via_parse_auto_config(struct hda_codec *codec) { - set_widgets_power_state(codec); - snd_hda_gen_hp_automute(codec, tbl); + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return -EINVAL; + + err = via_auto_create_multi_out_ctls(codec); + if (err < 0) + return err; + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = via_auto_create_speaker_ctls(codec); + if (err < 0) + return err; + err = via_auto_create_loopback_switch(codec); + if (err < 0) + return err; + err = via_auto_create_analog_input_ctls(codec); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + fill_dig_in(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + + if (spec->hp_dac_nid && spec->hp_mix_path.depth) { + err = via_hp_build(codec); + if (err < 0) + return err; + } + + err = via_smart51_build(codec); + if (err < 0) + return err; + + /* assign slave outs */ + if (spec->slave_dig_outs[0]) + codec->slave_dig_outs = spec->slave_dig_outs; + + return 1; +} + +static void via_auto_init_dig_outs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + if (spec->multiout.dig_out_nid) + init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); + if (spec->slave_dig_outs[0]) + init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); } -static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) +static void via_auto_init_dig_in(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + if (!spec->dig_in_nid) + return; + snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN); +} + +static void via_jack_output_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) { set_widgets_power_state(codec); - snd_hda_gen_line_automute(codec, tbl); + via_hp_automute(codec); } static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) @@ -592,55 +2736,41 @@ static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_t set_widgets_power_state(codec); } -#define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1) - -static void via_set_jack_unsol_events(struct hda_codec *codec) +/* initialize the unsolicited events */ +static void via_auto_init_unsol_event(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - hda_nid_t pin; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int ev; int i; + hda_jack_callback cb; + + if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) + snd_hda_jack_detect_enable_callback(codec, cfg->hp_pins[0], + VIA_HP_EVENT | VIA_JACK_EVENT, + via_jack_output_event); - spec->gen.hp_automute_hook = via_hp_automute; if (cfg->speaker_pins[0]) - spec->gen.line_automute_hook = via_line_automute; + ev = VIA_LINE_EVENT; + else + ev = 0; + cb = ev ? via_jack_output_event : via_jack_powerstate_event; for (i = 0; i < cfg->line_outs; i++) { - pin = cfg->line_out_pins[i]; - if (pin && !snd_hda_jack_tbl_get(codec, pin) && - is_jack_detectable(codec, pin)) - snd_hda_jack_detect_enable_callback(codec, pin, - VIA_JACK_EVENT, - via_jack_powerstate_event); + if (cfg->line_out_pins[i] && + is_jack_detectable(codec, cfg->line_out_pins[i])) + snd_hda_jack_detect_enable_callback(codec, cfg->line_out_pins[i], + ev | VIA_JACK_EVENT, cb); } for (i = 0; i < cfg->num_inputs; i++) { - pin = cfg->line_out_pins[i]; - if (pin && !snd_hda_jack_tbl_get(codec, pin) && - is_jack_detectable(codec, pin)) - snd_hda_jack_detect_enable_callback(codec, pin, + if (is_jack_detectable(codec, cfg->inputs[i].pin)) + snd_hda_jack_detect_enable_callback(codec, cfg->inputs[i].pin, VIA_JACK_EVENT, via_jack_powerstate_event); } } -static int via_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); - if (err < 0) - return err; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; - - via_set_jack_unsol_events(codec); - return 0; -} - static int via_init(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -653,47 +2783,63 @@ static int via_init(struct hda_codec *codec) set_widgets_power_state(codec); __analog_low_current_mode(codec, true); - snd_hda_gen_init(codec); + via_auto_init_multi_out(codec); + via_auto_init_hp_out(codec); + via_auto_init_speaker_out(codec); + via_auto_init_analog_input(codec); + via_auto_init_dig_outs(codec); + via_auto_init_dig_in(codec); + + via_auto_init_unsol_event(codec); - vt1708_update_hp_work(codec); + via_hp_automute(codec); + vt1708_update_hp_work(spec); return 0; } -static int vt1708_build_controls(struct hda_codec *codec) +static void vt1708_update_hp_jack_state(struct work_struct *work) { - /* In order not to create "Phantom Jack" controls, - temporary enable jackpoll */ - int err; - int old_interval = codec->jackpoll_interval; - codec->jackpoll_interval = msecs_to_jiffies(100); - err = via_build_controls(codec); - codec->jackpoll_interval = old_interval; - return err; + struct via_spec *spec = container_of(work, struct via_spec, + vt1708_hp_work.work); + if (spec->codec_type != VT1708) + return; + snd_hda_jack_set_dirty_all(spec->codec); + /* if jack state toggled */ + if (spec->vt1708_hp_present + != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { + spec->vt1708_hp_present ^= 1; + via_hp_automute(spec->codec); + } + if (spec->vt1708_jack_detect) + schedule_delayed_work(&spec->vt1708_hp_work, + msecs_to_jiffies(100)); } -static int vt1708_build_pcms(struct hda_codec *codec) +static int get_mux_nids(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int i, err; - - err = snd_hda_gen_build_pcms(codec); - if (err < 0 || codec->vendor_id != 0x11061708) - return err; - - /* We got noisy outputs on the right channel on VT1708 when - * 24bit samples are used. Until any workaround is found, - * disable the 24bit format, so far. - */ - for (i = 0; i < codec->num_pcms; i++) { - struct hda_pcm *info = &spec->gen.pcm_rec[i]; - if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || - info->pcm_type != HDA_PCM_TYPE_AUDIO) - continue; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = - SNDRV_PCM_FMTBIT_S16_LE; + hda_nid_t nid, conn[8]; + unsigned int type; + int i, n; + + for (i = 0; i < spec->num_adc_nids; i++) { + nid = spec->adc_nids[i]; + while (nid) { + type = get_wcaps_type(get_wcaps(codec, nid)); + if (type == AC_WID_PIN) + break; + n = snd_hda_get_connections(codec, nid, conn, + ARRAY_SIZE(conn)); + if (n <= 0) + break; + if (n > 1) { + spec->mux_nids[i] = nid; + break; + } + nid = conn[0]; + } } - return 0; } @@ -707,15 +2853,7 @@ static int patch_vt1708(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x17; - - /* set jackpoll_interval while parsing the codec */ - codec->jackpoll_interval = msecs_to_jiffies(100); - spec->vt1708_jack_detect = 1; - - /* don't support the input jack switching due to lack of unsol event */ - /* (it may work with polling, though, but it needs testing) */ - spec->gen.suppress_auto_mic = 1; + spec->aa_mix_nid = 0x17; /* Add HP and CD pin config connect bit re-config action */ vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); @@ -729,17 +2867,18 @@ static int patch_vt1708(struct hda_codec *codec) } /* add jack detect on/off control */ - spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; + if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) + return -ENOMEM; + + /* disable 32bit format on VT1708 */ + if (codec->vendor_id == 0x11061708) + spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.build_controls = vt1708_build_controls; - codec->patch_ops.build_pcms = vt1708_build_pcms; - - /* clear jackpoll_interval again; it's set dynamically */ - codec->jackpoll_interval = 0; + INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } @@ -753,7 +2892,7 @@ static int patch_vt1709(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x18; + spec->aa_mix_nid = 0x18; err = via_parse_auto_config(codec); if (err < 0) { @@ -797,7 +2936,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) /* PW0 (19h), SW1 (18h), AOW1 (11h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x19, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1b, &parm); update_power_state(codec, 0x18, parm); update_power_state(codec, 0x11, parm); @@ -806,7 +2945,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) if (is_8ch) { parm = AC_PWRST_D3; set_pin_power_state(codec, 0x22, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); update_power_state(codec, 0x26, parm); update_power_state(codec, 0x24, parm); @@ -814,7 +2953,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) /* PW7(23h), SW2(27h), AOW2(25h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x23, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); update_power_state(codec, 0x27, parm); update_power_state(codec, 0x25, parm); @@ -834,7 +2973,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) if (is_8ch) { update_power_state(codec, 0x25, parm); update_power_state(codec, 0x27, parm); - } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled) + } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) update_power_state(codec, 0x25, parm); } @@ -852,7 +2991,7 @@ static int patch_vt1708B(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x16; + spec->aa_mix_nid = 0x16; /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); @@ -877,6 +3016,58 @@ static const struct hda_verb vt1708S_init_verbs[] = { { } }; +/* fill out digital output widgets; one for master and one for slave outputs */ +static void fill_dig_outs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t nid; + int conn; + + nid = spec->autocfg.dig_out_pins[i]; + if (!nid) + continue; + conn = snd_hda_get_connections(codec, nid, &nid, 1); + if (conn < 1) + continue; + if (!spec->multiout.dig_out_nid) + spec->multiout.dig_out_nid = nid; + else { + spec->slave_dig_outs[0] = nid; + break; /* at most two dig outs */ + } + } +} + +static void fill_dig_in(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t dig_nid; + int i, err; + + if (!spec->autocfg.dig_in_pin) + return; + + dig_nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + unsigned int wcaps = get_wcaps(codec, dig_nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (!(wcaps & AC_WCAP_DIGITAL)) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + err = get_connection_index(codec, dig_nid, + spec->autocfg.dig_in_pin); + if (err >= 0) { + spec->dig_in_nid = dig_nid; + break; + } + } +} + static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, int offset, int num_steps, int step_size) { @@ -897,10 +3088,21 @@ static int patch_vt1708S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x16; + spec->aa_mix_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); + /* automatic parse from the BIOS config */ + err = via_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } + + spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; + + codec->patch_ops = via_patch_ops; + /* correct names for VT1708BCE */ if (get_codec_type(codec) == VT1708BCE) { kfree(codec->chip_name); @@ -917,18 +3119,6 @@ static int patch_vt1708S(struct hda_codec *codec) sizeof(codec->bus->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); } - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; - - codec->patch_ops = via_patch_ops; - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; return 0; } @@ -983,7 +3173,7 @@ static int patch_vt1702(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x1a; + spec->aa_mix_nid = 0x1a; /* limit AA path volume to 0 dB */ snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, @@ -1050,17 +3240,17 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) /* PW2 (26h), AOW2 (ah) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x2b, &parm); update_power_state(codec, 0xa, parm); /* PW0 (24h), AOW0 (8h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); - if (!spec->gen.indep_hp_enabled) /* check for redirected HP */ + if (!spec->hp_independent_mode) /* check for redirected HP */ set_pin_power_state(codec, 0x28, &parm); update_power_state(codec, 0x8, parm); - if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) + if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3) parm = parm2; update_power_state(codec, 0xb, parm); /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ @@ -1069,11 +3259,11 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) /* PW1 (25h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x2a, &parm); update_power_state(codec, 0x9, parm); - if (spec->gen.indep_hp_enabled) { + if (spec->hp_independent_mode) { /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); @@ -1093,9 +3283,9 @@ static int add_secret_dac_path(struct hda_codec *codec) hda_nid_t conn[8]; hda_nid_t nid; - if (!spec->gen.mixer_nid) + if (!spec->aa_mix_nid) return 0; - nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, + nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, ARRAY_SIZE(conn) - 1); for (i = 0; i < nums; i++) { if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) @@ -1110,7 +3300,7 @@ static int add_secret_dac_path(struct hda_codec *codec) !(caps & AC_WCAP_DIGITAL)) { conn[nums++] = nid; return snd_hda_override_conn_list(codec, - spec->gen.mixer_nid, + spec->aa_mix_nid, nums, conn); } } @@ -1128,7 +3318,7 @@ static int patch_vt1718S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x21; + spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); add_secret_dac_path(codec); @@ -1259,7 +3449,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x19, &parm); /* Smart 5.1 PW2(1bh) */ - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1b, &parm); update_power_state(codec, 0x18, parm); update_power_state(codec, 0x11, parm); @@ -1268,12 +3458,12 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x23, &parm); /* Smart 5.1 PW1(1ah) */ - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); update_power_state(codec, 0x27, parm); /* Smart 5.1 PW5(1eh) */ - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1e, &parm); update_power_state(codec, 0x25, parm); @@ -1285,7 +3475,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) mono_out = 0; else { present = snd_hda_jack_detect(codec, 0x1d); - if (!spec->gen.indep_hp_enabled && present) + if (!spec->hp_independent_mode && present) mono_out = 0; else mono_out = 1; @@ -1300,7 +3490,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) set_pin_power_state(codec, 0x1c, &parm); set_pin_power_state(codec, 0x1d, &parm); /* HP Independent Mode, power on AOW3 */ - if (spec->gen.indep_hp_enabled) + if (spec->hp_independent_mode) update_power_state(codec, 0x25, parm); /* force to D0 for internal Speaker */ @@ -1319,7 +3509,7 @@ static int patch_vt1716S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x16; + spec->aa_mix_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); @@ -1332,7 +3522,9 @@ static int patch_vt1716S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; - spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; + spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; + spec->num_mixers++; + spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; codec->patch_ops = via_patch_ops; @@ -1417,7 +3609,7 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) update_power_state(codec, 0x35, parm); } - if (spec->gen.indep_hp_enabled) + if (spec->hp_independent_mode) update_power_state(codec, 0x9, AC_PWRST_D0); /* Class-D */ @@ -1515,7 +3707,7 @@ static int patch_vt2002P(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x21; + spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); if (spec->codec_type == VT1802) @@ -1586,7 +3778,7 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec) set_pin_power_state(codec, 0x25, &parm); update_power_state(codec, 0x15, parm); update_power_state(codec, 0x35, parm); - if (spec->gen.indep_hp_enabled) + if (spec->hp_independent_mode) update_power_state(codec, 0x9, AC_PWRST_D0); /* Internal Speaker */ @@ -1639,7 +3831,7 @@ static int patch_vt1812(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x21; + spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); add_secret_dac_path(codec); @@ -1709,7 +3901,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); update_power_state(codec, 0x36, parm); - if (smart51_enabled(codec)) { + if (spec->smart51_enabled) { /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ set_pin_power_state(codec, 0x2b, &parm); update_power_state(codec, 0x3b, parm); @@ -1721,7 +3913,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); update_power_state(codec, 0x35, parm); - if (smart51_enabled(codec)) { + if (spec->smart51_enabled) { /* PW6(2ah), MW6(3ah), MUX6(1ah) */ set_pin_power_state(codec, 0x2a, &parm); update_power_state(codec, 0x3a, parm); @@ -1734,7 +3926,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) set_pin_power_state(codec, 0x28, &parm); update_power_state(codec, 0x38, parm); update_power_state(codec, 0x18, parm); - if (spec->gen.indep_hp_enabled) + if (spec->hp_independent_mode) update_conv_power_state(codec, 0xb, parm, 3); parm2 = parm; /* for pin 0x0b */ @@ -1742,7 +3934,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); update_power_state(codec, 0x34, parm); - if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) + if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3) parm = parm2; update_conv_power_state(codec, 0x8, parm, 0); /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ @@ -1759,7 +3951,7 @@ static int patch_vt3476(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x3f; + spec->aa_mix_nid = 0x3f; add_secret_dac_path(codec); /* automatic parse from the BIOS config */ diff --git a/trunk/sound/pci/intel8x0.c b/trunk/sound/pci/intel8x0.c index b8fe40531b9c..3b9be752f3e2 100644 --- a/trunk/sound/pci/intel8x0.c +++ b/trunk/sound/pci/intel8x0.c @@ -3266,13 +3266,11 @@ static int check_default_spdif_aclink(struct pci_dev *pci) w = snd_pci_quirk_lookup(pci, spdif_aclink_defaults); if (w) { if (w->value) - snd_printdd(KERN_INFO - "intel8x0: Using SPDIF over AC-Link for %s\n", - snd_pci_quirk_name(w)); + snd_printdd(KERN_INFO "intel8x0: Using SPDIF over " + "AC-Link for %s\n", w->name); else - snd_printdd(KERN_INFO - "intel8x0: Using integrated SPDIF DMA for %s\n", - snd_pci_quirk_name(w)); + snd_printdd(KERN_INFO "intel8x0: Using integrated " + "SPDIF DMA for %s\n", w->name); return w->value; } return 0; diff --git a/trunk/sound/pci/maestro3.c b/trunk/sound/pci/maestro3.c index c76ac1411210..9387533f70dc 100644 --- a/trunk/sound/pci/maestro3.c +++ b/trunk/sound/pci/maestro3.c @@ -2586,9 +2586,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, else { quirk = snd_pci_quirk_lookup(pci, m3_amp_quirk_list); if (quirk) { - snd_printdd(KERN_INFO - "maestro3: set amp-gpio for '%s'\n", - snd_pci_quirk_name(quirk)); + snd_printdd(KERN_INFO "maestro3: set amp-gpio " + "for '%s'\n", quirk->name); chip->amp_gpio = quirk->value; } else if (chip->allegro_flag) chip->amp_gpio = GPO_EXT_AMP_ALLEGRO; @@ -2598,9 +2597,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, quirk = snd_pci_quirk_lookup(pci, m3_irda_quirk_list); if (quirk) { - snd_printdd(KERN_INFO - "maestro3: enabled irda workaround for '%s'\n", - snd_pci_quirk_name(quirk)); + snd_printdd(KERN_INFO "maestro3: enabled irda workaround " + "for '%s'\n", quirk->name); chip->irda_workaround = 1; } quirk = snd_pci_quirk_lookup(pci, m3_hv_quirk_list); diff --git a/trunk/sound/pci/nm256/nm256.c b/trunk/sound/pci/nm256/nm256.c index 6febedb05936..563a193e36a3 100644 --- a/trunk/sound/pci/nm256/nm256.c +++ b/trunk/sound/pci/nm256/nm256.c @@ -1660,8 +1660,7 @@ static int snd_nm256_probe(struct pci_dev *pci, q = snd_pci_quirk_lookup(pci, nm256_quirks); if (q) { - snd_printdd(KERN_INFO "nm256: Enabled quirk for %s.\n", - snd_pci_quirk_name(q)); + snd_printdd(KERN_INFO "nm256: Enabled quirk for %s.\n", q->name); switch (q->value) { case NM_BLACKLISTED: printk(KERN_INFO "nm256: The device is blacklisted. " diff --git a/trunk/sound/pci/pcxhr/pcxhr_core.c b/trunk/sound/pci/pcxhr/pcxhr_core.c index 37b431b9b69d..b33db1e006e7 100644 --- a/trunk/sound/pci/pcxhr/pcxhr_core.c +++ b/trunk/sound/pci/pcxhr/pcxhr_core.c @@ -1012,12 +1012,13 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err, enum pcxhr_async_err_src err_src, int pipe, int is_capture) { +#ifdef CONFIG_SND_DEBUG_VERBOSE static char* err_src_name[] = { [PCXHR_ERR_PIPE] = "Pipe", [PCXHR_ERR_STREAM] = "Stream", [PCXHR_ERR_AUDIO] = "Audio" }; - +#endif if (err & 0xfff) err &= 0xfff; else diff --git a/trunk/sound/pci/rme9652/hdsp.c b/trunk/sound/pci/rme9652/hdsp.c index 94084cdb130c..4fae81f21efb 100644 --- a/trunk/sound/pci/rme9652/hdsp.c +++ b/trunk/sound/pci/rme9652/hdsp.c @@ -154,13 +154,10 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_BIGENDIAN_MODE 0x200 #define HDSP_RD_MULTIPLE 0x400 #define HDSP_9652_ENABLE_MIXER 0x800 -#define HDSP_S200 0x800 -#define HDSP_S300 (0x100 | HDSP_S200) /* dummy, purpose of 0x100 unknown */ -#define HDSP_CYCLIC_MODE 0x1000 #define HDSP_TDO 0x10000000 -#define HDSP_S_PROGRAM (HDSP_CYCLIC_MODE|HDSP_PROGRAM|HDSP_CONFIG_MODE_0) -#define HDSP_S_LOAD (HDSP_CYCLIC_MODE|HDSP_PROGRAM|HDSP_CONFIG_MODE_1) +#define HDSP_S_PROGRAM (HDSP_PROGRAM|HDSP_CONFIG_MODE_0) +#define HDSP_S_LOAD (HDSP_PROGRAM|HDSP_CONFIG_MODE_1) /* Control Register bits */ @@ -674,23 +671,13 @@ static unsigned int hdsp_read(struct hdsp *hdsp, int reg) static int hdsp_check_for_iobox (struct hdsp *hdsp) { - int i; - if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0; - for (i = 0; i < 500; i++) { - if (0 == (hdsp_read(hdsp, HDSP_statusRegister) & - HDSP_ConfigError)) { - if (i) { - snd_printd("Hammerfall-DSP: IO box found after %d ms\n", - (20 * i)); - } - return 0; - } - msleep(20); + if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) { + snd_printk("Hammerfall-DSP: no IO box connected!\n"); + hdsp->state &= ~HDSP_FirmwareLoaded; + return -EIO; } - snd_printk(KERN_ERR "Hammerfall-DSP: no IO box connected!\n"); - hdsp->state &= ~HDSP_FirmwareLoaded; - return -EIO; + return 0; } static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops, @@ -741,7 +728,6 @@ static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) { if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) { snd_printk ("Hammerfall-DSP: timeout waiting for download preparation\n"); - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200); return -EIO; } @@ -751,15 +737,17 @@ static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) { hdsp_write(hdsp, HDSP_fifoData, cache[i]); if (hdsp_fifo_wait (hdsp, 127, HDSP_LONG_WAIT)) { snd_printk ("Hammerfall-DSP: timeout during firmware loading\n"); - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200); return -EIO; } } - hdsp_fifo_wait(hdsp, 3, HDSP_LONG_WAIT); - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200); - ssleep(3); + + if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) { + snd_printk ("Hammerfall-DSP: timeout at end of firmware loading\n"); + return -EIO; + } + #ifdef SNDRV_BIG_ENDIAN hdsp->control2_register = HDSP_BIGENDIAN_MODE; #else @@ -785,51 +773,24 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp) { if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) { - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); - hdsp_write(hdsp, HDSP_fifoData, 0); - - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) { - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300); - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); - } + hdsp_write (hdsp, HDSP_control2Reg, HDSP_PROGRAM); + hdsp_write (hdsp, HDSP_fifoData, 0); + if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT) < 0) + return -EIO; - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200 | HDSP_PROGRAM); + hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD); hdsp_write (hdsp, HDSP_fifoData, 0); - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) { - hdsp->io_type = Multiface; - snd_printk("Hammerfall-DSP: Multiface found\n"); - return 0; - } - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); - hdsp_write(hdsp, HDSP_fifoData, 0); - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0) { + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT)) { + hdsp_write(hdsp, HDSP_control2Reg, HDSP_VERSION_BIT); + hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT)) + hdsp->io_type = RPM; + else + hdsp->io_type = Multiface; + } else { hdsp->io_type = Digiface; - snd_printk("Hammerfall-DSP: Digiface found\n"); - return 0; } - - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300); - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); - hdsp_write(hdsp, HDSP_fifoData, 0); - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0) { - hdsp->io_type = Multiface; - snd_printk("Hammerfall-DSP: Multiface found\n"); - return 0; - } - - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300); - hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); - hdsp_write(hdsp, HDSP_fifoData, 0); - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) { - hdsp->io_type = Multiface; - snd_printk("Hammerfall-DSP: Multiface found\n"); - return 0; - } - - hdsp->io_type = RPM; - snd_printk("Hammerfall-DSP: RPM found\n"); - return 0; } else { /* firmware was already loaded, get iobox type */ if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2) @@ -1713,50 +1674,127 @@ static int snd_hdsp_put_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_e return change; } -#define HDSP_TOGGLE_SETTING(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .private_value = xindex, \ - .info = snd_hdsp_info_toggle_setting, \ - .get = snd_hdsp_get_toggle_setting, \ - .put = snd_hdsp_put_toggle_setting \ -} +#define HDSP_SPDIF_OUT(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_spdif_bits, \ + .get = snd_hdsp_get_spdif_out, .put = snd_hdsp_put_spdif_out } -static int hdsp_toggle_setting(struct hdsp *hdsp, u32 regmask) +static int hdsp_spdif_out(struct hdsp *hdsp) { - return (hdsp->control_register & regmask) ? 1 : 0; + return (hdsp->control_register & HDSP_SPDIFOpticalOut) ? 1 : 0; } -static int hdsp_set_toggle_setting(struct hdsp *hdsp, u32 regmask, int out) +static int hdsp_set_spdif_output(struct hdsp *hdsp, int out) { if (out) - hdsp->control_register |= regmask; + hdsp->control_register |= HDSP_SPDIFOpticalOut; else - hdsp->control_register &= ~regmask; + hdsp->control_register &= ~HDSP_SPDIFOpticalOut; hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +#define snd_hdsp_info_spdif_bits snd_ctl_boolean_mono_info + +static int snd_hdsp_get_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = hdsp_spdif_out(hdsp); + return 0; +} + +static int snd_hdsp_put_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdsp->lock); + change = (int)val != hdsp_spdif_out(hdsp); + hdsp_set_spdif_output(hdsp, val); + spin_unlock_irq(&hdsp->lock); + return change; +} + +#define HDSP_SPDIF_PROFESSIONAL(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_spdif_bits, \ + .get = snd_hdsp_get_spdif_professional, .put = snd_hdsp_put_spdif_professional } + +static int hdsp_spdif_professional(struct hdsp *hdsp) +{ + return (hdsp->control_register & HDSP_SPDIFProfessional) ? 1 : 0; +} +static int hdsp_set_spdif_professional(struct hdsp *hdsp, int val) +{ + if (val) + hdsp->control_register |= HDSP_SPDIFProfessional; + else + hdsp->control_register &= ~HDSP_SPDIFProfessional; + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); return 0; } -#define snd_hdsp_info_toggle_setting snd_ctl_boolean_mono_info +static int snd_hdsp_get_spdif_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = hdsp_spdif_professional(hdsp); + return 0; +} -static int snd_hdsp_get_toggle_setting(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_hdsp_put_spdif_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - u32 regmask = kcontrol->private_value; + int change; + unsigned int val; + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; spin_lock_irq(&hdsp->lock); - ucontrol->value.integer.value[0] = hdsp_toggle_setting(hdsp, regmask); + change = (int)val != hdsp_spdif_professional(hdsp); + hdsp_set_spdif_professional(hdsp, val); spin_unlock_irq(&hdsp->lock); + return change; +} + +#define HDSP_SPDIF_EMPHASIS(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_spdif_bits, \ + .get = snd_hdsp_get_spdif_emphasis, .put = snd_hdsp_put_spdif_emphasis } + +static int hdsp_spdif_emphasis(struct hdsp *hdsp) +{ + return (hdsp->control_register & HDSP_SPDIFEmphasis) ? 1 : 0; +} + +static int hdsp_set_spdif_emphasis(struct hdsp *hdsp, int val) +{ + if (val) + hdsp->control_register |= HDSP_SPDIFEmphasis; + else + hdsp->control_register &= ~HDSP_SPDIFEmphasis; + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +static int snd_hdsp_get_spdif_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = hdsp_spdif_emphasis(hdsp); return 0; } -static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_hdsp_put_spdif_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - u32 regmask = kcontrol->private_value; int change; unsigned int val; @@ -1764,9 +1802,52 @@ static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol, return -EBUSY; val = ucontrol->value.integer.value[0] & 1; spin_lock_irq(&hdsp->lock); - change = (int) val != hdsp_toggle_setting(hdsp, regmask); - if (change) - hdsp_set_toggle_setting(hdsp, regmask, val); + change = (int)val != hdsp_spdif_emphasis(hdsp); + hdsp_set_spdif_emphasis(hdsp, val); + spin_unlock_irq(&hdsp->lock); + return change; +} + +#define HDSP_SPDIF_NON_AUDIO(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_hdsp_info_spdif_bits, \ + .get = snd_hdsp_get_spdif_nonaudio, .put = snd_hdsp_put_spdif_nonaudio } + +static int hdsp_spdif_nonaudio(struct hdsp *hdsp) +{ + return (hdsp->control_register & HDSP_SPDIFNonAudio) ? 1 : 0; +} + +static int hdsp_set_spdif_nonaudio(struct hdsp *hdsp, int val) +{ + if (val) + hdsp->control_register |= HDSP_SPDIFNonAudio; + else + hdsp->control_register &= ~HDSP_SPDIFNonAudio; + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +static int snd_hdsp_get_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = hdsp_spdif_nonaudio(hdsp); + return 0; +} + +static int snd_hdsp_put_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdsp->lock); + change = (int)val != hdsp_spdif_nonaudio(hdsp); + hdsp_set_spdif_nonaudio(hdsp, val); spin_unlock_irq(&hdsp->lock); return change; } @@ -2370,6 +2451,114 @@ static int snd_hdsp_put_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl return change; } +#define HDSP_XLR_BREAKOUT_CABLE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdsp_info_xlr_breakout_cable, \ + .get = snd_hdsp_get_xlr_breakout_cable, \ + .put = snd_hdsp_put_xlr_breakout_cable \ +} + +static int hdsp_xlr_breakout_cable(struct hdsp *hdsp) +{ + if (hdsp->control_register & HDSP_XLRBreakoutCable) + return 1; + return 0; +} + +static int hdsp_set_xlr_breakout_cable(struct hdsp *hdsp, int mode) +{ + if (mode) + hdsp->control_register |= HDSP_XLRBreakoutCable; + else + hdsp->control_register &= ~HDSP_XLRBreakoutCable; + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +#define snd_hdsp_info_xlr_breakout_cable snd_ctl_boolean_mono_info + +static int snd_hdsp_get_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdsp_xlr_breakout_cable(hdsp); + return 0; +} + +static int snd_hdsp_put_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + int change; + int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdsp->lock); + change = (int)val != hdsp_xlr_breakout_cable(hdsp); + hdsp_set_xlr_breakout_cable(hdsp, val); + spin_unlock_irq(&hdsp->lock); + return change; +} + +/* (De)activates old RME Analog Extension Board + These are connected to the internal ADAT connector + Switching this on desactivates external ADAT +*/ +#define HDSP_AEB(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdsp_info_aeb, \ + .get = snd_hdsp_get_aeb, \ + .put = snd_hdsp_put_aeb \ +} + +static int hdsp_aeb(struct hdsp *hdsp) +{ + if (hdsp->control_register & HDSP_AnalogExtensionBoard) + return 1; + return 0; +} + +static int hdsp_set_aeb(struct hdsp *hdsp, int mode) +{ + if (mode) + hdsp->control_register |= HDSP_AnalogExtensionBoard; + else + hdsp->control_register &= ~HDSP_AnalogExtensionBoard; + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +#define snd_hdsp_info_aeb snd_ctl_boolean_mono_info + +static int snd_hdsp_get_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdsp_aeb(hdsp); + return 0; +} + +static int snd_hdsp_put_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + int change; + int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdsp->lock); + change = (int)val != hdsp_aeb(hdsp); + hdsp_set_aeb(hdsp, val); + spin_unlock_irq(&hdsp->lock); + return change; +} + #define HDSP_PREF_SYNC_REF(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2558,6 +2747,58 @@ static int snd_hdsp_get_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_c return 0; } +#define HDSP_LINE_OUT(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdsp_info_line_out, \ + .get = snd_hdsp_get_line_out, \ + .put = snd_hdsp_put_line_out \ +} + +static int hdsp_line_out(struct hdsp *hdsp) +{ + return (hdsp->control_register & HDSP_LineOut) ? 1 : 0; +} + +static int hdsp_set_line_output(struct hdsp *hdsp, int out) +{ + if (out) + hdsp->control_register |= HDSP_LineOut; + else + hdsp->control_register &= ~HDSP_LineOut; + hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); + return 0; +} + +#define snd_hdsp_info_line_out snd_ctl_boolean_mono_info + +static int snd_hdsp_get_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&hdsp->lock); + ucontrol->value.integer.value[0] = hdsp_line_out(hdsp); + spin_unlock_irq(&hdsp->lock); + return 0; +} + +static int snd_hdsp_put_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); + int change; + unsigned int val; + + if (!snd_hdsp_use_is_exclusive(hdsp)) + return -EBUSY; + val = ucontrol->value.integer.value[0] & 1; + spin_lock_irq(&hdsp->lock); + change = (int)val != hdsp_line_out(hdsp); + hdsp_set_line_output(hdsp, val); + spin_unlock_irq(&hdsp->lock); + return change; +} + #define HDSP_PRECISE_POINTER(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_CARD, \ .name = xname, \ @@ -2949,7 +3190,7 @@ static struct snd_kcontrol_new snd_hdsp_9632_controls[] = { HDSP_DA_GAIN("DA Gain", 0), HDSP_AD_GAIN("AD Gain", 0), HDSP_PHONE_GAIN("Phones Gain", 0), -HDSP_TOGGLE_SETTING("XLR Breakout Cable", HDSP_XLRBreakoutCable), +HDSP_XLR_BREAKOUT_CABLE("XLR Breakout Cable", 0), HDSP_DDS_OFFSET("DDS Sample Rate Offset", 0) }; @@ -2991,10 +3232,10 @@ static struct snd_kcontrol_new snd_hdsp_controls[] = { }, HDSP_MIXER("Mixer", 0), HDSP_SPDIF_IN("IEC958 Input Connector", 0), -HDSP_TOGGLE_SETTING("IEC958 Output also on ADAT1", HDSP_SPDIFOpticalOut), -HDSP_TOGGLE_SETTING("IEC958 Professional Bit", HDSP_SPDIFProfessional), -HDSP_TOGGLE_SETTING("IEC958 Emphasis Bit", HDSP_SPDIFEmphasis), -HDSP_TOGGLE_SETTING("IEC958 Non-audio Bit", HDSP_SPDIFNonAudio), +HDSP_SPDIF_OUT("IEC958 Output also on ADAT1", 0), +HDSP_SPDIF_PROFESSIONAL("IEC958 Professional Bit", 0), +HDSP_SPDIF_EMPHASIS("IEC958 Emphasis Bit", 0), +HDSP_SPDIF_NON_AUDIO("IEC958 Non-audio Bit", 0), /* 'Sample Clock Source' complies with the alsa control naming scheme */ HDSP_CLOCK_SOURCE("Sample Clock Source", 0), { @@ -3014,7 +3255,7 @@ HDSP_AUTOSYNC_SAMPLE_RATE("External Rate", 0), HDSP_WC_SYNC_CHECK("Word Clock Lock Status", 0), HDSP_SPDIF_SYNC_CHECK("SPDIF Lock Status", 0), HDSP_ADATSYNC_SYNC_CHECK("ADAT Sync Lock Status", 0), -HDSP_TOGGLE_SETTING("Line Out", HDSP_LineOut), +HDSP_LINE_OUT("Line Out", 0), HDSP_PRECISE_POINTER("Precise Pointer", 0), HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0), }; @@ -3331,9 +3572,7 @@ static struct snd_kcontrol_new snd_hdsp_rpm_controls[] = { HDSP_MIXER("Mixer", 0) }; -static struct snd_kcontrol_new snd_hdsp_96xx_aeb = - HDSP_TOGGLE_SETTING("Analog Extension Board", - HDSP_AnalogExtensionBoard); +static struct snd_kcontrol_new snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0); static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK; static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp) @@ -3756,9 +3995,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) } snd_iprintf(buffer, "Phones Gain : %s\n", tmp); - snd_iprintf(buffer, "XLR Breakout Cable : %s\n", - hdsp_toggle_setting(hdsp, HDSP_XLRBreakoutCable) ? - "yes" : "no"); + snd_iprintf(buffer, "XLR Breakout Cable : %s\n", hdsp_xlr_breakout_cable(hdsp) ? "yes" : "no"); if (hdsp->control_register & HDSP_AnalogExtensionBoard) snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n"); @@ -4789,38 +5026,29 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632) ? 3 : 1); ++i) info.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i); info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp); - info.spdif_out = (unsigned char)hdsp_toggle_setting(hdsp, - HDSP_SPDIFOpticalOut); - info.spdif_professional = (unsigned char) - hdsp_toggle_setting(hdsp, HDSP_SPDIFProfessional); - info.spdif_emphasis = (unsigned char) - hdsp_toggle_setting(hdsp, HDSP_SPDIFEmphasis); - info.spdif_nonaudio = (unsigned char) - hdsp_toggle_setting(hdsp, HDSP_SPDIFNonAudio); + info.spdif_out = (unsigned char)hdsp_spdif_out(hdsp); + info.spdif_professional = (unsigned char)hdsp_spdif_professional(hdsp); + info.spdif_emphasis = (unsigned char)hdsp_spdif_emphasis(hdsp); + info.spdif_nonaudio = (unsigned char)hdsp_spdif_nonaudio(hdsp); info.spdif_sample_rate = hdsp_spdif_sample_rate(hdsp); info.system_sample_rate = hdsp->system_sample_rate; info.autosync_sample_rate = hdsp_external_sample_rate(hdsp); info.system_clock_mode = (unsigned char)hdsp_system_clock_mode(hdsp); info.clock_source = (unsigned char)hdsp_clock_source(hdsp); info.autosync_ref = (unsigned char)hdsp_autosync_ref(hdsp); - info.line_out = (unsigned char) - hdsp_toggle_setting(hdsp, HDSP_LineOut); + info.line_out = (unsigned char)hdsp_line_out(hdsp); if (hdsp->io_type == H9632) { info.da_gain = (unsigned char)hdsp_da_gain(hdsp); info.ad_gain = (unsigned char)hdsp_ad_gain(hdsp); info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp); - info.xlr_breakout_cable = - (unsigned char)hdsp_toggle_setting(hdsp, - HDSP_XLRBreakoutCable); + info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp); } else if (hdsp->io_type == RPM) { info.da_gain = (unsigned char) hdsp_rpm_input12(hdsp); info.ad_gain = (unsigned char) hdsp_rpm_input34(hdsp); } if (hdsp->io_type == H9632 || hdsp->io_type == H9652) - info.analog_extension_board = - (unsigned char)hdsp_toggle_setting(hdsp, - HDSP_AnalogExtensionBoard); + info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp); spin_unlock_irqrestore(&hdsp->lock, flags); if (copy_to_user(argp, &info, sizeof(info))) return -EFAULT; diff --git a/trunk/sound/pci/via82xx.c b/trunk/sound/pci/via82xx.c index d756a3562706..6442f611a07b 100644 --- a/trunk/sound/pci/via82xx.c +++ b/trunk/sound/pci/via82xx.c @@ -2517,7 +2517,7 @@ static int check_dxs_list(struct pci_dev *pci, int revision) w = snd_pci_quirk_lookup(pci, dxs_whitelist); if (w) { snd_printdd(KERN_INFO "via82xx: DXS white list for %s found\n", - snd_pci_quirk_name(w)); + w->name); return w->value; } diff --git a/trunk/sound/usb/caiaq/device.c b/trunk/sound/usb/caiaq/device.c index e4d6dbb0342d..c828f8189c25 100644 --- a/trunk/sound/usb/caiaq/device.c +++ b/trunk/sound/usb/caiaq/device.c @@ -48,10 +48,10 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, Audio 8 DJ}," "{Native Instruments, Traktor Audio 2}," "{Native Instruments, Session I/O}," - "{Native Instruments, GuitarRig mobile}," - "{Native Instruments, Traktor Kontrol X1}," - "{Native Instruments, Traktor Kontrol S4}," - "{Native Instruments, Maschine Controller}}"); + "{Native Instruments, GuitarRig mobile}" + "{Native Instruments, Traktor Kontrol X1}" + "{Native Instruments, Traktor Kontrol S4}" + "{Native Instruments, Maschine Controller}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */ diff --git a/trunk/sound/usb/card.c b/trunk/sound/usb/card.c index 803953a9bff3..ccf95cfe186f 100644 --- a/trunk/sound/usb/card.c +++ b/trunk/sound/usb/card.c @@ -646,7 +646,7 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) as->substream[0].need_setup_ep = as->substream[1].need_setup_ep = true; } - } + } } else { /* * otherwise we keep the rest of the system in the dark diff --git a/trunk/sound/usb/pcm.c b/trunk/sound/usb/pcm.c index 81f70a719bb9..d82e378d37cb 100644 --- a/trunk/sound/usb/pcm.c +++ b/trunk/sound/usb/pcm.c @@ -59,12 +59,7 @@ snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, /* Approximation based on number of samples per USB frame (ms), some truncation for 44.1 but the estimate is good enough */ - est_delay = frame_diff * rate / 1000; - if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) - est_delay = subs->last_delay - est_delay; - else - est_delay = subs->last_delay + est_delay; - + est_delay = subs->last_delay - (frame_diff * rate / 1000); if (est_delay < 0) est_delay = 0; return est_delay; @@ -83,7 +78,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream return SNDRV_PCM_POS_XRUN; spin_lock(&subs->lock); hwptr_done = subs->hwptr_done; - substream->runtime->delay = snd_usb_pcm_delay(subs, + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + substream->runtime->delay = snd_usb_pcm_delay(subs, substream->runtime->rate); spin_unlock(&subs->lock); return hwptr_done / (substream->runtime->frame_bits >> 3); @@ -1161,10 +1157,6 @@ static void retire_capture_urb(struct snd_usb_substream *subs, int i, period_elapsed = 0; unsigned long flags; unsigned char *cp; - int current_frame_number; - - /* read frame number here, update pointer in critical section */ - current_frame_number = usb_get_current_frame_number(subs->dev); stride = runtime->frame_bits >> 3; @@ -1179,7 +1171,9 @@ static void retire_capture_urb(struct snd_usb_substream *subs, if (!subs->txfr_quirk) bytes = frames * stride; if (bytes % (runtime->sample_bits >> 3) != 0) { +#ifdef CONFIG_SND_DEBUG_VERBOSE int oldbytes = bytes; +#endif bytes = frames * stride; snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n", oldbytes, bytes); @@ -1196,15 +1190,6 @@ static void retire_capture_urb(struct snd_usb_substream *subs, subs->transfer_done -= runtime->period_size; period_elapsed = 1; } - /* capture delay is by construction limited to one URB, - * reset delays here - */ - runtime->delay = subs->last_delay = 0; - - /* realign last_frame_number */ - subs->last_frame_number = current_frame_number; - subs->last_frame_number &= 0xFF; /* keep 8 LSBs */ - spin_unlock_irqrestore(&subs->lock, flags); /* copy a data chunk */ if (oldptr + bytes > runtime->buffer_size * stride) { diff --git a/trunk/sound/usb/quirks-table.h b/trunk/sound/usb/quirks-table.h index 64d25a7a4d59..820580a6dfc3 100644 --- a/trunk/sound/usb/quirks-table.h +++ b/trunk/sound/usb/quirks-table.h @@ -1750,7 +1750,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { /* .vendor_name = "Roland", */ /* .product_name = "A-PRO", */ - .ifnum = 1, + .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const struct snd_usb_midi_endpoint_info) { .out_cables = 0x0003, diff --git a/trunk/tools/perf/MANIFEST b/trunk/tools/perf/MANIFEST index 80db3f4bcf7a..39d41068484f 100644 --- a/trunk/tools/perf/MANIFEST +++ b/trunk/tools/perf/MANIFEST @@ -11,11 +11,21 @@ lib/rbtree.c include/linux/swab.h arch/*/include/asm/unistd*.h arch/*/include/asm/perf_regs.h +arch/*/include/uapi/asm/unistd*.h +arch/*/include/uapi/asm/perf_regs.h arch/*/lib/memcpy*.S arch/*/lib/memset*.S include/linux/poison.h include/linux/magic.h include/linux/hw_breakpoint.h +include/linux/rbtree_augmented.h +include/uapi/linux/perf_event.h +include/uapi/linux/const.h +include/uapi/linux/swab.h +include/uapi/linux/hw_breakpoint.h arch/x86/include/asm/svm.h arch/x86/include/asm/vmx.h arch/x86/include/asm/kvm_host.h +arch/x86/include/uapi/asm/svm.h +arch/x86/include/uapi/asm/vmx.h +arch/x86/include/uapi/asm/kvm.h diff --git a/trunk/tools/perf/Makefile b/trunk/tools/perf/Makefile index 891bc77bdb2c..8ab05e543ef4 100644 --- a/trunk/tools/perf/Makefile +++ b/trunk/tools/perf/Makefile @@ -58,7 +58,7 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -e s/arm.*/arm/ -e s/sa110/arm/ \ -e s/s390x/s390/ -e s/parisc64/parisc/ \ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ - -e s/sh[234].*/sh/ ) + -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ ) NO_PERF_REGS := 1 CC = $(CROSS_COMPILE)gcc