diff options
538 files changed, 14161 insertions, 8016 deletions
diff --git a/Documentation/devicetree/bindings/misc/atmel-ssc.txt b/Documentation/devicetree/bindings/misc/atmel-ssc.txt index 38e51ad2e07..a45ae08c8ed 100644 --- a/Documentation/devicetree/bindings/misc/atmel-ssc.txt +++ b/Documentation/devicetree/bindings/misc/atmel-ssc.txt @@ -7,9 +7,30 @@ Required properties: - reg: Should contain SSC registers location and length - interrupts: Should contain SSC interrupt -Example: + +Required properties for devices compatible with "atmel,at91sam9g45-ssc": +- dmas: DMA specifier, consisting of a phandle to DMA controller node, + the memory interface and SSC DMA channel ID (for tx and rx). + See Documentation/devicetree/bindings/dma/atmel-dma.txt for details. +- dma-names: Must be "tx", "rx". + +Examples: +- PDC transfer: ssc0: ssc@fffbc000 { compatible = "atmel,at91rm9200-ssc"; reg = <0xfffbc000 0x4000>; interrupts = <14 4 5>; }; + +- DMA transfer: +ssc0: ssc@f0010000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xf0010000 0x4000>; + interrupts = <28 4 5>; + dmas = <&dma0 1 13>, + <&dma0 1 14>; + dma-names = "tx", "rx"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>; + status = "disabled"; +}; diff --git a/Documentation/devicetree/bindings/serial/mrvl,pxa-ssp.txt b/Documentation/devicetree/bindings/serial/mrvl,pxa-ssp.txt new file mode 100644 index 00000000000..669b8140dd7 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/mrvl,pxa-ssp.txt @@ -0,0 +1,65 @@ +Device tree bindings for Marvell PXA SSP ports + +Required properties: + + - compatible: Must be one of + mrvl,pxa25x-ssp + mvrl,pxa25x-nssp + mrvl,pxa27x-ssp + mrvl,pxa3xx-ssp + mvrl,pxa168-ssp + mrvl,pxa910-ssp + mrvl,ce4100-ssp + mrvl,lpss-ssp + + - reg: The memory base + - dmas: Two dma phandles, one for rx, one for tx + - dma-names: Must be "rx", "tx" + + +Example for PXA3xx: + + ssp0: ssp@41000000 { + compatible = "mrvl,pxa3xx-ssp"; + reg = <0x41000000 0x40>; + ssp-id = <1>; + interrupts = <24>; + clock-names = "pxa27x-ssp.0"; + dmas = <&dma 13 + &dma 14>; + dma-names = "rx", "tx"; + }; + + ssp1: ssp@41700000 { + compatible = "mrvl,pxa3xx-ssp"; + reg = <0x41700000 0x40>; + ssp-id = <2>; + interrupts = <16>; + clock-names = "pxa27x-ssp.1"; + dmas = <&dma 15 + &dma 16>; + dma-names = "rx", "tx"; + }; + + ssp2: ssp@41900000 { + compatibl3 = "mrvl,pxa3xx-ssp"; + reg = <0x41900000 0x40>; + ssp-id = <3>; + interrupts = <0>; + clock-names = "pxa27x-ssp.2"; + dmas = <&dma 66 + &dma 67>; + dma-names = "rx", "tx"; + }; + + ssp3: ssp@41a00000 { + compatible = "mrvl,pxa3xx-ssp"; + reg = <0x41a00000 0x40>; + ssp-id = <4>; + interrupts = <13>; + clock-names = "pxa27x-ssp.3"; + dmas = <&dma 2 + &dma 3>; + dma-names = "rx", "tx"; + }; + diff --git a/Documentation/devicetree/bindings/sound/ak4554.c b/Documentation/devicetree/bindings/sound/ak4554.c new file mode 100644 index 00000000000..934fa02754b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4554.c @@ -0,0 +1,11 @@ +AK4554 ADC/DAC + +Required properties: + + - compatible : "asahi-kasei,ak4554" + +Example: + +ak4554-adc-dac { + compatible = "asahi-kasei,ak4554"; +}; diff --git a/Documentation/devicetree/bindings/sound/alc5632.txt b/Documentation/devicetree/bindings/sound/alc5632.txt index 8608f747dcf..ffd886d110b 100644 --- a/Documentation/devicetree/bindings/sound/alc5632.txt +++ b/Documentation/devicetree/bindings/sound/alc5632.txt @@ -13,6 +13,25 @@ Required properties: - #gpio-cells : Should be two. The first cell is the pin number and the second cell is used to specify optional parameters (currently unused). +Pins on the device (for linking into audio routes): + + * SPK_OUTP + * SPK_OUTN + * HP_OUT_L + * HP_OUT_R + * AUX_OUT_P + * AUX_OUT_N + * LINE_IN_L + * LINE_IN_R + * PHONE_P + * PHONE_N + * MIC1_P + * MIC1_N + * MIC2_P + * MIC2_N + * MICBIAS1 + * DMICDAT + Example: alc5632: alc5632@1e { diff --git a/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt b/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt new file mode 100644 index 00000000000..0720857089a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt @@ -0,0 +1,35 @@ +* Atmel at91sam9x5ek wm8731 audio complex + +Required properties: + - compatible: "atmel,sam9x5-wm8731-audio" + - atmel,model: The user-visible name of this sound complex. + - atmel,ssc-controller: The phandle of the SSC controller + - atmel,audio-codec: The phandle of the WM8731 audio codec + - atmel,audio-routing: A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. + +Available audio endpoints for the audio-routing table: + +Board connectors: + * Headphone Jack + * Line In Jack + +wm8731 pins: +cf Documentation/devicetree/bindings/sound/wm8731.txt + +Example: +sound { + compatible = "atmel,sam9x5-wm8731-audio"; + + atmel,model = "wm8731 @ AT91SAM9X5EK"; + + atmel,audio-routing = + "Headphone Jack", "RHPOUT", + "Headphone Jack", "LHPOUT", + "LLINEIN", "Line In Jack", + "RLINEIN", "Line In Jack"; + + atmel,ssc-controller = <&ssc0>; + atmel,audio-codec = <&wm8731>; +}; diff --git a/Documentation/devicetree/bindings/sound/atmel-wm8904.txt b/Documentation/devicetree/bindings/sound/atmel-wm8904.txt new file mode 100644 index 00000000000..8bbe50c884b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/atmel-wm8904.txt @@ -0,0 +1,55 @@ +Atmel ASoC driver with wm8904 audio codec complex + +Required properties: + - compatible: "atmel,asoc-wm8904" + - atmel,model: The user-visible name of this sound complex. + - atmel,audio-routing: A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the WM8904's pins, and the jacks on the board: + + WM8904 pins: + + * IN1L + * IN1R + * IN2L + * IN2R + * IN3L + * IN3R + * HPOUTL + * HPOUTR + * LINEOUTL + * LINEOUTR + * MICBIAS + + Board connectors: + + * Headphone Jack + * Line In Jack + * Mic + + - atmel,ssc-controller: The phandle of the SSC controller + - atmel,audio-codec: The phandle of the WM8904 audio codec + +Optional properties: + - pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt + +Example: +sound { + compatible = "atmel,asoc-wm8904"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pck0_as_mck>; + + atmel,model = "wm8904 @ AT91SAM9N12EK"; + + atmel,audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "IN2L", "Line In Jack", + "IN2R", "Line In Jack", + "Mic", "MICBIAS", + "IN1L", "Mic"; + + atmel,ssc-controller = <&ssc0>; + atmel,audio-codec = <&wm8904>; +}; diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt new file mode 100644 index 00000000000..f2ae335670f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt @@ -0,0 +1,54 @@ +Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller + +The Freescale S/PDIF audio block is a stereo transceiver that allows the +processor to receive and transmit digital audio via an coaxial cable or +a fibre cable. + +Required properties: + + - compatible : Compatible list, must contain "fsl,imx35-spdif". + + - reg : Offset and length of the register set for the device. + + - interrupts : Contains the spdif interrupt. + + - dmas : Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. + + - dma-names : Two dmas have to be defined, "tx" and "rx". + + - clocks : Contains an entry for each entry in clock-names. + + - clock-names : Includes the following entries: + "core" The core clock of spdif controller + "rxtx<0-7>" Clock source list for tx and rx clock. + This clock list should be identical to + the source list connecting to the spdif + clock mux in "SPDIF Transceiver Clock + Diagram" of SoC reference manual. It + can also be referred to TxClk_Source + bit of register SPDIF_STC. + +Example: + +spdif: spdif@02004000 { + compatible = "fsl,imx35-spdif"; + reg = <0x02004000 0x4000>; + interrupts = <0 52 0x04>; + dmas = <&sdma 14 18 0>, + <&sdma 15 18 0>; + dma-names = "rx", "tx"; + + clocks = <&clks 197>, <&clks 3>, + <&clks 197>, <&clks 107>, + <&clks 0>, <&clks 118>, + <&clks 62>, <&clks 139>, + <&clks 0>; + clock-names = "core", "rxtx0", + "rxtx1", "rxtx2", + "rxtx3", "rxtx4", + "rxtx5", "rxtx6", + "rxtx7"; + + status = "okay"; +}; diff --git a/Documentation/devicetree/bindings/powerpc/fsl/ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt index 5ff76c9c57d..4303b6ab620 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/ssi.txt +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -43,10 +43,22 @@ Required properties: together. This would still allow different sample sizes, but not different sample rates. +Required are also ac97 link bindings if ac97 is used. See +Documentation/devicetree/bindings/sound/soc-ac97link.txt for the necessary +bindings. + Optional properties: - codec-handle: Phandle to a 'codec' node that defines an audio codec connected to this SSI. This node is typically a child of an I2C or other control node. +- fsl,fiq-stream-filter: Bool property. Disabled DMA and use FIQ instead to + filter the codec stream. This is necessary for some boards + where an incompatible codec is connected to this SSI, e.g. + on pca100 and pcm043. +- dmas: Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. +- dma-names: Two dmas have to be defined, "tx" and "rx", if fsl,imx-fiq + is not defined. Child 'codec' node required properties: - compatible: Compatible list, contains the name of the codec diff --git a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt new file mode 100644 index 00000000000..7d13479f9c3 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt @@ -0,0 +1,34 @@ +Freescale i.MX audio complex with S/PDIF transceiver + +Required properties: + + - compatible : "fsl,imx-audio-spdif" + + - model : The user-visible name of this sound complex + + - spdif-controller : The phandle of the i.MX S/PDIF controller + + +Optional properties: + + - spdif-out : This is a boolean property. If present, the transmitting + function of S/PDIF will be enabled, indicating there's a physical + S/PDIF out connector/jack on the board or it's connecting to some + other IP block, such as an HDMI encoder/display-controller. + + - spdif-in : This is a boolean property. If present, the receiving + function of S/PDIF will be enabled, indicating there's a physical + S/PDIF in connector/jack on the board. + +* Note: At least one of these two properties should be set in the DT binding. + + +Example: + +sound-spdif { + compatible = "fsl,imx-audio-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + spdif-in; +}; diff --git a/Documentation/devicetree/bindings/sound/imx-audmux.txt b/Documentation/devicetree/bindings/sound/imx-audmux.txt index 215aa981721..f88a00e54c6 100644 --- a/Documentation/devicetree/bindings/sound/imx-audmux.txt +++ b/Documentation/devicetree/bindings/sound/imx-audmux.txt @@ -5,6 +5,15 @@ Required properties: or "fsl,imx31-audmux" for the version firstly used on i.MX31. - reg : Should contain AUDMUX registers location and length +An initial configuration can be setup using child nodes. + +Required properties of optional child nodes: +- fsl,audmux-port : Integer of the audmux port that is configured by this + child node. +- fsl,port-config : List of configuration options for the specific port. For + imx31-audmux and above, it is a list of tuples <ptcr pdcr>. For + imx21-audmux it is a list of pcr values. + Example: audmux@021d8000 { diff --git a/Documentation/devicetree/bindings/sound/mrvl,pxa-ssp.txt b/Documentation/devicetree/bindings/sound/mrvl,pxa-ssp.txt new file mode 100644 index 00000000000..74c9ba6c282 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mrvl,pxa-ssp.txt @@ -0,0 +1,28 @@ +Marvell PXA SSP CPU DAI bindings + +Required properties: + + compatible Must be "mrvl,pxa-ssp-dai" + port A phandle reference to a PXA ssp upstream device + +Example: + + /* upstream device */ + + ssp0: ssp@41000000 { + compatible = "mrvl,pxa3xx-ssp"; + reg = <0x41000000 0x40>; + interrupts = <24>; + clock-names = "pxa27x-ssp.0"; + dmas = <&dma 13 + &dma 14>; + dma-names = "rx", "tx"; + }; + + /* DAI as user */ + + ssp_dai0: ssp_dai@0 { + compatible = "mrvl,pxa-ssp-dai"; + port = <&ssp0>; + }; + diff --git a/Documentation/devicetree/bindings/sound/mrvl,pxa2xx-pcm.txt b/Documentation/devicetree/bindings/sound/mrvl,pxa2xx-pcm.txt new file mode 100644 index 00000000000..551fbb8348c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mrvl,pxa2xx-pcm.txt @@ -0,0 +1,15 @@ +DT bindings for ARM PXA2xx PCM platform driver + +This is just a dummy driver that registers the PXA ASoC platform driver. +It does not have any resources assigned. + +Required properties: + + - compatible 'mrvl,pxa-pcm-audio' + +Example: + + pxa_pcm_audio: snd_soc_pxa_audio { + compatible = "mrvl,pxa-pcm-audio"; + }; + diff --git a/Documentation/devicetree/bindings/sound/mvebu-audio.txt b/Documentation/devicetree/bindings/sound/mvebu-audio.txt new file mode 100644 index 00000000000..7e5fd37c1b3 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mvebu-audio.txt @@ -0,0 +1,29 @@ +* mvebu (Kirkwood, Dove, Armada 370) audio controller + +Required properties: + +- compatible: "marvell,mvebu-audio" + +- reg: physical base address of the controller and length of memory mapped + region. + +- interrupts: list of two irq numbers. + The first irq is used for data flow and the second one is used for errors. + +- clocks: one or two phandles. + The first one is mandatory and defines the internal clock. + The second one is optional and defines an external clock. + +- clock-names: names associated to the clocks: + "internal" for the internal clock + "extclk" for the external clock + +Example: + +i2s1: audio-controller@b4000 { + compatible = "marvell,mvebu-audio"; + reg = <0xb4000 0x2210>; + interrupts = <21>, <22>; + clocks = <&gate_clk 13>; + clock-names = "internal"; +}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt index 05ffecb5710..8b8903ef080 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt @@ -11,28 +11,8 @@ Required properties: - nvidia,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. Valid names for sources and - sinks are the ALC5632's pins: - - ALC5632 pins: - - * SPK_OUTP - * SPK_OUTN - * HP_OUT_L - * HP_OUT_R - * AUX_OUT_P - * AUX_OUT_N - * LINE_IN_L - * LINE_IN_R - * PHONE_P - * PHONE_N - * MIC1_P - * MIC1_N - * MIC2_P - * MIC2_N - * MICBIAS1 - * DMICDAT - - Board connectors: + sinks are the ALC5632's pins as documented in the binding for the device + and: * Headset Stereophone * Int Spk diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt index d130818700b..dc6224994d6 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt @@ -11,32 +11,12 @@ Required properties: - nvidia,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. Valid names for sources and - sinks are the RT5640's pins, and the jacks on the board: - - RT5640 pins: - - * DMIC1 - * DMIC2 - * MICBIAS1 - * IN1P - * IN1R - * IN2P - * IN2R - * HPOL - * HPOR - * LOUTL - * LOUTR - * MONOP - * MONON - * SPOLP - * SPOLN - * SPORP - * SPORN - - Board connectors: + sinks are the RT5640's pins (as documented in its binding), and the jacks + on the board: * Headphones * Speakers + * Mic Jack - nvidia,i2s-controller : The phandle of the Tegra I2S controller that's connected to the CODEC. diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt index d14510613a7..aab6ce0ad2f 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt @@ -11,31 +11,8 @@ Required properties: - nvidia,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. Valid names for sources and - sinks are the WM8753's pins, and the jacks on the board: - - WM8753 pins: - - * LOUT1 - * LOUT2 - * ROUT1 - * ROUT2 - * MONO1 - * MONO2 - * OUT3 - * OUT4 - * LINE1 - * LINE2 - * RXP - * RXN - * ACIN - * ACOP - * MIC1N - * MIC1 - * MIC2N - * MIC2 - * Mic Bias - - Board connectors: + sinks are the WM8753's pins as documented in the binding for the WM8753, + and the jacks on the board: * Headphone Jack * Mic Jack diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt index 3bf722deb72..4b44dfb6ca0 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt @@ -11,28 +11,8 @@ Required properties: - nvidia,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. Valid names for sources and - sinks are the WM8903's pins, and the jacks on the board: - - WM8903 pins: - - * IN1L - * IN1R - * IN2L - * IN2R - * IN3L - * IN3R - * DMICDAT - * HPOUTL - * HPOUTR - * LINEOUTL - * LINEOUTR - * LOP - * LON - * ROP - * RON - * MICBIAS - - Board connectors: + sinks are the WM8903's pins (documented in the WM8903 binding document), + and the jacks on the board: * Headphone Jack * Int Spk diff --git a/Documentation/devicetree/bindings/sound/pcm1792a.txt b/Documentation/devicetree/bindings/sound/pcm1792a.txt new file mode 100644 index 00000000000..970ba1ed576 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/pcm1792a.txt @@ -0,0 +1,18 @@ +Texas Instruments pcm1792a DT bindings + +This driver supports the SPI bus. + +Required properties: + + - compatible: "ti,pcm1792a" + +For required properties on SPI, please consult +Documentation/devicetree/bindings/spi/spi-bus.txt + +Examples: + + codec_spi: 1792a@0 { + compatible = "ti,pcm1792a"; + spi-max-frequency = <600000>; + }; + diff --git a/Documentation/devicetree/bindings/sound/rt5640.txt b/Documentation/devicetree/bindings/sound/rt5640.txt index 005bcb24d72..068a1141b06 100644 --- a/Documentation/devicetree/bindings/sound/rt5640.txt +++ b/Documentation/devicetree/bindings/sound/rt5640.txt @@ -18,6 +18,26 @@ Optional properties: - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. +Pins on the device (for linking into audio routes): + + * DMIC1 + * DMIC2 + * MICBIAS1 + * IN1P + * IN1R + * IN2P + * IN2R + * HPOL + * HPOR + * LOUTL + * LOUTR + * MONOP + * MONON + * SPOLP + * SPOLN + * SPORP + * SPORN + Example: rt5640 { diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt index 025e66b85a4..7386d444ada 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -2,7 +2,15 @@ Required SoC Specific Properties: -- compatible : "samsung,i2s-v5" +- compatible : should be one of the following. + - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. + - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with + secondary fifo, s/w reset control and internal mux for root clk src. + - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with + secondary fifo, s/w reset control, internal mux for root clk src and + TDM support. TDM (Time division multiplexing) is to allow transfer of + multiple channel audio data on single data line. + - reg: physical base address of the controller and length of memory mapped region. - dmas: list of DMA controller phandle and DMA request line ordered pairs. @@ -21,13 +29,6 @@ Required SoC Specific Properties: Optional SoC Specific Properties: -- samsung,supports-6ch: If the I2S Primary sound source has 5.1 Channel - support, this flag is enabled. -- samsung,supports-rstclr: This flag should be set if I2S software reset bit - control is required. When this flag is set I2S software reset bit will be - enabled or disabled based on need. -- samsung,supports-secdai:If I2S block has a secondary FIFO and internal DMA, - then this flag is enabled. - samsung,idma-addr: Internal DMA register base address of the audio sub system(used in secondary sound source). - pinctrl-0: Should specify pin control groups used for this controller. @@ -36,7 +37,7 @@ Optional SoC Specific Properties: Example: i2s0: i2s@03830000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s5pv210-i2s"; reg = <0x03830000 0x100>; dmas = <&pdma0 10 &pdma0 9 @@ -46,9 +47,6 @@ i2s0: i2s@03830000 { <&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_SCLK_I2S>; clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; - samsung,supports-6ch; - samsung,supports-rstclr; - samsung,supports-secdai; samsung,idma-addr = <0x03000000>; pinctrl-names = "default"; pinctrl-0 = <&i2s0_bus>; diff --git a/Documentation/devicetree/bindings/sound/soc-ac97link.txt b/Documentation/devicetree/bindings/sound/soc-ac97link.txt new file mode 100644 index 00000000000..80152a87f23 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/soc-ac97link.txt @@ -0,0 +1,28 @@ +AC97 link bindings + +These bindings can be included within any other device node. + +Required properties: + - pinctrl-names: Has to contain following states to setup the correct + pinmuxing for the used gpios: + "ac97-running": AC97-link is active + "ac97-reset": AC97-link reset state + "ac97-warm-reset": AC97-link warm reset state + - ac97-gpios: List of gpio phandles with args in the order ac97-sync, + ac97-sdata, ac97-reset + + +Example: + +ssi { + ... + + pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset"; + pinctrl-0 = <&ac97link_running>; + pinctrl-1 = <&ac97link_running>; + pinctrl-2 = <&ac97link_reset>; + pinctrl-3 = <&ac97link_warm_reset>; + ac97-gpios = <&gpio3 20 0 &gpio3 22 0 &gpio3 28 0>; + + ... +}; diff --git a/Documentation/devicetree/bindings/sound/ti,pcm1681.txt b/Documentation/devicetree/bindings/sound/ti,pcm1681.txt new file mode 100644 index 00000000000..4df17185ab8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,pcm1681.txt @@ -0,0 +1,15 @@ +Texas Instruments PCM1681 8-channel PWM Processor + +Required properties: + + - compatible: Should contain "ti,pcm1681". + - reg: The i2c address. Should contain <0x4c>. + +Examples: + + i2c_bus { + pcm1681@4c { + compatible = "ti,pcm1681"; + reg = <0x4c>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt index f47c3f589fd..705a6b156c6 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt @@ -3,7 +3,14 @@ Texas Instruments - tlv320aic3x Codec module The tlv320aic3x serial control bus communicates through I2C protocols Required properties: -- compatible - "string" - "ti,tlv320aic3x" + +- compatible - "string" - One of: + "ti,tlv320aic3x" - Generic TLV320AIC3x device + "ti,tlv320aic33" - TLV320AIC33 + "ti,tlv320aic3007" - TLV320AIC3007 + "ti,tlv320aic3106" - TLV320AIC3106 + + - reg - <int> - I2C slave address diff --git a/Documentation/devicetree/bindings/sound/wm8731.txt b/Documentation/devicetree/bindings/sound/wm8731.txt index 15f70048469..236690e99b8 100644 --- a/Documentation/devicetree/bindings/sound/wm8731.txt +++ b/Documentation/devicetree/bindings/sound/wm8731.txt @@ -16,3 +16,12 @@ codec: wm8731@1a { compatible = "wlf,wm8731"; reg = <0x1a>; }; + +Available audio endpoints for an audio-routing table: + * LOUT: Left Channel Line Output + * ROUT: Right Channel Line Output + * LHPOUT: Left Channel Headphone Output + * RHPOUT: Right Channel Headphone Output + * LLINEIN: Left Channel Line Input + * RLINEIN: Right Channel Line Input + * MICIN: Microphone Input diff --git a/Documentation/devicetree/bindings/sound/wm8753.txt b/Documentation/devicetree/bindings/sound/wm8753.txt index e65277a0fb6..8eee6128210 100644 --- a/Documentation/devicetree/bindings/sound/wm8753.txt +++ b/Documentation/devicetree/bindings/sound/wm8753.txt @@ -10,9 +10,31 @@ Required properties: - reg : the I2C address of the device for I2C, the chip select number for SPI. +Pins on the device (for linking into audio routes): + + * LOUT1 + * LOUT2 + * ROUT1 + * ROUT2 + * MONO1 + * MONO2 + * OUT3 + * OUT4 + * LINE1 + * LINE2 + * RXP + * RXN + * ACIN + * ACOP + * MIC1N + * MIC1 + * MIC2N + * MIC2 + * Mic Bias + Example: -codec: wm8737@1a { +codec: wm8753@1a { compatible = "wlf,wm8753"; reg = <0x1a>; }; diff --git a/Documentation/devicetree/bindings/sound/wm8903.txt b/Documentation/devicetree/bindings/sound/wm8903.txt index f102cbc4269..94ec32c194b 100644 --- a/Documentation/devicetree/bindings/sound/wm8903.txt +++ b/Documentation/devicetree/bindings/sound/wm8903.txt @@ -28,6 +28,25 @@ Optional properties: performed. If any entry has the value 0xffffffff, that GPIO's configuration will not be modified. +Pins on the device (for linking into audio routes): + + * IN1L + * IN1R + * IN2L + * IN2R + * IN3L + * IN3R + * DMICDAT + * HPOUTL + * HPOUTR + * LINEOUTL + * LINEOUTR + * LOP + * LON + * ROP + * RON + * MICBIAS + Example: codec: wm8903@1a { diff --git a/Documentation/devicetree/bindings/sound/wm8994.txt b/Documentation/devicetree/bindings/sound/wm8994.txt index f2f3e80934d..e045e90a092 100644 --- a/Documentation/devicetree/bindings/sound/wm8994.txt +++ b/Documentation/devicetree/bindings/sound/wm8994.txt @@ -32,6 +32,10 @@ Optional properties: The second cell is the flags, encoded as the trigger masks from Documentation/devicetree/bindings/interrupts.txt + - clocks : A list of up to two phandle and clock specifier pairs + - clock-names : A list of clock names sorted in the same order as clocks. + Valid clock names are "MCLK1" and "MCLK2". + - wlf,gpio-cfg : A list of GPIO configuration register values. If absent, no configuration of these registers is performed. If any value is over 0xffff then the register will be left as default. If present 11 diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 15356aca938..7f9d4f53882 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2953,7 +2953,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. improve throughput, but will also increase the amount of memory reserved for use by the client. - swapaccount[=0|1] + swapaccount=[0|1] [KNL] Enable accounting of swap in memory resource controller if no parameter or 1 is given or disable it if 0 is given (See Documentation/cgroups/memory.txt) diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 809d72b8eff..a46ddb85e83 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -244,6 +244,7 @@ STAC9227/9228/9229/927x 5stack-no-fp D965 5stack without front panel dell-3stack Dell Dimension E520 dell-bios Fixes with Dell BIOS setup + dell-bios-amic Fixes with Dell BIOS setup including analog mic volknob Fixes with volume-knob widget 0x24 auto BIOS setup (default) diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index c3c912d023c..42a0a39b77e 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -454,6 +454,8 @@ The generic parser supports the following hints: - 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_io (bool): try probing multi-I/O config (e.g. shared + line-in/surround, mic/clfe jacks) - multi_cap_vol (bool): provide multiple capture volumes - inv_dmic_split (bool): provide split internal mic volume/switch for phase-inverted digital mics diff --git a/MAINTAINERS b/MAINTAINERS index 7cacc88dc79..ee738a6bfc3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -595,6 +595,7 @@ S: Supported F: sound/soc/codecs/adau* F: sound/soc/codecs/adav* F: sound/soc/codecs/ad1* +F: sound/soc/codecs/ad7* F: sound/soc/codecs/ssm* F: sound/soc/codecs/sigmadsp.* @@ -5581,9 +5582,9 @@ S: Maintained F: drivers/media/tuners/mxl5007t.* MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE) -M: Andrew Gallatin <gallatin@myri.com> +M: Hyong-Youb Kim <hykim@myri.com> L: netdev@vger.kernel.org -W: http://www.myri.com/scs/download-Myri10GE.html +W: https://www.myricom.com/support/downloads/myri10ge.html S: Supported F: drivers/net/ethernet/myricom/myri10ge/ @@ -5884,7 +5885,7 @@ F: drivers/i2c/busses/i2c-omap.c F: include/linux/i2c-omap.h OMAP DEVICE TREE SUPPORT -M: Benoît Cousson <b-cousson@ti.com> +M: Benoît Cousson <bcousson@baylibre.com> M: Tony Lindgren <tony@atomide.com> L: linux-omap@vger.kernel.org L: devicetree@vger.kernel.org @@ -5964,14 +5965,14 @@ S: Maintained F: drivers/char/hw_random/omap-rng.c OMAP HWMOD SUPPORT -M: Benoît Cousson <b-cousson@ti.com> +M: Benoît Cousson <bcousson@baylibre.com> M: Paul Walmsley <paul@pwsan.com> L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/mach-omap2/omap_hwmod.* OMAP HWMOD DATA FOR OMAP4-BASED DEVICES -M: Benoît Cousson <b-cousson@ti.com> +M: Benoît Cousson <bcousson@baylibre.com> L: linux-omap@vger.kernel.org S: Maintained F: arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -7366,7 +7367,6 @@ F: drivers/net/ethernet/sfc/ SGI GRU DRIVER M: Dimitri Sivanich <sivanich@sgi.com> -M: Robin Holt <holt@sgi.com> S: Maintained F: drivers/misc/sgi-gru/ @@ -7386,7 +7386,8 @@ S: Maintained for 2.6. F: Documentation/sgi-visws.txt SGI XP/XPC/XPNET DRIVER -M: Robin Holt <holt@sgi.com> +M: Cliff Whickman <cpw@sgi.com> +M: Robin Holt <robinmholt@gmail.com> S: Maintained F: drivers/misc/sgi-xp/ @@ -7675,6 +7676,17 @@ F: include/sound/ F: include/uapi/sound/ F: sound/ +SOUND - COMPRESSED AUDIO +M: Vinod Koul <vinod.koul@intel.com> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git +S: Supported +F: Documentation/sound/alsa/compress_offload.txt +F: include/sound/compress_driver.h +F: include/uapi/sound/compress_* +F: sound/core/compress_offload.c +F: sound/soc/soc-compress.c + SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC) M: Liam Girdwood <lgirdwood@gmail.com> M: Mark Brown <broonie@kernel.org> @@ -7682,6 +7694,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git L: alsa-devel@alsa-project.org (moderated for non-subscribers) W: http://alsa-project.org/main/index.php/ASoC S: Supported +F: Documentation/sound/alsa/soc/ F: sound/soc/ F: include/sound/soc* @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 11 SUBLEVEL = 0 -EXTRAVERSION = -rc5 +EXTRAVERSION = -rc7 NAME = Linux for Workgroups # *DOCUMENTATION* diff --git a/arch/Kconfig b/arch/Kconfig index 8d2ae24b9f4..1feb169274f 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -407,6 +407,12 @@ config CLONE_BACKWARDS2 help Architecture has the first two arguments of clone(2) swapped. +config CLONE_BACKWARDS3 + bool + help + Architecture has tls passed as the 3rd argument of clone(2), + not the 5th one. + config ODD_RT_SIGACTION bool help diff --git a/arch/arc/lib/strchr-700.S b/arch/arc/lib/strchr-700.S index 99c10475d47..9c548c7cf00 100644 --- a/arch/arc/lib/strchr-700.S +++ b/arch/arc/lib/strchr-700.S @@ -39,9 +39,18 @@ ARC_ENTRY strchr ld.a r2,[r0,4] sub r12,r6,r7 bic r12,r12,r6 +#ifdef __LITTLE_ENDIAN__ and r7,r12,r4 breq r7,0,.Loop ; For speed, we want this branch to be unaligned. b .Lfound_char ; Likewise this one. +#else + and r12,r12,r4 + breq r12,0,.Loop ; For speed, we want this branch to be unaligned. + lsr_s r12,r12,7 + bic r2,r7,r6 + b.d .Lfound_char_b + and_s r2,r2,r12 +#endif ; /* We require this code address to be unaligned for speed... */ .Laligned: ld_s r2,[r0] @@ -95,6 +104,7 @@ ARC_ENTRY strchr lsr r7,r7,7 bic r2,r7,r6 +.Lfound_char_b: norm r2,r2 sub_s r0,r0,4 asr_s r2,r2,3 diff --git a/arch/arm/boot/dts/at91sam9n12ek.dts b/arch/arm/boot/dts/at91sam9n12ek.dts index d59b70c6a6a..3d77dbe406f 100644 --- a/arch/arm/boot/dts/at91sam9n12ek.dts +++ b/arch/arm/boot/dts/at91sam9n12ek.dts @@ -14,11 +14,11 @@ compatible = "atmel,at91sam9n12ek", "atmel,at91sam9n12", "atmel,at91sam9"; chosen { - bootargs = "mem=128M console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2"; + bootargs = "console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2"; }; memory { - reg = <0x20000000 0x10000000>; + reg = <0x20000000 0x8000000>; }; clocks { diff --git a/arch/arm/boot/dts/at91sam9x5ek.dtsi b/arch/arm/boot/dts/at91sam9x5ek.dtsi index b753855b205..49e3c45818c 100644 --- a/arch/arm/boot/dts/at91sam9x5ek.dtsi +++ b/arch/arm/boot/dts/at91sam9x5ek.dtsi @@ -94,8 +94,9 @@ usb0: ohci@00600000 { status = "okay"; - num-ports = <2>; - atmel,vbus-gpio = <&pioD 19 GPIO_ACTIVE_LOW + num-ports = <3>; + atmel,vbus-gpio = <0 /* &pioD 18 GPIO_ACTIVE_LOW *//* Activate to have access to port A */ + &pioD 19 GPIO_ACTIVE_LOW &pioD 20 GPIO_ACTIVE_LOW >; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index ef57277fc38..376090f0723 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -405,7 +405,7 @@ }; i2s0: i2s@03830000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s5pv210-i2s"; reg = <0x03830000 0x100>; dmas = <&pdma0 10 &pdma0 9 @@ -415,16 +415,13 @@ <&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_SCLK_I2S>; clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; - samsung,supports-6ch; - samsung,supports-rstclr; - samsung,supports-secdai; samsung,idma-addr = <0x03000000>; pinctrl-names = "default"; pinctrl-0 = <&i2s0_bus>; }; i2s1: i2s@12D60000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s3c6410-i2s"; reg = <0x12D60000 0x100>; dmas = <&pdma1 12 &pdma1 11>; @@ -436,7 +433,7 @@ }; i2s2: i2s@12D70000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s3c6410-i2s"; reg = <0x12D70000 0x100>; dmas = <&pdma0 12 &pdma0 11>; diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts index 365760b33a2..40e6fb28033 100644 --- a/arch/arm/boot/dts/tegra20-seaboard.dts +++ b/arch/arm/boot/dts/tegra20-seaboard.dts @@ -830,6 +830,8 @@ regulator-max-microvolt = <5000000>; enable-active-high; gpio = <&gpio 24 0>; /* PD0 */ + regulator-always-on; + regulator-boot-on; }; }; diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts index ed4b901b022..37c93d3c481 100644 --- a/arch/arm/boot/dts/tegra20-trimslice.dts +++ b/arch/arm/boot/dts/tegra20-trimslice.dts @@ -412,6 +412,8 @@ regulator-max-microvolt = <5000000>; enable-active-high; gpio = <&gpio 170 0>; /* PV2 */ + regulator-always-on; + regulator-boot-on; }; }; diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts index ab67c94db28..a3d0ebad78a 100644 --- a/arch/arm/boot/dts/tegra20-whistler.dts +++ b/arch/arm/boot/dts/tegra20-whistler.dts @@ -588,6 +588,8 @@ regulator-max-microvolt = <5000000>; enable-active-high; gpio = <&tca6416 0 0>; /* GPIO_PMU0 */ + regulator-always-on; + regulator-boot-on; }; vbus3_reg: regulator@3 { @@ -598,6 +600,8 @@ regulator-max-microvolt = <5000000>; enable-active-high; gpio = <&tca6416 1 0>; /* GPIO_PMU1 */ + regulator-always-on; + regulator-boot-on; }; }; diff --git a/arch/arm/include/asm/smp_plat.h b/arch/arm/include/asm/smp_plat.h index 6462a721ebd..a252c0bfacf 100644 --- a/arch/arm/include/asm/smp_plat.h +++ b/arch/arm/include/asm/smp_plat.h @@ -88,4 +88,7 @@ static inline u32 mpidr_hash_size(void) { return 1 << mpidr_hash.bits; } + +extern int platform_can_cpu_hotplug(void); + #endif diff --git a/arch/arm/include/asm/spinlock.h b/arch/arm/include/asm/spinlock.h index f8b8965666e..b07c09e5a0a 100644 --- a/arch/arm/include/asm/spinlock.h +++ b/arch/arm/include/asm/spinlock.h @@ -107,7 +107,7 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock) " subs %1, %0, %0, ror #16\n" " addeq %0, %0, %4\n" " strexeq %2, %0, [%3]" - : "=&r" (slock), "=&r" (contended), "=r" (res) + : "=&r" (slock), "=&r" (contended), "=&r" (res) : "r" (&lock->slock), "I" (1 << TICKET_SHIFT) : "cc"); } while (res); @@ -168,17 +168,20 @@ static inline void arch_write_lock(arch_rwlock_t *rw) static inline int arch_write_trylock(arch_rwlock_t *rw) { - unsigned long tmp; + unsigned long contended, res; - __asm__ __volatile__( -" ldrex %0, [%1]\n" -" teq %0, #0\n" -" strexeq %0, %2, [%1]" - : "=&r" (tmp) - : "r" (&rw->lock), "r" (0x80000000) - : "cc"); + do { + __asm__ __volatile__( + " ldrex %0, [%2]\n" + " mov %1, #0\n" + " teq %0, #0\n" + " strexeq %1, %3, [%2]" + : "=&r" (contended), "=&r" (res) + : "r" (&rw->lock), "r" (0x80000000) + : "cc"); + } while (res); - if (tmp == 0) { + if (!contended) { smp_mb(); return 1; } else { @@ -254,18 +257,26 @@ static inline void arch_read_unlock(arch_rwlock_t *rw) static inline int arch_read_trylock(arch_rwlock_t *rw) { - unsigned long tmp, tmp2 = 1; + unsigned long contended, res; - __asm__ __volatile__( -" ldrex %0, [%2]\n" -" adds %0, %0, #1\n" -" strexpl %1, %0, [%2]\n" - : "=&r" (tmp), "+r" (tmp2) - : "r" (&rw->lock) - : "cc"); + do { + __asm__ __volatile__( + " ldrex %0, [%2]\n" + " mov %1, #0\n" + " adds %0, %0, #1\n" + " strexpl %1, %0, [%2]" + : "=&r" (contended), "=&r" (res) + : "r" (&rw->lock) + : "cc"); + } while (res); - smp_mb(); - return tmp2 == 0; + /* If the lock is negative, then it is already held for write. */ + if (contended < 0x80000000) { + smp_mb(); + return 1; + } else { + return 0; + } } /* read_can_lock - would read_trylock() succeed? */ diff --git a/arch/arm/include/asm/tlb.h b/arch/arm/include/asm/tlb.h index 46e7cfb3e72..0baf7f0d939 100644 --- a/arch/arm/include/asm/tlb.h +++ b/arch/arm/include/asm/tlb.h @@ -43,6 +43,7 @@ struct mmu_gather { struct mm_struct *mm; unsigned int fullmm; struct vm_area_struct *vma; + unsigned long start, end; unsigned long range_start; unsigned long range_end; unsigned int nr; @@ -107,10 +108,12 @@ static inline void tlb_flush_mmu(struct mmu_gather *tlb) } static inline void -tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned int fullmm) +tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) { tlb->mm = mm; - tlb->fullmm = fullmm; + tlb->fullmm = !(start | (end+1)); + tlb->start = start; + tlb->end = end; tlb->vma = NULL; tlb->max = ARRAY_SIZE(tlb->local); tlb->pages = tlb->local; diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index d40d0ef389d..9cbe70c8b0e 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -357,7 +357,8 @@ ENDPROC(__pabt_svc) .endm .macro kuser_cmpxchg_check -#if !defined(CONFIG_CPU_32v6K) && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG) +#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS) && \ + !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG) #ifndef CONFIG_MMU #warning "NPTL on non MMU needs fixing" #else diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c index 25442f45114..918875d96d5 100644 --- a/arch/arm/kernel/fiq.c +++ b/arch/arm/kernel/fiq.c @@ -84,17 +84,14 @@ int show_fiq_list(struct seq_file *p, int prec) void set_fiq_handler(void *start, unsigned int length) { -#if defined(CONFIG_CPU_USE_DOMAINS) - void *base = (void *)0xffff0000; -#else void *base = vectors_page; -#endif unsigned offset = FIQ_OFFSET; memcpy(base + offset, start, length); + if (!cache_is_vipt_nonaliasing()) + flush_icache_range((unsigned long)base + offset, offset + + length); flush_icache_range(0xffff0000 + offset, 0xffff0000 + offset + length); - if (!vectors_high()) - flush_icache_range(offset, offset + length); } int claim_fiq(struct fiq_handler *f) diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 4fb074c446b..57221e349a7 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -15,6 +15,7 @@ #include <asm/mmu_context.h> #include <asm/cacheflush.h> #include <asm/mach-types.h> +#include <asm/smp_plat.h> #include <asm/system_misc.h> extern const unsigned char relocate_new_kernel[]; @@ -39,6 +40,14 @@ int machine_kexec_prepare(struct kimage *image) int i, err; /* + * Validate that if the current HW supports SMP, then the SW supports + * and implements CPU hotplug for the current HW. If not, we won't be + * able to kexec reliably, so fail the prepare operation. + */ + if (num_possible_cpus() > 1 && !platform_can_cpu_hotplug()) + return -EINVAL; + + /* * No segment at default ATAGs address. try to locate * a dtb using magic. */ @@ -73,6 +82,7 @@ void machine_crash_nonpanic_core(void *unused) crash_save_cpu(®s, smp_processor_id()); flush_cache_all(); + set_cpu_online(smp_processor_id(), false); atomic_dec(&waiting_for_crash_ipi); while (1) cpu_relax(); @@ -134,10 +144,13 @@ void machine_kexec(struct kimage *image) unsigned long reboot_code_buffer_phys; void *reboot_code_buffer; - if (num_online_cpus() > 1) { - pr_err("kexec: error: multiple CPUs still online\n"); - return; - } + /* + * This can only happen if machine_shutdown() failed to disable some + * CPU, and that can only happen if the checks in + * machine_kexec_prepare() were not correct. If this fails, we can't + * reliably kexec anyway, so BUG_ON is appropriate. + */ + BUG_ON(num_online_cpus() > 1); page_list = image->head & PAGE_MASK; diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index d9f5cd4e533..e186ee1e63f 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -53,7 +53,12 @@ armpmu_map_cache_event(const unsigned (*cache_map) static int armpmu_map_hw_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config) { - int mapping = (*event_map)[config]; + int mapping; + + if (config >= PERF_COUNT_HW_MAX) + return -EINVAL; + + mapping = (*event_map)[config]; return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping; } @@ -253,6 +258,9 @@ validate_event(struct pmu_hw_events *hw_events, struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct pmu *leader_pmu = event->group_leader->pmu; + if (is_software_event(event)) + return 1; + if (event->pmu != leader_pmu || event->state < PERF_EVENT_STATE_OFF) return 1; diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 536c85fe72a..94f6b05f9e2 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -462,7 +462,7 @@ int in_gate_area_no_mm(unsigned long addr) { return in_gate_area(NULL, addr); } -#define is_gate_vma(vma) ((vma) = &gate_vma) +#define is_gate_vma(vma) ((vma) == &gate_vma) #else #define is_gate_vma(vma) 0 #endif diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index c2b4f8f0be9..2dc19349eb1 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -145,6 +145,16 @@ int boot_secondary(unsigned int cpu, struct task_struct *idle) return -ENOSYS; } +int platform_can_cpu_hotplug(void) +{ +#ifdef CONFIG_HOTPLUG_CPU + if (smp_ops.cpu_kill) + return 1; +#endif + + return 0; +} + #ifdef CONFIG_HOTPLUG_CPU static void percpu_timer_stop(void); diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c index 4a519907043..db9cf692d4d 100644 --- a/arch/arm/kvm/coproc.c +++ b/arch/arm/kvm/coproc.c @@ -146,7 +146,11 @@ static bool pm_fake(struct kvm_vcpu *vcpu, #define access_pmintenclr pm_fake /* Architected CP15 registers. - * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 + * CRn denotes the primary register number, but is copied to the CRm in the + * user space API for 64-bit register access in line with the terminology used + * in the ARM ARM. + * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit + * registers preceding 32-bit ones. */ static const struct coproc_reg cp15_regs[] = { /* CSSELR: swapped by interrupt.S. */ @@ -154,8 +158,8 @@ static const struct coproc_reg cp15_regs[] = { NULL, reset_unknown, c0_CSSELR }, /* TTBR0/TTBR1: swapped by interrupt.S. */ - { CRm( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 }, - { CRm( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 }, + { CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 }, + { CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 }, /* TTBCR: swapped by interrupt.S. */ { CRn( 2), CRm( 0), Op1( 0), Op2( 2), is32, @@ -182,7 +186,7 @@ static const struct coproc_reg cp15_regs[] = { NULL, reset_unknown, c6_IFAR }, /* PAR swapped by interrupt.S */ - { CRn( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR }, + { CRm64( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR }, /* * DC{C,I,CI}SW operations: @@ -399,12 +403,13 @@ static bool index_to_params(u64 id, struct coproc_params *params) | KVM_REG_ARM_OPC1_MASK)) return false; params->is_64bit = true; - params->CRm = ((id & KVM_REG_ARM_CRM_MASK) + /* CRm to CRn: see cp15_to_index for details */ + params->CRn = ((id & KVM_REG_ARM_CRM_MASK) >> KVM_REG_ARM_CRM_SHIFT); params->Op1 = ((id & KVM_REG_ARM_OPC1_MASK) >> KVM_REG_ARM_OPC1_SHIFT); params->Op2 = 0; - params->CRn = 0; + params->CRm = 0; return true; default: return false; @@ -898,7 +903,14 @@ static u64 cp15_to_index(const struct coproc_reg *reg) if (reg->is_64) { val |= KVM_REG_SIZE_U64; val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT); - val |= (reg->CRm << KVM_REG_ARM_CRM_SHIFT); + /* + * CRn always denotes the primary coproc. reg. nr. for the + * in-kernel representation, but the user space API uses the + * CRm for the encoding, because it is modelled after the + * MRRC/MCRR instructions: see the ARM ARM rev. c page + * B3-1445 + */ + val |= (reg->CRn << KVM_REG_ARM_CRM_SHIFT); } else { val |= KVM_REG_SIZE_U32; val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT); diff --git a/arch/arm/kvm/coproc.h b/arch/arm/kvm/coproc.h index b7301d3e479..0461d5c8d3d 100644 --- a/arch/arm/kvm/coproc.h +++ b/arch/arm/kvm/coproc.h @@ -135,6 +135,8 @@ static inline int cmp_reg(const struct coproc_reg *i1, return -1; if (i1->CRn != i2->CRn) return i1->CRn - i2->CRn; + if (i1->is_64 != i2->is_64) + return i2->is_64 - i1->is_64; if (i1->CRm != i2->CRm) return i1->CRm - i2->CRm; if (i1->Op1 != i2->Op1) @@ -145,6 +147,7 @@ static inline int cmp_reg(const struct coproc_reg *i1, #define CRn(_x) .CRn = _x #define CRm(_x) .CRm = _x +#define CRm64(_x) .CRn = _x, .CRm = 0 #define Op1(_x) .Op1 = _x #define Op2(_x) .Op2 = _x #define is64 .is_64 = true diff --git a/arch/arm/kvm/coproc_a15.c b/arch/arm/kvm/coproc_a15.c index 685063a6d0c..cf93472b9dd 100644 --- a/arch/arm/kvm/coproc_a15.c +++ b/arch/arm/kvm/coproc_a15.c @@ -114,7 +114,11 @@ static bool access_l2ectlr(struct kvm_vcpu *vcpu, /* * A15-specific CP15 registers. - * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 + * CRn denotes the primary register number, but is copied to the CRm in the + * user space API for 64-bit register access in line with the terminology used + * in the ARM ARM. + * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit + * registers preceding 32-bit ones. */ static const struct coproc_reg a15_regs[] = { /* MPIDR: we use VMPIDR for guest access. */ diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c index b8e06b7a283..0c25d9487d5 100644 --- a/arch/arm/kvm/mmio.c +++ b/arch/arm/kvm/mmio.c @@ -63,7 +63,8 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, struct kvm_exit_mmio *mmio) { - unsigned long rt, len; + unsigned long rt; + int len; bool is_write, sign_extend; if (kvm_vcpu_dabt_isextabt(vcpu)) { diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index ca6bea4859b..0988d9e04dd 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -85,6 +85,12 @@ static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc) return p; } +static bool page_empty(void *ptr) +{ + struct page *ptr_page = virt_to_page(ptr); + return page_count(ptr_page) == 1; +} + static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr) { pmd_t *pmd_table = pmd_offset(pud, 0); @@ -103,12 +109,6 @@ static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr) put_page(virt_to_page(pmd)); } -static bool pmd_empty(pmd_t *pmd) -{ - struct page *pmd_page = virt_to_page(pmd); - return page_count(pmd_page) == 1; -} - static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr) { if (pte_present(*pte)) { @@ -118,12 +118,6 @@ static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr) } } -static bool pte_empty(pte_t *pte) -{ - struct page *pte_page = virt_to_page(pte); - return page_count(pte_page) == 1; -} - static void unmap_range(struct kvm *kvm, pgd_t *pgdp, unsigned long long start, u64 size) { @@ -132,37 +126,37 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp, pmd_t *pmd; pte_t *pte; unsigned long long addr = start, end = start + size; - u64 range; + u64 next; while (addr < end) { pgd = pgdp + pgd_index(addr); pud = pud_offset(pgd, addr); if (pud_none(*pud)) { - addr += PUD_SIZE; + addr = pud_addr_end(addr, end); continue; } pmd = pmd_offset(pud, addr); if (pmd_none(*pmd)) { - addr += PMD_SIZE; + addr = pmd_addr_end(addr, end); continue; } pte = pte_offset_kernel(pmd, addr); clear_pte_entry(kvm, pte, addr); - range = PAGE_SIZE; + next = addr + PAGE_SIZE; /* If we emptied the pte, walk back up the ladder */ - if (pte_empty(pte)) { + if (page_empty(pte)) { clear_pmd_entry(kvm, pmd, addr); - range = PMD_SIZE; - if (pmd_empty(pmd)) { + next = pmd_addr_end(addr, end); + if (page_empty(pmd) && !page_empty(pud)) { clear_pud_entry(kvm, pud, addr); - range = PUD_SIZE; + next = pud_addr_end(addr, end); } } - addr += range; + addr = next; } } diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index 2abee6626aa..916e5a14291 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -227,6 +227,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("usart", "f8020000.serial", &usart1_clk), CLKDEV_CON_DEV_ID("usart", "f8024000.serial", &usart2_clk), CLKDEV_CON_DEV_ID("usart", "f8028000.serial", &usart3_clk), + CLKDEV_CON_DEV_ID("usart", "f8040000.serial", &uart0_clk), + CLKDEV_CON_DEV_ID("usart", "f8044000.serial", &uart1_clk), CLKDEV_CON_DEV_ID("t0_clk", "f8008000.timer", &tcb0_clk), CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk), CLKDEV_CON_DEV_ID("mci_clk", "f0008000.mmc", &mmc0_clk), diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c index dff4ddc5ef8..139e42da25f 100644 --- a/arch/arm/mach-davinci/board-dm355-leopard.c +++ b/arch/arm/mach-davinci/board-dm355-leopard.c @@ -75,6 +75,7 @@ static struct davinci_nand_pdata davinci_nand_data = { .parts = davinci_nand_partitions, .nr_parts = ARRAY_SIZE(davinci_nand_partitions), .ecc_mode = NAND_ECC_HW_SYNDROME, + .ecc_bits = 4, .bbt_options = NAND_BBT_USE_FLASH, }; diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index a33686a6fbb..fa4bfaf952d 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -153,6 +153,7 @@ static struct davinci_nand_pdata davinci_evm_nandflash_data = { .parts = davinci_evm_nandflash_partition, .nr_parts = ARRAY_SIZE(davinci_evm_nandflash_partition), .ecc_mode = NAND_ECC_HW, + .ecc_bits = 1, .bbt_options = NAND_BBT_USE_FLASH, .timing = &davinci_evm_nandflash_timing, }; diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index fbb8e5ab1dc..0c005e876ca 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -90,6 +90,7 @@ static struct davinci_nand_pdata davinci_nand_data = { .parts = davinci_nand_partitions, .nr_parts = ARRAY_SIZE(davinci_nand_partitions), .ecc_mode = NAND_ECC_HW, + .ecc_bits = 1, .options = 0, }; diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c index 2bc112adf56..808233b60e3 100644 --- a/arch/arm/mach-davinci/board-neuros-osd2.c +++ b/arch/arm/mach-davinci/board-neuros-osd2.c @@ -88,6 +88,7 @@ static struct davinci_nand_pdata davinci_ntosd2_nandflash_data = { .parts = davinci_ntosd2_nandflash_partition, .nr_parts = ARRAY_SIZE(davinci_ntosd2_nandflash_partition), .ecc_mode = NAND_ECC_HW, + .ecc_bits = 1, .bbt_options = NAND_BBT_USE_FLASH, }; diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c index 00247c77131..304f069ebf5 100644 --- a/arch/arm/mach-dove/common.c +++ b/arch/arm/mach-dove/common.c @@ -108,8 +108,8 @@ static void __init dove_clk_init(void) orion_clkdev_add(NULL, "sdhci-dove.1", sdio1); orion_clkdev_add(NULL, "orion_nand", nand); orion_clkdev_add(NULL, "cafe1000-ccic.0", camera); - orion_clkdev_add(NULL, "kirkwood-i2s.0", i2s0); - orion_clkdev_add(NULL, "kirkwood-i2s.1", i2s1); + orion_clkdev_add(NULL, "mvebu-audio.0", i2s0); + orion_clkdev_add(NULL, "mvebu-audio.1", i2s1); orion_clkdev_add(NULL, "mv_crypto", crypto); orion_clkdev_add(NULL, "dove-ac97", ac97); orion_clkdev_add(NULL, "dove-pdma", pdma); diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index e9238b5567e..1663de09098 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -264,7 +264,7 @@ void __init kirkwood_clk_init(void) orion_clkdev_add(NULL, MV_XOR_NAME ".1", xor1); orion_clkdev_add("0", "pcie", pex0); orion_clkdev_add("1", "pcie", pex1); - orion_clkdev_add(NULL, "kirkwood-i2s", audio); + orion_clkdev_add(NULL, "mvebu-audio", audio); orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".0", runit); orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".1", runit); @@ -560,7 +560,7 @@ void __init kirkwood_timer_init(void) /***************************************************************************** * Audio ****************************************************************************/ -static struct resource kirkwood_i2s_resources[] = { +static struct resource kirkwood_audio_resources[] = { [0] = { .start = AUDIO_PHYS_BASE, .end = AUDIO_PHYS_BASE + SZ_16K - 1, @@ -573,29 +573,23 @@ static struct resource kirkwood_i2s_resources[] = { }, }; -static struct kirkwood_asoc_platform_data kirkwood_i2s_data = { +static struct kirkwood_asoc_platform_data kirkwood_audio_data = { .burst = 128, }; -static struct platform_device kirkwood_i2s_device = { - .name = "kirkwood-i2s", +static struct platform_device kirkwood_audio_device = { + .name = "mvebu-audio", .id = -1, - .num_resources = ARRAY_SIZE(kirkwood_i2s_resources), - .resource = kirkwood_i2s_resources, + .num_resources = ARRAY_SIZE(kirkwood_audio_resources), + .resource = kirkwood_audio_resources, .dev = { - .platform_data = &kirkwood_i2s_data, + .platform_data = &kirkwood_audio_data, }, }; -static struct platform_device kirkwood_pcm_device = { - .name = "kirkwood-pcm-audio", - .id = -1, -}; - void __init kirkwood_audio_init(void) { - platform_device_register(&kirkwood_i2s_device); - platform_device_register(&kirkwood_pcm_device); + platform_device_register(&kirkwood_audio_device); } /***************************************************************************** diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c index f6eeb87e4e9..827d15009a8 100644 --- a/arch/arm/mach-omap2/board-n8x0.c +++ b/arch/arm/mach-omap2/board-n8x0.c @@ -122,11 +122,7 @@ static struct musb_hdrc_config musb_config = { }; static struct musb_hdrc_platform_data tusb_data = { -#ifdef CONFIG_USB_GADGET_MUSB_HDRC .mode = MUSB_OTG, -#else - .mode = MUSB_HOST, -#endif .set_power = tusb_set_power, .min_power = 25, /* x2 = 50 mA drawn from VBUS as peripheral */ .power = 100, /* Max 100 mA VBUS for host mode */ diff --git a/arch/arm/mach-omap2/board-rx51.c b/arch/arm/mach-omap2/board-rx51.c index d2ea68ea678..7735105561d 100644 --- a/arch/arm/mach-omap2/board-rx51.c +++ b/arch/arm/mach-omap2/board-rx51.c @@ -85,7 +85,7 @@ static struct omap_board_mux board_mux[] __initdata = { static struct omap_musb_board_data musb_board_data = { .interface_type = MUSB_INTERFACE_ULPI, - .mode = MUSB_PERIPHERAL, + .mode = MUSB_OTG, .power = 0, }; diff --git a/arch/arm/mach-omap2/usb-musb.c b/arch/arm/mach-omap2/usb-musb.c index 8c4de2708cf..bc897231bd1 100644 --- a/arch/arm/mach-omap2/usb-musb.c +++ b/arch/arm/mach-omap2/usb-musb.c @@ -38,11 +38,8 @@ static struct musb_hdrc_config musb_config = { }; static struct musb_hdrc_platform_data musb_plat = { -#ifdef CONFIG_USB_GADGET_MUSB_HDRC .mode = MUSB_OTG, -#else - .mode = MUSB_HOST, -#endif + /* .clock is set dynamically */ .config = &musb_config, diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index db5c2cab8fd..cd2c88e7a8f 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -809,15 +809,18 @@ config KUSER_HELPERS the CPU type fitted to the system. This permits binaries to be run on ARMv4 through to ARMv7 without modification. + See Documentation/arm/kernel_user_helpers.txt for details. + However, the fixed address nature of these helpers can be used by ROP (return orientated programming) authors when creating exploits. If all of the binaries and libraries which run on your platform are built specifically for your platform, and make no use of - these helpers, then you can turn this option off. However, - when such an binary or library is run, it will receive a SIGILL - signal, which will terminate the program. + these helpers, then you can turn this option off to hinder + such exploits. However, in that case, if a binary or library + relying on those helpers is run, it will receive a SIGILL signal, + which will terminate the program. Say N here only if you are absolutely certain that you do not need these helpers; otherwise, the safe option is to say Y. diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index 8e11e96eab5..c83f27b6bdd 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -30,6 +30,8 @@ #include <linux/platform_device.h> #include <linux/spi/pxa2xx_spi.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/irq.h> #include <mach/hardware.h> @@ -60,6 +62,30 @@ struct ssp_device *pxa_ssp_request(int port, const char *label) } EXPORT_SYMBOL(pxa_ssp_request); +struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node, + const char *label) +{ + struct ssp_device *ssp = NULL; + + mutex_lock(&ssp_lock); + + list_for_each_entry(ssp, &ssp_list, node) { + if (ssp->of_node == of_node && ssp->use_count == 0) { + ssp->use_count++; + ssp->label = label; + break; + } + } + + mutex_unlock(&ssp_lock); + + if (&ssp->node == &ssp_list) + return NULL; + + return ssp; +} +EXPORT_SYMBOL(pxa_ssp_request_of); + void pxa_ssp_free(struct ssp_device *ssp) { mutex_lock(&ssp_lock); @@ -72,96 +98,126 @@ void pxa_ssp_free(struct ssp_device *ssp) } EXPORT_SYMBOL(pxa_ssp_free); +#ifdef CONFIG_OF +static const struct of_device_id pxa_ssp_of_ids[] = { + { .compatible = "mrvl,pxa25x-ssp", .data = (void *) PXA25x_SSP }, + { .compatible = "mvrl,pxa25x-nssp", .data = (void *) PXA25x_NSSP }, + { .compatible = "mrvl,pxa27x-ssp", .data = (void *) PXA27x_SSP }, + { .compatible = "mrvl,pxa3xx-ssp", .data = (void *) PXA3xx_SSP }, + { .compatible = "mvrl,pxa168-ssp", .data = (void *) PXA168_SSP }, + { .compatible = "mrvl,pxa910-ssp", .data = (void *) PXA910_SSP }, + { .compatible = "mrvl,ce4100-ssp", .data = (void *) CE4100_SSP }, + { .compatible = "mrvl,lpss-ssp", .data = (void *) LPSS_SSP }, + { }, +}; +MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids); +#endif + static int pxa_ssp_probe(struct platform_device *pdev) { - const struct platform_device_id *id = platform_get_device_id(pdev); struct resource *res; struct ssp_device *ssp; - int ret = 0; + struct device *dev = &pdev->dev; - ssp = kzalloc(sizeof(struct ssp_device), GFP_KERNEL); - if (ssp == NULL) { - dev_err(&pdev->dev, "failed to allocate memory"); + ssp = devm_kzalloc(dev, sizeof(struct ssp_device), GFP_KERNEL); + if (ssp == NULL) return -ENOMEM; - } - ssp->pdev = pdev; - ssp->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(ssp->clk)) { - ret = PTR_ERR(ssp->clk); - goto err_free; - } + ssp->pdev = pdev; - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (res == NULL) { - dev_err(&pdev->dev, "no SSP RX DRCMR defined\n"); - ret = -ENODEV; - goto err_free_clk; - } - ssp->drcmr_rx = res->start; + ssp->clk = devm_clk_get(dev, NULL); + if (IS_ERR(ssp->clk)) + return PTR_ERR(ssp->clk); + + if (dev->of_node) { + struct of_phandle_args dma_spec; + struct device_node *np = dev->of_node; + + /* + * FIXME: we should allocate the DMA channel from this + * context and pass the channel down to the ssp users. + * For now, we lookup the rx and tx indices manually + */ + + /* rx */ + of_parse_phandle_with_args(np, "dmas", "#dma-cells", + 0, &dma_spec); + ssp->drcmr_rx = dma_spec.args[0]; + of_node_put(dma_spec.np); + + /* tx */ + of_parse_phandle_with_args(np, "dmas", "#dma-cells", + 1, &dma_spec); + ssp->drcmr_tx = dma_spec.args[0]; + of_node_put(dma_spec.np); + } else { + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res == NULL) { + dev_err(dev, "no SSP RX DRCMR defined\n"); + return -ENODEV; + } + ssp->drcmr_rx = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (res == NULL) { - dev_err(&pdev->dev, "no SSP TX DRCMR defined\n"); - ret = -ENODEV; - goto err_free_clk; + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res == NULL) { + dev_err(dev, "no SSP TX DRCMR defined\n"); + return -ENODEV; + } + ssp->drcmr_tx = res->start; } - ssp->drcmr_tx = res->start; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { - dev_err(&pdev->dev, "no memory resource defined\n"); - ret = -ENODEV; - goto err_free_clk; + dev_err(dev, "no memory resource defined\n"); + return -ENODEV; } - res = request_mem_region(res->start, resource_size(res), - pdev->name); + res = devm_request_mem_region(dev, res->start, resource_size(res), + pdev->name); if (res == NULL) { - dev_err(&pdev->dev, "failed to request memory resource\n"); - ret = -EBUSY; - goto err_free_clk; + dev_err(dev, "failed to request memory resource\n"); + return -EBUSY; } ssp->phys_base = res->start; - ssp->mmio_base = ioremap(res->start, resource_size(res)); + ssp->mmio_base = devm_ioremap(dev, res->start, resource_size(res)); if (ssp->mmio_base == NULL) { - dev_err(&pdev->dev, "failed to ioremap() registers\n"); - ret = -ENODEV; - goto err_free_mem; + dev_err(dev, "failed to ioremap() registers\n"); + return -ENODEV; } ssp->irq = platform_get_irq(pdev, 0); if (ssp->irq < 0) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); - ret = -ENODEV; - goto err_free_io; + dev_err(dev, "no IRQ resource defined\n"); + return -ENODEV; + } + + if (dev->of_node) { + const struct of_device_id *id = + of_match_device(of_match_ptr(pxa_ssp_of_ids), dev); + ssp->type = (int) id->data; + } else { + const struct platform_device_id *id = + platform_get_device_id(pdev); + ssp->type = (int) id->driver_data; + + /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id + * starts from 0, do a translation here + */ + ssp->port_id = pdev->id + 1; } - /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id - * starts from 0, do a translation here - */ - ssp->port_id = pdev->id + 1; ssp->use_count = 0; - ssp->type = (int)id->driver_data; + ssp->of_node = dev->of_node; mutex_lock(&ssp_lock); list_add(&ssp->node, &ssp_list); mutex_unlock(&ssp_lock); platform_set_drvdata(pdev, ssp); - return 0; -err_free_io: - iounmap(ssp->mmio_base); -err_free_mem: - release_mem_region(res->start, resource_size(res)); -err_free_clk: - clk_put(ssp->clk); -err_free: - kfree(ssp); - return ret; + return 0; } static int pxa_ssp_remove(struct platform_device *pdev) @@ -201,8 +257,9 @@ static struct platform_driver pxa_ssp_driver = { .probe = pxa_ssp_probe, .remove = pxa_ssp_remove, .driver = { - .owner = THIS_MODULE, - .name = "pxa2xx-ssp", + .owner = THIS_MODULE, + .name = "pxa2xx-ssp", + .of_match_table = of_match_ptr(pxa_ssp_of_ids), }, .id_table = ssp_id_table, }; diff --git a/arch/arm/plat-samsung/init.c b/arch/arm/plat-samsung/init.c index 3e5c4619caa..50a3ea0037d 100644 --- a/arch/arm/plat-samsung/init.c +++ b/arch/arm/plat-samsung/init.c @@ -55,12 +55,13 @@ void __init s3c_init_cpu(unsigned long idcode, printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); - if (cpu->map_io == NULL || cpu->init == NULL) { + if (cpu->init == NULL) { printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); panic("Unsupported Samsung CPU"); } - cpu->map_io(); + if (cpu->map_io) + cpu->map_io(); } /* s3c24xx_init_clocks diff --git a/arch/arm/plat-samsung/s3c-dma-ops.c b/arch/arm/plat-samsung/s3c-dma-ops.c index 0cc40aea3f5..98b10ba67dc 100644 --- a/arch/arm/plat-samsung/s3c-dma-ops.c +++ b/arch/arm/plat-samsung/s3c-dma-ops.c @@ -82,7 +82,8 @@ static int s3c_dma_config(unsigned ch, struct samsung_dma_config *param) static int s3c_dma_prepare(unsigned ch, struct samsung_dma_prep *param) { struct cb_data *data; - int len = (param->cap == DMA_CYCLIC) ? param->period : param->len; + dma_addr_t pos = param->buf; + dma_addr_t end = param->buf + param->len; list_for_each_entry(data, &dma_list, node) if (data->ch == ch) @@ -94,7 +95,15 @@ static int s3c_dma_prepare(unsigned ch, struct samsung_dma_prep *param) data->fp_param = param->fp_param; } - s3c2410_dma_enqueue(ch, (void *)data, param->buf, len); + if (param->cap != DMA_CYCLIC) { + s3c2410_dma_enqueue(ch, (void *)data, param->buf, param->len); + return 0; + } + + while (pos < end) { + s3c2410_dma_enqueue(ch, (void *)data, pos, param->period); + pos += param->period; + } return 0; } diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index c9770ba5c7d..8a6295c8620 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -170,6 +170,7 @@ static void __init xen_percpu_init(void *unused) per_cpu(xen_vcpu, cpu) = vcpup; enable_percpu_irq(xen_events_irq, 0); + put_cpu(); } static void xen_restart(enum reboot_mode reboot_mode, const char *cmd) diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index c92de4163eb..b25763bc0ec 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -42,14 +42,15 @@ #define TPIDR_EL1 18 /* Thread ID, Privileged */ #define AMAIR_EL1 19 /* Aux Memory Attribute Indirection Register */ #define CNTKCTL_EL1 20 /* Timer Control Register (EL1) */ +#define PAR_EL1 21 /* Physical Address Register */ /* 32bit specific registers. Keep them at the end of the range */ -#define DACR32_EL2 21 /* Domain Access Control Register */ -#define IFSR32_EL2 22 /* Instruction Fault Status Register */ -#define FPEXC32_EL2 23 /* Floating-Point Exception Control Register */ -#define DBGVCR32_EL2 24 /* Debug Vector Catch Register */ -#define TEECR32_EL1 25 /* ThumbEE Configuration Register */ -#define TEEHBR32_EL1 26 /* ThumbEE Handler Base Register */ -#define NR_SYS_REGS 27 +#define DACR32_EL2 22 /* Domain Access Control Register */ +#define IFSR32_EL2 23 /* Instruction Fault Status Register */ +#define FPEXC32_EL2 24 /* Floating-Point Exception Control Register */ +#define DBGVCR32_EL2 25 /* Debug Vector Catch Register */ +#define TEECR32_EL1 26 /* ThumbEE Configuration Register */ +#define TEEHBR32_EL1 27 /* ThumbEE Handler Base Register */ +#define NR_SYS_REGS 28 /* 32bit mapping */ #define c0_MPIDR (MPIDR_EL1 * 2) /* MultiProcessor ID Register */ @@ -69,6 +70,8 @@ #define c5_AIFSR (AFSR1_EL1 * 2) /* Auxiliary Instr Fault Status R */ #define c6_DFAR (FAR_EL1 * 2) /* Data Fault Address Register */ #define c6_IFAR (c6_DFAR + 1) /* Instruction Fault Address Register */ +#define c7_PAR (PAR_EL1 * 2) /* Physical Address Register */ +#define c7_PAR_high (c7_PAR + 1) /* PAR top 32 bits */ #define c10_PRRR (MAIR_EL1 * 2) /* Primary Region Remap Register */ #define c10_NMRR (c10_PRRR + 1) /* Normal Memory Remap Register */ #define c12_VBAR (VBAR_EL1 * 2) /* Vector Base Address Register */ diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 644d7395686..0859a4ddd1e 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -129,7 +129,7 @@ struct kvm_vcpu_arch { struct kvm_mmu_memory_cache mmu_page_cache; /* Target CPU and feature flags */ - u32 target; + int target; DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES); /* Detect first run of a vcpu */ diff --git a/arch/arm64/include/asm/tlb.h b/arch/arm64/include/asm/tlb.h index 46b3beb4b77..717031a762c 100644 --- a/arch/arm64/include/asm/tlb.h +++ b/arch/arm64/include/asm/tlb.h @@ -35,6 +35,7 @@ struct mmu_gather { struct mm_struct *mm; unsigned int fullmm; struct vm_area_struct *vma; + unsigned long start, end; unsigned long range_start; unsigned long range_end; unsigned int nr; @@ -97,10 +98,12 @@ static inline void tlb_flush_mmu(struct mmu_gather *tlb) } static inline void -tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned int fullmm) +tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) { tlb->mm = mm; - tlb->fullmm = fullmm; + tlb->fullmm = !(start | (end+1)); + tlb->start = start; + tlb->end = end; tlb->vma = NULL; tlb->max = ARRAY_SIZE(tlb->local); tlb->pages = tlb->local; diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index 9ba33c40cdf..12e6ccb8869 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -107,7 +107,12 @@ armpmu_map_cache_event(const unsigned (*cache_map) static int armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config) { - int mapping = (*event_map)[config]; + int mapping; + + if (config >= PERF_COUNT_HW_MAX) + return -EINVAL; + + mapping = (*event_map)[config]; return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping; } @@ -317,6 +322,9 @@ validate_event(struct pmu_hw_events *hw_events, struct hw_perf_event fake_event = event->hw; struct pmu *leader_pmu = event->group_leader->pmu; + if (is_software_event(event)) + return 1; + if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF) return 1; diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S index ff985e3d8b7..1ac0bbbdddb 100644 --- a/arch/arm64/kvm/hyp.S +++ b/arch/arm64/kvm/hyp.S @@ -214,6 +214,7 @@ __kvm_hyp_code_start: mrs x21, tpidr_el1 mrs x22, amair_el1 mrs x23, cntkctl_el1 + mrs x24, par_el1 stp x4, x5, [x3] stp x6, x7, [x3, #16] @@ -225,6 +226,7 @@ __kvm_hyp_code_start: stp x18, x19, [x3, #112] stp x20, x21, [x3, #128] stp x22, x23, [x3, #144] + str x24, [x3, #160] .endm .macro restore_sysregs @@ -243,6 +245,7 @@ __kvm_hyp_code_start: ldp x18, x19, [x3, #112] ldp x20, x21, [x3, #128] ldp x22, x23, [x3, #144] + ldr x24, [x3, #160] msr vmpidr_el2, x4 msr csselr_el1, x5 @@ -264,6 +267,7 @@ __kvm_hyp_code_start: msr tpidr_el1, x21 msr amair_el1, x22 msr cntkctl_el1, x23 + msr par_el1, x24 .endm .macro skip_32bit_state tmp, target @@ -600,6 +604,8 @@ END(__kvm_vcpu_run) // void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa); ENTRY(__kvm_tlb_flush_vmid_ipa) + dsb ishst + kern_hyp_va x0 ldr x2, [x0, #KVM_VTTBR] msr vttbr_el2, x2 @@ -621,6 +627,7 @@ ENTRY(__kvm_tlb_flush_vmid_ipa) ENDPROC(__kvm_tlb_flush_vmid_ipa) ENTRY(__kvm_flush_vm_context) + dsb ishst tlbi alle1is ic ialluis dsb sy @@ -753,6 +760,10 @@ el1_trap: */ tbnz x1, #7, 1f // S1PTW is set + /* Preserve PAR_EL1 */ + mrs x3, par_el1 + push x3, xzr + /* * Permission fault, HPFAR_EL2 is invalid. * Resolve the IPA the hard way using the guest VA. @@ -766,6 +777,8 @@ el1_trap: /* Read result */ mrs x3, par_el1 + pop x0, xzr // Restore PAR_EL1 from the stack + msr par_el1, x0 tbnz x3, #0, 3f // Bail out if we failed the translation ubfx x3, x3, #12, #36 // Extract IPA lsl x3, x3, #4 // and present it like HPFAR diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 94923609753..02e9d09e1d8 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -211,6 +211,9 @@ static const struct sys_reg_desc sys_reg_descs[] = { /* FAR_EL1 */ { Op0(0b11), Op1(0b000), CRn(0b0110), CRm(0b0000), Op2(0b000), NULL, reset_unknown, FAR_EL1 }, + /* PAR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0111), CRm(0b0100), Op2(0b000), + NULL, reset_unknown, PAR_EL1 }, /* PMINTENSET_EL1 */ { Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b001), diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig index 33a97929d05..77d442ab28c 100644 --- a/arch/hexagon/Kconfig +++ b/arch/hexagon/Kconfig @@ -158,6 +158,7 @@ source "kernel/Kconfig.hz" endmenu source "init/Kconfig" +source "kernel/Kconfig.freezer" source "drivers/Kconfig" source "fs/Kconfig" diff --git a/arch/ia64/include/asm/tlb.h b/arch/ia64/include/asm/tlb.h index ef3a9de0195..bc5efc7c3f3 100644 --- a/arch/ia64/include/asm/tlb.h +++ b/arch/ia64/include/asm/tlb.h @@ -22,7 +22,7 @@ * unmapping a portion of the virtual address space, these hooks are called according to * the following template: * - * tlb <- tlb_gather_mmu(mm, full_mm_flush); // start unmap for address space MM + * tlb <- tlb_gather_mmu(mm, start, end); // start unmap for address space MM * { * for each vma that needs a shootdown do { * tlb_start_vma(tlb, vma); @@ -58,6 +58,7 @@ struct mmu_gather { unsigned int max; unsigned char fullmm; /* non-zero means full mm flush */ unsigned char need_flush; /* really unmapped some PTEs? */ + unsigned long start, end; unsigned long start_addr; unsigned long end_addr; struct page **pages; @@ -155,13 +156,15 @@ static inline void __tlb_alloc_page(struct mmu_gather *tlb) static inline void -tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned int full_mm_flush) +tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) { tlb->mm = mm; tlb->max = ARRAY_SIZE(tlb->local); tlb->pages = tlb->local; tlb->nr = 0; - tlb->fullmm = full_mm_flush; + tlb->fullmm = !(start | (end+1)); + tlb->start = start; + tlb->end = end; tlb->start_addr = ~0UL; } diff --git a/arch/m68k/emu/natfeat.c b/arch/m68k/emu/natfeat.c index 2291a7d69d4..fa277aecfb7 100644 --- a/arch/m68k/emu/natfeat.c +++ b/arch/m68k/emu/natfeat.c @@ -18,9 +18,11 @@ #include <asm/machdep.h> #include <asm/natfeat.h> +extern long nf_get_id2(const char *feature_name); + asm("\n" -" .global nf_get_id,nf_call\n" -"nf_get_id:\n" +" .global nf_get_id2,nf_call\n" +"nf_get_id2:\n" " .short 0x7300\n" " rts\n" "nf_call:\n" @@ -29,12 +31,25 @@ asm("\n" "1: moveq.l #0,%d0\n" " rts\n" " .section __ex_table,\"a\"\n" -" .long nf_get_id,1b\n" +" .long nf_get_id2,1b\n" " .long nf_call,1b\n" " .previous"); -EXPORT_SYMBOL_GPL(nf_get_id); EXPORT_SYMBOL_GPL(nf_call); +long nf_get_id(const char *feature_name) +{ + /* feature_name may be in vmalloc()ed memory, so make a copy */ + char name_copy[32]; + size_t n; + + n = strlcpy(name_copy, feature_name, sizeof(name_copy)); + if (n >= sizeof(name_copy)) + return 0; + + return nf_get_id2(name_copy); +} +EXPORT_SYMBOL_GPL(nf_get_id); + void nfprint(const char *fmt, ...) { static char buf[256]; diff --git a/arch/m68k/include/asm/div64.h b/arch/m68k/include/asm/div64.h index 444ea8a09e9..ef881cfbbca 100644 --- a/arch/m68k/include/asm/div64.h +++ b/arch/m68k/include/asm/div64.h @@ -15,16 +15,17 @@ unsigned long long n64; \ } __n; \ unsigned long __rem, __upper; \ + unsigned long __base = (base); \ \ __n.n64 = (n); \ if ((__upper = __n.n32[0])) { \ asm ("divul.l %2,%1:%0" \ - : "=d" (__n.n32[0]), "=d" (__upper) \ - : "d" (base), "0" (__n.n32[0])); \ + : "=d" (__n.n32[0]), "=d" (__upper) \ + : "d" (__base), "0" (__n.n32[0])); \ } \ asm ("divu.l %2,%1:%0" \ - : "=d" (__n.n32[1]), "=d" (__rem) \ - : "d" (base), "1" (__upper), "0" (__n.n32[1])); \ + : "=d" (__n.n32[1]), "=d" (__rem) \ + : "d" (__base), "1" (__upper), "0" (__n.n32[1])); \ (n) = __n.n64; \ __rem; \ }) diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index d22a4ecffff..4fab52294d9 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -28,7 +28,7 @@ config MICROBLAZE select GENERIC_CLOCKEVENTS select GENERIC_IDLE_POLL_SETUP select MODULES_USE_ELF_RELA - select CLONE_BACKWARDS + select CLONE_BACKWARDS3 config SWAP def_bool n diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index e773659ccf9..46048d24328 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -803,6 +803,32 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, dec_insn.next_pc_inc; return 1; break; +#ifdef CONFIG_CPU_CAVIUM_OCTEON + case lwc2_op: /* This is bbit0 on Octeon */ + if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0) + *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); + else + *contpc = regs->cp0_epc + 8; + return 1; + case ldc2_op: /* This is bbit032 on Octeon */ + if ((regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) == 0) + *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); + else + *contpc = regs->cp0_epc + 8; + return 1; + case swc2_op: /* This is bbit1 on Octeon */ + if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) + *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); + else + *contpc = regs->cp0_epc + 8; + return 1; + case sdc2_op: /* This is bbit132 on Octeon */ + if (regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) + *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); + else + *contpc = regs->cp0_epc + 8; + return 1; +#endif case cop0_op: case cop1_op: case cop2_op: diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 99dbab1c59a..d60bf98fa5c 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -55,6 +55,7 @@ config GENERIC_CSUM source "init/Kconfig" +source "kernel/Kconfig.freezer" menu "Processor type and features" diff --git a/arch/s390/include/asm/tlb.h b/arch/s390/include/asm/tlb.h index b75d7d68668..6d6d92b4ea1 100644 --- a/arch/s390/include/asm/tlb.h +++ b/arch/s390/include/asm/tlb.h @@ -32,6 +32,7 @@ struct mmu_gather { struct mm_struct *mm; struct mmu_table_batch *batch; unsigned int fullmm; + unsigned long start, end; }; struct mmu_table_batch { @@ -48,10 +49,13 @@ extern void tlb_remove_table(struct mmu_gather *tlb, void *table); static inline void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, - unsigned int full_mm_flush) + unsigned long start, + unsigned long end) { tlb->mm = mm; - tlb->fullmm = full_mm_flush; + tlb->start = start; + tlb->end = end; + tlb->fullmm = !(start | (end+1)); tlb->batch = NULL; if (tlb->fullmm) __tlb_flush_mm(mm); diff --git a/arch/score/Kconfig b/arch/score/Kconfig index c8def8bc902..5fc237581ca 100644 --- a/arch/score/Kconfig +++ b/arch/score/Kconfig @@ -87,6 +87,8 @@ config STACKTRACE_SUPPORT source "init/Kconfig" +source "kernel/Kconfig.freezer" + config MMU def_bool y diff --git a/arch/sh/include/asm/tlb.h b/arch/sh/include/asm/tlb.h index e61d43d9f68..362192ed12f 100644 --- a/arch/sh/include/asm/tlb.h +++ b/arch/sh/include/asm/tlb.h @@ -36,10 +36,12 @@ static inline void init_tlb_gather(struct mmu_gather *tlb) } static inline void -tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned int full_mm_flush) +tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) { tlb->mm = mm; - tlb->fullmm = full_mm_flush; + tlb->start = start; + tlb->end = end; + tlb->fullmm = !(start | (end+1)); init_tlb_gather(tlb); } diff --git a/arch/um/include/asm/tlb.h b/arch/um/include/asm/tlb.h index 4febacd1a8a..29b0301c18a 100644 --- a/arch/um/include/asm/tlb.h +++ b/arch/um/include/asm/tlb.h @@ -45,10 +45,12 @@ static inline void init_tlb_gather(struct mmu_gather *tlb) } static inline void -tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned int full_mm_flush) +tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) { tlb->mm = mm; - tlb->fullmm = full_mm_flush; + tlb->start = start; + tlb->end = end; + tlb->fullmm = !(start | (end+1)); init_tlb_gather(tlb); } diff --git a/arch/x86/include/asm/bootparam_utils.h b/arch/x86/include/asm/bootparam_utils.h index 653668d140f..4a8cb8d7cbd 100644 --- a/arch/x86/include/asm/bootparam_utils.h +++ b/arch/x86/include/asm/bootparam_utils.h @@ -35,9 +35,9 @@ static void sanitize_boot_params(struct boot_params *boot_params) */ if (boot_params->sentinel) { /* fields in boot_params are left uninitialized, clear them */ - memset(&boot_params->olpc_ofw_header, 0, + memset(&boot_params->ext_ramdisk_image, 0, (char *)&boot_params->efi_info - - (char *)&boot_params->olpc_ofw_header); + (char *)&boot_params->ext_ramdisk_image); memset(&boot_params->kbd_status, 0, (char *)&boot_params->hdr - (char *)&boot_params->kbd_status); diff --git a/arch/x86/include/asm/microcode_amd.h b/arch/x86/include/asm/microcode_amd.h index 50e5c58ced2..4c019179a57 100644 --- a/arch/x86/include/asm/microcode_amd.h +++ b/arch/x86/include/asm/microcode_amd.h @@ -59,7 +59,7 @@ static inline u16 find_equiv_id(struct equiv_cpu_entry *equiv_cpu_table, extern int __apply_microcode_amd(struct microcode_amd *mc_amd); extern int apply_microcode_amd(int cpu); -extern enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size); +extern enum ucode_state load_microcode_amd(u8 family, const u8 *data, size_t size); #ifdef CONFIG_MICROCODE_AMD_EARLY #ifdef CONFIG_X86_32 diff --git a/arch/x86/include/asm/pgtable-2level.h b/arch/x86/include/asm/pgtable-2level.h index f2b489cf160..3bf2dd0cf61 100644 --- a/arch/x86/include/asm/pgtable-2level.h +++ b/arch/x86/include/asm/pgtable-2level.h @@ -55,9 +55,53 @@ static inline pmd_t native_pmdp_get_and_clear(pmd_t *xp) #define native_pmdp_get_and_clear(xp) native_local_pmdp_get_and_clear(xp) #endif +#ifdef CONFIG_MEM_SOFT_DIRTY + +/* + * Bits _PAGE_BIT_PRESENT, _PAGE_BIT_FILE, _PAGE_BIT_SOFT_DIRTY and + * _PAGE_BIT_PROTNONE are taken, split up the 28 bits of offset + * into this range. + */ +#define PTE_FILE_MAX_BITS 28 +#define PTE_FILE_SHIFT1 (_PAGE_BIT_PRESENT + 1) +#define PTE_FILE_SHIFT2 (_PAGE_BIT_FILE + 1) +#define PTE_FILE_SHIFT3 (_PAGE_BIT_PROTNONE + 1) +#define PTE_FILE_SHIFT4 (_PAGE_BIT_SOFT_DIRTY + 1) +#define PTE_FILE_BITS1 (PTE_FILE_SHIFT2 - PTE_FILE_SHIFT1 - 1) +#define PTE_FILE_BITS2 (PTE_FILE_SHIFT3 - PTE_FILE_SHIFT2 - 1) +#define PTE_FILE_BITS3 (PTE_FILE_SHIFT4 - PTE_FILE_SHIFT3 - 1) + +#define pte_to_pgoff(pte) \ + ((((pte).pte_low >> (PTE_FILE_SHIFT1)) \ + & ((1U << PTE_FILE_BITS1) - 1))) \ + + ((((pte).pte_low >> (PTE_FILE_SHIFT2)) \ + & ((1U << PTE_FILE_BITS2) - 1)) \ + << (PTE_FILE_BITS1)) \ + + ((((pte).pte_low >> (PTE_FILE_SHIFT3)) \ + & ((1U << PTE_FILE_BITS3) - 1)) \ + << (PTE_FILE_BITS1 + PTE_FILE_BITS2)) \ + + ((((pte).pte_low >> (PTE_FILE_SHIFT4))) \ + << (PTE_FILE_BITS1 + PTE_FILE_BITS2 + PTE_FILE_BITS3)) + +#define pgoff_to_pte(off) \ + ((pte_t) { .pte_low = \ + ((((off)) & ((1U << PTE_FILE_BITS1) - 1)) << PTE_FILE_SHIFT1) \ + + ((((off) >> PTE_FILE_BITS1) \ + & ((1U << PTE_FILE_BITS2) - 1)) \ + << PTE_FILE_SHIFT2) \ + + ((((off) >> (PTE_FILE_BITS1 + PTE_FILE_BITS2)) \ + & ((1U << PTE_FILE_BITS3) - 1)) \ + << PTE_FILE_SHIFT3) \ + + ((((off) >> \ + (PTE_FILE_BITS1 + PTE_FILE_BITS2 + PTE_FILE_BITS3))) \ + << PTE_FILE_SHIFT4) \ + + _PAGE_FILE }) + +#else /* CONFIG_MEM_SOFT_DIRTY */ + /* * Bits _PAGE_BIT_PRESENT, _PAGE_BIT_FILE and _PAGE_BIT_PROTNONE are taken, - * split up the 29 bits of offset into this range: + * split up the 29 bits of offset into this range. */ #define PTE_FILE_MAX_BITS 29 #define PTE_FILE_SHIFT1 (_PAGE_BIT_PRESENT + 1) @@ -88,6 +132,8 @@ static inline pmd_t native_pmdp_get_and_clear(pmd_t *xp) << PTE_FILE_SHIFT3) \ + _PAGE_FILE }) +#endif /* CONFIG_MEM_SOFT_DIRTY */ + /* Encode and de-code a swap entry */ #if _PAGE_BIT_FILE < _PAGE_BIT_PROTNONE #define SWP_TYPE_BITS (_PAGE_BIT_FILE - _PAGE_BIT_PRESENT - 1) diff --git a/arch/x86/include/asm/pgtable-3level.h b/arch/x86/include/asm/pgtable-3level.h index 4cc9f2b7cdc..81bb91b49a8 100644 --- a/arch/x86/include/asm/pgtable-3level.h +++ b/arch/x86/include/asm/pgtable-3level.h @@ -179,6 +179,9 @@ static inline pmd_t native_pmdp_get_and_clear(pmd_t *pmdp) /* * Bits 0, 6 and 7 are taken in the low part of the pte, * put the 32 bits of offset into the high part. + * + * For soft-dirty tracking 11 bit is taken from + * the low part of pte as well. */ #define pte_to_pgoff(pte) ((pte).pte_high) #define pgoff_to_pte(off) \ diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 7dc305a4605..1c00631164c 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -314,6 +314,36 @@ static inline pmd_t pmd_mksoft_dirty(pmd_t pmd) return pmd_set_flags(pmd, _PAGE_SOFT_DIRTY); } +static inline pte_t pte_swp_mksoft_dirty(pte_t pte) +{ + return pte_set_flags(pte, _PAGE_SWP_SOFT_DIRTY); +} + +static inline int pte_swp_soft_dirty(pte_t pte) +{ + return pte_flags(pte) & _PAGE_SWP_SOFT_DIRTY; +} + +static inline pte_t pte_swp_clear_soft_dirty(pte_t pte) +{ + return pte_clear_flags(pte, _PAGE_SWP_SOFT_DIRTY); +} + +static inline pte_t pte_file_clear_soft_dirty(pte_t pte) +{ + return pte_clear_flags(pte, _PAGE_SOFT_DIRTY); +} + +static inline pte_t pte_file_mksoft_dirty(pte_t pte) +{ + return pte_set_flags(pte, _PAGE_SOFT_DIRTY); +} + +static inline int pte_file_soft_dirty(pte_t pte) +{ + return pte_flags(pte) & _PAGE_SOFT_DIRTY; +} + /* * Mask out unsupported bits in a present pgprot. Non-present pgprots * can use those bits for other purposes, so leave them be. diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h index c98ac63aae4..f4843e03113 100644 --- a/arch/x86/include/asm/pgtable_types.h +++ b/arch/x86/include/asm/pgtable_types.h @@ -61,12 +61,27 @@ * they do not conflict with each other. */ +#define _PAGE_BIT_SOFT_DIRTY _PAGE_BIT_HIDDEN + #ifdef CONFIG_MEM_SOFT_DIRTY -#define _PAGE_SOFT_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_HIDDEN) +#define _PAGE_SOFT_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_SOFT_DIRTY) #else #define _PAGE_SOFT_DIRTY (_AT(pteval_t, 0)) #endif +/* + * Tracking soft dirty bit when a page goes to a swap is tricky. + * We need a bit which can be stored in pte _and_ not conflict + * with swap entry format. On x86 bits 6 and 7 are *not* involved + * into swap entry computation, but bit 6 is used for nonlinear + * file mapping, so we borrow bit 7 for soft dirty tracking. + */ +#ifdef CONFIG_MEM_SOFT_DIRTY +#define _PAGE_SWP_SOFT_DIRTY _PAGE_PSE +#else +#define _PAGE_SWP_SOFT_DIRTY (_AT(pteval_t, 0)) +#endif + #if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE) #define _PAGE_NX (_AT(pteval_t, 1) << _PAGE_BIT_NX) #else diff --git a/arch/x86/include/asm/spinlock.h b/arch/x86/include/asm/spinlock.h index 33692eaabab..e3ddd7db723 100644 --- a/arch/x86/include/asm/spinlock.h +++ b/arch/x86/include/asm/spinlock.h @@ -233,8 +233,4 @@ static inline void arch_write_unlock(arch_rwlock_t *rw) #define arch_read_relax(lock) cpu_relax() #define arch_write_relax(lock) cpu_relax() -/* The {read|write|spin}_lock() on x86 are full memory barriers. */ -static inline void smp_mb__after_lock(void) { } -#define ARCH_HAS_SMP_MB_AFTER_LOCK - #endif /* _ASM_X86_SPINLOCK_H */ diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index f654ecefea5..08a089043cc 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -512,7 +512,7 @@ static void early_init_amd(struct cpuinfo_x86 *c) static const int amd_erratum_383[]; static const int amd_erratum_400[]; -static bool cpu_has_amd_erratum(const int *erratum); +static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum); static void init_amd(struct cpuinfo_x86 *c) { @@ -729,11 +729,11 @@ static void init_amd(struct cpuinfo_x86 *c) value &= ~(1ULL << 24); wrmsrl_safe(MSR_AMD64_BU_CFG2, value); - if (cpu_has_amd_erratum(amd_erratum_383)) + if (cpu_has_amd_erratum(c, amd_erratum_383)) set_cpu_bug(c, X86_BUG_AMD_TLB_MMATCH); } - if (cpu_has_amd_erratum(amd_erratum_400)) + if (cpu_has_amd_erratum(c, amd_erratum_400)) set_cpu_bug(c, X86_BUG_AMD_APIC_C1E); rdmsr_safe(MSR_AMD64_PATCH_LEVEL, &c->microcode, &dummy); @@ -878,23 +878,13 @@ static const int amd_erratum_400[] = static const int amd_erratum_383[] = AMD_OSVW_ERRATUM(3, AMD_MODEL_RANGE(0x10, 0, 0, 0xff, 0xf)); -static bool cpu_has_amd_erratum(const int *erratum) + +static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum) { - struct cpuinfo_x86 *cpu = __this_cpu_ptr(&cpu_info); int osvw_id = *erratum++; u32 range; u32 ms; - /* - * If called early enough that current_cpu_data hasn't been initialized - * yet, fall back to boot_cpu_data. - */ - if (cpu->x86 == 0) - cpu = &boot_cpu_data; - - if (cpu->x86_vendor != X86_VENDOR_AMD) - return false; - if (osvw_id >= 0 && osvw_id < 65536 && cpu_has(cpu, X86_FEATURE_OSVW)) { u64 osvw_len; diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index fbc9210b45b..a45d8d4ace1 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -2270,6 +2270,7 @@ __init int intel_pmu_init(void) case 70: case 71: case 63: + case 69: x86_pmu.late_ack = true; memcpy(hw_cache_event_ids, snb_hw_cache_event_ids, sizeof(hw_cache_event_ids)); memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs, sizeof(hw_cache_extra_regs)); diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index cad791dbde9..1fb6c72717b 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c @@ -314,8 +314,8 @@ static struct uncore_event_desc snbep_uncore_imc_events[] = { static struct uncore_event_desc snbep_uncore_qpi_events[] = { INTEL_UNCORE_EVENT_DESC(clockticks, "event=0x14"), INTEL_UNCORE_EVENT_DESC(txl_flits_active, "event=0x00,umask=0x06"), - INTEL_UNCORE_EVENT_DESC(drs_data, "event=0x02,umask=0x08"), - INTEL_UNCORE_EVENT_DESC(ncb_data, "event=0x03,umask=0x04"), + INTEL_UNCORE_EVENT_DESC(drs_data, "event=0x102,umask=0x08"), + INTEL_UNCORE_EVENT_DESC(ncb_data, "event=0x103,umask=0x04"), { /* end: all zeroes */ }, }; diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index 7a0adb7ee43..7123b5df479 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c @@ -145,10 +145,9 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) return 0; } -static unsigned int verify_patch_size(int cpu, u32 patch_size, +static unsigned int verify_patch_size(u8 family, u32 patch_size, unsigned int size) { - struct cpuinfo_x86 *c = &cpu_data(cpu); u32 max_size; #define F1XH_MPB_MAX_SIZE 2048 @@ -156,7 +155,7 @@ static unsigned int verify_patch_size(int cpu, u32 patch_size, #define F15H_MPB_MAX_SIZE 4096 #define F16H_MPB_MAX_SIZE 3458 - switch (c->x86) { + switch (family) { case 0x14: max_size = F14H_MPB_MAX_SIZE; break; @@ -277,9 +276,8 @@ static void cleanup(void) * driver cannot continue functioning normally. In such cases, we tear * down everything we've used up so far and exit. */ -static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover) +static int verify_and_add_patch(u8 family, u8 *fw, unsigned int leftover) { - struct cpuinfo_x86 *c = &cpu_data(cpu); struct microcode_header_amd *mc_hdr; struct ucode_patch *patch; unsigned int patch_size, crnt_size, ret; @@ -299,7 +297,7 @@ static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover) /* check if patch is for the current family */ proc_fam = ((proc_fam >> 8) & 0xf) + ((proc_fam >> 20) & 0xff); - if (proc_fam != c->x86) + if (proc_fam != family) return crnt_size; if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) { @@ -308,7 +306,7 @@ static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover) return crnt_size; } - ret = verify_patch_size(cpu, patch_size, leftover); + ret = verify_patch_size(family, patch_size, leftover); if (!ret) { pr_err("Patch-ID 0x%08x: size mismatch.\n", mc_hdr->patch_id); return crnt_size; @@ -339,7 +337,8 @@ static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover) return crnt_size; } -static enum ucode_state __load_microcode_amd(int cpu, const u8 *data, size_t size) +static enum ucode_state __load_microcode_amd(u8 family, const u8 *data, + size_t size) { enum ucode_state ret = UCODE_ERROR; unsigned int leftover; @@ -362,7 +361,7 @@ static enum ucode_state __load_microcode_amd(int cpu, const u8 *data, size_t siz } while (leftover) { - crnt_size = verify_and_add_patch(cpu, fw, leftover); + crnt_size = verify_and_add_patch(family, fw, leftover); if (crnt_size < 0) return ret; @@ -373,22 +372,22 @@ static enum ucode_state __load_microcode_amd(int cpu, const u8 *data, size_t siz return UCODE_OK; } -enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size) +enum ucode_state load_microcode_amd(u8 family, const u8 *data, size_t size) { enum ucode_state ret; /* free old equiv table */ free_equiv_cpu_table(); - ret = __load_microcode_amd(cpu, data, size); + ret = __load_microcode_amd(family, data, size); if (ret != UCODE_OK) cleanup(); #if defined(CONFIG_MICROCODE_AMD_EARLY) && defined(CONFIG_X86_32) /* save BSP's matching patch for early load */ - if (cpu_data(cpu).cpu_index == boot_cpu_data.cpu_index) { - struct ucode_patch *p = find_patch(cpu); + if (cpu_data(smp_processor_id()).cpu_index == boot_cpu_data.cpu_index) { + struct ucode_patch *p = find_patch(smp_processor_id()); if (p) { memset(amd_bsp_mpb, 0, MPB_MAX_SIZE); memcpy(amd_bsp_mpb, p->data, min_t(u32, ksize(p->data), @@ -441,7 +440,7 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device, goto fw_release; } - ret = load_microcode_amd(cpu, fw->data, fw->size); + ret = load_microcode_amd(c->x86, fw->data, fw->size); fw_release: release_firmware(fw); diff --git a/arch/x86/kernel/microcode_amd_early.c b/arch/x86/kernel/microcode_amd_early.c index 1d14ffee574..6073104ccaa 100644 --- a/arch/x86/kernel/microcode_amd_early.c +++ b/arch/x86/kernel/microcode_amd_early.c @@ -238,25 +238,17 @@ static void __init collect_cpu_sig_on_bsp(void *arg) uci->cpu_sig.sig = cpuid_eax(0x00000001); } #else -static void collect_cpu_info_amd_early(struct cpuinfo_x86 *c, - struct ucode_cpu_info *uci) +void load_ucode_amd_ap(void) { + unsigned int cpu = smp_processor_id(); + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; u32 rev, eax; rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax); eax = cpuid_eax(0x00000001); - uci->cpu_sig.sig = eax; uci->cpu_sig.rev = rev; - c->microcode = rev; - c->x86 = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff); -} - -void load_ucode_amd_ap(void) -{ - unsigned int cpu = smp_processor_id(); - - collect_cpu_info_amd_early(&cpu_data(cpu), ucode_cpu_info + cpu); + uci->cpu_sig.sig = eax; if (cpu && !ucode_loaded) { void *ucode; @@ -265,8 +257,10 @@ void load_ucode_amd_ap(void) return; ucode = (void *)(initrd_start + ucode_offset); - if (load_microcode_amd(0, ucode, ucode_size) != UCODE_OK) + eax = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff); + if (load_microcode_amd(eax, ucode, ucode_size) != UCODE_OK) return; + ucode_loaded = true; } @@ -278,6 +272,8 @@ int __init save_microcode_in_initrd_amd(void) { enum ucode_state ret; void *ucode; + u32 eax; + #ifdef CONFIG_X86_32 unsigned int bsp = boot_cpu_data.cpu_index; struct ucode_cpu_info *uci = ucode_cpu_info + bsp; @@ -293,7 +289,10 @@ int __init save_microcode_in_initrd_amd(void) return 0; ucode = (void *)(initrd_start + ucode_offset); - ret = load_microcode_amd(0, ucode, ucode_size); + eax = cpuid_eax(0x00000001); + eax = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff); + + ret = load_microcode_amd(eax, ucode, ucode_size); if (ret != UCODE_OK) return -EINVAL; diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c index dbded5aedb8..30277e27431 100644 --- a/arch/x86/kernel/sys_x86_64.c +++ b/arch/x86/kernel/sys_x86_64.c @@ -101,7 +101,7 @@ static void find_start_end(unsigned long flags, unsigned long *begin, *begin = new_begin; } } else { - *begin = TASK_UNMAPPED_BASE; + *begin = current->mm->mmap_legacy_base; *end = TASK_SIZE; } } diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c index 62c29a5bfe2..25e7e1372bb 100644 --- a/arch/x86/mm/mmap.c +++ b/arch/x86/mm/mmap.c @@ -112,11 +112,13 @@ static unsigned long mmap_legacy_base(void) */ void arch_pick_mmap_layout(struct mm_struct *mm) { + mm->mmap_legacy_base = mmap_legacy_base(); + mm->mmap_base = mmap_base(); + if (mmap_is_legacy()) { - mm->mmap_base = mmap_legacy_base(); + mm->mmap_base = mm->mmap_legacy_base; mm->get_unmapped_area = arch_get_unmapped_area; } else { - mm->mmap_base = mmap_base(); mm->get_unmapped_area = arch_get_unmapped_area_topdown; } } diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 056d11faef2..8f3eea6b80c 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -313,6 +313,17 @@ static void xen_align_and_add_e820_region(u64 start, u64 size, int type) e820_add_region(start, end - start, type); } +void xen_ignore_unusable(struct e820entry *list, size_t map_size) +{ + struct e820entry *entry; + unsigned int i; + + for (i = 0, entry = list; i < map_size; i++, entry++) { + if (entry->type == E820_UNUSABLE) + entry->type = E820_RAM; + } +} + /** * machine_specific_memory_setup - Hook for machine specific memory setup. **/ @@ -353,6 +364,17 @@ char * __init xen_memory_setup(void) } BUG_ON(rc); + /* + * Xen won't allow a 1:1 mapping to be created to UNUSABLE + * regions, so if we're using the machine memory map leave the + * region as RAM as it is in the pseudo-physical map. + * + * UNUSABLE regions in domUs are not handled and will need + * a patch in the future. + */ + if (xen_initial_domain()) + xen_ignore_unusable(map, memmap.nr_entries); + /* Make sure the Xen-supplied memory map is well-ordered. */ sanitize_e820_map(map, memmap.nr_entries, &memmap.nr_entries); diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index ca92754eb84..b81c88e51da 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -694,8 +694,15 @@ static void __init xen_hvm_smp_prepare_cpus(unsigned int max_cpus) static int xen_hvm_cpu_up(unsigned int cpu, struct task_struct *tidle) { int rc; - rc = native_cpu_up(cpu, tidle); - WARN_ON (xen_smp_intr_init(cpu)); + /* + * xen_smp_intr_init() needs to run before native_cpu_up() + * so that IPI vectors are set up on the booting CPU before + * it is marked online in native_cpu_up(). + */ + rc = xen_smp_intr_init(cpu); + WARN_ON(rc); + if (!rc) + rc = native_cpu_up(cpu, tidle); return rc; } diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index e1284b8dc6e..3270d3c8ba4 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -908,9 +908,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) device->cap._DDC = 1; } - if (acpi_video_init_brightness(device)) - return; - if (acpi_video_backlight_support()) { struct backlight_properties props; struct pci_dev *pdev; @@ -920,6 +917,9 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) static int count = 0; char *name; + result = acpi_video_init_brightness(device); + if (result) + return; name = kasprintf(GFP_KERNEL, "acpi_video%d", count); if (!name) return; @@ -979,11 +979,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) if (result) printk(KERN_ERR PREFIX "Create sysfs link\n"); - } else { - /* Remove the brightness object. */ - kfree(device->brightness->levels); - kfree(device->brightness); - device->brightness = NULL; } } diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 1c41722bb7e..20fd337a573 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -289,24 +289,24 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) /* Disable sending Early R_OK. * With "cached read" HDD testing and multiple ports busy on a SATA - * host controller, 3726 PMP will very rarely drop a deferred + * host controller, 3x26 PMP will very rarely drop a deferred * R_OK that was intended for the host. Symptom will be all * 5 drives under test will timeout, get reset, and recover. */ - if (vendor == 0x1095 && devid == 0x3726) { + if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { u32 reg; err_mask = sata_pmp_read(&ap->link, PMP_GSCR_SII_POL, ®); if (err_mask) { rc = -EIO; - reason = "failed to read Sil3726 Private Register"; + reason = "failed to read Sil3x26 Private Register"; goto fail; } reg &= ~0x1; err_mask = sata_pmp_write(&ap->link, PMP_GSCR_SII_POL, reg); if (err_mask) { rc = -EIO; - reason = "failed to write Sil3726 Private Register"; + reason = "failed to write Sil3x26 Private Register"; goto fail; } } @@ -383,8 +383,8 @@ static void sata_pmp_quirks(struct ata_port *ap) u16 devid = sata_pmp_gscr_devid(gscr); struct ata_link *link; - if (vendor == 0x1095 && devid == 0x3726) { - /* sil3726 quirks */ + if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { + /* sil3x26 quirks */ ata_for_each_link(link, ap, EDGE) { /* link reports offline after LPM */ link->flags |= ATA_LFLAG_NO_LPM; diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 19720a0a4a6..851bd3f43ac 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -293,6 +293,7 @@ static void fsl_sata_set_irq_coalescing(struct ata_host *host, { struct sata_fsl_host_priv *host_priv = host->private_data; void __iomem *hcr_base = host_priv->hcr_base; + unsigned long flags; if (count > ICC_MAX_INT_COUNT_THRESHOLD) count = ICC_MAX_INT_COUNT_THRESHOLD; @@ -305,12 +306,12 @@ static void fsl_sata_set_irq_coalescing(struct ata_host *host, (count > ICC_MIN_INT_COUNT_THRESHOLD)) ticks = ICC_SAFE_INT_TICKS; - spin_lock(&host->lock); + spin_lock_irqsave(&host->lock, flags); iowrite32((count << 24 | ticks), hcr_base + ICC); intr_coalescing_count = count; intr_coalescing_ticks = ticks; - spin_unlock(&host->lock); + spin_unlock_irqrestore(&host->lock, flags); DPRINTK("interrupt coalescing, count = 0x%x, ticks = %x\n", intr_coalescing_count, intr_coalescing_ticks); diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index d047d92a456..e9a4f46d962 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -86,11 +86,11 @@ struct ecx_plat_data { #define SGPIO_SIGNALS 3 #define ECX_ACTIVITY_BITS 0x300000 -#define ECX_ACTIVITY_SHIFT 2 +#define ECX_ACTIVITY_SHIFT 0 #define ECX_LOCATE_BITS 0x80000 #define ECX_LOCATE_SHIFT 1 #define ECX_FAULT_BITS 0x400000 -#define ECX_FAULT_SHIFT 0 +#define ECX_FAULT_SHIFT 2 static inline int sgpio_bit_shift(struct ecx_plat_data *pdata, u32 port, u32 shift) { diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 99cb944a002..4d45dba7fb8 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -906,16 +906,10 @@ bio_pageinc(struct bio *bio) int i; bio_for_each_segment(bv, bio, i) { - page = bv->bv_page; /* Non-zero page count for non-head members of - * compound pages is no longer allowed by the kernel, - * but this has never been seen here. + * compound pages is no longer allowed by the kernel. */ - if (unlikely(PageCompound(page))) - if (compound_trans_head(page) != page) { - pr_crit("page tail used for block I/O\n"); - BUG(); - } + page = compound_trans_head(bv->bv_page); atomic_inc(&page->_count); } } @@ -924,10 +918,13 @@ static void bio_pagedec(struct bio *bio) { struct bio_vec *bv; + struct page *page; int i; - bio_for_each_segment(bv, bio, i) - atomic_dec(&bv->bv_page->_count); + bio_for_each_segment(bv, bio, i) { + page = compound_trans_head(bv->bv_page); + atomic_dec(&page->_count); + } } static void diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 1bdb882c845..4e5739773c3 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -581,11 +581,15 @@ struct samsung_div_clock exynos4x12_div_clks[] __initdata = { DIV(none, "div_spi1_isp", "mout_spi1_isp", E4X12_DIV_ISP, 16, 4), DIV(none, "div_spi1_isp_pre", "div_spi1_isp", E4X12_DIV_ISP, 20, 8), DIV(none, "div_uart_isp", "mout_uart_isp", E4X12_DIV_ISP, 28, 4), - DIV(div_isp0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3), - DIV(div_isp1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3), + DIV_F(div_isp0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3, + CLK_GET_RATE_NOCACHE, 0), + DIV_F(div_isp1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3, + CLK_GET_RATE_NOCACHE, 0), DIV(none, "div_mpwm", "div_isp1", E4X12_DIV_ISP1, 0, 3), - DIV(div_mcuisp0, "div_mcuisp0", "aclk400_mcuisp", E4X12_DIV_ISP1, 4, 3), - DIV(div_mcuisp1, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, 8, 3), + DIV_F(div_mcuisp0, "div_mcuisp0", "aclk400_mcuisp", E4X12_DIV_ISP1, + 4, 3, CLK_GET_RATE_NOCACHE, 0), + DIV_F(div_mcuisp1, "div_mcuisp1", "div_mcuisp0", E4X12_DIV_ISP1, + 8, 3, CLK_GET_RATE_NOCACHE, 0), DIV(sclk_fimg2d, "sclk_fimg2d", "mout_g2d", DIV_DMC1, 0, 4), }; @@ -863,57 +867,57 @@ struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { GATE_DA(i2s0, "samsung-i2s.0", "i2s0", "aclk100", E4X12_GATE_IP_MAUDIO, 3, 0, 0, "iis"), GATE(fimc_isp, "isp", "aclk200", E4X12_GATE_ISP0, 0, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(fimc_drc, "drc", "aclk200", E4X12_GATE_ISP0, 1, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(fimc_fd, "fd", "aclk200", E4X12_GATE_ISP0, 2, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(fimc_lite0, "lite0", "aclk200", E4X12_GATE_ISP0, 3, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(fimc_lite1, "lite1", "aclk200", E4X12_GATE_ISP0, 4, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(mcuisp, "mcuisp", "aclk200", E4X12_GATE_ISP0, 5, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(gicisp, "gicisp", "aclk200", E4X12_GATE_ISP0, 7, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_isp, "smmu_isp", "aclk200", E4X12_GATE_ISP0, 8, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_drc, "smmu_drc", "aclk200", E4X12_GATE_ISP0, 9, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_fd, "smmu_fd", "aclk200", E4X12_GATE_ISP0, 10, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_lite0, "smmu_lite0", "aclk200", E4X12_GATE_ISP0, 11, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_lite1, "smmu_lite1", "aclk200", E4X12_GATE_ISP0, 12, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(ppmuispmx, "ppmuispmx", "aclk200", E4X12_GATE_ISP0, 20, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(ppmuispx, "ppmuispx", "aclk200", E4X12_GATE_ISP0, 21, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(mcuctl_isp, "mcuctl_isp", "aclk200", E4X12_GATE_ISP0, 23, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(mpwm_isp, "mpwm_isp", "aclk200", E4X12_GATE_ISP0, 24, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(i2c0_isp, "i2c0_isp", "aclk200", E4X12_GATE_ISP0, 25, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(i2c1_isp, "i2c1_isp", "aclk200", E4X12_GATE_ISP0, 26, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(mtcadc_isp, "mtcadc_isp", "aclk200", E4X12_GATE_ISP0, 27, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(pwm_isp, "pwm_isp", "aclk200", E4X12_GATE_ISP0, 28, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(wdt_isp, "wdt_isp", "aclk200", E4X12_GATE_ISP0, 30, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(uart_isp, "uart_isp", "aclk200", E4X12_GATE_ISP0, 31, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(asyncaxim, "asyncaxim", "aclk200", E4X12_GATE_ISP1, 0, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(smmu_ispcx, "smmu_ispcx", "aclk200", E4X12_GATE_ISP1, 4, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(spi0_isp, "spi0_isp", "aclk200", E4X12_GATE_ISP1, 12, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(spi1_isp, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13, - CLK_IGNORE_UNUSED, 0), + CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(g2d, "g2d", "aclk200", GATE_IP_DMC, 23, 0, 0), }; diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 5c205b60a82..089d3e30e22 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -71,6 +71,7 @@ static DEFINE_SPINLOCK(armpll_lock); static DEFINE_SPINLOCK(ddrpll_lock); static DEFINE_SPINLOCK(iopll_lock); static DEFINE_SPINLOCK(armclk_lock); +static DEFINE_SPINLOCK(swdtclk_lock); static DEFINE_SPINLOCK(ddrclk_lock); static DEFINE_SPINLOCK(dciclk_lock); static DEFINE_SPINLOCK(gem0clk_lock); @@ -293,7 +294,7 @@ static void __init zynq_clk_setup(struct device_node *np) } clks[swdt] = clk_register_mux(NULL, clk_output_name[swdt], swdt_ext_clk_mux_parents, 2, CLK_SET_RATE_PARENT, - SLCR_SWDT_CLK_SEL, 0, 1, 0, &gem0clk_lock); + SLCR_SWDT_CLK_SEL, 0, 1, 0, &swdtclk_lock); /* DDR clocks */ clk = clk_register_divider(NULL, "ddr2x_div", "ddrpll", 0, @@ -364,8 +365,9 @@ static void __init zynq_clk_setup(struct device_node *np) CLK_SET_RATE_PARENT, SLCR_GEM0_CLK_CTRL, 20, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &gem0clk_lock); - clk = clk_register_mux(NULL, "gem0_emio_mux", gem0_mux_parents, 2, 0, - SLCR_GEM0_CLK_CTRL, 6, 1, 0, &gem0clk_lock); + clk = clk_register_mux(NULL, "gem0_emio_mux", gem0_mux_parents, 2, + CLK_SET_RATE_PARENT, SLCR_GEM0_CLK_CTRL, 6, 1, 0, + &gem0clk_lock); clks[gem0] = clk_register_gate(NULL, clk_output_name[gem0], "gem0_emio_mux", CLK_SET_RATE_PARENT, SLCR_GEM0_CLK_CTRL, 0, 0, &gem0clk_lock); @@ -386,8 +388,9 @@ static void __init zynq_clk_setup(struct device_node *np) CLK_SET_RATE_PARENT, SLCR_GEM1_CLK_CTRL, 20, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &gem1clk_lock); - clk = clk_register_mux(NULL, "gem1_emio_mux", gem1_mux_parents, 2, 0, - SLCR_GEM1_CLK_CTRL, 6, 1, 0, &gem1clk_lock); + clk = clk_register_mux(NULL, "gem1_emio_mux", gem1_mux_parents, 2, + CLK_SET_RATE_PARENT, SLCR_GEM1_CLK_CTRL, 6, 1, 0, + &gem1clk_lock); clks[gem1] = clk_register_gate(NULL, clk_output_name[gem1], "gem1_emio_mux", CLK_SET_RATE_PARENT, SLCR_GEM1_CLK_CTRL, 0, 0, &gem1clk_lock); diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 19e36603b23..3bc8414533c 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -500,7 +500,8 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo, &status)) goto log_fail; - while (status == SDVO_CMD_STATUS_PENDING && retry--) { + while ((status == SDVO_CMD_STATUS_PENDING || + status == SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED) && retry--) { udelay(15); if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, SDVO_I2C_CMD_STATUS, diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index dc53a527126..9e657833080 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -85,9 +85,17 @@ static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *sg, enum dma_data_direction dir) { + struct drm_i915_gem_object *obj = attachment->dmabuf->priv; + + mutex_lock(&obj->base.dev->struct_mutex); + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); sg_free_table(sg); kfree(sg); + + i915_gem_object_unpin_pages(obj); + + mutex_unlock(&obj->base.dev->struct_mutex); } static void i915_gem_dmabuf_release(struct dma_buf *dma_buf) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 6f514297c48..53cddd98540 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -752,6 +752,8 @@ will not assert AGPBUSY# and will only be delivered when out of C3. */ #define INSTPM_FORCE_ORDERING (1<<7) /* GEN6+ */ +#define INSTPM_TLB_INVALIDATE (1<<9) +#define INSTPM_SYNC_FLUSH (1<<5) #define ACTHD 0x020c8 #define FW_BLC 0x020d8 #define FW_BLC2 0x020dc diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e38b4578665..be79f477a38 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10042,6 +10042,8 @@ struct intel_display_error_state { u32 power_well_driver; + int num_transcoders; + struct intel_cursor_error_state { u32 control; u32 position; @@ -10050,16 +10052,7 @@ struct intel_display_error_state { } cursor[I915_MAX_PIPES]; struct intel_pipe_error_state { - enum transcoder cpu_transcoder; - u32 conf; u32 source; - - u32 htotal; - u32 hblank; - u32 hsync; - u32 vtotal; - u32 vblank; - u32 vsync; } pipe[I915_MAX_PIPES]; struct intel_plane_error_state { @@ -10071,6 +10064,19 @@ struct intel_display_error_state { u32 surface; u32 tile_offset; } plane[I915_MAX_PIPES]; + + struct intel_transcoder_error_state { + enum transcoder cpu_transcoder; + + u32 conf; + + u32 htotal; + u32 hblank; + u32 hsync; + u32 vtotal; + u32 vblank; + u32 vsync; + } transcoder[4]; }; struct intel_display_error_state * @@ -10078,9 +10084,17 @@ intel_display_capture_error_state(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct intel_display_error_state *error; - enum transcoder cpu_transcoder; + int transcoders[] = { + TRANSCODER_A, + TRANSCODER_B, + TRANSCODER_C, + TRANSCODER_EDP, + }; int i; + if (INTEL_INFO(dev)->num_pipes == 0) + return NULL; + error = kmalloc(sizeof(*error), GFP_ATOMIC); if (error == NULL) return NULL; @@ -10089,9 +10103,6 @@ intel_display_capture_error_state(struct drm_device *dev) error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER); for_each_pipe(i) { - cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, i); - error->pipe[i].cpu_transcoder = cpu_transcoder; - if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) { error->cursor[i].control = I915_READ(CURCNTR(i)); error->cursor[i].position = I915_READ(CURPOS(i)); @@ -10115,14 +10126,25 @@ intel_display_capture_error_state(struct drm_device *dev) error->plane[i].tile_offset = I915_READ(DSPTILEOFF(i)); } - error->pipe[i].conf = I915_READ(PIPECONF(cpu_transcoder)); error->pipe[i].source = I915_READ(PIPESRC(i)); - error->pipe[i].htotal = I915_READ(HTOTAL(cpu_transcoder)); - error->pipe[i].hblank = I915_READ(HBLANK(cpu_transcoder)); - error->pipe[i].hsync = I915_READ(HSYNC(cpu_transcoder)); - error->pipe[i].vtotal = I915_READ(VTOTAL(cpu_transcoder)); - error->pipe[i].vblank = I915_READ(VBLANK(cpu_transcoder)); - error->pipe[i].vsync = I915_READ(VSYNC(cpu_transcoder)); + } + + error->num_transcoders = INTEL_INFO(dev)->num_pipes; + if (HAS_DDI(dev_priv->dev)) + error->num_transcoders++; /* Account for eDP. */ + + for (i = 0; i < error->num_transcoders; i++) { + enum transcoder cpu_transcoder = transcoders[i]; + + error->transcoder[i].cpu_transcoder = cpu_transcoder; + + error->transcoder[i].conf = I915_READ(PIPECONF(cpu_transcoder)); + error->transcoder[i].htotal = I915_READ(HTOTAL(cpu_transcoder)); + error->transcoder[i].hblank = I915_READ(HBLANK(cpu_transcoder)); + error->transcoder[i].hsync = I915_READ(HSYNC(cpu_transcoder)); + error->transcoder[i].vtotal = I915_READ(VTOTAL(cpu_transcoder)); + error->transcoder[i].vblank = I915_READ(VBLANK(cpu_transcoder)); + error->transcoder[i].vsync = I915_READ(VSYNC(cpu_transcoder)); } /* In the code above we read the registers without checking if the power @@ -10144,22 +10166,16 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m, { int i; + if (!error) + return; + err_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes); if (HAS_POWER_WELL(dev)) err_printf(m, "PWR_WELL_CTL2: %08x\n", error->power_well_driver); for_each_pipe(i) { err_printf(m, "Pipe [%d]:\n", i); - err_printf(m, " CPU transcoder: %c\n", - transcoder_name(error->pipe[i].cpu_transcoder)); - err_printf(m, " CONF: %08x\n", error->pipe[i].conf); err_printf(m, " SRC: %08x\n", error->pipe[i].source); - err_printf(m, " HTOTAL: %08x\n", error->pipe[i].htotal); - err_printf(m, " HBLANK: %08x\n", error->pipe[i].hblank); - err_printf(m, " HSYNC: %08x\n", error->pipe[i].hsync); - err_printf(m, " VTOTAL: %08x\n", error->pipe[i].vtotal); - err_printf(m, " VBLANK: %08x\n", error->pipe[i].vblank); - err_printf(m, " VSYNC: %08x\n", error->pipe[i].vsync); err_printf(m, "Plane [%d]:\n", i); err_printf(m, " CNTR: %08x\n", error->plane[i].control); @@ -10180,5 +10196,17 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m, err_printf(m, " POS: %08x\n", error->cursor[i].position); err_printf(m, " BASE: %08x\n", error->cursor[i].base); } + + for (i = 0; i < error->num_transcoders; i++) { + err_printf(m, " CPU transcoder: %c\n", + transcoder_name(error->transcoder[i].cpu_transcoder)); + err_printf(m, " CONF: %08x\n", error->transcoder[i].conf); + err_printf(m, " HTOTAL: %08x\n", error->transcoder[i].htotal); + err_printf(m, " HBLANK: %08x\n", error->transcoder[i].hblank); + err_printf(m, " HSYNC: %08x\n", error->transcoder[i].hsync); + err_printf(m, " VTOTAL: %08x\n", error->transcoder[i].vtotal); + err_printf(m, " VBLANK: %08x\n", error->transcoder[i].vblank); + err_printf(m, " VSYNC: %08x\n", error->transcoder[i].vsync); + } } #endif diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 664118d8c1d..079ef0129e7 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -968,6 +968,18 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring) I915_WRITE(mmio, (u32)ring->status_page.gfx_addr); POSTING_READ(mmio); + + /* Flush the TLB for this page */ + if (INTEL_INFO(dev)->gen >= 6) { + u32 reg = RING_INSTPM(ring->mmio_base); + I915_WRITE(reg, + _MASKED_BIT_ENABLE(INSTPM_TLB_INVALIDATE | + INSTPM_SYNC_FLUSH)); + if (wait_for((I915_READ(reg) & INSTPM_SYNC_FLUSH) == 0, + 1000)) + DRM_ERROR("%s: wait for SyncFlush to complete for TLB invalidation timed out\n", + ring->name); + } } static int diff --git a/drivers/gpu/drm/nouveau/core/core/mm.c b/drivers/gpu/drm/nouveau/core/core/mm.c index d8291724dbd..7a4e0891c5f 100644 --- a/drivers/gpu/drm/nouveau/core/core/mm.c +++ b/drivers/gpu/drm/nouveau/core/core/mm.c @@ -98,6 +98,8 @@ nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, u32 splitoff; u32 s, e; + BUG_ON(!type); + list_for_each_entry(this, &mm->free, fl_entry) { e = this->offset + this->length; s = this->offset; @@ -162,6 +164,8 @@ nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, struct nouveau_mm_node *prev, *this, *next; u32 mask = align - 1; + BUG_ON(!type); + list_for_each_entry_reverse(this, &mm->free, fl_entry) { u32 e = this->offset + this->length; u32 s = this->offset; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h index d5502267c30..9d2cd200625 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h @@ -20,8 +20,8 @@ nouveau_mc(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_MC]; } -#define nouveau_mc_create(p,e,o,d) \ - nouveau_mc_create_((p), (e), (o), sizeof(**d), (void **)d) +#define nouveau_mc_create(p,e,o,m,d) \ + nouveau_mc_create_((p), (e), (o), (m), sizeof(**d), (void **)d) #define nouveau_mc_destroy(p) ({ \ struct nouveau_mc *pmc = (p); _nouveau_mc_dtor(nv_object(pmc)); \ }) @@ -33,7 +33,8 @@ nouveau_mc(void *obj) }) int nouveau_mc_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, int, void **); + struct nouveau_oclass *, const struct nouveau_mc_intr *, + int, void **); void _nouveau_mc_dtor(struct nouveau_object *); int _nouveau_mc_init(struct nouveau_object *); int _nouveau_mc_fini(struct nouveau_object *, bool); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c index 19e3a9a63a0..ab7ef0ac9e3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c @@ -40,15 +40,15 @@ nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, return ret; switch (pfb914 & 0x00000003) { - case 0x00000000: pfb->ram->type = NV_MEM_TYPE_DDR1; break; - case 0x00000001: pfb->ram->type = NV_MEM_TYPE_DDR2; break; - case 0x00000002: pfb->ram->type = NV_MEM_TYPE_GDDR3; break; + case 0x00000000: ram->type = NV_MEM_TYPE_DDR1; break; + case 0x00000001: ram->type = NV_MEM_TYPE_DDR2; break; + case 0x00000002: ram->type = NV_MEM_TYPE_GDDR3; break; case 0x00000003: break; } - pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; - pfb->ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; - pfb->ram->tags = nv_rd32(pfb, 0x100320); + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->tags = nv_rd32(pfb, 0x100320); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c index 7192aa6e557..63a6aab8602 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c @@ -38,8 +38,8 @@ nv4e_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; - pfb->ram->type = NV_MEM_TYPE_STOLEN; + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->type = NV_MEM_TYPE_STOLEN; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c index bcca883018f..cce65cc5651 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c @@ -30,8 +30,9 @@ struct nvc0_ltcg_priv { struct nouveau_ltcg base; u32 part_nr; u32 subp_nr; - struct nouveau_mm tags; u32 num_tags; + u32 tag_base; + struct nouveau_mm tags; struct nouveau_mm_node *tag_ram; }; @@ -117,10 +118,6 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv) u32 tag_size, tag_margin, tag_align; int ret; - nv_wr32(priv, 0x17e8d8, priv->part_nr); - if (nv_device(pfb)->card_type >= NV_E0) - nv_wr32(priv, 0x17e000, priv->part_nr); - /* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */ priv->num_tags = (pfb->ram->size >> 17) / 4; if (priv->num_tags > (1 << 17)) @@ -142,7 +139,7 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv) tag_size += tag_align; tag_size = (tag_size + 0xfff) >> 12; /* round up */ - ret = nouveau_mm_tail(&pfb->vram, 0, tag_size, tag_size, 1, + ret = nouveau_mm_tail(&pfb->vram, 1, tag_size, tag_size, 1, &priv->tag_ram); if (ret) { priv->num_tags = 0; @@ -152,7 +149,7 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv) tag_base += tag_align - 1; ret = do_div(tag_base, tag_align); - nv_wr32(priv, 0x17e8d4, tag_base); + priv->tag_base = tag_base; } ret = nouveau_mm_init(&priv->tags, 0, priv->num_tags, 1); @@ -182,8 +179,6 @@ nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine, } priv->subp_nr = nv_rd32(priv, 0x17e8dc) >> 28; - nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */ - ret = nvc0_ltcg_init_tag_ram(pfb, priv); if (ret) return ret; @@ -209,13 +204,32 @@ nvc0_ltcg_dtor(struct nouveau_object *object) nouveau_ltcg_destroy(ltcg); } +static int +nvc0_ltcg_init(struct nouveau_object *object) +{ + struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object; + struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg; + int ret; + + ret = nouveau_ltcg_init(ltcg); + if (ret) + return ret; + + nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */ + nv_wr32(priv, 0x17e8d8, priv->part_nr); + if (nv_device(ltcg)->card_type >= NV_E0) + nv_wr32(priv, 0x17e000, priv->part_nr); + nv_wr32(priv, 0x17e8d4, priv->tag_base); + return 0; +} + struct nouveau_oclass nvc0_ltcg_oclass = { .handle = NV_SUBDEV(LTCG, 0xc0), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nvc0_ltcg_ctor, .dtor = nvc0_ltcg_dtor, - .init = _nouveau_ltcg_init, + .init = nvc0_ltcg_init, .fini = _nouveau_ltcg_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c index 1c0330b8c9a..ec9cd6f10f9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c @@ -80,7 +80,9 @@ _nouveau_mc_dtor(struct nouveau_object *object) int nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, int length, void **pobject) + struct nouveau_oclass *oclass, + const struct nouveau_mc_intr *intr_map, + int length, void **pobject) { struct nouveau_device *device = nv_device(parent); struct nouveau_mc *pmc; @@ -92,6 +94,8 @@ nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + pmc->intr_map = intr_map; + ret = request_irq(device->pdev->irq, nouveau_mc_intr, IRQF_SHARED, "nouveau", pmc); if (ret < 0) diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c index 8c769715227..64aa4edb0d9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c @@ -50,12 +50,11 @@ nv04_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv04_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nv04_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c index 51919371810..d9891782bf2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c @@ -36,12 +36,11 @@ nv44_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv44_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nv04_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c index f25fc5fc7dd..2b1afe225db 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c @@ -53,12 +53,11 @@ nv50_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nv50_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nv50_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c index e82fd21b504..0d57b4d3e00 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c @@ -54,12 +54,11 @@ nv98_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv98_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nv98_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nv98_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c index c5da3babbc6..104175c5a2d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c @@ -57,12 +57,11 @@ nvc0_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvc0_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, &priv); + ret = nouveau_mc_create(parent, engine, oclass, nvc0_mc_intr, &priv); *pobject = nv_object(priv); if (ret) return ret; - priv->base.intr_map = nvc0_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 0782bd2f1e0..6a13ffb53bd 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -606,6 +606,24 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) regp->ramdac_a34 = 0x1; } +static int +nv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) +{ + struct nv04_display *disp = nv04_display(crtc->dev); + struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + int ret; + + ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM); + if (ret == 0) { + if (disp->image[nv_crtc->index]) + nouveau_bo_unpin(disp->image[nv_crtc->index]); + nouveau_bo_ref(nvfb->nvbo, &disp->image[nv_crtc->index]); + } + + return ret; +} + /** * Sets up registers for the given mode/adjusted_mode pair. * @@ -622,10 +640,15 @@ nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_device *dev = crtc->dev; struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nouveau_drm *drm = nouveau_drm(dev); + int ret; NV_DEBUG(drm, "CTRC mode on CRTC %d:\n", nv_crtc->index); drm_mode_debug_printmodeline(adjusted_mode); + ret = nv_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; + /* unlock must come after turning off FP_TG_CONTROL in output_prepare */ nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1); @@ -722,6 +745,7 @@ static void nv_crtc_commit(struct drm_crtc *crtc) static void nv_crtc_destroy(struct drm_crtc *crtc) { + struct nv04_display *disp = nv04_display(crtc->dev); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); if (!nv_crtc) @@ -729,6 +753,10 @@ static void nv_crtc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(crtc); + if (disp->image[nv_crtc->index]) + nouveau_bo_unpin(disp->image[nv_crtc->index]); + nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]); + nouveau_bo_unmap(nv_crtc->cursor.nvbo); nouveau_bo_unpin(nv_crtc->cursor.nvbo); nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); @@ -754,6 +782,16 @@ nv_crtc_gamma_load(struct drm_crtc *crtc) } static void +nv_crtc_disable(struct drm_crtc *crtc) +{ + struct nv04_display *disp = nv04_display(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + if (disp->image[nv_crtc->index]) + nouveau_bo_unpin(disp->image[nv_crtc->index]); + nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]); +} + +static void nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start, uint32_t size) { @@ -791,7 +829,6 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, struct drm_framebuffer *drm_fb; struct nouveau_framebuffer *fb; int arb_burst, arb_lwm; - int ret; NV_DEBUG(drm, "index %d\n", nv_crtc->index); @@ -801,10 +838,8 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, return 0; } - /* If atomic, we want to switch to the fb we were passed, so - * now we update pointers to do that. (We don't pin; just - * assume we're already pinned and update the base address.) + * now we update pointers to do that. */ if (atomic) { drm_fb = passed_fb; @@ -812,17 +847,6 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, } else { drm_fb = crtc->fb; fb = nouveau_framebuffer(crtc->fb); - /* If not atomic, we can go ahead and pin, and unpin the - * old fb we were passed. - */ - ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM); - if (ret) - return ret; - - if (passed_fb) { - struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb); - nouveau_bo_unpin(ofb->nvbo); - } } nv_crtc->fb.offset = fb->nvbo->bo.offset; @@ -877,6 +901,9 @@ static int nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { + int ret = nv_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false); } @@ -1027,6 +1054,7 @@ static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { .mode_set_base = nv04_crtc_mode_set_base, .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic, .load_lut = nv_crtc_gamma_load, + .disable = nv_crtc_disable, }; int diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h index a0a031dad13..9928187f0a7 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.h @@ -81,6 +81,7 @@ struct nv04_display { uint32_t saved_vga_font[4][16384]; uint32_t dac_users[4]; struct nouveau_object *core; + struct nouveau_bo *image[2]; }; static inline struct nv04_display * diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 907d20ef6d4..a03e75deaca 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -577,6 +577,9 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, ret = nv50_display_flip_next(crtc, fb, chan, 0); if (ret) goto fail_unreserve; + } else { + struct nv04_display *dispnv04 = nv04_display(dev); + nouveau_bo_ref(new_bo, &dispnv04->image[nouveau_crtc(crtc)->index]); } ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); diff --git a/drivers/gpu/drm/nouveau/nv40_pm.c b/drivers/gpu/drm/nouveau/nv40_pm.c index 3af5bcd0b20..625f80d53dc 100644 --- a/drivers/gpu/drm/nouveau/nv40_pm.c +++ b/drivers/gpu/drm/nouveau/nv40_pm.c @@ -131,7 +131,7 @@ nv40_calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll, if (clk < pll->vco1.max_freq) pll->vco2.max_freq = 0; - pclk->pll_calc(pclk, pll, clk, &coef); + ret = pclk->pll_calc(pclk, pll, clk, &coef); if (ret == 0) return -ERANGE; diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 274b8e1b889..9f19259667d 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -2163,7 +2163,7 @@ void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v); WREG32(reg, tmp_); \ } while (0) #define WREG32_AND(reg, and) WREG32_P(reg, 0, and) -#define WREG32_OR(reg, or) WREG32_P(reg, or, ~or) +#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or)) #define WREG32_PLL_P(reg, val, mask) \ do { \ uint32_t tmp_ = RREG32_PLL(reg); \ diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index f1c15754e73..b79f4f5cdd6 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -356,6 +356,14 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo, return -EINVAL; } + if (bo->tbo.sync_obj) { + r = radeon_fence_wait(bo->tbo.sync_obj, false); + if (r) { + DRM_ERROR("Failed waiting for UVD message (%d)!\n", r); + return r; + } + } + r = radeon_bo_kmap(bo, &ptr); if (r) { DRM_ERROR("Failed mapping the UVD message (%d)!\n", r); diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index bcc68ec204a..f5e92cfcc14 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -744,10 +744,10 @@ static void rv770_init_golden_registers(struct radeon_device *rdev) (const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers)); radeon_program_register_sequence(rdev, rv730_golden_registers, - (const u32)ARRAY_SIZE(rv770_golden_registers)); + (const u32)ARRAY_SIZE(rv730_golden_registers)); radeon_program_register_sequence(rdev, rv730_mgcg_init, - (const u32)ARRAY_SIZE(rv770_mgcg_init)); + (const u32)ARRAY_SIZE(rv730_mgcg_init)); break; case CHIP_RV710: radeon_program_register_sequence(rdev, @@ -758,18 +758,18 @@ static void rv770_init_golden_registers(struct radeon_device *rdev) (const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers)); radeon_program_register_sequence(rdev, rv710_golden_registers, - (const u32)ARRAY_SIZE(rv770_golden_registers)); + (const u32)ARRAY_SIZE(rv710_golden_registers)); radeon_program_register_sequence(rdev, rv710_mgcg_init, - (const u32)ARRAY_SIZE(rv770_mgcg_init)); + (const u32)ARRAY_SIZE(rv710_mgcg_init)); break; case CHIP_RV740: radeon_program_register_sequence(rdev, rv740_golden_registers, - (const u32)ARRAY_SIZE(rv770_golden_registers)); + (const u32)ARRAY_SIZE(rv740_golden_registers)); radeon_program_register_sequence(rdev, rv740_mgcg_init, - (const u32)ARRAY_SIZE(rv770_mgcg_init)); + (const u32)ARRAY_SIZE(rv740_mgcg_init)); break; default: break; diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 5f4749e60b0..c1cd5698b8a 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -232,7 +232,8 @@ static int adjd_s311_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = adjd_s311_read_data(indio_dev, chan->address, val); + ret = adjd_s311_read_data(indio_dev, + ADJD_S311_DATA_REG(chan->address), val); if (ret < 0) return ret; return IIO_VAL_INT; diff --git a/drivers/md/dm-cache-policy-mq.c b/drivers/md/dm-cache-policy-mq.c index dc112a7137f..4296155090b 100644 --- a/drivers/md/dm-cache-policy-mq.c +++ b/drivers/md/dm-cache-policy-mq.c @@ -959,23 +959,21 @@ out: return r; } -static void remove_mapping(struct mq_policy *mq, dm_oblock_t oblock) +static void mq_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock) { - struct entry *e = hash_lookup(mq, oblock); + struct mq_policy *mq = to_mq_policy(p); + struct entry *e; + + mutex_lock(&mq->lock); + + e = hash_lookup(mq, oblock); BUG_ON(!e || !e->in_cache); del(mq, e); e->in_cache = false; push(mq, e); -} -static void mq_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock) -{ - struct mq_policy *mq = to_mq_policy(p); - - mutex_lock(&mq->lock); - remove_mapping(mq, oblock); mutex_unlock(&mq->lock); } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 07f257d44a1..e48cb339c0c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3714,11 +3714,17 @@ static int bond_neigh_init(struct neighbour *n) * The bonding ndo_neigh_setup is called at init time beofre any * slave exists. So we must declare proxy setup function which will * be used at run time to resolve the actual slave neigh param setup. + * + * It's also called by master devices (such as vlans) to setup their + * underlying devices. In that case - do nothing, we're already set up from + * our init. */ static int bond_neigh_setup(struct net_device *dev, struct neigh_parms *parms) { - parms->neigh_setup = bond_neigh_init; + /* modify only our neigh_parms */ + if (parms->dev == dev) + parms->neigh_setup = bond_neigh_init; return 0; } diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c index 25723d8ee20..925ab8ec932 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -649,7 +649,7 @@ static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len) if ((mc->ptr + rec_len) > mc->end) goto decode_failed; - memcpy(cf->data, mc->ptr, rec_len); + memcpy(cf->data, mc->ptr, cf->can_dlc); mc->ptr += rec_len; } diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c index f1b121ee552..55d79cb53a7 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -199,7 +199,7 @@ static int arc_emac_rx(struct net_device *ndev, int budget) struct arc_emac_priv *priv = netdev_priv(ndev); unsigned int work_done; - for (work_done = 0; work_done <= budget; work_done++) { + for (work_done = 0; work_done < budget; work_done++) { unsigned int *last_rx_bd = &priv->last_rx_bd; struct net_device_stats *stats = &priv->stats; struct buffer_state *rx_buff = &priv->rx_buff[*last_rx_bd]; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index d80e34b8285..00b88cbfde2 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1333,6 +1333,8 @@ enum { BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN, BNX2X_SP_RTNL_VFPF_STORM_RX_MODE, BNX2X_SP_RTNL_HYPERVISOR_VLAN, + BNX2X_SP_RTNL_TX_STOP, + BNX2X_SP_RTNL_TX_RESUME, }; struct bnx2x_prev_path_list { @@ -1502,6 +1504,7 @@ struct bnx2x { #define BC_SUPPORTS_DCBX_MSG_NON_PMF (1 << 21) #define IS_VF_FLAG (1 << 22) #define INTERRUPTS_ENABLED_FLAG (1 << 23) +#define BC_SUPPORTS_RMMOD_CMD (1 << 24) #define BP_NOMCP(bp) ((bp)->flags & NO_MCP_FLAG) @@ -1830,6 +1833,8 @@ struct bnx2x { int fp_array_size; u32 dump_preset_idx; + bool stats_started; + struct semaphore stats_sema; }; /* Tx queues may be less or equal to Rx queues */ @@ -2451,4 +2456,6 @@ enum bnx2x_pci_bus_speed { BNX2X_PCI_LINK_SPEED_5000 = 5000, BNX2X_PCI_LINK_SPEED_8000 = 8000 }; + +void bnx2x_set_local_cmng(struct bnx2x *bp); #endif /* bnx2x.h */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 0c94df47e0e..fcf2761d882 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -30,10 +30,8 @@ #include "bnx2x_dcb.h" /* forward declarations of dcbx related functions */ -static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp); static void bnx2x_pfc_set_pfc(struct bnx2x *bp); static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp); -static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp); static void bnx2x_dcbx_get_ets_pri_pg_tbl(struct bnx2x *bp, u32 *set_configuration_ets_pg, u32 *pri_pg_tbl); @@ -425,30 +423,52 @@ static void bnx2x_pfc_set_pfc(struct bnx2x *bp) bnx2x_pfc_clear(bp); } -static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp) +int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp) { struct bnx2x_func_state_params func_params = {NULL}; + int rc; func_params.f_obj = &bp->func_obj; func_params.cmd = BNX2X_F_CMD_TX_STOP; + __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags); + __set_bit(RAMROD_RETRY, &func_params.ramrod_flags); + DP(BNX2X_MSG_DCB, "STOP TRAFFIC\n"); - return bnx2x_func_state_change(bp, &func_params); + + rc = bnx2x_func_state_change(bp, &func_params); + if (rc) { + BNX2X_ERR("Unable to hold traffic for HW configuration\n"); + bnx2x_panic(); + } + + return rc; } -static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp) +int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp) { struct bnx2x_func_state_params func_params = {NULL}; struct bnx2x_func_tx_start_params *tx_params = &func_params.params.tx_start; + int rc; func_params.f_obj = &bp->func_obj; func_params.cmd = BNX2X_F_CMD_TX_START; + __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags); + __set_bit(RAMROD_RETRY, &func_params.ramrod_flags); + bnx2x_dcbx_fw_struct(bp, tx_params); DP(BNX2X_MSG_DCB, "START TRAFFIC\n"); - return bnx2x_func_state_change(bp, &func_params); + + rc = bnx2x_func_state_change(bp, &func_params); + if (rc) { + BNX2X_ERR("Unable to resume traffic after HW configuration\n"); + bnx2x_panic(); + } + + return rc; } static void bnx2x_dcbx_2cos_limit_update_ets_config(struct bnx2x *bp) @@ -744,7 +764,9 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state) if (IS_MF(bp)) bnx2x_link_sync_notify(bp); - bnx2x_dcbx_stop_hw_tx(bp); + set_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state); + + schedule_delayed_work(&bp->sp_rtnl_task, 0); return; } @@ -753,7 +775,13 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state) bnx2x_pfc_set_pfc(bp); bnx2x_dcbx_update_ets_params(bp); - bnx2x_dcbx_resume_hw_tx(bp); + + /* ets may affect cmng configuration: reinit it in hw */ + bnx2x_set_local_cmng(bp); + + set_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state); + + schedule_delayed_work(&bp->sp_rtnl_task, 0); return; case BNX2X_DCBX_STATE_TX_RELEASED: @@ -2363,21 +2391,24 @@ static u8 bnx2x_dcbnl_get_featcfg(struct net_device *netdev, int featid, case DCB_FEATCFG_ATTR_PG: if (bp->dcbx_local_feat.ets.enabled) *flags |= DCB_FEATCFG_ENABLE; - if (bp->dcbx_error & DCBX_LOCAL_ETS_ERROR) + if (bp->dcbx_error & (DCBX_LOCAL_ETS_ERROR | + DCBX_REMOTE_MIB_ERROR)) *flags |= DCB_FEATCFG_ERROR; break; case DCB_FEATCFG_ATTR_PFC: if (bp->dcbx_local_feat.pfc.enabled) *flags |= DCB_FEATCFG_ENABLE; if (bp->dcbx_error & (DCBX_LOCAL_PFC_ERROR | - DCBX_LOCAL_PFC_MISMATCH)) + DCBX_LOCAL_PFC_MISMATCH | + DCBX_REMOTE_MIB_ERROR)) *flags |= DCB_FEATCFG_ERROR; break; case DCB_FEATCFG_ATTR_APP: if (bp->dcbx_local_feat.app.enabled) *flags |= DCB_FEATCFG_ENABLE; if (bp->dcbx_error & (DCBX_LOCAL_APP_ERROR | - DCBX_LOCAL_APP_MISMATCH)) + DCBX_LOCAL_APP_MISMATCH | + DCBX_REMOTE_MIB_ERROR)) *flags |= DCB_FEATCFG_ERROR; break; default: diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h index 125bd1b6586..804b8f64463 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h @@ -199,4 +199,7 @@ extern const struct dcbnl_rtnl_ops bnx2x_dcbnl_ops; int bnx2x_dcbnl_update_applist(struct bnx2x *bp, bool delall); #endif /* BCM_DCBNL */ +int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp); +int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp); + #endif /* BNX2X_DCB_H */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index 5018e52ae2a..32767f6aa33 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -1300,6 +1300,9 @@ struct drv_func_mb { #define DRV_MSG_CODE_EEE_RESULTS_ACK 0xda000000 + #define DRV_MSG_CODE_RMMOD 0xdb000000 + #define REQ_BC_VER_4_RMMOD_CMD 0x0007080f + #define DRV_MSG_CODE_SET_MF_BW 0xe0000000 #define REQ_BC_VER_4_SET_MF_BW 0x00060202 #define DRV_MSG_CODE_SET_MF_BW_ACK 0xe1000000 @@ -1372,6 +1375,8 @@ struct drv_func_mb { #define FW_MSG_CODE_EEE_RESULS_ACK 0xda100000 + #define FW_MSG_CODE_RMMOD_ACK 0xdb100000 + #define FW_MSG_CODE_SET_MF_BW_SENT 0xe0000000 #define FW_MSG_CODE_SET_MF_BW_DONE 0xe1000000 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index e06186c305d..8bdc8b97300 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -2261,6 +2261,23 @@ static void bnx2x_set_requested_fc(struct bnx2x *bp) bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_BOTH; } +static void bnx2x_init_dropless_fc(struct bnx2x *bp) +{ + u32 pause_enabled = 0; + + if (!CHIP_IS_E1(bp) && bp->dropless_fc && bp->link_vars.link_up) { + if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX) + pause_enabled = 1; + + REG_WR(bp, BAR_USTRORM_INTMEM + + USTORM_ETH_PAUSE_ENABLED_OFFSET(BP_PORT(bp)), + pause_enabled); + } + + DP(NETIF_MSG_IFUP | NETIF_MSG_LINK, "dropless_fc is %s\n", + pause_enabled ? "enabled" : "disabled"); +} + int bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode) { int rc, cfx_idx = bnx2x_get_link_cfg_idx(bp); @@ -2294,6 +2311,8 @@ int bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode) bnx2x_release_phy_lock(bp); + bnx2x_init_dropless_fc(bp); + bnx2x_calc_fc_adv(bp); if (bp->link_vars.link_up) { @@ -2315,6 +2334,8 @@ void bnx2x_link_set(struct bnx2x *bp) bnx2x_phy_init(&bp->link_params, &bp->link_vars); bnx2x_release_phy_lock(bp); + bnx2x_init_dropless_fc(bp); + bnx2x_calc_fc_adv(bp); } else BNX2X_ERR("Bootcode is missing - can not set link\n"); @@ -2476,7 +2497,7 @@ static void bnx2x_cmng_fns_init(struct bnx2x *bp, u8 read_cfg, u8 cmng_type) input.port_rate = bp->link_vars.line_speed; - if (cmng_type == CMNG_FNS_MINMAX) { + if (cmng_type == CMNG_FNS_MINMAX && input.port_rate) { int vn; /* read mf conf from shmem */ @@ -2533,6 +2554,21 @@ static void storm_memset_cmng(struct bnx2x *bp, } } +/* init cmng mode in HW according to local configuration */ +void bnx2x_set_local_cmng(struct bnx2x *bp) +{ + int cmng_fns = bnx2x_get_cmng_fns_mode(bp); + + if (cmng_fns != CMNG_FNS_NONE) { + bnx2x_cmng_fns_init(bp, false, cmng_fns); + storm_memset_cmng(bp, &bp->cmng, BP_PORT(bp)); + } else { + /* rate shaping and fairness are disabled */ + DP(NETIF_MSG_IFUP, + "single function mode without fairness\n"); + } +} + /* This function is called upon link interrupt */ static void bnx2x_link_attn(struct bnx2x *bp) { @@ -2541,20 +2577,9 @@ static void bnx2x_link_attn(struct bnx2x *bp) bnx2x_link_update(&bp->link_params, &bp->link_vars); - if (bp->link_vars.link_up) { + bnx2x_init_dropless_fc(bp); - /* dropless flow control */ - if (!CHIP_IS_E1(bp) && bp->dropless_fc) { - int port = BP_PORT(bp); - u32 pause_enabled = 0; - - if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX) - pause_enabled = 1; - - REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_ETH_PAUSE_ENABLED_OFFSET(port), - pause_enabled); - } + if (bp->link_vars.link_up) { if (bp->link_vars.mac_type != MAC_TYPE_EMAC) { struct host_port_stats *pstats; @@ -2568,17 +2593,8 @@ static void bnx2x_link_attn(struct bnx2x *bp) bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP); } - if (bp->link_vars.link_up && bp->link_vars.line_speed) { - int cmng_fns = bnx2x_get_cmng_fns_mode(bp); - - if (cmng_fns != CMNG_FNS_NONE) { - bnx2x_cmng_fns_init(bp, false, cmng_fns); - storm_memset_cmng(bp, &bp->cmng, BP_PORT(bp)); - } else - /* rate shaping and fairness are disabled */ - DP(NETIF_MSG_IFUP, - "single function mode without fairness\n"); - } + if (bp->link_vars.link_up && bp->link_vars.line_speed) + bnx2x_set_local_cmng(bp); __bnx2x_link_report(bp); @@ -9639,6 +9655,12 @@ sp_rtnl_not_reset: &bp->sp_rtnl_state)) bnx2x_pf_set_vfs_vlan(bp); + if (test_and_clear_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state)) + bnx2x_dcbx_stop_hw_tx(bp); + + if (test_and_clear_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state)) + bnx2x_dcbx_resume_hw_tx(bp); + /* work which needs rtnl lock not-taken (as it takes the lock itself and * can be called from other contexts as well) */ @@ -10362,6 +10384,10 @@ static void bnx2x_get_common_hwinfo(struct bnx2x *bp) bp->flags |= (val >= REQ_BC_VER_4_DCBX_ADMIN_MSG_NON_PMF) ? BC_SUPPORTS_DCBX_MSG_NON_PMF : 0; + + bp->flags |= (val >= REQ_BC_VER_4_RMMOD_CMD) ? + BC_SUPPORTS_RMMOD_CMD : 0; + boot_mode = SHMEM_RD(bp, dev_info.port_feature_config[BP_PORT(bp)].mba_config) & PORT_FEATURE_MBA_BOOT_AGENT_TYPE_MASK; @@ -11137,6 +11163,9 @@ static bool bnx2x_get_dropless_info(struct bnx2x *bp) int tmp; u32 cfg; + if (IS_VF(bp)) + return 0; + if (IS_MF(bp) && !CHIP_IS_E1x(bp)) { /* Take function: tmp = func */ tmp = BP_ABS_FUNC(bp); @@ -11524,6 +11553,7 @@ static int bnx2x_init_bp(struct bnx2x *bp) mutex_init(&bp->port.phy_mutex); mutex_init(&bp->fw_mb_mutex); spin_lock_init(&bp->stats_lock); + sema_init(&bp->stats_sema, 1); INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task); INIT_DELAYED_WORK(&bp->sp_rtnl_task, bnx2x_sp_rtnl_task); @@ -12817,13 +12847,17 @@ static void __bnx2x_remove(struct pci_dev *pdev, bnx2x_dcbnl_update_applist(bp, true); #endif + if (IS_PF(bp) && + !BP_NOMCP(bp) && + (bp->flags & BC_SUPPORTS_RMMOD_CMD)) + bnx2x_fw_command(bp, DRV_MSG_CODE_RMMOD, 0); + /* Close the interface - either directly or implicitly */ if (remove_netdev) { unregister_netdev(dev); } else { rtnl_lock(); - if (netif_running(dev)) - bnx2x_close(dev); + dev_close(dev); rtnl_unlock(); } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index 95861efb505..ad83f4b4877 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -1747,11 +1747,8 @@ void bnx2x_iov_init_dq(struct bnx2x *bp) void bnx2x_iov_init_dmae(struct bnx2x *bp) { - DP(BNX2X_MSG_IOV, "SRIOV is %s\n", IS_SRIOV(bp) ? "ON" : "OFF"); - if (!IS_SRIOV(bp)) - return; - - REG_WR(bp, DMAE_REG_BACKWARD_COMP_EN, 0); + if (pci_find_ext_capability(bp->pdev, PCI_EXT_CAP_ID_SRIOV)) + REG_WR(bp, DMAE_REG_BACKWARD_COMP_EN, 0); } static int bnx2x_vf_bus(struct bnx2x *bp, int vfid) @@ -3084,8 +3081,9 @@ void bnx2x_disable_sriov(struct bnx2x *bp) pci_disable_sriov(bp->pdev); } -static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx, - struct bnx2x_virtf *vf) +static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx, + struct bnx2x_virtf **vf, + struct pf_vf_bulletin_content **bulletin) { if (bp->state != BNX2X_STATE_OPEN) { BNX2X_ERR("vf ndo called though PF is down\n"); @@ -3103,12 +3101,22 @@ static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx, return -EINVAL; } - if (!vf) { + /* init members */ + *vf = BP_VF(bp, vfidx); + *bulletin = BP_VF_BULLETIN(bp, vfidx); + + if (!*vf) { BNX2X_ERR("vf ndo called but vf was null. vfidx was %d\n", vfidx); return -EINVAL; } + if (!*bulletin) { + BNX2X_ERR("vf ndo called but Bulletin Board struct is null. vfidx was %d\n", + vfidx); + return -EINVAL; + } + return 0; } @@ -3116,17 +3124,19 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, struct ifla_vf_info *ivi) { struct bnx2x *bp = netdev_priv(dev); - struct bnx2x_virtf *vf = BP_VF(bp, vfidx); - struct bnx2x_vlan_mac_obj *mac_obj = &bnx2x_vfq(vf, 0, mac_obj); - struct bnx2x_vlan_mac_obj *vlan_obj = &bnx2x_vfq(vf, 0, vlan_obj); - struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx); + struct bnx2x_virtf *vf = NULL; + struct pf_vf_bulletin_content *bulletin = NULL; + struct bnx2x_vlan_mac_obj *mac_obj; + struct bnx2x_vlan_mac_obj *vlan_obj; int rc; - /* sanity */ - rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf); + /* sanity and init */ + rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); if (rc) return rc; - if (!mac_obj || !vlan_obj || !bulletin) { + mac_obj = &bnx2x_vfq(vf, 0, mac_obj); + vlan_obj = &bnx2x_vfq(vf, 0, vlan_obj); + if (!mac_obj || !vlan_obj) { BNX2X_ERR("VF partially initialized\n"); return -EINVAL; } @@ -3183,11 +3193,11 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac) { struct bnx2x *bp = netdev_priv(dev); int rc, q_logical_state; - struct bnx2x_virtf *vf = BP_VF(bp, vfidx); - struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx); + struct bnx2x_virtf *vf = NULL; + struct pf_vf_bulletin_content *bulletin = NULL; - /* sanity */ - rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf); + /* sanity and init */ + rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); if (rc) return rc; if (!is_valid_ether_addr(mac)) { @@ -3249,11 +3259,11 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos) { struct bnx2x *bp = netdev_priv(dev); int rc, q_logical_state; - struct bnx2x_virtf *vf = BP_VF(bp, vfidx); - struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx); + struct bnx2x_virtf *vf = NULL; + struct pf_vf_bulletin_content *bulletin = NULL; - /* sanity */ - rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf); + /* sanity and init */ + rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); if (rc) return rc; @@ -3463,7 +3473,7 @@ int bnx2x_vf_pci_alloc(struct bnx2x *bp) alloc_mem_err: BNX2X_PCI_FREE(bp->vf2pf_mbox, bp->vf2pf_mbox_mapping, sizeof(struct bnx2x_vf_mbx_msg)); - BNX2X_PCI_FREE(bp->vf2pf_mbox, bp->vf2pf_mbox_mapping, + BNX2X_PCI_FREE(bp->vf2pf_mbox, bp->pf2vf_bulletin_mapping, sizeof(union pf_vf_bulletin)); return -ENOMEM; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index 98366abd02b..d63d1327b05 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -221,7 +221,8 @@ static int bnx2x_stats_comp(struct bnx2x *bp) * Statistics service functions */ -static void bnx2x_stats_pmf_update(struct bnx2x *bp) +/* should be called under stats_sema */ +static void __bnx2x_stats_pmf_update(struct bnx2x *bp) { struct dmae_command *dmae; u32 opcode; @@ -518,7 +519,8 @@ static void bnx2x_func_stats_init(struct bnx2x *bp) *stats_comp = 0; } -static void bnx2x_stats_start(struct bnx2x *bp) +/* should be called under stats_sema */ +static void __bnx2x_stats_start(struct bnx2x *bp) { /* vfs travel through here as part of the statistics FSM, but no action * is required @@ -534,13 +536,34 @@ static void bnx2x_stats_start(struct bnx2x *bp) bnx2x_hw_stats_post(bp); bnx2x_storm_stats_post(bp); + + bp->stats_started = true; +} + +static void bnx2x_stats_start(struct bnx2x *bp) +{ + if (down_timeout(&bp->stats_sema, HZ/10)) + BNX2X_ERR("Unable to acquire stats lock\n"); + __bnx2x_stats_start(bp); + up(&bp->stats_sema); } static void bnx2x_stats_pmf_start(struct bnx2x *bp) { + if (down_timeout(&bp->stats_sema, HZ/10)) + BNX2X_ERR("Unable to acquire stats lock\n"); bnx2x_stats_comp(bp); - bnx2x_stats_pmf_update(bp); - bnx2x_stats_start(bp); + __bnx2x_stats_pmf_update(bp); + __bnx2x_stats_start(bp); + up(&bp->stats_sema); +} + +static void bnx2x_stats_pmf_update(struct bnx2x *bp) +{ + if (down_timeout(&bp->stats_sema, HZ/10)) + BNX2X_ERR("Unable to acquire stats lock\n"); + __bnx2x_stats_pmf_update(bp); + up(&bp->stats_sema); } static void bnx2x_stats_restart(struct bnx2x *bp) @@ -550,8 +573,11 @@ static void bnx2x_stats_restart(struct bnx2x *bp) */ if (IS_VF(bp)) return; + if (down_timeout(&bp->stats_sema, HZ/10)) + BNX2X_ERR("Unable to acquire stats lock\n"); bnx2x_stats_comp(bp); - bnx2x_stats_start(bp); + __bnx2x_stats_start(bp); + up(&bp->stats_sema); } static void bnx2x_bmac_stats_update(struct bnx2x *bp) @@ -888,9 +914,7 @@ static int bnx2x_storm_stats_validate_counters(struct bnx2x *bp) /* Make sure we use the value of the counter * used for sending the last stats ramrod. */ - spin_lock_bh(&bp->stats_lock); cur_stats_counter = bp->stats_counter - 1; - spin_unlock_bh(&bp->stats_lock); /* are storm stats valid? */ if (le16_to_cpu(counters->xstats_counter) != cur_stats_counter) { @@ -1227,12 +1251,18 @@ static void bnx2x_stats_update(struct bnx2x *bp) { u32 *stats_comp = bnx2x_sp(bp, stats_comp); - if (bnx2x_edebug_stats_stopped(bp)) + /* we run update from timer context, so give up + * if somebody is in the middle of transition + */ + if (down_trylock(&bp->stats_sema)) return; + if (bnx2x_edebug_stats_stopped(bp) || !bp->stats_started) + goto out; + if (IS_PF(bp)) { if (*stats_comp != DMAE_COMP_VAL) - return; + goto out; if (bp->port.pmf) bnx2x_hw_stats_update(bp); @@ -1242,7 +1272,7 @@ static void bnx2x_stats_update(struct bnx2x *bp) BNX2X_ERR("storm stats were not updated for 3 times\n"); bnx2x_panic(); } - return; + goto out; } } else { /* vf doesn't collect HW statistics, and doesn't get completions @@ -1256,7 +1286,7 @@ static void bnx2x_stats_update(struct bnx2x *bp) /* vf is done */ if (IS_VF(bp)) - return; + goto out; if (netif_msg_timer(bp)) { struct bnx2x_eth_stats *estats = &bp->eth_stats; @@ -1267,6 +1297,9 @@ static void bnx2x_stats_update(struct bnx2x *bp) bnx2x_hw_stats_post(bp); bnx2x_storm_stats_post(bp); + +out: + up(&bp->stats_sema); } static void bnx2x_port_stats_stop(struct bnx2x *bp) @@ -1332,6 +1365,11 @@ static void bnx2x_stats_stop(struct bnx2x *bp) { int update = 0; + if (down_timeout(&bp->stats_sema, HZ/10)) + BNX2X_ERR("Unable to acquire stats lock\n"); + + bp->stats_started = false; + bnx2x_stats_comp(bp); if (bp->port.pmf) @@ -1348,6 +1386,8 @@ static void bnx2x_stats_stop(struct bnx2x *bp) bnx2x_hw_stats_post(bp); bnx2x_stats_comp(bp); } + + up(&bp->stats_sema); } static void bnx2x_stats_do_nothing(struct bnx2x *bp) @@ -1376,15 +1416,17 @@ static const struct { void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event) { enum bnx2x_stats_state state; + void (*action)(struct bnx2x *bp); if (unlikely(bp->panic)) return; spin_lock_bh(&bp->stats_lock); state = bp->stats_state; bp->stats_state = bnx2x_stats_stm[state][event].next_state; + action = bnx2x_stats_stm[state][event].action; spin_unlock_bh(&bp->stats_lock); - bnx2x_stats_stm[state][event].action(bp); + action(bp); if ((event != STATS_EVENT_UPDATE) || netif_msg_timer(bp)) DP(BNX2X_MSG_STATS, "state %d -> event %d -> state %d\n", diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index ddebc7a5dda..0da2214ef1b 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -17796,8 +17796,10 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev, done: if (state == pci_channel_io_perm_failure) { - tg3_napi_enable(tp); - dev_close(netdev); + if (netdev) { + tg3_napi_enable(tp); + dev_close(netdev); + } err = PCI_ERS_RESULT_DISCONNECT; } else { pci_disable_device(pdev); @@ -17827,7 +17829,8 @@ static pci_ers_result_t tg3_io_slot_reset(struct pci_dev *pdev) rtnl_lock(); if (pci_enable_device(pdev)) { - netdev_err(netdev, "Cannot re-enable PCI device after reset.\n"); + dev_err(&pdev->dev, + "Cannot re-enable PCI device after reset.\n"); goto done; } @@ -17835,7 +17838,7 @@ static pci_ers_result_t tg3_io_slot_reset(struct pci_dev *pdev) pci_restore_state(pdev); pci_save_state(pdev); - if (!netif_running(netdev)) { + if (!netdev || !netif_running(netdev)) { rc = PCI_ERS_RESULT_RECOVERED; goto done; } @@ -17847,7 +17850,7 @@ static pci_ers_result_t tg3_io_slot_reset(struct pci_dev *pdev) rc = PCI_ERS_RESULT_RECOVERED; done: - if (rc != PCI_ERS_RESULT_RECOVERED && netif_running(netdev)) { + if (rc != PCI_ERS_RESULT_RECOVERED && netdev && netif_running(netdev)) { tg3_napi_enable(tp); dev_close(netdev); } diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c index 687ec4a8bb4..9c89dc8fe10 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c @@ -455,11 +455,6 @@ static int alloc_pg_chunk(struct adapter *adapter, struct sge_fl *q, q->pg_chunk.offset = 0; mapping = pci_map_page(adapter->pdev, q->pg_chunk.page, 0, q->alloc_size, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(adapter->pdev, mapping))) { - __free_pages(q->pg_chunk.page, order); - q->pg_chunk.page = NULL; - return -EIO; - } q->pg_chunk.mapping = mapping; } sd->pg_chunk = q->pg_chunk; @@ -954,75 +949,40 @@ static inline unsigned int calc_tx_descs(const struct sk_buff *skb) return flits_to_desc(flits); } - -/* map_skb - map a packet main body and its page fragments - * @pdev: the PCI device - * @skb: the packet - * @addr: placeholder to save the mapped addresses - * - * map the main body of an sk_buff and its page fragments, if any. - */ -static int map_skb(struct pci_dev *pdev, const struct sk_buff *skb, - dma_addr_t *addr) -{ - const skb_frag_t *fp, *end; - const struct skb_shared_info *si; - - *addr = pci_map_single(pdev, skb->data, skb_headlen(skb), - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(pdev, *addr)) - goto out_err; - - si = skb_shinfo(skb); - end = &si->frags[si->nr_frags]; - - for (fp = si->frags; fp < end; fp++) { - *++addr = skb_frag_dma_map(&pdev->dev, fp, 0, skb_frag_size(fp), - DMA_TO_DEVICE); - if (pci_dma_mapping_error(pdev, *addr)) - goto unwind; - } - return 0; - -unwind: - while (fp-- > si->frags) - dma_unmap_page(&pdev->dev, *--addr, skb_frag_size(fp), - DMA_TO_DEVICE); - - pci_unmap_single(pdev, addr[-1], skb_headlen(skb), PCI_DMA_TODEVICE); -out_err: - return -ENOMEM; -} - /** - * write_sgl - populate a scatter/gather list for a packet + * make_sgl - populate a scatter/gather list for a packet * @skb: the packet * @sgp: the SGL to populate * @start: start address of skb main body data to include in the SGL * @len: length of skb main body data to include in the SGL - * @addr: the list of the mapped addresses + * @pdev: the PCI device * - * Copies the scatter/gather list for the buffers that make up a packet + * Generates a scatter/gather list for the buffers that make up a packet * and returns the SGL size in 8-byte words. The caller must size the SGL * appropriately. */ -static inline unsigned int write_sgl(const struct sk_buff *skb, +static inline unsigned int make_sgl(const struct sk_buff *skb, struct sg_ent *sgp, unsigned char *start, - unsigned int len, const dma_addr_t *addr) + unsigned int len, struct pci_dev *pdev) { - unsigned int i, j = 0, k = 0, nfrags; + dma_addr_t mapping; + unsigned int i, j = 0, nfrags; if (len) { + mapping = pci_map_single(pdev, start, len, PCI_DMA_TODEVICE); sgp->len[0] = cpu_to_be32(len); - sgp->addr[j++] = cpu_to_be64(addr[k++]); + sgp->addr[0] = cpu_to_be64(mapping); + j = 1; } nfrags = skb_shinfo(skb)->nr_frags; for (i = 0; i < nfrags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + mapping = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag), + DMA_TO_DEVICE); sgp->len[j] = cpu_to_be32(skb_frag_size(frag)); - sgp->addr[j] = cpu_to_be64(addr[k++]); + sgp->addr[j] = cpu_to_be64(mapping); j ^= 1; if (j == 0) ++sgp; @@ -1178,7 +1138,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, const struct port_info *pi, unsigned int pidx, unsigned int gen, struct sge_txq *q, unsigned int ndesc, - unsigned int compl, const dma_addr_t *addr) + unsigned int compl) { unsigned int flits, sgl_flits, cntrl, tso_info; struct sg_ent *sgp, sgl[MAX_SKB_FRAGS / 2 + 1]; @@ -1236,7 +1196,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, } sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; - sgl_flits = write_sgl(skb, sgp, skb->data, skb_headlen(skb), addr); + sgl_flits = make_sgl(skb, sgp, skb->data, skb_headlen(skb), adap->pdev); write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits, gen, htonl(V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | compl), @@ -1267,7 +1227,6 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) struct netdev_queue *txq; struct sge_qset *qs; struct sge_txq *q; - dma_addr_t addr[MAX_SKB_FRAGS + 1]; /* * The chip min packet length is 9 octets but play safe and reject @@ -1296,11 +1255,6 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - if (unlikely(map_skb(adap->pdev, skb, addr) < 0)) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - q->in_use += ndesc; if (unlikely(credits - ndesc < q->stop_thres)) { t3_stop_tx_queue(txq, qs, q); @@ -1358,7 +1312,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) if (likely(!skb_shared(skb))) skb_orphan(skb); - write_tx_pkt_wr(adap, skb, pi, pidx, gen, q, ndesc, compl, addr); + write_tx_pkt_wr(adap, skb, pi, pidx, gen, q, ndesc, compl); check_ring_tx_db(adap, q); return NETDEV_TX_OK; } @@ -1623,8 +1577,7 @@ static void setup_deferred_unmapping(struct sk_buff *skb, struct pci_dev *pdev, */ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb, struct sge_txq *q, unsigned int pidx, - unsigned int gen, unsigned int ndesc, - const dma_addr_t *addr) + unsigned int gen, unsigned int ndesc) { unsigned int sgl_flits, flits; struct work_request_hdr *from; @@ -1645,9 +1598,9 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb, flits = skb_transport_offset(skb) / 8; sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; - sgl_flits = write_sgl(skb, sgp, skb_transport_header(skb), - skb_tail_pointer(skb) - - skb_transport_header(skb), addr); + sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb), + skb->tail - skb->transport_header, + adap->pdev); if (need_skb_unmap()) { setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits); skb->destructor = deferred_unmap_destructor; @@ -1705,11 +1658,6 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); goto again; } - if (map_skb(adap->pdev, skb, (dma_addr_t *)skb->head)) { - spin_unlock(&q->lock); - return NET_XMIT_SUCCESS; - } - gen = q->gen; q->in_use += ndesc; pidx = q->pidx; @@ -1720,7 +1668,7 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); } spin_unlock(&q->lock); - write_ofld_wr(adap, skb, q, pidx, gen, ndesc, (dma_addr_t *)skb->head); + write_ofld_wr(adap, skb, q, pidx, gen, ndesc); check_ring_tx_db(adap, q); return NET_XMIT_SUCCESS; } @@ -1738,7 +1686,6 @@ static void restart_offloadq(unsigned long data) struct sge_txq *q = &qs->txq[TXQ_OFLD]; const struct port_info *pi = netdev_priv(qs->netdev); struct adapter *adap = pi->adapter; - unsigned int written = 0; spin_lock(&q->lock); again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); @@ -1758,14 +1705,10 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); break; } - if (map_skb(adap->pdev, skb, (dma_addr_t *)skb->head)) - break; - gen = q->gen; q->in_use += ndesc; pidx = q->pidx; q->pidx += ndesc; - written += ndesc; if (q->pidx >= q->size) { q->pidx -= q->size; q->gen ^= 1; @@ -1773,8 +1716,7 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); __skb_unlink(skb, &q->sendq); spin_unlock(&q->lock); - write_ofld_wr(adap, skb, q, pidx, gen, ndesc, - (dma_addr_t *)skb->head); + write_ofld_wr(adap, skb, q, pidx, gen, ndesc); spin_lock(&q->lock); } spin_unlock(&q->lock); @@ -1784,9 +1726,8 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); set_bit(TXQ_LAST_PKT_DB, &q->flags); #endif wmb(); - if (likely(written)) - t3_write_reg(adap, A_SG_KDOORBELL, - F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); + t3_write_reg(adap, A_SG_KDOORBELL, + F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); } /** diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 6e6e0a117ee..8ec5d74ad44 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -3048,6 +3048,9 @@ int be_cmd_get_func_config(struct be_adapter *adapter) adapter->max_event_queues = le16_to_cpu(desc->eq_count); adapter->if_cap_flags = le32_to_cpu(desc->cap_flags); + + /* Clear flags that driver is not interested in */ + adapter->if_cap_flags &= BE_IF_CAP_FLAGS_WANT; } err: mutex_unlock(&adapter->mbox_lock); diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 5228d88c5a0..1b3b9e88641 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -563,6 +563,12 @@ enum be_if_flags { BE_IF_FLAGS_MULTICAST = 0x1000 }; +#define BE_IF_CAP_FLAGS_WANT (BE_IF_FLAGS_RSS | BE_IF_FLAGS_PROMISCUOUS |\ + BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_VLAN_PROMISCUOUS |\ + BE_IF_FLAGS_VLAN | BE_IF_FLAGS_MCAST_PROMISCUOUS |\ + BE_IF_FLAGS_PASS_L3L4_ERRORS | BE_IF_FLAGS_MULTICAST |\ + BE_IF_FLAGS_UNTAGGED) + /* An RX interface is an object with one or more MAC addresses and * filtering capabilities. */ struct be_cmd_req_if_create { diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 181edb52245..4559c35eea1 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2563,8 +2563,8 @@ static int be_close(struct net_device *netdev) /* Wait for all pending tx completions to arrive so that * all tx skbs are freed. */ - be_tx_compl_clean(adapter); netif_tx_disable(netdev); + be_tx_compl_clean(adapter); be_rx_qs_destroy(adapter); diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index c896079728e..ef94a591f9e 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -931,17 +931,20 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u32 base) } /* Allocate and setup a new buffer for receiving */ -static void skge_rx_setup(struct skge_port *skge, struct skge_element *e, - struct sk_buff *skb, unsigned int bufsize) +static int skge_rx_setup(struct skge_port *skge, struct skge_element *e, + struct sk_buff *skb, unsigned int bufsize) { struct skge_rx_desc *rd = e->desc; - u64 map; + dma_addr_t map; map = pci_map_single(skge->hw->pdev, skb->data, bufsize, PCI_DMA_FROMDEVICE); - rd->dma_lo = map; - rd->dma_hi = map >> 32; + if (pci_dma_mapping_error(skge->hw->pdev, map)) + return -1; + + rd->dma_lo = lower_32_bits(map); + rd->dma_hi = upper_32_bits(map); e->skb = skb; rd->csum1_start = ETH_HLEN; rd->csum2_start = ETH_HLEN; @@ -953,6 +956,7 @@ static void skge_rx_setup(struct skge_port *skge, struct skge_element *e, rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | bufsize; dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, bufsize); + return 0; } /* Resume receiving using existing skb, @@ -1014,7 +1018,10 @@ static int skge_rx_fill(struct net_device *dev) return -ENOMEM; skb_reserve(skb, NET_IP_ALIGN); - skge_rx_setup(skge, e, skb, skge->rx_buf_size); + if (skge_rx_setup(skge, e, skb, skge->rx_buf_size) < 0) { + dev_kfree_skb(skb); + return -EIO; + } } while ((e = e->next) != ring->start); ring->to_clean = ring->start; @@ -2544,7 +2551,7 @@ static int skge_up(struct net_device *dev) BUG_ON(skge->dma & 7); - if ((u64)skge->dma >> 32 != ((u64) skge->dma + skge->mem_size) >> 32) { + if (upper_32_bits(skge->dma) != upper_32_bits(skge->dma + skge->mem_size)) { dev_err(&hw->pdev->dev, "pci_alloc_consistent region crosses 4G boundary\n"); err = -EINVAL; goto free_pci_mem; @@ -2729,7 +2736,7 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, struct skge_tx_desc *td; int i; u32 control, len; - u64 map; + dma_addr_t map; if (skb_padto(skb, ETH_ZLEN)) return NETDEV_TX_OK; @@ -2743,11 +2750,14 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, e->skb = skb; len = skb_headlen(skb); map = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(hw->pdev, map)) + goto mapping_error; + dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, len); - td->dma_lo = map; - td->dma_hi = map >> 32; + td->dma_lo = lower_32_bits(map); + td->dma_hi = upper_32_bits(map); if (skb->ip_summed == CHECKSUM_PARTIAL) { const int offset = skb_checksum_start_offset(skb); @@ -2778,14 +2788,16 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, map = skb_frag_dma_map(&hw->pdev->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); + if (dma_mapping_error(&hw->pdev->dev, map)) + goto mapping_unwind; e = e->next; e->skb = skb; tf = e->desc; BUG_ON(tf->control & BMU_OWN); - tf->dma_lo = map; - tf->dma_hi = (u64) map >> 32; + tf->dma_lo = lower_32_bits(map); + tf->dma_hi = upper_32_bits(map); dma_unmap_addr_set(e, mapaddr, map); dma_unmap_len_set(e, maplen, skb_frag_size(frag)); @@ -2815,6 +2827,26 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb, } return NETDEV_TX_OK; + +mapping_unwind: + e = skge->tx_ring.to_use; + pci_unmap_single(hw->pdev, + dma_unmap_addr(e, mapaddr), + dma_unmap_len(e, maplen), + PCI_DMA_TODEVICE); + while (i-- > 0) { + e = e->next; + pci_unmap_page(hw->pdev, + dma_unmap_addr(e, mapaddr), + dma_unmap_len(e, maplen), + PCI_DMA_TODEVICE); + } + +mapping_error: + if (net_ratelimit()) + dev_warn(&hw->pdev->dev, "%s: tx mapping error\n", dev->name); + dev_kfree_skb(skb); + return NETDEV_TX_OK; } @@ -3045,11 +3077,13 @@ static struct sk_buff *skge_rx_get(struct net_device *dev, pci_dma_sync_single_for_cpu(skge->hw->pdev, dma_unmap_addr(e, mapaddr), - len, PCI_DMA_FROMDEVICE); + dma_unmap_len(e, maplen), + PCI_DMA_FROMDEVICE); skb_copy_from_linear_data(e->skb, skb->data, len); pci_dma_sync_single_for_device(skge->hw->pdev, dma_unmap_addr(e, mapaddr), - len, PCI_DMA_FROMDEVICE); + dma_unmap_len(e, maplen), + PCI_DMA_FROMDEVICE); skge_rx_reuse(e, skge->rx_buf_size); } else { struct sk_buff *nskb; @@ -3058,13 +3092,17 @@ static struct sk_buff *skge_rx_get(struct net_device *dev, if (!nskb) goto resubmit; + if (skge_rx_setup(skge, e, nskb, skge->rx_buf_size) < 0) { + dev_kfree_skb(nskb); + goto resubmit; + } + pci_unmap_single(skge->hw->pdev, dma_unmap_addr(e, mapaddr), dma_unmap_len(e, maplen), PCI_DMA_FROMDEVICE); skb = e->skb; prefetch(skb->data); - skge_rx_setup(skge, e, nskb, skge->rx_buf_size); } skb_put(skb, len); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index c571de85d0f..5472cbd3402 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -46,7 +46,7 @@ #include "mlx5_core.h" enum { - CMD_IF_REV = 4, + CMD_IF_REV = 5, }; enum { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index c02cbcfd0fb..443cc4d7b02 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -268,7 +268,7 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq) case MLX5_EVENT_TYPE_PAGE_REQUEST: { u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id); - s16 npages = be16_to_cpu(eqe->data.req_pages.num_pages); + s32 npages = be32_to_cpu(eqe->data.req_pages.num_pages); mlx5_core_dbg(dev, "page request for func 0x%x, napges %d\n", func_id, npages); mlx5_core_req_pages_handler(dev, func_id, npages); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 72a5222447f..f012658b6a9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -113,7 +113,7 @@ int mlx5_cmd_query_hca_cap(struct mlx5_core_dev *dev, caps->log_max_srq = out->hca_cap.log_max_srqs & 0x1f; caps->local_ca_ack_delay = out->hca_cap.local_ca_ack_delay & 0x1f; caps->log_max_mcg = out->hca_cap.log_max_mcg; - caps->max_qp_mcg = be16_to_cpu(out->hca_cap.max_qp_mcg); + caps->max_qp_mcg = be32_to_cpu(out->hca_cap.max_qp_mcg) & 0xffffff; caps->max_ra_res_qp = 1 << (out->hca_cap.log_max_ra_res_qp & 0x3f); caps->max_ra_req_qp = 1 << (out->hca_cap.log_max_ra_req_qp & 0x3f); caps->max_srq_wqes = 1 << out->hca_cap.log_max_srq_sz; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 748f10a155c..3e6670c4a7c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -55,33 +55,9 @@ enum { }; static DEFINE_SPINLOCK(health_lock); - static LIST_HEAD(health_list); static struct work_struct health_work; -static health_handler_t reg_handler; -int mlx5_register_health_report_handler(health_handler_t handler) -{ - spin_lock_irq(&health_lock); - if (reg_handler) { - spin_unlock_irq(&health_lock); - return -EEXIST; - } - reg_handler = handler; - spin_unlock_irq(&health_lock); - - return 0; -} -EXPORT_SYMBOL(mlx5_register_health_report_handler); - -void mlx5_unregister_health_report_handler(void) -{ - spin_lock_irq(&health_lock); - reg_handler = NULL; - spin_unlock_irq(&health_lock); -} -EXPORT_SYMBOL(mlx5_unregister_health_report_handler); - static void health_care(struct work_struct *work) { struct mlx5_core_health *health, *n; @@ -98,11 +74,8 @@ static void health_care(struct work_struct *work) priv = container_of(health, struct mlx5_priv, health); dev = container_of(priv, struct mlx5_core_dev, priv); mlx5_core_warn(dev, "handling bad device here\n"); + /* nothing yet */ spin_lock_irq(&health_lock); - if (reg_handler) - reg_handler(dev->pdev, health->health, - sizeof(health->health)); - list_del_init(&health->list); spin_unlock_irq(&health_lock); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index 4a3e137931a..3a2408d4482 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -43,10 +43,16 @@ enum { MLX5_PAGES_TAKE = 2 }; +enum { + MLX5_BOOT_PAGES = 1, + MLX5_INIT_PAGES = 2, + MLX5_POST_INIT_PAGES = 3 +}; + struct mlx5_pages_req { struct mlx5_core_dev *dev; u32 func_id; - s16 npages; + s32 npages; struct work_struct work; }; @@ -64,27 +70,23 @@ struct mlx5_query_pages_inbox { struct mlx5_query_pages_outbox { struct mlx5_outbox_hdr hdr; - __be16 num_boot_pages; + __be16 rsvd; __be16 func_id; - __be16 init_pages; - __be16 num_pages; + __be32 num_pages; }; struct mlx5_manage_pages_inbox { struct mlx5_inbox_hdr hdr; - __be16 rsvd0; + __be16 rsvd; __be16 func_id; - __be16 rsvd1; - __be16 num_entries; - u8 rsvd2[16]; + __be32 num_entries; __be64 pas[0]; }; struct mlx5_manage_pages_outbox { struct mlx5_outbox_hdr hdr; - u8 rsvd0[2]; - __be16 num_entries; - u8 rsvd1[20]; + __be32 num_entries; + u8 rsvd[4]; __be64 pas[0]; }; @@ -146,7 +148,7 @@ static struct page *remove_page(struct mlx5_core_dev *dev, u64 addr) } static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id, - s16 *pages, s16 *init_pages, u16 *boot_pages) + s32 *npages, int boot) { struct mlx5_query_pages_inbox in; struct mlx5_query_pages_outbox out; @@ -155,6 +157,8 @@ static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id, memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_PAGES); + in.hdr.opmod = boot ? cpu_to_be16(MLX5_BOOT_PAGES) : cpu_to_be16(MLX5_INIT_PAGES); + err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); if (err) return err; @@ -162,15 +166,7 @@ static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id, if (out.hdr.status) return mlx5_cmd_status_to_err(&out.hdr); - if (pages) - *pages = be16_to_cpu(out.num_pages); - - if (init_pages) - *init_pages = be16_to_cpu(out.init_pages); - - if (boot_pages) - *boot_pages = be16_to_cpu(out.num_boot_pages); - + *npages = be32_to_cpu(out.num_pages); *func_id = be16_to_cpu(out.func_id); return err; @@ -224,7 +220,7 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES); in->hdr.opmod = cpu_to_be16(MLX5_PAGES_GIVE); in->func_id = cpu_to_be16(func_id); - in->num_entries = cpu_to_be16(npages); + in->num_entries = cpu_to_be32(npages); err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out)); mlx5_core_dbg(dev, "err %d\n", err); if (err) { @@ -292,7 +288,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages, in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES); in.hdr.opmod = cpu_to_be16(MLX5_PAGES_TAKE); in.func_id = cpu_to_be16(func_id); - in.num_entries = cpu_to_be16(npages); + in.num_entries = cpu_to_be32(npages); mlx5_core_dbg(dev, "npages %d, outlen %d\n", npages, outlen); err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen); if (err) { @@ -306,7 +302,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages, goto out_free; } - num_claimed = be16_to_cpu(out->num_entries); + num_claimed = be32_to_cpu(out->num_entries); if (nclaimed) *nclaimed = num_claimed; @@ -345,7 +341,7 @@ static void pages_work_handler(struct work_struct *work) } void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id, - s16 npages) + s32 npages) { struct mlx5_pages_req *req; @@ -364,20 +360,18 @@ void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id, int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot) { - u16 uninitialized_var(boot_pages); - s16 uninitialized_var(init_pages); u16 uninitialized_var(func_id); + s32 uninitialized_var(npages); int err; - err = mlx5_cmd_query_pages(dev, &func_id, NULL, &init_pages, - &boot_pages); + err = mlx5_cmd_query_pages(dev, &func_id, &npages, boot); if (err) return err; + mlx5_core_dbg(dev, "requested %d %s pages for func_id 0x%x\n", + npages, boot ? "boot" : "init", func_id); - mlx5_core_dbg(dev, "requested %d init pages and %d boot pages for func_id 0x%x\n", - init_pages, boot_pages, func_id); - return give_pages(dev, func_id, boot ? boot_pages : init_pages, 0); + return give_pages(dev, func_id, npages, 0); } static int optimal_reclaimed_pages(void) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 92da9980a0a..9d4bb7f8390 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -3266,6 +3266,11 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev) u8 val; int ret, max_sds_rings = adapter->max_sds_rings; + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { + netdev_info(netdev, "Device is resetting\n"); + return -EBUSY; + } + if (qlcnic_get_diag_lock(adapter)) { netdev_info(netdev, "Device in diagnostics mode\n"); return -EBUSY; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c index 9f4b8d5f086..345d987aede 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c @@ -629,7 +629,8 @@ int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter) return -EIO; } - qlcnic_set_drv_version(adapter); + if (adapter->portnum == 0) + qlcnic_set_drv_version(adapter); qlcnic_83xx_idc_attach_driver(adapter); return 0; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index ee013fcc332..bc05d016c85 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -2165,7 +2165,8 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto err_out_disable_mbx_intr; - qlcnic_set_drv_version(adapter); + if (adapter->portnum == 0) + qlcnic_set_drv_version(adapter); pci_set_drvdata(pdev, adapter); @@ -3085,7 +3086,8 @@ done: adapter->fw_fail_cnt = 0; adapter->flags &= ~QLCNIC_FW_HANG; clear_bit(__QLCNIC_RESETTING, &adapter->state); - qlcnic_set_drv_version(adapter); + if (adapter->portnum == 0) + qlcnic_set_drv_version(adapter); if (!qlcnic_clr_drv_state(adapter)) qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 10ed82b3bac..660c3f5b223 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -170,9 +170,9 @@ static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter, if (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_2_BEACON) { err = qlcnic_get_beacon_state(adapter, &h_beacon_state); - if (!err) { - dev_info(&adapter->pdev->dev, - "Failed to get current beacon state\n"); + if (err) { + netdev_err(adapter->netdev, + "Failed to get current beacon state\n"); } else { if (h_beacon_state == QLCNIC_BEACON_DISABLE) ahw->beacon_state = 0; diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index 6f35f8404d6..d2e591955bd 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -524,6 +524,7 @@ rx_status_loop: PCI_DMA_FROMDEVICE); if (dma_mapping_error(&cp->pdev->dev, new_mapping)) { dev->stats.rx_dropped++; + kfree_skb(new_skb); goto rx_next; } diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index b5eb4195fc9..85e5c97191d 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -7088,7 +7088,7 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) RTL_W8(Cfg9346, Cfg9346_Unlock); RTL_W8(Config1, RTL_R8(Config1) | PMEnable); - RTL_W8(Config5, RTL_R8(Config5) & PMEStatus); + RTL_W8(Config5, RTL_R8(Config5) & (BWF | MWF | UWF | LanWake | PMEStatus)); if ((RTL_R8(Config3) & (LinkUp | MagicPacket)) != 0) tp->features |= RTL_FEATURE_WOL; if ((RTL_R8(Config5) & (UWF | BWF | MWF)) != 0) diff --git a/drivers/net/ethernet/sfc/filter.c b/drivers/net/ethernet/sfc/filter.c index 2a469b27a50..30d744235d2 100644 --- a/drivers/net/ethernet/sfc/filter.c +++ b/drivers/net/ethernet/sfc/filter.c @@ -675,7 +675,7 @@ s32 efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec, BUILD_BUG_ON(EFX_FILTER_INDEX_UC_DEF != 0); BUILD_BUG_ON(EFX_FILTER_INDEX_MC_DEF != EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF); - rep_index = spec->type - EFX_FILTER_INDEX_UC_DEF; + rep_index = spec->type - EFX_FILTER_UC_DEF; ins_index = rep_index; spin_lock_bh(&state->lock); diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index c9d942a5c33..1ef9d8a555a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -33,10 +33,15 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) struct stmmac_priv *priv = (struct stmmac_priv *)p; unsigned int txsize = priv->dma_tx_size; unsigned int entry = priv->cur_tx % txsize; - struct dma_desc *desc = priv->dma_tx + entry; + struct dma_desc *desc; unsigned int nopaged_len = skb_headlen(skb); unsigned int bmax, len; + if (priv->extend_desc) + desc = (struct dma_desc *)(priv->dma_etx + entry); + else + desc = priv->dma_tx + entry; + if (priv->plat->enh_desc) bmax = BUF_SIZE_8KiB; else @@ -54,7 +59,11 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) STMMAC_RING_MODE); wmb(); entry = (++priv->cur_tx) % txsize; - desc = priv->dma_tx + entry; + + if (priv->extend_desc) + desc = (struct dma_desc *)(priv->dma_etx + entry); + else + desc = priv->dma_tx + entry; desc->des2 = dma_map_single(priv->device, skb->data + bmax, len, DMA_TO_DEVICE); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f2ccb36e868..0a9bb9d30c3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -939,15 +939,20 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, skb = __netdev_alloc_skb(priv->dev, priv->dma_buf_sz + NET_IP_ALIGN, GFP_KERNEL); - if (unlikely(skb == NULL)) { + if (!skb) { pr_err("%s: Rx init fails; skb is NULL\n", __func__); - return 1; + return -ENOMEM; } skb_reserve(skb, NET_IP_ALIGN); priv->rx_skbuff[i] = skb; priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, priv->dma_buf_sz, DMA_FROM_DEVICE); + if (dma_mapping_error(priv->device, priv->rx_skbuff_dma[i])) { + pr_err("%s: DMA mapping error\n", __func__); + dev_kfree_skb_any(skb); + return -EINVAL; + } p->des2 = priv->rx_skbuff_dma[i]; @@ -958,6 +963,16 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, return 0; } +static void stmmac_free_rx_buffers(struct stmmac_priv *priv, int i) +{ + if (priv->rx_skbuff[i]) { + dma_unmap_single(priv->device, priv->rx_skbuff_dma[i], + priv->dma_buf_sz, DMA_FROM_DEVICE); + dev_kfree_skb_any(priv->rx_skbuff[i]); + } + priv->rx_skbuff[i] = NULL; +} + /** * init_dma_desc_rings - init the RX/TX descriptor rings * @dev: net device structure @@ -965,13 +980,14 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, * and allocates the socket buffers. It suppors the chained and ring * modes. */ -static void init_dma_desc_rings(struct net_device *dev) +static int init_dma_desc_rings(struct net_device *dev) { int i; struct stmmac_priv *priv = netdev_priv(dev); unsigned int txsize = priv->dma_tx_size; unsigned int rxsize = priv->dma_rx_size; unsigned int bfsize = 0; + int ret = -ENOMEM; /* Set the max buffer size according to the DESC mode * and the MTU. Note that RING mode allows 16KiB bsize. @@ -992,34 +1008,60 @@ static void init_dma_desc_rings(struct net_device *dev) dma_extended_desc), &priv->dma_rx_phy, GFP_KERNEL); + if (!priv->dma_erx) + goto err_dma; + priv->dma_etx = dma_alloc_coherent(priv->device, txsize * sizeof(struct dma_extended_desc), &priv->dma_tx_phy, GFP_KERNEL); - if ((!priv->dma_erx) || (!priv->dma_etx)) - return; + if (!priv->dma_etx) { + dma_free_coherent(priv->device, priv->dma_rx_size * + sizeof(struct dma_extended_desc), + priv->dma_erx, priv->dma_rx_phy); + goto err_dma; + } } else { priv->dma_rx = dma_alloc_coherent(priv->device, rxsize * sizeof(struct dma_desc), &priv->dma_rx_phy, GFP_KERNEL); + if (!priv->dma_rx) + goto err_dma; + priv->dma_tx = dma_alloc_coherent(priv->device, txsize * sizeof(struct dma_desc), &priv->dma_tx_phy, GFP_KERNEL); - if ((!priv->dma_rx) || (!priv->dma_tx)) - return; + if (!priv->dma_tx) { + dma_free_coherent(priv->device, priv->dma_rx_size * + sizeof(struct dma_desc), + priv->dma_rx, priv->dma_rx_phy); + goto err_dma; + } } priv->rx_skbuff_dma = kmalloc_array(rxsize, sizeof(dma_addr_t), GFP_KERNEL); + if (!priv->rx_skbuff_dma) + goto err_rx_skbuff_dma; + priv->rx_skbuff = kmalloc_array(rxsize, sizeof(struct sk_buff *), GFP_KERNEL); + if (!priv->rx_skbuff) + goto err_rx_skbuff; + priv->tx_skbuff_dma = kmalloc_array(txsize, sizeof(dma_addr_t), GFP_KERNEL); + if (!priv->tx_skbuff_dma) + goto err_tx_skbuff_dma; + priv->tx_skbuff = kmalloc_array(txsize, sizeof(struct sk_buff *), GFP_KERNEL); + if (!priv->tx_skbuff) + goto err_tx_skbuff; + if (netif_msg_probe(priv)) { pr_debug("(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", __func__, (u32) priv->dma_rx_phy, (u32) priv->dma_tx_phy); @@ -1034,8 +1076,9 @@ static void init_dma_desc_rings(struct net_device *dev) else p = priv->dma_rx + i; - if (stmmac_init_rx_buffers(priv, p, i)) - break; + ret = stmmac_init_rx_buffers(priv, p, i); + if (ret) + goto err_init_rx_buffers; if (netif_msg_probe(priv)) pr_debug("[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i], @@ -1081,20 +1124,44 @@ static void init_dma_desc_rings(struct net_device *dev) if (netif_msg_hw(priv)) stmmac_display_rings(priv); + + return 0; +err_init_rx_buffers: + while (--i >= 0) + stmmac_free_rx_buffers(priv, i); + kfree(priv->tx_skbuff); +err_tx_skbuff: + kfree(priv->tx_skbuff_dma); +err_tx_skbuff_dma: + kfree(priv->rx_skbuff); +err_rx_skbuff: + kfree(priv->rx_skbuff_dma); +err_rx_skbuff_dma: + if (priv->extend_desc) { + dma_free_coherent(priv->device, priv->dma_tx_size * + sizeof(struct dma_extended_desc), + priv->dma_etx, priv->dma_tx_phy); + dma_free_coherent(priv->device, priv->dma_rx_size * + sizeof(struct dma_extended_desc), + priv->dma_erx, priv->dma_rx_phy); + } else { + dma_free_coherent(priv->device, + priv->dma_tx_size * sizeof(struct dma_desc), + priv->dma_tx, priv->dma_tx_phy); + dma_free_coherent(priv->device, + priv->dma_rx_size * sizeof(struct dma_desc), + priv->dma_rx, priv->dma_rx_phy); + } +err_dma: + return ret; } static void dma_free_rx_skbufs(struct stmmac_priv *priv) { int i; - for (i = 0; i < priv->dma_rx_size; i++) { - if (priv->rx_skbuff[i]) { - dma_unmap_single(priv->device, priv->rx_skbuff_dma[i], - priv->dma_buf_sz, DMA_FROM_DEVICE); - dev_kfree_skb_any(priv->rx_skbuff[i]); - } - priv->rx_skbuff[i] = NULL; - } + for (i = 0; i < priv->dma_rx_size; i++) + stmmac_free_rx_buffers(priv, i); } static void dma_free_tx_skbufs(struct stmmac_priv *priv) @@ -1560,12 +1627,17 @@ static int stmmac_open(struct net_device *dev) priv->dma_tx_size = STMMAC_ALIGN(dma_txsize); priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize); priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); - init_dma_desc_rings(dev); + + ret = init_dma_desc_rings(dev); + if (ret < 0) { + pr_err("%s: DMA descriptors initialization failed\n", __func__); + goto dma_desc_error; + } /* DMA initialization and SW reset */ ret = stmmac_init_dma_engine(priv); if (ret < 0) { - pr_err("%s: DMA initialization failed\n", __func__); + pr_err("%s: DMA engine initialization failed\n", __func__); goto init_error; } @@ -1672,6 +1744,7 @@ wolirq_error: init_error: free_dma_desc_resources(priv); +dma_desc_error: if (priv->phydev) phy_disconnect(priv->phydev); phy_error: diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index 1d6dc41f755..d01cacf8a7c 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -2100,7 +2100,7 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); } - netif_rx(skb); + netif_receive_skb(skb); stats->rx_bytes += pkt_len; stats->rx_packets++; @@ -2884,6 +2884,7 @@ out: return ret; err_iounmap: + netif_napi_del(&vptr->napi); iounmap(regs); err_free_dev: free_netdev(netdev); @@ -2904,6 +2905,7 @@ static int velocity_remove(struct device *dev) struct velocity_info *vptr = netdev_priv(netdev); unregister_netdev(netdev); + netif_napi_del(&vptr->napi); iounmap(vptr->mac_regs); free_netdev(netdev); velocity_nics--; diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index 51f2bc37610..2dcc60fb37f 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -210,8 +210,7 @@ static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id) pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0)); pci_write_config_byte(pcidev,0x5a,0xc0); WriteLPCReg(0x28, 0x70 ); - if (via_ircc_open(pcidev, &info, 0x3076) == 0) - rc=0; + rc = via_ircc_open(pcidev, &info, 0x3076); } else rc = -ENODEV; //IR not turn on } else { //Not VT1211 @@ -249,8 +248,7 @@ static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id) info.irq=FirIRQ; info.dma=FirDRQ1; info.dma2=FirDRQ0; - if (via_ircc_open(pcidev, &info, 0x3096) == 0) - rc=0; + rc = via_ircc_open(pcidev, &info, 0x3096); } else rc = -ENODEV; //IR not turn on !!!!! }//Not VT1211 diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index d0f9c2fd1d4..16b43bf544b 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -739,6 +739,10 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) return -EADDRNOTAVAIL; } + if (data && data[IFLA_MACVLAN_FLAGS] && + nla_get_u16(data[IFLA_MACVLAN_FLAGS]) & ~MACVLAN_FLAG_NOPROMISC) + return -EINVAL; + if (data && data[IFLA_MACVLAN_MODE]) { switch (nla_get_u32(data[IFLA_MACVLAN_MODE])) { case MACVLAN_MODE_PRIVATE: diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index a98fb0ed6ae..ea53abb2098 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -68,6 +68,8 @@ static const struct proto_ops macvtap_socket_ops; #define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \ NETIF_F_TSO6 | NETIF_F_UFO) #define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO) +#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG) + /* * RCU usage: * The macvtap_queue and the macvlan_dev are loosely coupled, the @@ -278,7 +280,8 @@ static int macvtap_forward(struct net_device *dev, struct sk_buff *skb) { struct macvlan_dev *vlan = netdev_priv(dev); struct macvtap_queue *q = macvtap_get_queue(dev, skb); - netdev_features_t features; + netdev_features_t features = TAP_FEATURES; + if (!q) goto drop; @@ -287,9 +290,11 @@ static int macvtap_forward(struct net_device *dev, struct sk_buff *skb) skb->dev = dev; /* Apply the forward feature mask so that we perform segmentation - * according to users wishes. + * according to users wishes. This only works if VNET_HDR is + * enabled. */ - features = netif_skb_features(skb) & vlan->tap_features; + if (q->flags & IFF_VNET_HDR) + features |= vlan->tap_features; if (netif_needs_gso(skb, features)) { struct sk_buff *segs = __skb_gso_segment(skb, features, false); @@ -818,10 +823,13 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; } - if (vlan) + if (vlan) { + local_bh_disable(); macvlan_start_xmit(skb, vlan->dev); - else + local_bh_enable(); + } else { kfree_skb(skb); + } rcu_read_unlock(); return total_len; @@ -912,8 +920,11 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, done: rcu_read_lock(); vlan = rcu_dereference(q->vlan); - if (vlan) + if (vlan) { + preempt_disable(); macvlan_count_rx(vlan, copied - vnet_hdr_len, ret == 0, 0); + preempt_enable(); + } rcu_read_unlock(); return ret ? ret : copied; @@ -1058,8 +1069,7 @@ static int set_offload(struct macvtap_queue *q, unsigned long arg) /* tap_features are the same as features on tun/tap and * reflect user expectations. */ - vlan->tap_features = vlan->dev->features & - (feature_mask | ~TUN_OFFLOADS); + vlan->tap_features = feature_mask; vlan->set_features = features; netdev_update_features(vlan->dev); @@ -1155,10 +1165,6 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, TUN_F_TSO_ECN | TUN_F_UFO)) return -EINVAL; - /* TODO: only accept frames with the features that - got enabled for forwarded frames */ - if (!(q->flags & IFF_VNET_HDR)) - return -EINVAL; rtnl_lock(); ret = set_offload(q, arg); rtnl_unlock(); diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 8e7af835434..138de837977 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -23,7 +23,7 @@ #define RTL821x_INER_INIT 0x6400 #define RTL821x_INSR 0x13 -#define RTL8211E_INER_LINK_STAT 0x10 +#define RTL8211E_INER_LINK_STATUS 0x400 MODULE_DESCRIPTION("Realtek PHY driver"); MODULE_AUTHOR("Johnson Leung"); @@ -57,7 +57,7 @@ static int rtl8211e_config_intr(struct phy_device *phydev) if (phydev->interrupts == PHY_INTERRUPT_ENABLED) err = phy_write(phydev, RTL821x_INER, - RTL8211E_INER_LINK_STAT); + RTL8211E_INER_LINK_STATUS); else err = phy_write(phydev, RTL821x_INER, 0); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index db690a37226..71af122edf2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1074,8 +1074,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, u32 rxhash; if (!(tun->flags & TUN_NO_PI)) { - if ((len -= sizeof(pi)) > total_len) + if (len < sizeof(pi)) return -EINVAL; + len -= sizeof(pi); if (memcpy_fromiovecend((void *)&pi, iv, 0, sizeof(pi))) return -EFAULT; @@ -1083,8 +1084,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (tun->flags & TUN_VNET_HDR) { - if ((len -= tun->vnet_hdr_sz) > total_len) + if (len < tun->vnet_hdr_sz) return -EINVAL; + len -= tun->vnet_hdr_sz; if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso))) return -EFAULT; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index cba1d46e672..86292e6aaf4 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2816,13 +2816,16 @@ exit: static int hso_get_config_data(struct usb_interface *interface) { struct usb_device *usbdev = interface_to_usbdev(interface); - u8 config_data[17]; + u8 *config_data = kmalloc(17, GFP_KERNEL); u32 if_num = interface->altsetting->desc.bInterfaceNumber; s32 result; + if (!config_data) + return -ENOMEM; if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x86, 0xC0, 0, 0, config_data, 17, USB_CTRL_SET_TIMEOUT) != 0x11) { + kfree(config_data); return -EIO; } @@ -2873,6 +2876,7 @@ static int hso_get_config_data(struct usb_interface *interface) if (config_data[16] & 0x1) result |= HSO_INFO_CRC_BUG; + kfree(config_data); return result; } @@ -2886,6 +2890,11 @@ static int hso_probe(struct usb_interface *interface, struct hso_shared_int *shared_int; struct hso_device *tmp_dev = NULL; + if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { + dev_err(&interface->dev, "Not our interface\n"); + return -ENODEV; + } + if_num = interface->altsetting->desc.bInterfaceNumber; /* Get the interface/port specification from either driver_info or from @@ -2895,10 +2904,6 @@ static int hso_probe(struct usb_interface *interface, else port_spec = hso_get_config_data(interface); - if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { - dev_err(&interface->dev, "Not our interface\n"); - return -ENODEV; - } /* Check if we need to switch to alt interfaces prior to port * configuration */ if (interface->num_altsetting > 1) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index f4c6db419dd..767f7af3bd4 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1386,7 +1386,7 @@ static int vxlan_open(struct net_device *dev) return -ENOTCONN; if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip)) && - ! vxlan_group_used(vn, vxlan->default_dst.remote_ip)) { + vxlan_group_used(vn, vxlan->default_dst.remote_ip)) { vxlan_sock_hold(vs); dev_hold(dev); queue_work(vxlan_wq, &vxlan->igmp_join); @@ -1793,8 +1793,6 @@ static void vxlan_dellink(struct net_device *dev, struct list_head *head) struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); struct vxlan_dev *vxlan = netdev_priv(dev); - flush_workqueue(vxlan_wq); - spin_lock(&vn->sock_lock); hlist_del_rcu(&vxlan->hlist); spin_unlock(&vn->sock_lock); diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index 7365674366f..010b252be58 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -1406,11 +1406,8 @@ static void cw1200_do_unjoin(struct cw1200_common *priv) if (!priv->join_status) goto done; - if (priv->join_status > CW1200_JOIN_STATUS_IBSS) { - wiphy_err(priv->hw->wiphy, "Unexpected: join status: %d\n", - priv->join_status); - BUG_ON(1); - } + if (priv->join_status == CW1200_JOIN_STATUS_AP) + goto done; cancel_work_sync(&priv->update_filtering_work); cancel_work_sync(&priv->set_beacon_wakeup_period_work); diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c index ac074731335..e5090309824 100644 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -523,9 +523,9 @@ static int prism2_ioctl_giwaplist(struct net_device *dev, data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1); - memcpy(extra, &addr, sizeof(struct sockaddr) * data->length); + memcpy(extra, addr, sizeof(struct sockaddr) * data->length); data->flags = 1; /* has quality information */ - memcpy(extra + sizeof(struct sockaddr) * data->length, &qual, + memcpy(extra + sizeof(struct sockaddr) * data->length, qual, sizeof(struct iw_quality) * data->length); kfree(addr); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index b9b2bb51e60..f2ed62e3734 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -4460,12 +4460,12 @@ il4965_irq_tasklet(struct il_priv *il) * is killed. Hence update the killswitch state here. The * rfkill handler will care about restarting if needed. */ - if (!test_bit(S_ALIVE, &il->status)) { - if (hw_rf_kill) - set_bit(S_RFKILL, &il->status); - else - clear_bit(S_RFKILL, &il->status); + if (hw_rf_kill) { + set_bit(S_RFKILL, &il->status); + } else { + clear_bit(S_RFKILL, &il->status); wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill); + il_force_reset(il, true); } handled |= CSR_INT_BIT_RF_KILL; @@ -5334,6 +5334,9 @@ il4965_alive_start(struct il_priv *il) il->active_rate = RATES_MASK; + il_power_update_mode(il, true); + D_INFO("Updated power mode\n"); + if (il_is_associated(il)) { struct il_rxon_cmd *active_rxon = (struct il_rxon_cmd *)&il->active; @@ -5364,9 +5367,6 @@ il4965_alive_start(struct il_priv *il) D_INFO("ALIVE processing complete.\n"); wake_up(&il->wait_command_queue); - il_power_update_mode(il, true); - D_INFO("Updated power mode\n"); - return; restart: diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index 3195aad440d..b03e22ef546 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -4660,6 +4660,7 @@ il_force_reset(struct il_priv *il, bool external) return 0; } +EXPORT_SYMBOL(il_force_reset); int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 822f1a00efb..319387263e1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1068,7 +1068,10 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; - if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + return; + + if (ctx->vif) ieee80211_chswitch_done(ctx->vif, is_success); } diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index a70c7b9d9ba..ff8cc75c189 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -97,8 +97,6 @@ #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) -#define APMG_RTC_INT_STT_RFKILL (0x10000000) - /* Device system time */ #define DEVICE_SYSTEM_TIME_REG 0xA0206C diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index ad9bbca9921..7fd6fbfbc1b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -138,6 +138,20 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) schedule_work(&mvm->roc_done_wk); } +static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + const char *errmsg) +{ + if (vif->type != NL80211_IFTYPE_STATION) + return false; + if (vif->bss_conf.assoc && vif->bss_conf.dtim_period) + return false; + if (errmsg) + IWL_ERR(mvm, "%s\n", errmsg); + ieee80211_connection_loss(vif); + return true; +} + /* * Handles a FW notification for an event that is known to the driver. * @@ -163,8 +177,13 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, * P2P Device discoveribility, while there are other higher priority * events in the system). */ - WARN_ONCE(!le32_to_cpu(notif->status), - "Failed to schedule time event\n"); + if (WARN_ONCE(!le32_to_cpu(notif->status), + "Failed to schedule time event\n")) { + if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) { + iwl_mvm_te_clear_data(mvm, te_data); + return; + } + } if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_END) { IWL_DEBUG_TE(mvm, @@ -180,14 +199,8 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, * By now, we should have finished association * and know the dtim period. */ - if (te_data->vif->type == NL80211_IFTYPE_STATION && - (!te_data->vif->bss_conf.assoc || - !te_data->vif->bss_conf.dtim_period)) { - IWL_ERR(mvm, - "No assocation and the time event is over already...\n"); - ieee80211_connection_loss(te_data->vif); - } - + iwl_mvm_te_check_disconnect(mvm, te_data->vif, + "No assocation and the time event is over already..."); iwl_mvm_te_clear_data(mvm, te_data); } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) { te_data->running = true; diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index f600e68a410..fd848cd1583 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -888,14 +888,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); if (hw_rfkill) { - /* - * Clear the interrupt in APMG if the NIC is going down. - * Note that when the NIC exits RFkill (else branch), we - * can't access prph and the NIC will be reset in - * start_hw anyway. - */ - iwl_write_prph(trans, APMG_RTC_INT_STT_REG, - APMG_RTC_INT_STT_RFKILL); set_bit(STATUS_RFKILL, &trans_pcie->status); if (test_and_clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 96cfcdd3907..390e2f058af 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1502,16 +1502,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, spin_lock_init(&trans_pcie->reg_lock); init_waitqueue_head(&trans_pcie->ucode_write_waitq); - /* W/A - seems to solve weird behavior. We need to remove this if we - * don't want to stay in L1 all the time. This wastes a lot of power */ - pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM); - if (pci_enable_device(pdev)) { err = -ENODEV; goto out_no_pci; } + /* W/A - seems to solve weird behavior. We need to remove this if we + * don't want to stay in L1 all the time. This wastes a lot of power */ + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); + pci_set_master(pdev); err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index 4941f201d6c..b8ba1f925e7 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -98,10 +98,12 @@ static int zd1201_fw_upload(struct usb_device *dev, int apfw) goto exit; err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4, - USB_DIR_IN | 0x40, 0,0, &ret, sizeof(ret), ZD1201_FW_TIMEOUT); + USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT); if (err < 0) goto exit; + memcpy(&ret, buf, sizeof(ret)); + if (ret & 0x80) { err = -EIO; goto exit; diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 6bb7cf2de55..b10ba00cc3e 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -392,6 +392,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob, mem = (unsigned long) dt_alloc(size + 4, __alignof__(struct device_node)); + memset((void *)mem, 0, size); + ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef); pr_debug(" unflattening %lx...\n", mem); diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c index c47fd1e5450..94716c77980 100644 --- a/drivers/pinctrl/pinctrl-sunxi.c +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -278,6 +278,7 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sunxi_pinctrl_group *g = &pctl->groups[group]; + unsigned long flags; u32 val, mask; u16 strength; u8 dlevel; @@ -295,22 +296,35 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, * 3: 40mA */ dlevel = strength / 10 - 1; + + spin_lock_irqsave(&pctl->lock, flags); + val = readl(pctl->membase + sunxi_dlevel_reg(g->pin)); mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin); writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin), pctl->membase + sunxi_dlevel_reg(g->pin)); + + spin_unlock_irqrestore(&pctl->lock, flags); break; case PIN_CONFIG_BIAS_PULL_UP: + spin_lock_irqsave(&pctl->lock, flags); + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin), pctl->membase + sunxi_pull_reg(g->pin)); + + spin_unlock_irqrestore(&pctl->lock, flags); break; case PIN_CONFIG_BIAS_PULL_DOWN: + spin_lock_irqsave(&pctl->lock, flags); + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin), pctl->membase + sunxi_pull_reg(g->pin)); + + spin_unlock_irqrestore(&pctl->lock, flags); break; default: break; @@ -360,11 +374,17 @@ static void sunxi_pmx_set(struct pinctrl_dev *pctldev, u8 config) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + unsigned long flags; + u32 val, mask; + + spin_lock_irqsave(&pctl->lock, flags); - u32 val = readl(pctl->membase + sunxi_mux_reg(pin)); - u32 mask = MUX_PINS_MASK << sunxi_mux_offset(pin); + val = readl(pctl->membase + sunxi_mux_reg(pin)); + mask = MUX_PINS_MASK << sunxi_mux_offset(pin); writel((val & ~mask) | config << sunxi_mux_offset(pin), pctl->membase + sunxi_mux_reg(pin)); + + spin_unlock_irqrestore(&pctl->lock, flags); } static int sunxi_pmx_enable(struct pinctrl_dev *pctldev, @@ -464,8 +484,21 @@ static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip, struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev); u32 reg = sunxi_data_reg(offset); u8 index = sunxi_data_offset(offset); + unsigned long flags; + u32 regval; + + spin_lock_irqsave(&pctl->lock, flags); + + regval = readl(pctl->membase + reg); - writel((value & DATA_PINS_MASK) << index, pctl->membase + reg); + if (value) + regval |= BIT(index); + else + regval &= ~(BIT(index)); + + writel(regval, pctl->membase + reg); + + spin_unlock_irqrestore(&pctl->lock, flags); } static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc, @@ -526,6 +559,8 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); u32 reg = sunxi_irq_cfg_reg(d->hwirq); u8 index = sunxi_irq_cfg_offset(d->hwirq); + unsigned long flags; + u32 regval; u8 mode; switch (type) { @@ -548,7 +583,13 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, return -EINVAL; } - writel((mode & IRQ_CFG_IRQ_MASK) << index, pctl->membase + reg); + spin_lock_irqsave(&pctl->lock, flags); + + regval = readl(pctl->membase + reg); + regval &= ~IRQ_CFG_IRQ_MASK; + writel(regval | (mode << index), pctl->membase + reg); + + spin_unlock_irqrestore(&pctl->lock, flags); return 0; } @@ -560,14 +601,19 @@ static void sunxi_pinctrl_irq_mask_ack(struct irq_data *d) u8 ctrl_idx = sunxi_irq_ctrl_offset(d->hwirq); u32 status_reg = sunxi_irq_status_reg(d->hwirq); u8 status_idx = sunxi_irq_status_offset(d->hwirq); + unsigned long flags; u32 val; + spin_lock_irqsave(&pctl->lock, flags); + /* Mask the IRQ */ val = readl(pctl->membase + ctrl_reg); writel(val & ~(1 << ctrl_idx), pctl->membase + ctrl_reg); /* Clear the IRQ */ writel(1 << status_idx, pctl->membase + status_reg); + + spin_unlock_irqrestore(&pctl->lock, flags); } static void sunxi_pinctrl_irq_mask(struct irq_data *d) @@ -575,11 +621,16 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d) struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); u32 reg = sunxi_irq_ctrl_reg(d->hwirq); u8 idx = sunxi_irq_ctrl_offset(d->hwirq); + unsigned long flags; u32 val; + spin_lock_irqsave(&pctl->lock, flags); + /* Mask the IRQ */ val = readl(pctl->membase + reg); writel(val & ~(1 << idx), pctl->membase + reg); + + spin_unlock_irqrestore(&pctl->lock, flags); } static void sunxi_pinctrl_irq_unmask(struct irq_data *d) @@ -588,6 +639,7 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d) struct sunxi_desc_function *func; u32 reg = sunxi_irq_ctrl_reg(d->hwirq); u8 idx = sunxi_irq_ctrl_offset(d->hwirq); + unsigned long flags; u32 val; func = sunxi_pinctrl_desc_find_function_by_pin(pctl, @@ -597,9 +649,13 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d) /* Change muxing to INT mode */ sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval); + spin_lock_irqsave(&pctl->lock, flags); + /* Unmask the IRQ */ val = readl(pctl->membase + reg); writel(val | (1 << idx), pctl->membase + reg); + + spin_unlock_irqrestore(&pctl->lock, flags); } static struct irq_chip sunxi_pinctrl_irq_chip = { @@ -752,6 +808,8 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, pctl); + spin_lock_init(&pctl->lock); + pctl->membase = of_iomap(node, 0); if (!pctl->membase) return -ENOMEM; diff --git a/drivers/pinctrl/pinctrl-sunxi.h b/drivers/pinctrl/pinctrl-sunxi.h index d68047d8f69..01c494f8a14 100644 --- a/drivers/pinctrl/pinctrl-sunxi.h +++ b/drivers/pinctrl/pinctrl-sunxi.h @@ -14,6 +14,7 @@ #define __PINCTRL_SUNXI_H #include <linux/kernel.h> +#include <linux/spinlock.h> #define PA_BASE 0 #define PB_BASE 32 @@ -407,6 +408,7 @@ struct sunxi_pinctrl { unsigned ngroups; int irq; int irq_array[SUNXI_IRQ_NUMBER]; + spinlock_t lock; struct pinctrl_dev *pctl_dev; }; diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index 0f9f8596b30..f9119525f55 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -330,7 +330,7 @@ static int __init olpc_ec_init_module(void) return platform_driver_register(&olpc_ec_plat_driver); } -module_init(olpc_ec_init_module); +arch_initcall(olpc_ec_init_module); MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>"); MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 97bb05edcb5..d6970f47ae7 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -53,7 +53,6 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); #define HPWMI_ALS_QUERY 0x3 #define HPWMI_HARDWARE_QUERY 0x4 #define HPWMI_WIRELESS_QUERY 0x5 -#define HPWMI_BIOS_QUERY 0x9 #define HPWMI_HOTKEY_QUERY 0xc #define HPWMI_WIRELESS2_QUERY 0x1b #define HPWMI_POSTCODEERROR_QUERY 0x2a @@ -293,19 +292,6 @@ static int hp_wmi_tablet_state(void) return (state & 0x4) ? 1 : 0; } -static int hp_wmi_enable_hotkeys(void) -{ - int ret; - int query = 0x6e; - - ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query), - 0); - - if (ret) - return -EINVAL; - return 0; -} - static int hp_wmi_set_block(void *data, bool blocked) { enum hp_wmi_radio r = (enum hp_wmi_radio) data; @@ -1009,8 +995,6 @@ static int __init hp_wmi_init(void) err = hp_wmi_input_setup(); if (err) return err; - - hp_wmi_enable_hotkeys(); } if (bios_capable) { diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 2ac045f27f1..3a1b6bf326a 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2440,7 +2440,10 @@ static ssize_t sony_nc_gfx_switch_status_show(struct device *dev, if (pos < 0) return pos; - return snprintf(buffer, PAGE_SIZE, "%s\n", pos ? "speed" : "stamina"); + return snprintf(buffer, PAGE_SIZE, "%s\n", + pos == SPEED ? "speed" : + pos == STAMINA ? "stamina" : + pos == AUTO ? "auto" : "unknown"); } static int sony_nc_gfx_switch_setup(struct platform_device *pd, @@ -4320,7 +4323,8 @@ static int sony_pic_add(struct acpi_device *device) goto err_free_resources; } - if (sonypi_compat_init()) + result = sonypi_compat_init(); + if (result) goto err_remove_input; /* request io port */ diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index 767fee2ab34..26019531db1 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -23,6 +23,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/interrupt.h> +#include <linux/delay.h> #include <linux/rtc.h> #include <linux/slab.h> #include <linux/of_device.h> @@ -119,24 +120,39 @@ static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev) } #endif /* CONFIG_STMP3XXX_RTC_WATCHDOG */ -static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) +static int stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) { + int timeout = 5000; /* 3ms according to i.MX28 Ref Manual */ /* - * The datasheet doesn't say which way round the - * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, - * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS + * The i.MX28 Applications Processor Reference Manual, Rev. 1, 2010 + * states: + * | The order in which registers are updated is + * | Persistent 0, 1, 2, 3, 4, 5, Alarm, Seconds. + * | (This list is in bitfield order, from LSB to MSB, as they would + * | appear in the STALE_REGS and NEW_REGS bitfields of the HW_RTC_STAT + * | register. For example, the Seconds register corresponds to + * | STALE_REGS or NEW_REGS containing 0x80.) */ - while (readl(rtc_data->io + STMP3XXX_RTC_STAT) & - (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT)) - cpu_relax(); + do { + if (!(readl(rtc_data->io + STMP3XXX_RTC_STAT) & + (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT))) + return 0; + udelay(1); + } while (--timeout > 0); + return (readl(rtc_data->io + STMP3XXX_RTC_STAT) & + (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT)) ? -ETIME : 0; } /* Time read/write */ static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) { + int ret; struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); - stmp3xxx_wait_time(rtc_data); + ret = stmp3xxx_wait_time(rtc_data); + if (ret) + return ret; + rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_SECONDS), rtc_tm); return 0; } @@ -146,8 +162,7 @@ static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS); - stmp3xxx_wait_time(rtc_data); - return 0; + return stmp3xxx_wait_time(rtc_data); } /* interrupt(s) handler */ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 1d4c8fe7275..c82fe65c412 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -102,10 +102,13 @@ static void zfcp_erp_action_dismiss_port(struct zfcp_port *port) if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE) zfcp_erp_action_dismiss(&port->erp_action); - else - shost_for_each_device(sdev, port->adapter->scsi_host) + else { + spin_lock(port->adapter->scsi_host->host_lock); + __shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) zfcp_erp_action_dismiss_lun(sdev); + spin_unlock(port->adapter->scsi_host->host_lock); + } } static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter) @@ -592,9 +595,11 @@ static void _zfcp_erp_lun_reopen_all(struct zfcp_port *port, int clear, { struct scsi_device *sdev; - shost_for_each_device(sdev, port->adapter->scsi_host) + spin_lock(port->adapter->scsi_host->host_lock); + __shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) _zfcp_erp_lun_reopen(sdev, clear, id, 0); + spin_unlock(port->adapter->scsi_host->host_lock); } static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act) @@ -1434,8 +1439,10 @@ void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask) atomic_set_mask(common_mask, &port->status); read_unlock_irqrestore(&adapter->port_list_lock, flags); - shost_for_each_device(sdev, adapter->scsi_host) + spin_lock_irqsave(adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, adapter->scsi_host) atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status); + spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); } /** @@ -1469,11 +1476,13 @@ void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask) } read_unlock_irqrestore(&adapter->port_list_lock, flags); - shost_for_each_device(sdev, adapter->scsi_host) { + spin_lock_irqsave(adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, adapter->scsi_host) { atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status); if (clear_counter) atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); } + spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); } /** @@ -1487,16 +1496,19 @@ void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask) { struct scsi_device *sdev; u32 common_mask = mask & ZFCP_COMMON_FLAGS; + unsigned long flags; atomic_set_mask(mask, &port->status); if (!common_mask) return; - shost_for_each_device(sdev, port->adapter->scsi_host) + spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status); + spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); } /** @@ -1511,6 +1523,7 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask) struct scsi_device *sdev; u32 common_mask = mask & ZFCP_COMMON_FLAGS; u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED; + unsigned long flags; atomic_clear_mask(mask, &port->status); @@ -1520,13 +1533,15 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask) if (clear_counter) atomic_set(&port->erp_counter, 0); - shost_for_each_device(sdev, port->adapter->scsi_host) + spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); + __shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) { atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status); if (clear_counter) atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); } + spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); } /** diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 665e3cfaaf8..de0598eaacd 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -224,11 +224,9 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio) { - spin_lock_irq(&qdio->req_q_lock); if (atomic_read(&qdio->req_q_free) || !(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) return 1; - spin_unlock_irq(&qdio->req_q_lock); return 0; } @@ -246,9 +244,8 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) { long ret; - spin_unlock_irq(&qdio->req_q_lock); - ret = wait_event_interruptible_timeout(qdio->req_q_wq, - zfcp_qdio_sbal_check(qdio), 5 * HZ); + ret = wait_event_interruptible_lock_irq_timeout(qdio->req_q_wq, + zfcp_qdio_sbal_check(qdio), qdio->req_q_lock, 5 * HZ); if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) return -EIO; @@ -262,7 +259,6 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1"); } - spin_lock_irq(&qdio->req_q_lock); return -EIO; } diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 3f01bbf0609..890639274bc 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -27,6 +27,16 @@ static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \ static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \ zfcp_sysfs_##_feat##_##_name##_show, NULL); +#define ZFCP_DEFINE_ATTR_CONST(_feat, _name, _format, _value) \ +static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \ + struct device_attribute *at,\ + char *buf) \ +{ \ + return sprintf(buf, _format, _value); \ +} \ +static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \ + zfcp_sysfs_##_feat##_##_name##_show, NULL); + #define ZFCP_DEFINE_A_ATTR(_name, _format, _value) \ static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \ struct device_attribute *at,\ @@ -75,6 +85,8 @@ ZFCP_DEFINE_ATTR(zfcp_unit, unit, in_recovery, "%d\n", ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_denied, "%d\n", (zfcp_unit_sdev_status(unit) & ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0); +ZFCP_DEFINE_ATTR_CONST(unit, access_shared, "%d\n", 0); +ZFCP_DEFINE_ATTR_CONST(unit, access_readonly, "%d\n", 0); static ssize_t zfcp_sysfs_port_failed_show(struct device *dev, struct device_attribute *attr, @@ -347,6 +359,8 @@ static struct attribute *zfcp_unit_attrs[] = { &dev_attr_unit_in_recovery.attr, &dev_attr_unit_status.attr, &dev_attr_unit_access_denied.attr, + &dev_attr_unit_access_shared.attr, + &dev_attr_unit_access_readonly.attr, NULL }; static struct attribute_group zfcp_unit_attr_group = { diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 48b2918e0d6..92ff027746f 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1353,7 +1353,6 @@ config SCSI_LPFC tristate "Emulex LightPulse Fibre Channel Support" depends on PCI && SCSI select SCSI_FC_ATTRS - select GENERIC_CSUM select CRC_T10DIF help This lpfc driver supports the Emulex LightPulse diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c index e25eba5713c..b3b5125faa7 100644 --- a/drivers/staging/comedi/drivers.c +++ b/drivers/staging/comedi/drivers.c @@ -482,7 +482,7 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) ret = comedi_device_postconfig(dev); if (ret < 0) { comedi_device_detach(dev); - module_put(dev->driver->module); + module_put(driv->module); } /* On success, the driver module count has been incremented. */ return ret; diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 609dbc2f715..83b4ef4dfcf 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1119,11 +1119,11 @@ static int usbtmc_probe(struct usb_interface *intf, /* Determine if it is a Rigol or not */ data->rigol_quirk = 0; dev_dbg(&intf->dev, "Trying to find if device Vendor 0x%04X Product 0x%04X has the RIGOL quirk\n", - data->usb_dev->descriptor.idVendor, - data->usb_dev->descriptor.idProduct); + le16_to_cpu(data->usb_dev->descriptor.idVendor), + le16_to_cpu(data->usb_dev->descriptor.idProduct)); for(n = 0; usbtmc_id_quirk[n].idVendor > 0; n++) { - if ((usbtmc_id_quirk[n].idVendor == data->usb_dev->descriptor.idVendor) && - (usbtmc_id_quirk[n].idProduct == data->usb_dev->descriptor.idProduct)) { + if ((usbtmc_id_quirk[n].idVendor == le16_to_cpu(data->usb_dev->descriptor.idVendor)) && + (usbtmc_id_quirk[n].idProduct == le16_to_cpu(data->usb_dev->descriptor.idProduct))) { dev_dbg(&intf->dev, "Setting this device as having the RIGOL quirk\n"); data->rigol_quirk = 1; break; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index a6359889507..5b44cd47da5 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -78,6 +78,12 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x04d8, 0x000c), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* CarrolTouch 4000U */ + { USB_DEVICE(0x04e7, 0x0009), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* CarrolTouch 4500U */ + { USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Samsung Android phone modem - ID conflict with SPH-I500 */ { USB_DEVICE(0x04e8, 0x6601), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index f80d0330d54..8e3c878f38c 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1391,21 +1391,20 @@ iso_stream_schedule ( /* Behind the scheduling threshold? */ if (unlikely(start < next)) { + unsigned now2 = (now - base) & (mod - 1); /* USB_ISO_ASAP: Round up to the first available slot */ if (urb->transfer_flags & URB_ISO_ASAP) start += (next - start + period - 1) & -period; /* - * Not ASAP: Use the next slot in the stream. If - * the entire URB falls before the threshold, fail. + * Not ASAP: Use the next slot in the stream, + * no matter what. */ - else if (start + span - period < next) { - ehci_dbg(ehci, "iso urb late %p (%u+%u < %u)\n", + else if (start + span - period < now2) { + ehci_dbg(ehci, "iso underrun %p (%u+%u < %u)\n", urb, start + base, - span - period, next + base); - status = -EXDEV; - goto fail; + span - period, now2 + base); } } diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 08613e24189..0f1d193fef0 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -304,6 +304,11 @@ static int __init ohci_pci_init(void) pr_info("%s: " DRIVER_DESC "\n", hcd_name); ohci_init_driver(&ohci_pci_hc_driver, &pci_overrides); + + /* Entries for the PCI suspend/resume callbacks are special */ + ohci_pci_hc_driver.pci_suspend = ohci_suspend; + ohci_pci_hc_driver.pci_resume = ohci_resume; + return pci_register_driver(&ohci_pci_driver); } module_init(ohci_pci_init); diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index eb3c8c142fa..eeb27208c0d 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -830,7 +830,7 @@ static int adu_probe(struct usb_interface *interface, /* let the user know what node this device is now attached to */ dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d\n", - udev->descriptor.idProduct, dev->serial_number, + le16_to_cpu(udev->descriptor.idProduct), dev->serial_number, (dev->minor - ADU_MINOR_BASE)); exit: dbg(2, " %s : leave, return value %p (dev)", __func__, dev); diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index ca266280895..e1859b8ef56 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -15,7 +15,7 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "otg_fsm.h" +#include "phy-fsm-usb.h" #include <linux/usb/otg.h> #include <linux/ioctl.h> diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index c520b3548e7..7f4596606e1 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -29,7 +29,7 @@ #include <linux/usb/gadget.h> #include <linux/usb/otg.h> -#include "phy-otg-fsm.h" +#include "phy-fsm-usb.h" /* Change USB protocol when there is a protocol change */ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 5a979729f8e..58c17fdc85e 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -2303,7 +2303,7 @@ static int keyspan_startup(struct usb_serial *serial) if (d_details == NULL) { dev_err(&serial->dev->dev, "%s - unknown product id %x\n", __func__, le16_to_cpu(serial->dev->descriptor.idProduct)); - return 1; + return -ENODEV; } /* Setup private data for serial driver */ diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 51da424327b..b01300164fc 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -90,6 +90,7 @@ struct urbtracker { struct list_head urblist_entry; struct kref ref_count; struct urb *urb; + struct usb_ctrlrequest *setup; }; enum mos7715_pp_modes { @@ -271,6 +272,7 @@ static void destroy_urbtracker(struct kref *kref) struct mos7715_parport *mos_parport = urbtrack->mos_parport; usb_free_urb(urbtrack->urb); + kfree(urbtrack->setup); kfree(urbtrack); kref_put(&mos_parport->ref_count, destroy_mos_parport); } @@ -355,7 +357,6 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, struct urbtracker *urbtrack; int ret_val; unsigned long flags; - struct usb_ctrlrequest setup; struct usb_serial *serial = mos_parport->serial; struct usb_device *usbdev = serial->dev; @@ -373,14 +374,20 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, kfree(urbtrack); return -ENOMEM; } - setup.bRequestType = (__u8)0x40; - setup.bRequest = (__u8)0x0e; - setup.wValue = get_reg_value(reg, dummy); - setup.wIndex = get_reg_index(reg); - setup.wLength = 0; + urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_KERNEL); + if (!urbtrack->setup) { + usb_free_urb(urbtrack->urb); + kfree(urbtrack); + return -ENOMEM; + } + urbtrack->setup->bRequestType = (__u8)0x40; + urbtrack->setup->bRequest = (__u8)0x0e; + urbtrack->setup->wValue = get_reg_value(reg, dummy); + urbtrack->setup->wIndex = get_reg_index(reg); + urbtrack->setup->wLength = 0; usb_fill_control_urb(urbtrack->urb, usbdev, usb_sndctrlpipe(usbdev, 0), - (unsigned char *)&setup, + (unsigned char *)urbtrack->setup, NULL, 0, async_complete, urbtrack); kref_init(&urbtrack->ref_count); INIT_LIST_HEAD(&urbtrack->urblist_entry); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index d953d674f22..3bac4693c03 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2193,7 +2193,7 @@ static int mos7810_check(struct usb_serial *serial) static int mos7840_probe(struct usb_serial *serial, const struct usb_device_id *id) { - u16 product = serial->dev->descriptor.idProduct; + u16 product = le16_to_cpu(serial->dev->descriptor.idProduct); u8 *buf; int device_type; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 375b5a400b6..5c9f9b1d773 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -1536,14 +1536,15 @@ static int ti_download_firmware(struct ti_device *tdev) char buf[32]; /* try ID specific firmware first, then try generic firmware */ - sprintf(buf, "ti_usb-v%04x-p%04x.fw", dev->descriptor.idVendor, - dev->descriptor.idProduct); + sprintf(buf, "ti_usb-v%04x-p%04x.fw", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); status = request_firmware(&fw_p, buf, &dev->dev); if (status != 0) { buf[0] = '\0'; - if (dev->descriptor.idVendor == MTS_VENDOR_ID) { - switch (dev->descriptor.idProduct) { + if (le16_to_cpu(dev->descriptor.idVendor) == MTS_VENDOR_ID) { + switch (le16_to_cpu(dev->descriptor.idProduct)) { case MTS_CDMA_PRODUCT_ID: strcpy(buf, "mts_cdma.fw"); break; diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 8257d30c407..85365784040 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -291,18 +291,18 @@ static void usb_wwan_indat_callback(struct urb *urb) tty_flip_buffer_push(&port->port); } else dev_dbg(dev, "%s: empty read urb received\n", __func__); - - /* Resubmit urb so we continue receiving */ - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err) { - if (err != -EPERM) { - dev_err(dev, "%s: resubmit read urb failed. (%d)\n", __func__, err); - /* busy also in error unless we are killed */ - usb_mark_last_busy(port->serial->dev); - } - } else { + } + /* Resubmit urb so we continue receiving */ + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + if (err != -EPERM) { + dev_err(dev, "%s: resubmit read urb failed. (%d)\n", + __func__, err); + /* busy also in error unless we are killed */ usb_mark_last_busy(port->serial->dev); } + } else { + usb_mark_last_busy(port->serial->dev); } } diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 16968c89949..d3493ca0525 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -1226,6 +1226,12 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb) } spin_lock_irqsave(&xfer->lock, flags); rpipe = xfer->ep->hcpriv; + if (rpipe == NULL) { + pr_debug("%s: xfer id 0x%08X has no RPIPE. %s", + __func__, wa_xfer_id(xfer), + "Probably already aborted.\n" ); + goto out_unlock; + } /* Check the delayed list -> if there, release and complete */ spin_lock_irqsave(&wa->xfer_list_lock, flags2); if (!list_empty(&xfer->list_node) && xfer->seg == NULL) @@ -1644,8 +1650,7 @@ static void wa_xfer_result_cb(struct urb *urb) break; } usb_status = xfer_result->bTransferStatus & 0x3f; - if (usb_status == WA_XFER_STATUS_ABORTED - || usb_status == WA_XFER_STATUS_NOT_FOUND) + if (usb_status == WA_XFER_STATUS_NOT_FOUND) /* taken care of already */ break; xfer_id = xfer_result->dwTransferID; diff --git a/drivers/xen/events.c b/drivers/xen/events.c index a58ac435a9a..5e8be462aed 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -348,7 +348,7 @@ static void init_evtchn_cpu_bindings(void) for_each_possible_cpu(i) memset(per_cpu(cpu_evtchn_mask, i), - (i == 0) ? ~0 : 0, sizeof(*per_cpu(cpu_evtchn_mask, i))); + (i == 0) ? ~0 : 0, NR_EVENT_CHANNELS/8); } static inline void clear_evtchn(int port) @@ -1493,8 +1493,10 @@ void rebind_evtchn_irq(int evtchn, int irq) /* Rebind an evtchn so that it gets delivered to a specific cpu */ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) { + struct shared_info *s = HYPERVISOR_shared_info; struct evtchn_bind_vcpu bind_vcpu; int evtchn = evtchn_from_irq(irq); + int masked; if (!VALID_EVTCHN(evtchn)) return -1; @@ -1511,6 +1513,12 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) bind_vcpu.vcpu = tcpu; /* + * Mask the event while changing the VCPU binding to prevent + * it being delivered on an unexpected VCPU. + */ + masked = sync_test_and_set_bit(evtchn, BM(s->evtchn_mask)); + + /* * If this fails, it usually just indicates that we're dealing with a * virq or IPI channel, which don't actually need to be rebound. Ignore * it, but don't do the xenlinux-level rebind in that case. @@ -1518,6 +1526,9 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0) bind_evtchn_to_cpu(evtchn, tcpu); + if (!masked) + unmask_evtchn(evtchn); + return 0; } diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 5e376bb9341..8defc6b3f9a 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -40,7 +40,7 @@ struct inode *bfs_iget(struct super_block *sb, unsigned long ino) int block, off; inode = iget_locked(sb, ino); - if (IS_ERR(inode)) + if (!inode) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) return inode; @@ -1045,12 +1045,22 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs, int bio_uncopy_user(struct bio *bio) { struct bio_map_data *bmd = bio->bi_private; - int ret = 0; + struct bio_vec *bvec; + int ret = 0, i; - if (!bio_flagged(bio, BIO_NULL_MAPPED)) - ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, - bmd->nr_sgvecs, bio_data_dir(bio) == READ, - 0, bmd->is_our_pages); + if (!bio_flagged(bio, BIO_NULL_MAPPED)) { + /* + * if we're in a workqueue, the request is orphaned, so + * don't copy into a random user address space, just free. + */ + if (current->mm) + ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, + bmd->nr_sgvecs, bio_data_dir(bio) == READ, + 0, bmd->is_our_pages); + else if (bmd->is_our_pages) + bio_for_each_segment_all(bvec, bio, i) + __free_page(bvec->bv_page); + } bio_free_map_data(bmd); bio_put(bio); return ret; diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 45e57cc3820..fc6f4f3a1a9 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -43,17 +43,18 @@ cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server) server->secmech.md5 = crypto_alloc_shash("md5", 0, 0); if (IS_ERR(server->secmech.md5)) { cifs_dbg(VFS, "could not allocate crypto md5\n"); - return PTR_ERR(server->secmech.md5); + rc = PTR_ERR(server->secmech.md5); + server->secmech.md5 = NULL; + return rc; } size = sizeof(struct shash_desc) + crypto_shash_descsize(server->secmech.md5); server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL); if (!server->secmech.sdescmd5) { - rc = -ENOMEM; crypto_free_shash(server->secmech.md5); server->secmech.md5 = NULL; - return rc; + return -ENOMEM; } server->secmech.sdescmd5->shash.tfm = server->secmech.md5; server->secmech.sdescmd5->shash.flags = 0x0; @@ -421,7 +422,7 @@ find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp) if (blobptr + attrsize > blobend) break; if (type == NTLMSSP_AV_NB_DOMAIN_NAME) { - if (!attrsize) + if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN) break; if (!ses->domainName) { ses->domainName = @@ -591,6 +592,7 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) static int crypto_hmacmd5_alloc(struct TCP_Server_Info *server) { + int rc; unsigned int size; /* check if already allocated */ @@ -600,7 +602,9 @@ static int crypto_hmacmd5_alloc(struct TCP_Server_Info *server) server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0); if (IS_ERR(server->secmech.hmacmd5)) { cifs_dbg(VFS, "could not allocate crypto hmacmd5\n"); - return PTR_ERR(server->secmech.hmacmd5); + rc = PTR_ERR(server->secmech.hmacmd5); + server->secmech.hmacmd5 = NULL; + return rc; } size = sizeof(struct shash_desc) + diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 4bdd547dbf6..85ea98d139f 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -147,18 +147,17 @@ cifs_read_super(struct super_block *sb) goto out_no_root; } + if (cifs_sb_master_tcon(cifs_sb)->nocase) + sb->s_d_op = &cifs_ci_dentry_ops; + else + sb->s_d_op = &cifs_dentry_ops; + sb->s_root = d_make_root(inode); if (!sb->s_root) { rc = -ENOMEM; goto out_no_root; } - /* do that *after* d_make_root() - we want NULL ->d_op for root here */ - if (cifs_sb_master_tcon(cifs_sb)->nocase) - sb->s_d_op = &cifs_ci_dentry_ops; - else - sb->s_d_op = &cifs_dentry_ops; - #ifdef CONFIG_CIFS_NFSD_EXPORT if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { cifs_dbg(FYI, "export ops supported\n"); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 1fdc3704105..52ca861ed35 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -44,6 +44,7 @@ #define MAX_TREE_SIZE (2 + MAX_SERVER_SIZE + 1 + MAX_SHARE_SIZE + 1) #define MAX_SERVER_SIZE 15 #define MAX_SHARE_SIZE 80 +#define CIFS_MAX_DOMAINNAME_LEN 256 /* max domain name length */ #define MAX_USERNAME_SIZE 256 /* reasonable maximum for current servers */ #define MAX_PASSWORD_SIZE 512 /* max for windows seems to be 256 wide chars */ @@ -369,6 +370,9 @@ struct smb_version_operations { void (*generate_signingkey)(struct TCP_Server_Info *server); int (*calc_signature)(struct smb_rqst *rqst, struct TCP_Server_Info *server); + int (*query_mf_symlink)(const unsigned char *path, char *pbuf, + unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb, + unsigned int xid); }; struct smb_version_values { diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index f7e584d047e..b29a012bed3 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -497,5 +497,7 @@ void cifs_writev_complete(struct work_struct *work); struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete); void cifs_writedata_release(struct kref *refcount); - +int open_query_close_cifs_symlink(const unsigned char *path, char *pbuf, + unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb, + unsigned int xid); #endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index fa68813396b..d67c550c498 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1675,7 +1675,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (strnlen(string, 256) == 256) { + if (strnlen(string, CIFS_MAX_DOMAINNAME_LEN) + == CIFS_MAX_DOMAINNAME_LEN) { printk(KERN_WARNING "CIFS: domain name too" " long\n"); goto cifs_parse_mount_err; @@ -2276,8 +2277,8 @@ cifs_put_smb_ses(struct cifs_ses *ses) #ifdef CONFIG_KEYS -/* strlen("cifs:a:") + INET6_ADDRSTRLEN + 1 */ -#define CIFSCREDS_DESC_SIZE (7 + INET6_ADDRSTRLEN + 1) +/* strlen("cifs:a:") + CIFS_MAX_DOMAINNAME_LEN + 1 */ +#define CIFSCREDS_DESC_SIZE (7 + CIFS_MAX_DOMAINNAME_LEN + 1) /* Populate username and pw fields from keyring if possible */ static int diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1e57f36ea1b..7e36ae34e94 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -647,6 +647,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) oflags, &oplock, &cfile->fid.netfid, xid); if (rc == 0) { cifs_dbg(FYI, "posix reopen succeeded\n"); + oparms.reconnect = true; goto reopen_success; } /* diff --git a/fs/cifs/link.c b/fs/cifs/link.c index b83c3f5646b..562044f700e 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -305,67 +305,89 @@ CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr) } int -CIFSCheckMFSymlink(struct cifs_fattr *fattr, - const unsigned char *path, - struct cifs_sb_info *cifs_sb, unsigned int xid) +open_query_close_cifs_symlink(const unsigned char *path, char *pbuf, + unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb, + unsigned int xid) { int rc; int oplock = 0; __u16 netfid = 0; struct tcon_link *tlink; - struct cifs_tcon *pTcon; + struct cifs_tcon *ptcon; struct cifs_io_parms io_parms; - u8 *buf; - char *pbuf; - unsigned int bytes_read = 0; int buf_type = CIFS_NO_BUFFER; - unsigned int link_len = 0; FILE_ALL_INFO file_info; - if (!CIFSCouldBeMFSymlink(fattr)) - /* it's not a symlink */ - return 0; - tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); - pTcon = tlink_tcon(tlink); + ptcon = tlink_tcon(tlink); - rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, + rc = CIFSSMBOpen(xid, ptcon, path, FILE_OPEN, GENERIC_READ, CREATE_NOT_DIR, &netfid, &oplock, &file_info, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc != 0) - goto out; + if (rc != 0) { + cifs_put_tlink(tlink); + return rc; + } if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { - CIFSSMBClose(xid, pTcon, netfid); + CIFSSMBClose(xid, ptcon, netfid); + cifs_put_tlink(tlink); /* it's not a symlink */ - goto out; + return rc; } - buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); - if (!buf) { - rc = -ENOMEM; - goto out; - } - pbuf = buf; io_parms.netfid = netfid; io_parms.pid = current->tgid; - io_parms.tcon = pTcon; + io_parms.tcon = ptcon; io_parms.offset = 0; io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type); - CIFSSMBClose(xid, pTcon, netfid); - if (rc != 0) { - kfree(buf); + rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type); + CIFSSMBClose(xid, ptcon, netfid); + cifs_put_tlink(tlink); + return rc; +} + + +int +CIFSCheckMFSymlink(struct cifs_fattr *fattr, + const unsigned char *path, + struct cifs_sb_info *cifs_sb, unsigned int xid) +{ + int rc = 0; + u8 *buf = NULL; + unsigned int link_len = 0; + unsigned int bytes_read = 0; + struct cifs_tcon *ptcon; + + if (!CIFSCouldBeMFSymlink(fattr)) + /* it's not a symlink */ + return 0; + + buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); + if (!buf) { + rc = -ENOMEM; goto out; } + ptcon = tlink_tcon(cifs_sb_tlink(cifs_sb)); + if ((ptcon->ses) && (ptcon->ses->server->ops->query_mf_symlink)) + rc = ptcon->ses->server->ops->query_mf_symlink(path, buf, + &bytes_read, cifs_sb, xid); + else + goto out; + + if (rc != 0) + goto out; + + if (bytes_read == 0) /* not a symlink */ + goto out; + rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); - kfree(buf); if (rc == -EINVAL) { /* it's not a symlink */ rc = 0; @@ -381,7 +403,7 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr, fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; fattr->cf_dtype = DT_LNK; out: - cifs_put_tlink(tlink); + kfree(buf); return rc; } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index ab877846939..69d2c826a23 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -111,6 +111,14 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, return; } + /* + * If we know that the inode will need to be revalidated immediately, + * then don't create a new dentry for it. We'll end up doing an on + * the wire call either way and this spares us an invalidation. + */ + if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) + return; + dentry = d_alloc(parent, name); if (!dentry) return; diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 79358e341fd..08dd37bb23a 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -197,7 +197,7 @@ static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses, bytes_ret = 0; } else bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->domainName, - 256, nls_cp); + CIFS_MAX_DOMAINNAME_LEN, nls_cp); bcc_ptr += 2 * bytes_ret; bcc_ptr += 2; /* account for null terminator */ @@ -255,8 +255,8 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, /* copy domain */ if (ses->domainName != NULL) { - strncpy(bcc_ptr, ses->domainName, 256); - bcc_ptr += strnlen(ses->domainName, 256); + strncpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN); + bcc_ptr += strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN); } /* else we will send a null domain name so the server will default to its own domain */ *bcc_ptr = 0; diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 6457690731a..60943978aec 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -944,6 +944,7 @@ struct smb_version_operations smb1_operations = { .mand_lock = cifs_mand_lock, .mand_unlock_range = cifs_unlock_range, .push_mand_locks = cifs_push_mandatory_locks, + .query_mf_symlink = open_query_close_cifs_symlink, }; struct smb_version_values smb1_values = { diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 301b191270b..4f2300d020c 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -42,6 +42,7 @@ static int smb2_crypto_shash_allocate(struct TCP_Server_Info *server) { + int rc; unsigned int size; if (server->secmech.sdeschmacsha256 != NULL) @@ -50,7 +51,9 @@ smb2_crypto_shash_allocate(struct TCP_Server_Info *server) server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0); if (IS_ERR(server->secmech.hmacsha256)) { cifs_dbg(VFS, "could not allocate crypto hmacsha256\n"); - return PTR_ERR(server->secmech.hmacsha256); + rc = PTR_ERR(server->secmech.hmacsha256); + server->secmech.hmacsha256 = NULL; + return rc; } size = sizeof(struct shash_desc) + @@ -87,7 +90,9 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server) server->secmech.sdeschmacsha256 = NULL; crypto_free_shash(server->secmech.hmacsha256); server->secmech.hmacsha256 = NULL; - return PTR_ERR(server->secmech.cmacaes); + rc = PTR_ERR(server->secmech.cmacaes); + server->secmech.cmacaes = NULL; + return rc; } size = sizeof(struct shash_desc) + diff --git a/fs/dcache.c b/fs/dcache.c index 87bdb5329c3..83cfb834db0 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2724,6 +2724,17 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen, return memcpy(buffer, temp, sz); } +char *simple_dname(struct dentry *dentry, char *buffer, int buflen) +{ + char *end = buffer + buflen; + /* these dentries are never renamed, so d_lock is not needed */ + if (prepend(&end, &buflen, " (deleted)", 11) || + prepend_name(&end, &buflen, &dentry->d_name) || + prepend(&end, &buflen, "/", 1)) + end = ERR_PTR(-ENAMETOOLONG); + return end; +} + /* * Write full pathname from the root of the filesystem into the buffer. */ diff --git a/fs/efs/inode.c b/fs/efs/inode.c index f3913eb2c47..d15ccf20f1b 100644 --- a/fs/efs/inode.c +++ b/fs/efs/inode.c @@ -57,7 +57,7 @@ struct inode *efs_iget(struct super_block *super, unsigned long ino) struct inode *inode; inode = iget_locked(super, ino); - if (IS_ERR(inode)) + if (!inode) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) return inode; diff --git a/fs/exec.c b/fs/exec.c index 9c73def8764..fd774c7cb48 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -608,7 +608,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift) return -ENOMEM; lru_add_drain(); - tlb_gather_mmu(&tlb, mm, 0); + tlb_gather_mmu(&tlb, mm, old_start, old_end); if (new_end > old_start) { /* * when the old and new regions overlap clear from new_end. @@ -625,7 +625,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift) free_pgd_range(&tlb, old_start, old_end, new_end, vma->vm_next ? vma->vm_next->vm_start : USER_PGTABLES_CEILING); } - tlb_finish_mmu(&tlb, new_end, old_end); + tlb_finish_mmu(&tlb, old_start, old_end); /* * Shrink the vma to just the new range. Always succeeds. diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b577e45425b..0ab26fbf338 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2086,6 +2086,7 @@ extern int ext4_sync_inode(handle_t *, struct inode *); extern void ext4_dirty_inode(struct inode *, int); extern int ext4_change_inode_journal_flag(struct inode *, int); extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); +extern int ext4_inode_attach_jinode(struct inode *inode); extern int ext4_can_truncate(struct inode *inode); extern void ext4_truncate(struct inode *); extern int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length); diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 72a3600aedb..17ac112ab10 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -255,10 +255,10 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, set_buffer_prio(bh); if (ext4_handle_valid(handle)) { err = jbd2_journal_dirty_metadata(handle, bh); - if (err) { - /* Errors can only happen if there is a bug */ - handle->h_err = err; - __ext4_journal_stop(where, line, handle); + /* Errors can only happen if there is a bug */ + if (WARN_ON_ONCE(err)) { + ext4_journal_abort_handle(where, line, __func__, bh, + handle, err); } } else { if (inode) diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 6f4cc567c38..319c9d26279 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -219,7 +219,6 @@ static int ext4_file_open(struct inode * inode, struct file * filp) { struct super_block *sb = inode->i_sb; struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); - struct ext4_inode_info *ei = EXT4_I(inode); struct vfsmount *mnt = filp->f_path.mnt; struct path path; char buf[64], *cp; @@ -259,22 +258,10 @@ static int ext4_file_open(struct inode * inode, struct file * filp) * Set up the jbd2_inode if we are opening the inode for * writing and the journal is present */ - if (sbi->s_journal && !ei->jinode && (filp->f_mode & FMODE_WRITE)) { - struct jbd2_inode *jinode = jbd2_alloc_inode(GFP_KERNEL); - - spin_lock(&inode->i_lock); - if (!ei->jinode) { - if (!jinode) { - spin_unlock(&inode->i_lock); - return -ENOMEM; - } - ei->jinode = jinode; - jbd2_journal_init_jbd_inode(ei->jinode, inode); - jinode = NULL; - } - spin_unlock(&inode->i_lock); - if (unlikely(jinode != NULL)) - jbd2_free_inode(jinode); + if (filp->f_mode & FMODE_WRITE) { + int ret = ext4_inode_attach_jinode(inode); + if (ret < 0) + return ret; } return dquot_file_open(inode, filp); } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index dd32a2eacd0..c2ca04e67a4 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3533,6 +3533,18 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length) offset; } + if (offset & (sb->s_blocksize - 1) || + (offset + length) & (sb->s_blocksize - 1)) { + /* + * Attach jinode to inode for jbd2 if we do any zeroing of + * partial block + */ + ret = ext4_inode_attach_jinode(inode); + if (ret < 0) + goto out_mutex; + + } + first_block_offset = round_up(offset, sb->s_blocksize); last_block_offset = round_down((offset + length), sb->s_blocksize) - 1; @@ -3601,6 +3613,31 @@ out_mutex: return ret; } +int ext4_inode_attach_jinode(struct inode *inode) +{ + struct ext4_inode_info *ei = EXT4_I(inode); + struct jbd2_inode *jinode; + + if (ei->jinode || !EXT4_SB(inode->i_sb)->s_journal) + return 0; + + jinode = jbd2_alloc_inode(GFP_KERNEL); + spin_lock(&inode->i_lock); + if (!ei->jinode) { + if (!jinode) { + spin_unlock(&inode->i_lock); + return -ENOMEM; + } + ei->jinode = jinode; + jbd2_journal_init_jbd_inode(ei->jinode, inode); + jinode = NULL; + } + spin_unlock(&inode->i_lock); + if (unlikely(jinode != NULL)) + jbd2_free_inode(jinode); + return 0; +} + /* * ext4_truncate() * @@ -3661,6 +3698,12 @@ void ext4_truncate(struct inode *inode) return; } + /* If we zero-out tail of the page, we have to create jinode for jbd2 */ + if (inode->i_size & (inode->i_sb->s_blocksize - 1)) { + if (ext4_inode_attach_jinode(inode) < 0) + return; + } + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) credits = ext4_writepage_trans_blocks(inode); else diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 9491ac0590f..c0427e2f664 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -77,8 +77,10 @@ static void swap_inode_data(struct inode *inode1, struct inode *inode2) memswap(ei1->i_data, ei2->i_data, sizeof(ei1->i_data)); memswap(&ei1->i_flags, &ei2->i_flags, sizeof(ei1->i_flags)); memswap(&ei1->i_disksize, &ei2->i_disksize, sizeof(ei1->i_disksize)); - memswap(&ei1->i_es_tree, &ei2->i_es_tree, sizeof(ei1->i_es_tree)); - memswap(&ei1->i_es_lru_nr, &ei2->i_es_lru_nr, sizeof(ei1->i_es_lru_nr)); + ext4_es_remove_extent(inode1, 0, EXT_MAX_BLOCKS); + ext4_es_remove_extent(inode2, 0, EXT_MAX_BLOCKS); + ext4_es_lru_del(inode1); + ext4_es_lru_del(inode2); isize = i_size_read(inode1); i_size_write(inode1, i_size_read(inode2)); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 36b141e420b..b59373b625e 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1359,7 +1359,7 @@ static const struct mount_opts { {Opt_delalloc, EXT4_MOUNT_DELALLOC, MOPT_EXT4_ONLY | MOPT_SET | MOPT_EXPLICIT}, {Opt_nodelalloc, EXT4_MOUNT_DELALLOC, - MOPT_EXT4_ONLY | MOPT_CLEAR | MOPT_EXPLICIT}, + MOPT_EXT4_ONLY | MOPT_CLEAR}, {Opt_journal_checksum, EXT4_MOUNT_JOURNAL_CHECKSUM, MOPT_EXT4_ONLY | MOPT_SET}, {Opt_journal_async_commit, (EXT4_MOUNT_JOURNAL_ASYNC_COMMIT | @@ -3483,7 +3483,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) } if (test_opt(sb, DIOREAD_NOLOCK)) { ext4_msg(sb, KERN_ERR, "can't mount with " - "both data=journal and delalloc"); + "both data=journal and dioread_nolock"); goto failed_mount; } if (test_opt(sb, DELALLOC)) @@ -4727,6 +4727,21 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) goto restore_opts; } + if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) { + if (test_opt2(sb, EXPLICIT_DELALLOC)) { + ext4_msg(sb, KERN_ERR, "can't mount with " + "both data=journal and delalloc"); + err = -EINVAL; + goto restore_opts; + } + if (test_opt(sb, DIOREAD_NOLOCK)) { + ext4_msg(sb, KERN_ERR, "can't mount with " + "both data=journal and dioread_nolock"); + err = -EINVAL; + goto restore_opts; + } + } + if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED) ext4_abort(sb, "Abort forced by user"); diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 9435384562a..544a809819c 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1838,14 +1838,14 @@ int __init gfs2_glock_init(void) glock_workqueue = alloc_workqueue("glock_workqueue", WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_FREEZABLE, 0); - if (IS_ERR(glock_workqueue)) - return PTR_ERR(glock_workqueue); + if (!glock_workqueue) + return -ENOMEM; gfs2_delete_workqueue = alloc_workqueue("delete_workqueue", WQ_MEM_RECLAIM | WQ_FREEZABLE, 0); - if (IS_ERR(gfs2_delete_workqueue)) { + if (!gfs2_delete_workqueue) { destroy_workqueue(glock_workqueue); - return PTR_ERR(gfs2_delete_workqueue); + return -ENOMEM; } register_shrinker(&glock_shrinker); diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 5f2e5224c51..e2e0a90396e 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -47,7 +47,8 @@ static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh) * None of the buffers should be dirty, locked, or pinned. */ -static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) +static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync, + unsigned int nr_revokes) { struct gfs2_sbd *sdp = gl->gl_sbd; struct list_head *head = &gl->gl_ail_list; @@ -57,7 +58,9 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) gfs2_log_lock(sdp); spin_lock(&sdp->sd_ail_lock); - list_for_each_entry_safe(bd, tmp, head, bd_ail_gl_list) { + list_for_each_entry_safe_reverse(bd, tmp, head, bd_ail_gl_list) { + if (nr_revokes == 0) + break; bh = bd->bd_bh; if (bh->b_state & b_state) { if (fsync) @@ -65,6 +68,7 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) gfs2_ail_error(gl, bh); } gfs2_trans_add_revoke(sdp, bd); + nr_revokes--; } GLOCK_BUG_ON(gl, !fsync && atomic_read(&gl->gl_ail_count)); spin_unlock(&sdp->sd_ail_lock); @@ -91,7 +95,7 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl) WARN_ON_ONCE(current->journal_info); current->journal_info = &tr; - __gfs2_ail_flush(gl, 0); + __gfs2_ail_flush(gl, 0, tr.tr_revokes); gfs2_trans_end(sdp); gfs2_log_flush(sdp, NULL); @@ -101,15 +105,19 @@ void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) { struct gfs2_sbd *sdp = gl->gl_sbd; unsigned int revokes = atomic_read(&gl->gl_ail_count); + unsigned int max_revokes = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / sizeof(u64); int ret; if (!revokes) return; - ret = gfs2_trans_begin(sdp, 0, revokes); + while (revokes > max_revokes) + max_revokes += (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header)) / sizeof(u64); + + ret = gfs2_trans_begin(sdp, 0, max_revokes); if (ret) return; - __gfs2_ail_flush(gl, fsync); + __gfs2_ail_flush(gl, fsync, max_revokes); gfs2_trans_end(sdp); gfs2_log_flush(sdp, NULL); } diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index bbb2715171c..64915eeae5a 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -594,7 +594,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, } gfs2_glock_dq_uninit(ghs); if (IS_ERR(d)) - return PTR_RET(d); + return PTR_ERR(d); return error; } else if (error != -ENOENT) { goto fail_gunlock; @@ -1750,6 +1750,10 @@ static ssize_t gfs2_getxattr(struct dentry *dentry, const char *name, struct gfs2_holder gh; int ret; + /* For selinux during lookup */ + if (gfs2_glock_is_locked_by_me(ip->i_gl)) + return generic_getxattr(dentry, name, data, size); + gfs2_holder_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); ret = gfs2_glock_nq(&gh); if (ret == 0) { diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index e04d0e09ee7..7b0f5043cf2 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -155,7 +155,7 @@ static int __init init_gfs2_fs(void) goto fail_wq; gfs2_control_wq = alloc_workqueue("gfs2_control", - WQ_NON_REENTRANT | WQ_UNBOUND | WQ_FREEZABLE, 0); + WQ_UNBOUND | WQ_FREEZABLE, 0); if (!gfs2_control_wq) goto fail_recovery; diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index a3f868ae3fd..d19b30ababf 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -463,6 +463,14 @@ static struct inode *hugetlbfs_get_root(struct super_block *sb, return inode; } +/* + * Hugetlbfs is not reclaimable; therefore its i_mmap_mutex will never + * be taken from reclaim -- unlike regular filesystems. This needs an + * annotation because huge_pmd_share() does an allocation under + * i_mmap_mutex. + */ +struct lock_class_key hugetlbfs_i_mmap_mutex_key; + static struct inode *hugetlbfs_get_inode(struct super_block *sb, struct inode *dir, umode_t mode, dev_t dev) @@ -474,6 +482,8 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb, struct hugetlbfs_inode_info *info; inode->i_ino = get_next_ino(); inode_init_owner(inode, dir, mode); + lockdep_set_class(&inode->i_mapping->i_mmap_mutex, + &hugetlbfs_i_mmap_mutex_key); inode->i_mapping->a_ops = &hugetlbfs_aops; inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; @@ -916,14 +926,8 @@ static int get_hstate_idx(int page_size_log) return h - hstates; } -static char *hugetlb_dname(struct dentry *dentry, char *buffer, int buflen) -{ - return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)", - dentry->d_name.name); -} - static struct dentry_operations anon_ops = { - .d_dname = hugetlb_dname + .d_dname = simple_dname }; /* diff --git a/fs/namespace.c b/fs/namespace.c index 7b1ca9ba0b0..a45ba4f267f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1429,7 +1429,7 @@ struct vfsmount *collect_mounts(struct path *path) CL_COPY_ALL | CL_PRIVATE); namespace_unlock(); if (IS_ERR(tree)) - return NULL; + return ERR_CAST(tree); return &tree->mnt; } diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index dc9a913784a..2d8be51f90d 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -345,8 +345,7 @@ static void nilfs_end_bio_write(struct bio *bio, int err) if (err == -EOPNOTSUPP) { set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); - bio_put(bio); - /* to be detected by submit_seg_bio() */ + /* to be detected by nilfs_segbuf_submit_bio() */ } if (!uptodate) @@ -377,12 +376,12 @@ static int nilfs_segbuf_submit_bio(struct nilfs_segment_buffer *segbuf, bio->bi_private = segbuf; bio_get(bio); submit_bio(mode, bio); + segbuf->sb_nbio++; if (bio_flagged(bio, BIO_EOPNOTSUPP)) { bio_put(bio); err = -EOPNOTSUPP; goto failed; } - segbuf->sb_nbio++; bio_put(bio); wi->bio = NULL; diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 79736a28d84..2abf97b2a59 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -1757,7 +1757,7 @@ try_again: goto out; } else if (ret == 1) { clusters_need = wc->w_clen; - ret = ocfs2_refcount_cow(inode, filp, di_bh, + ret = ocfs2_refcount_cow(inode, di_bh, wc->w_cpos, wc->w_clen, UINT_MAX); if (ret) { mlog_errno(ret); diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index eb760d8acd5..30544ce8e9f 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -2153,11 +2153,9 @@ int ocfs2_empty_dir(struct inode *inode) { int ret; struct ocfs2_empty_dir_priv priv = { - .ctx.actor = ocfs2_empty_dir_filldir + .ctx.actor = ocfs2_empty_dir_filldir, }; - memset(&priv, 0, sizeof(priv)); - if (ocfs2_dir_indexed(inode)) { ret = ocfs2_empty_dir_dx(inode, &priv); if (ret) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 41000f223ca..3261d71319e 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -370,7 +370,7 @@ static int ocfs2_cow_file_pos(struct inode *inode, if (!(ext_flags & OCFS2_EXT_REFCOUNTED)) goto out; - return ocfs2_refcount_cow(inode, NULL, fe_bh, cpos, 1, cpos+1); + return ocfs2_refcount_cow(inode, fe_bh, cpos, 1, cpos+1); out: return status; @@ -899,7 +899,7 @@ static int ocfs2_zero_extend_get_range(struct inode *inode, zero_clusters = last_cpos - zero_cpos; if (needs_cow) { - rc = ocfs2_refcount_cow(inode, NULL, di_bh, zero_cpos, + rc = ocfs2_refcount_cow(inode, di_bh, zero_cpos, zero_clusters, UINT_MAX); if (rc) { mlog_errno(rc); @@ -2078,7 +2078,7 @@ static int ocfs2_prepare_inode_for_refcount(struct inode *inode, *meta_level = 1; - ret = ocfs2_refcount_cow(inode, file, di_bh, cpos, clusters, UINT_MAX); + ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX); if (ret) mlog_errno(ret); out: diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 96f9ac237e8..0a992737dca 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -537,7 +537,7 @@ static inline int ocfs2_calc_extend_credits(struct super_block *sb, extent_blocks = 1 + 1 + le16_to_cpu(root_el->l_tree_depth); return bitmap_blocks + sysfile_bitmap_blocks + extent_blocks + - ocfs2_quota_trans_credits(sb) + bits_wanted; + ocfs2_quota_trans_credits(sb); } static inline int ocfs2_calc_symlink_credits(struct super_block *sb) diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c index f1fc172175b..452068b4574 100644 --- a/fs/ocfs2/move_extents.c +++ b/fs/ocfs2/move_extents.c @@ -69,7 +69,7 @@ static int __ocfs2_move_extent(handle_t *handle, u64 ino = ocfs2_metadata_cache_owner(context->et.et_ci); u64 old_blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cpos); - ret = ocfs2_duplicate_clusters_by_page(handle, context->file, cpos, + ret = ocfs2_duplicate_clusters_by_page(handle, inode, cpos, p_cpos, new_p_cpos, len); if (ret) { mlog_errno(ret); diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 9f6b96a0961..a70d604593b 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -49,7 +49,6 @@ struct ocfs2_cow_context { struct inode *inode; - struct file *file; u32 cow_start; u32 cow_len; struct ocfs2_extent_tree data_et; @@ -66,7 +65,7 @@ struct ocfs2_cow_context { u32 *num_clusters, unsigned int *extent_flags); int (*cow_duplicate_clusters)(handle_t *handle, - struct file *file, + struct inode *inode, u32 cpos, u32 old_cluster, u32 new_cluster, u32 new_len); }; @@ -2922,14 +2921,12 @@ static int ocfs2_clear_cow_buffer(handle_t *handle, struct buffer_head *bh) } int ocfs2_duplicate_clusters_by_page(handle_t *handle, - struct file *file, + struct inode *inode, u32 cpos, u32 old_cluster, u32 new_cluster, u32 new_len) { int ret = 0, partial; - struct inode *inode = file_inode(file); - struct ocfs2_caching_info *ci = INODE_CACHE(inode); - struct super_block *sb = ocfs2_metadata_cache_get_super(ci); + struct super_block *sb = inode->i_sb; u64 new_block = ocfs2_clusters_to_blocks(sb, new_cluster); struct page *page; pgoff_t page_index; @@ -2978,13 +2975,6 @@ int ocfs2_duplicate_clusters_by_page(handle_t *handle, if (PAGE_CACHE_SIZE <= OCFS2_SB(sb)->s_clustersize) BUG_ON(PageDirty(page)); - if (PageReadahead(page)) { - page_cache_async_readahead(mapping, - &file->f_ra, file, - page, page_index, - readahead_pages); - } - if (!PageUptodate(page)) { ret = block_read_full_page(page, ocfs2_get_block); if (ret) { @@ -3004,7 +2994,8 @@ int ocfs2_duplicate_clusters_by_page(handle_t *handle, } } - ocfs2_map_and_dirty_page(inode, handle, from, to, + ocfs2_map_and_dirty_page(inode, + handle, from, to, page, 0, &new_block); mark_page_accessed(page); unlock: @@ -3020,12 +3011,11 @@ unlock: } int ocfs2_duplicate_clusters_by_jbd(handle_t *handle, - struct file *file, + struct inode *inode, u32 cpos, u32 old_cluster, u32 new_cluster, u32 new_len) { int ret = 0; - struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; struct ocfs2_caching_info *ci = INODE_CACHE(inode); int i, blocks = ocfs2_clusters_to_blocks(sb, new_len); @@ -3150,7 +3140,7 @@ static int ocfs2_replace_clusters(handle_t *handle, /*If the old clusters is unwritten, no need to duplicate. */ if (!(ext_flags & OCFS2_EXT_UNWRITTEN)) { - ret = context->cow_duplicate_clusters(handle, context->file, + ret = context->cow_duplicate_clusters(handle, context->inode, cpos, old, new, len); if (ret) { mlog_errno(ret); @@ -3428,35 +3418,12 @@ static int ocfs2_replace_cow(struct ocfs2_cow_context *context) return ret; } -static void ocfs2_readahead_for_cow(struct inode *inode, - struct file *file, - u32 start, u32 len) -{ - struct address_space *mapping; - pgoff_t index; - unsigned long num_pages; - int cs_bits = OCFS2_SB(inode->i_sb)->s_clustersize_bits; - - if (!file) - return; - - mapping = file->f_mapping; - num_pages = (len << cs_bits) >> PAGE_CACHE_SHIFT; - if (!num_pages) - num_pages = 1; - - index = ((loff_t)start << cs_bits) >> PAGE_CACHE_SHIFT; - page_cache_sync_readahead(mapping, &file->f_ra, file, - index, num_pages); -} - /* * Starting at cpos, try to CoW write_len clusters. Don't CoW * past max_cpos. This will stop when it runs into a hole or an * unrefcounted extent. */ static int ocfs2_refcount_cow_hunk(struct inode *inode, - struct file *file, struct buffer_head *di_bh, u32 cpos, u32 write_len, u32 max_cpos) { @@ -3485,8 +3452,6 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode, BUG_ON(cow_len == 0); - ocfs2_readahead_for_cow(inode, file, cow_start, cow_len); - context = kzalloc(sizeof(struct ocfs2_cow_context), GFP_NOFS); if (!context) { ret = -ENOMEM; @@ -3508,7 +3473,6 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode, context->ref_root_bh = ref_root_bh; context->cow_duplicate_clusters = ocfs2_duplicate_clusters_by_page; context->get_clusters = ocfs2_di_get_clusters; - context->file = file; ocfs2_init_dinode_extent_tree(&context->data_et, INODE_CACHE(inode), di_bh); @@ -3537,7 +3501,6 @@ out: * clusters between cpos and cpos+write_len are safe to modify. */ int ocfs2_refcount_cow(struct inode *inode, - struct file *file, struct buffer_head *di_bh, u32 cpos, u32 write_len, u32 max_cpos) { @@ -3557,7 +3520,7 @@ int ocfs2_refcount_cow(struct inode *inode, num_clusters = write_len; if (ext_flags & OCFS2_EXT_REFCOUNTED) { - ret = ocfs2_refcount_cow_hunk(inode, file, di_bh, cpos, + ret = ocfs2_refcount_cow_hunk(inode, di_bh, cpos, num_clusters, max_cpos); if (ret) { mlog_errno(ret); diff --git a/fs/ocfs2/refcounttree.h b/fs/ocfs2/refcounttree.h index 7754608c83a..6422bbcdb52 100644 --- a/fs/ocfs2/refcounttree.h +++ b/fs/ocfs2/refcounttree.h @@ -53,7 +53,7 @@ int ocfs2_prepare_refcount_change_for_del(struct inode *inode, int *credits, int *ref_blocks); int ocfs2_refcount_cow(struct inode *inode, - struct file *filep, struct buffer_head *di_bh, + struct buffer_head *di_bh, u32 cpos, u32 write_len, u32 max_cpos); typedef int (ocfs2_post_refcount_func)(struct inode *inode, @@ -85,11 +85,11 @@ int ocfs2_refcount_cow_xattr(struct inode *inode, u32 cpos, u32 write_len, struct ocfs2_post_refcount *post); int ocfs2_duplicate_clusters_by_page(handle_t *handle, - struct file *file, + struct inode *inode, u32 cpos, u32 old_cluster, u32 new_cluster, u32 new_len); int ocfs2_duplicate_clusters_by_jbd(handle_t *handle, - struct file *file, + struct inode *inode, u32 cpos, u32 old_cluster, u32 new_cluster, u32 new_len); int ocfs2_cow_sync_writeback(struct super_block *sb, diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 75f2890abbd..0ff80f9b930 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -230,8 +230,6 @@ static int proc_readfd_common(struct file *file, struct dir_context *ctx, if (!dir_emit_dots(file, ctx)) goto out; - if (!dir_emit_dots(file, ctx)) - goto out; files = get_files_struct(p); if (!files) goto out; diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 94441a40733..737e15615b0 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -271,7 +271,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, de = next; } while (de); spin_unlock(&proc_subdir_lock); - return 0; + return 1; } int proc_readdir(struct file *file, struct dir_context *ctx) diff --git a/fs/proc/root.c b/fs/proc/root.c index 229e366598d..e0a790da726 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -205,7 +205,9 @@ static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentr static int proc_root_readdir(struct file *file, struct dir_context *ctx) { if (ctx->pos < FIRST_PROCESS_ENTRY) { - proc_readdir(file, ctx); + int error = proc_readdir(file, ctx); + if (unlikely(error <= 0)) + return error; ctx->pos = FIRST_PROCESS_ENTRY; } diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index dbf61f6174f..107d026f5d6 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -730,8 +730,16 @@ static inline void clear_soft_dirty(struct vm_area_struct *vma, * of how soft-dirty works. */ pte_t ptent = *pte; - ptent = pte_wrprotect(ptent); - ptent = pte_clear_flags(ptent, _PAGE_SOFT_DIRTY); + + if (pte_present(ptent)) { + ptent = pte_wrprotect(ptent); + ptent = pte_clear_flags(ptent, _PAGE_SOFT_DIRTY); + } else if (is_swap_pte(ptent)) { + ptent = pte_swp_clear_soft_dirty(ptent); + } else if (pte_file(ptent)) { + ptent = pte_file_clear_soft_dirty(ptent); + } + set_pte_at(vma->vm_mm, addr, pte, ptent); #endif } @@ -752,14 +760,15 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); for (; addr != end; pte++, addr += PAGE_SIZE) { ptent = *pte; - if (!pte_present(ptent)) - continue; if (cp->type == CLEAR_REFS_SOFT_DIRTY) { clear_soft_dirty(vma, addr, pte); continue; } + if (!pte_present(ptent)) + continue; + page = vm_normal_page(vma, addr, ptent); if (!page) continue; @@ -859,7 +868,7 @@ typedef struct { } pagemap_entry_t; struct pagemapread { - int pos, len; + int pos, len; /* units: PM_ENTRY_BYTES, not bytes */ pagemap_entry_t *buffer; bool v2; }; @@ -867,7 +876,7 @@ struct pagemapread { #define PAGEMAP_WALK_SIZE (PMD_SIZE) #define PAGEMAP_WALK_MASK (PMD_MASK) -#define PM_ENTRY_BYTES sizeof(u64) +#define PM_ENTRY_BYTES sizeof(pagemap_entry_t) #define PM_STATUS_BITS 3 #define PM_STATUS_OFFSET (64 - PM_STATUS_BITS) #define PM_STATUS_MASK (((1LL << PM_STATUS_BITS) - 1) << PM_STATUS_OFFSET) @@ -930,8 +939,10 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, flags = PM_PRESENT; page = vm_normal_page(vma, addr, pte); } else if (is_swap_pte(pte)) { - swp_entry_t entry = pte_to_swp_entry(pte); - + swp_entry_t entry; + if (pte_swp_soft_dirty(pte)) + flags2 |= __PM_SOFT_DIRTY; + entry = pte_to_swp_entry(pte); frame = swp_type(entry) | (swp_offset(entry) << MAX_SWAPFILES_SHIFT); flags = PM_SWAP; @@ -1116,8 +1127,8 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, goto out_task; pm.v2 = soft_dirty_cleared; - pm.len = PM_ENTRY_BYTES * (PAGEMAP_WALK_SIZE >> PAGE_SHIFT); - pm.buffer = kmalloc(pm.len, GFP_TEMPORARY); + pm.len = (PAGEMAP_WALK_SIZE >> PAGE_SHIFT); + pm.buffer = kmalloc(pm.len * PM_ENTRY_BYTES, GFP_TEMPORARY); ret = -ENOMEM; if (!pm.buffer) goto out_task; diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 2f47ade1b56..0807ddf97b0 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -417,6 +417,36 @@ static inline pmd_t pmd_mksoft_dirty(pmd_t pmd) { return pmd; } + +static inline pte_t pte_swp_mksoft_dirty(pte_t pte) +{ + return pte; +} + +static inline int pte_swp_soft_dirty(pte_t pte) +{ + return 0; +} + +static inline pte_t pte_swp_clear_soft_dirty(pte_t pte) +{ + return pte; +} + +static inline pte_t pte_file_clear_soft_dirty(pte_t pte) +{ + return pte; +} + +static inline pte_t pte_file_mksoft_dirty(pte_t pte) +{ + return pte; +} + +static inline int pte_file_soft_dirty(pte_t pte) +{ + return 0; +} #endif #ifndef __HAVE_PFNMAP_TRACKING diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h index 13821c339a4..5672d7ea1fa 100644 --- a/include/asm-generic/tlb.h +++ b/include/asm-generic/tlb.h @@ -112,7 +112,7 @@ struct mmu_gather { #define HAVE_GENERIC_MMU_GATHER -void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, bool fullmm); +void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end); void tlb_flush_mmu(struct mmu_gather *tlb); void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end); diff --git a/include/dt-bindings/sound/fsl-imx-audmux.h b/include/dt-bindings/sound/fsl-imx-audmux.h new file mode 100644 index 00000000000..50b09e96f24 --- /dev/null +++ b/include/dt-bindings/sound/fsl-imx-audmux.h @@ -0,0 +1,56 @@ +#ifndef __DT_FSL_IMX_AUDMUX_H +#define __DT_FSL_IMX_AUDMUX_H + +#define MX27_AUDMUX_HPCR1_SSI0 0 +#define MX27_AUDMUX_HPCR2_SSI1 1 +#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 +#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 +#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 +#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 + +#define MX31_AUDMUX_PORT1_SSI0 0 +#define MX31_AUDMUX_PORT2_SSI1 1 +#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 +#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 +#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 +#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 +#define MX31_AUDMUX_PORT7_SSI_PINS_7 6 + +#define MX51_AUDMUX_PORT1_SSI0 0 +#define MX51_AUDMUX_PORT2_SSI1 1 +#define MX51_AUDMUX_PORT3 2 +#define MX51_AUDMUX_PORT4 3 +#define MX51_AUDMUX_PORT5 4 +#define MX51_AUDMUX_PORT6 5 +#define MX51_AUDMUX_PORT7 6 + +/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) +#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) +#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) +#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) +#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) +#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) +#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) +#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) +#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) +#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) + +/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) +#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) +#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) +#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) +#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) +#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) +#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) +#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) +#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) + +#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) +#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) +#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) + +#endif /* __DT_FSL_IMX_AUDMUX_H */ diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index deb0ae58b99..66a0e5384ed 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h @@ -11,7 +11,7 @@ struct atmel_ssc_platform_data { struct ssc_device { struct list_head list; - resource_size_t phybase; + dma_addr_t phybase; void __iomem *regs; struct platform_device *pdev; struct atmel_ssc_platform_data *pdata; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index b90337c9d46..4a12532da8c 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -336,6 +336,7 @@ extern int d_validate(struct dentry *, struct dentry *); * helper function for dentry_operations.d_dname() members */ extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); +extern char *simple_dname(struct dentry *, char *, int); extern char *__d_path(const struct path *, const struct path *, char *, int); extern char *d_absolute_path(const struct path *, char *, int); diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index b99cd23f347..79640e015a8 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -5,45 +5,13 @@ #include <linux/bitmap.h> #include <linux/if.h> +#include <linux/ip.h> #include <linux/netdevice.h> #include <linux/rcupdate.h> #include <linux/timer.h> #include <linux/sysctl.h> #include <linux/rtnetlink.h> -enum -{ - IPV4_DEVCONF_FORWARDING=1, - IPV4_DEVCONF_MC_FORWARDING, - IPV4_DEVCONF_PROXY_ARP, - IPV4_DEVCONF_ACCEPT_REDIRECTS, - IPV4_DEVCONF_SECURE_REDIRECTS, - IPV4_DEVCONF_SEND_REDIRECTS, - IPV4_DEVCONF_SHARED_MEDIA, - IPV4_DEVCONF_RP_FILTER, - IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, - IPV4_DEVCONF_BOOTP_RELAY, - IPV4_DEVCONF_LOG_MARTIANS, - IPV4_DEVCONF_TAG, - IPV4_DEVCONF_ARPFILTER, - IPV4_DEVCONF_MEDIUM_ID, - IPV4_DEVCONF_NOXFRM, - IPV4_DEVCONF_NOPOLICY, - IPV4_DEVCONF_FORCE_IGMP_VERSION, - IPV4_DEVCONF_ARP_ANNOUNCE, - IPV4_DEVCONF_ARP_IGNORE, - IPV4_DEVCONF_PROMOTE_SECONDARIES, - IPV4_DEVCONF_ARP_ACCEPT, - IPV4_DEVCONF_ARP_NOTIFY, - IPV4_DEVCONF_ACCEPT_LOCAL, - IPV4_DEVCONF_SRC_VMARK, - IPV4_DEVCONF_PROXY_ARP_PVLAN, - IPV4_DEVCONF_ROUTE_LOCALNET, - __IPV4_DEVCONF_MAX -}; - -#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1) - struct ipv4_devconf { void *sysctl; int data[IPV4_DEVCONF_MAX]; diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 850e95bc766..b8b7dc75575 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -101,6 +101,7 @@ struct inet6_skb_parm { #define IP6SKB_FORWARDED 2 #define IP6SKB_REROUTED 4 #define IP6SKB_ROUTERALERT 8 +#define IP6SKB_FRAGMENTED 16 }; #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) diff --git a/include/linux/mfd/arizona/gpio.h b/include/linux/mfd/arizona/gpio.h new file mode 100644 index 00000000000..d2146bb74f8 --- /dev/null +++ b/include/linux/mfd/arizona/gpio.h @@ -0,0 +1,96 @@ +/* + * GPIO configuration for Arizona devices + * + * Copyright 2013 Wolfson Microelectronics. PLC. + * + * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ARIZONA_GPIO_H +#define _ARIZONA_GPIO_H + +#define ARIZONA_GP_FN_TXLRCLK 0x00 +#define ARIZONA_GP_FN_GPIO 0x01 +#define ARIZONA_GP_FN_IRQ1 0x02 +#define ARIZONA_GP_FN_IRQ2 0x03 +#define ARIZONA_GP_FN_OPCLK 0x04 +#define ARIZONA_GP_FN_FLL1_OUT 0x05 +#define ARIZONA_GP_FN_FLL2_OUT 0x06 +#define ARIZONA_GP_FN_PWM1 0x08 +#define ARIZONA_GP_FN_PWM2 0x09 +#define ARIZONA_GP_FN_SYSCLK_UNDERCLOCKED 0x0A +#define ARIZONA_GP_FN_ASYNCCLK_UNDERCLOCKED 0x0B +#define ARIZONA_GP_FN_FLL1_LOCK 0x0C +#define ARIZONA_GP_FN_FLL2_LOCK 0x0D +#define ARIZONA_GP_FN_FLL1_CLOCK_OK 0x0F +#define ARIZONA_GP_FN_FLL2_CLOCK_OK 0x10 +#define ARIZONA_GP_FN_HEADPHONE_DET 0x12 +#define ARIZONA_GP_FN_MIC_DET 0x13 +#define ARIZONA_GP_FN_WSEQ_STATUS 0x15 +#define ARIZONA_GP_FN_CIF_ADDRESS_ERROR 0x16 +#define ARIZONA_GP_FN_ASRC1_LOCK 0x1A +#define ARIZONA_GP_FN_ASRC2_LOCK 0x1B +#define ARIZONA_GP_FN_ASRC_CONFIG_ERROR 0x1C +#define ARIZONA_GP_FN_DRC1_SIGNAL_DETECT 0x1D +#define ARIZONA_GP_FN_DRC1_ANTICLIP 0x1E +#define ARIZONA_GP_FN_DRC1_DECAY 0x1F +#define ARIZONA_GP_FN_DRC1_NOISE 0x20 +#define ARIZONA_GP_FN_DRC1_QUICK_RELEASE 0x21 +#define ARIZONA_GP_FN_DRC2_SIGNAL_DETECT 0x22 +#define ARIZONA_GP_FN_DRC2_ANTICLIP 0x23 +#define ARIZONA_GP_FN_DRC2_DECAY 0x24 +#define ARIZONA_GP_FN_DRC2_NOISE 0x25 +#define ARIZONA_GP_FN_DRC2_QUICK_RELEASE 0x26 +#define ARIZONA_GP_FN_MIXER_DROPPED_SAMPLE 0x27 +#define ARIZONA_GP_FN_AIF1_CONFIG_ERROR 0x28 +#define ARIZONA_GP_FN_AIF2_CONFIG_ERROR 0x29 +#define ARIZONA_GP_FN_AIF3_CONFIG_ERROR 0x2A +#define ARIZONA_GP_FN_SPK_TEMP_SHUTDOWN 0x2B +#define ARIZONA_GP_FN_SPK_TEMP_WARNING 0x2C +#define ARIZONA_GP_FN_UNDERCLOCKED 0x2D +#define ARIZONA_GP_FN_OVERCLOCKED 0x2E +#define ARIZONA_GP_FN_DSP_IRQ1 0x35 +#define ARIZONA_GP_FN_DSP_IRQ2 0x36 +#define ARIZONA_GP_FN_ASYNC_OPCLK 0x3D +#define ARIZONA_GP_FN_BOOT_DONE 0x44 +#define ARIZONA_GP_FN_DSP1_RAM_READY 0x45 +#define ARIZONA_GP_FN_SYSCLK_ENA_STATUS 0x4B +#define ARIZONA_GP_FN_ASYNCCLK_ENA_STATUS 0x4C + +#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */ +#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */ +#define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */ +#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */ +#define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */ +#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */ +#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */ +#define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */ +#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */ +#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */ +#define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */ +#define ARIZONA_GPN_FN_MASK 0x007F /* GPN_DB */ +#define ARIZONA_GPN_FN_SHIFT 0 /* GPN_DB */ +#define ARIZONA_GPN_FN_WIDTH 7 /* GPN_DB */ + +#endif diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 737685e9e85..68029b30c3d 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -309,21 +309,20 @@ struct mlx5_hca_cap { __be16 max_desc_sz_rq; u8 rsvd21[2]; __be16 max_desc_sz_sq_dc; - u8 rsvd22[4]; - __be16 max_qp_mcg; - u8 rsvd23; + __be32 max_qp_mcg; + u8 rsvd22[3]; u8 log_max_mcg; - u8 rsvd24; + u8 rsvd23; u8 log_max_pd; - u8 rsvd25; + u8 rsvd24; u8 log_max_xrcd; - u8 rsvd26[42]; + u8 rsvd25[42]; __be16 log_uar_page_sz; - u8 rsvd27[28]; + u8 rsvd26[28]; u8 log_msx_atomic_size_qp; - u8 rsvd28[2]; + u8 rsvd27[2]; u8 log_msx_atomic_size_dc; - u8 rsvd29[76]; + u8 rsvd28[76]; }; @@ -472,9 +471,8 @@ struct mlx5_eqe_cmd { struct mlx5_eqe_page_req { u8 rsvd0[2]; __be16 func_id; - u8 rsvd1[2]; - __be16 num_pages; - __be32 rsvd2[5]; + __be32 num_pages; + __be32 rsvd1[5]; }; union ev_data { diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 2aa258b0ced..8888381fc15 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -358,7 +358,7 @@ struct mlx5_caps { u32 reserved_lkey; u8 local_ca_ack_delay; u8 log_max_mcg; - u16 max_qp_mcg; + u32 max_qp_mcg; int min_page_sz; }; @@ -691,7 +691,7 @@ void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev); int mlx5_pagealloc_start(struct mlx5_core_dev *dev); void mlx5_pagealloc_stop(struct mlx5_core_dev *dev); void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id, - s16 npages); + s32 npages); int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot); int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev); void mlx5_register_debugfs(void); @@ -731,9 +731,6 @@ void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev); int mlx5_db_alloc(struct mlx5_core_dev *dev, struct mlx5_db *db); void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db); -typedef void (*health_handler_t)(struct pci_dev *pdev, struct health_buffer __iomem *buf, int size); -int mlx5_register_health_report_handler(health_handler_t handler); -void mlx5_unregister_health_report_handler(void); const char *mlx5_command_str(int command); int mlx5_cmdif_debugfs_init(struct mlx5_core_dev *dev); void mlx5_cmdif_debugfs_cleanup(struct mlx5_core_dev *dev); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index fb425aa16c0..faf4b7c1ad1 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -332,6 +332,7 @@ struct mm_struct { unsigned long pgoff, unsigned long flags); #endif unsigned long mmap_base; /* base of mmap area */ + unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */ unsigned long task_size; /* size of task vm space */ unsigned long highest_vm_end; /* highest vma end address */ pgd_t * pgd; diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index 88272591a89..9efc04dd255 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -36,6 +36,7 @@ struct samsung_i2s { */ #define QUIRK_NO_MUXPSR (1 << 2) #define QUIRK_NEED_RSTCLR (1 << 3) +#define QUIRK_SUPPORTS_TDM (1 << 4) /* Quirks of the I2S controller */ u32 quirks; dma_addr_t idma_addr; diff --git a/include/linux/platform_data/omap-abe-twl6040.h b/include/linux/platform_data/omap-abe-twl6040.h deleted file mode 100644 index 5d298ac10fc..00000000000 --- a/include/linux/platform_data/omap-abe-twl6040.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * omap-abe-twl6040.h - ASoC machine driver OMAP4+ devices, header. - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. - * - * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef _OMAP_ABE_TWL6040_H_ -#define _OMAP_ABE_TWL6040_H_ - -/* To select if only one channel is connected in a stereo port */ -#define ABE_TWL6040_LEFT (1 << 0) -#define ABE_TWL6040_RIGHT (1 << 1) - -struct omap_abe_twl6040_data { - char *card_name; - /* Feature flags for connected audio pins */ - u8 has_hs; - u8 has_hf; - bool has_ep; - u8 has_aux; - u8 has_vibra; - bool has_dmic; - bool has_hsmic; - bool has_mainmic; - bool has_submic; - u8 has_afm; - /* Other features */ - bool jack_detection; /* board can detect jack events */ - int mclk_freq; /* MCLK frequency speed for twl6040 */ -}; - -#endif /* _OMAP_ABE_TWL6040_H_ */ diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 467cc6307b6..49444203328 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -21,6 +21,8 @@ #include <linux/list.h> #include <linux/io.h> +#include <linux/of.h> + /* * SSP Serial Port Registers @@ -190,6 +192,8 @@ struct ssp_device { int irq; int drcmr_rx; int drcmr_tx; + + struct device_node *of_node; }; /** @@ -218,11 +222,18 @@ static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg) #ifdef CONFIG_ARCH_PXA struct ssp_device *pxa_ssp_request(int port, const char *label); void pxa_ssp_free(struct ssp_device *); +struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node, + const char *label); #else static inline struct ssp_device *pxa_ssp_request(int port, const char *label) { return NULL; } +static inline struct ssp_device *pxa_ssp_request_of(const struct device_node *n, + const char *name) +{ + return NULL; +} static inline void pxa_ssp_free(struct ssp_device *ssp) {} #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index d722490da03..078066daffd 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1532,6 +1532,8 @@ static inline pid_t task_pgrp_nr(struct task_struct *tsk) * Test if a process is not yet dead (at most zombie state) * If pid_alive fails, then pointers within the task structure * can be stale and must not be dereferenced. + * + * Return: 1 if the process is alive. 0 otherwise. */ static inline int pid_alive(struct task_struct *p) { @@ -1543,6 +1545,8 @@ static inline int pid_alive(struct task_struct *p) * @tsk: Task structure to be checked. * * Check if a task structure is the first user space task the kernel created. + * + * Return: 1 if the task structure is init. 0 otherwise. */ static inline int is_global_init(struct task_struct *tsk) { @@ -1894,6 +1898,8 @@ extern struct task_struct *idle_task(int cpu); /** * is_idle_task - is the specified task an idle task? * @p: the task in question. + * + * Return: 1 if @p is an idle task. 0 otherwise. */ static inline bool is_idle_task(const struct task_struct *p) { diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 7d537ced949..75f34949d9a 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -117,9 +117,17 @@ do { \ #endif /*arch_spin_is_contended*/ #endif -/* The lock does not imply full memory barrier. */ -#ifndef ARCH_HAS_SMP_MB_AFTER_LOCK -static inline void smp_mb__after_lock(void) { smp_mb(); } +/* + * Despite its name it doesn't necessarily has to be a full barrier. + * It should only guarantee that a STORE before the critical section + * can not be reordered with a LOAD inside this section. + * spin_lock() is the one-way barrier, this LOAD can not escape out + * of the region. So the default implementation simply ensures that + * a STORE can not move into the critical section, smp_wmb() should + * serialize it with another STORE done by spin_lock(). + */ +#ifndef smp_mb__before_spinlock +#define smp_mb__before_spinlock() smp_wmb() #endif /** diff --git a/include/linux/swapops.h b/include/linux/swapops.h index c5fd30d2a41..8d4fa82bfb9 100644 --- a/include/linux/swapops.h +++ b/include/linux/swapops.h @@ -67,6 +67,8 @@ static inline swp_entry_t pte_to_swp_entry(pte_t pte) swp_entry_t arch_entry; BUG_ON(pte_file(pte)); + if (pte_swp_soft_dirty(pte)) + pte = pte_swp_clear_soft_dirty(pte); arch_entry = __pte_to_swp_entry(pte); return swp_entry(__swp_type(arch_entry), __swp_offset(arch_entry)); } diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 4147d700a29..84662ecc7b5 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -802,9 +802,14 @@ asmlinkage long sys_vfork(void); asmlinkage long sys_clone(unsigned long, unsigned long, int __user *, int, int __user *); #else +#ifdef CONFIG_CLONE_BACKWARDS3 +asmlinkage long sys_clone(unsigned long, unsigned long, int, int __user *, + int __user *, int); +#else asmlinkage long sys_clone(unsigned long, unsigned long, int __user *, int __user *, int); #endif +#endif asmlinkage long sys_execve(const char __user *filename, const char __user *const __user *argv, diff --git a/include/linux/wait.h b/include/linux/wait.h index f487a4750b7..a67fc163559 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -811,6 +811,63 @@ do { \ __ret; \ }) +#define __wait_event_interruptible_lock_irq_timeout(wq, condition, \ + lock, ret) \ +do { \ + DEFINE_WAIT(__wait); \ + \ + for (;;) { \ + prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (signal_pending(current)) { \ + ret = -ERESTARTSYS; \ + break; \ + } \ + spin_unlock_irq(&lock); \ + ret = schedule_timeout(ret); \ + spin_lock_irq(&lock); \ + if (!ret) \ + break; \ + } \ + finish_wait(&wq, &__wait); \ +} while (0) + +/** + * wait_event_interruptible_lock_irq_timeout - sleep until a condition gets true or a timeout elapses. + * The condition is checked under the lock. This is expected + * to be called with the lock taken. + * @wq: the waitqueue to wait on + * @condition: a C expression for the event to wait for + * @lock: a locked spinlock_t, which will be released before schedule() + * and reacquired afterwards. + * @timeout: timeout, in jiffies + * + * The process is put to sleep (TASK_INTERRUPTIBLE) until the + * @condition evaluates to true or signal is received. The @condition is + * checked each time the waitqueue @wq is woken up. + * + * wake_up() has to be called after changing any variable that could + * change the result of the wait condition. + * + * This is supposed to be called while holding the lock. The lock is + * dropped before going to sleep and is reacquired afterwards. + * + * The function returns 0 if the @timeout elapsed, -ERESTARTSYS if it + * was interrupted by a signal, and the remaining jiffies otherwise + * if the condition evaluated to true before the timeout elapsed. + */ +#define wait_event_interruptible_lock_irq_timeout(wq, condition, lock, \ + timeout) \ +({ \ + int __ret = timeout; \ + \ + if (!(condition)) \ + __wait_event_interruptible_lock_irq_timeout( \ + wq, condition, lock, __ret); \ + __ret; \ +}) + /* * These are the old interfaces to sleep waiting for an event. diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h index f18b91966d3..8a358a2c97e 100644 --- a/include/net/busy_poll.h +++ b/include/net/busy_poll.h @@ -122,7 +122,7 @@ static inline bool sk_busy_loop(struct sock *sk, int nonblock) if (rc > 0) /* local bh are disabled so it is ok to use _BH */ NET_ADD_STATS_BH(sock_net(sk), - LINUX_MIB_LOWLATENCYRXPACKETS, rc); + LINUX_MIB_BUSYPOLLRXPACKETS, rc); } while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) && !need_resched() && !busy_loop_timeout(end_time)); @@ -162,11 +162,6 @@ static inline bool sk_can_busy_loop(struct sock *sk) return false; } -static inline bool sk_busy_poll(struct sock *sk, int nonblock) -{ - return false; -} - static inline void skb_mark_napi_id(struct sk_buff *skb, struct napi_struct *napi) { diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 260f83f16bc..f667248202b 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -135,6 +135,8 @@ extern void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, extern void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu); extern void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark); +extern void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, + u32 mark); extern void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk); struct netlink_callback; diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 781b3cf86a2..a354db5b766 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -145,20 +145,6 @@ static inline u8 ip_tunnel_ecn_encap(u8 tos, const struct iphdr *iph, return INET_ECN_encapsulate(tos, inner); } -static inline void tunnel_ip_select_ident(struct sk_buff *skb, - const struct iphdr *old_iph, - struct dst_entry *dst) -{ - struct iphdr *iph = ip_hdr(skb); - - /* Use inner packet iph-id if possible. */ - if (skb->protocol == htons(ETH_P_IP) && old_iph->id) - iph->id = old_iph->id; - else - __ip_select_ident(iph, dst, - (skb_shinfo(skb)->gso_segs ?: 1) - 1); -} - int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto); int iptunnel_xmit(struct net *net, struct rtable *rt, struct sk_buff *skb, diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 6eab63363e5..e5ae0c50fa9 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -683,13 +683,19 @@ struct psched_ratecfg { u64 rate_bytes_ps; /* bytes per second */ u32 mult; u16 overhead; + u8 linklayer; u8 shift; }; static inline u64 psched_l2t_ns(const struct psched_ratecfg *r, unsigned int len) { - return ((u64)(len + r->overhead) * r->mult) >> r->shift; + len += r->overhead; + + if (unlikely(r->linklayer == TC_LINKLAYER_ATM)) + return ((u64)(DIV_ROUND_UP(len,48)*53) * r->mult) >> r->shift; + + return ((u64)len * r->mult) >> r->shift; } extern void psched_ratecfg_precompute(struct psched_ratecfg *r, const struct tc_ratespec *conf); @@ -700,6 +706,7 @@ static inline void psched_ratecfg_getrate(struct tc_ratespec *res, memset(res, 0, sizeof(*res)); res->rate = r->rate_bytes_ps; res->overhead = r->overhead; + res->linklayer = (r->linklayer & TC_LINKLAYER_MASK); } #endif diff --git a/include/sound/core.h b/include/sound/core.h index c586617cfa0..2a14f1f02d4 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -27,6 +27,7 @@ #include <linux/rwsem.h> /* struct rw_semaphore */ #include <linux/pm.h> /* pm_message_t */ #include <linux/stringify.h> +#include <linux/printk.h> /* number of supported soundcards */ #ifdef CONFIG_SND_DYNAMIC_MINORS @@ -376,6 +377,11 @@ void __snd_printk(unsigned int level, const char *file, int line, #define snd_BUG() WARN(1, "BUG?\n") /** + * Suppress high rates of output when CONFIG_SND_DEBUG is enabled. + */ +#define snd_printd_ratelimit() printk_ratelimit() + +/** * snd_BUG_ON - debugging check macro * @cond: condition to evaluate * @@ -398,6 +404,8 @@ static inline void _snd_printd(int level, const char *format, ...) {} unlikely(__ret_warn_on); \ }) +static inline bool snd_printd_ratelimit(void) { return false; } + #endif /* CONFIG_SND_DEBUG */ #ifdef CONFIG_SND_DEBUG_VERBOSE diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 2fd3d251d9a..56e818e4a1c 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -6,13 +6,6 @@ /* PCM */ -struct pxa2xx_pcm_dma_params { - char *name; /* stream identifier */ - u32 dcmd; /* DMA descriptor dcmd field */ - volatile u32 *drcmr; /* the DMA request channel to use */ - u32 dev_addr; /* device physical address for DMA */ -}; - extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream); diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h new file mode 100644 index 00000000000..d35412ae03b --- /dev/null +++ b/include/sound/rcar_snd.h @@ -0,0 +1,84 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef RCAR_SND_H +#define RCAR_SND_H + +#include <linux/sh_clk.h> + +#define RSND_GEN1_SRU 0 +#define RSND_GEN1_ADG 1 +#define RSND_GEN1_SSI 2 + +#define RSND_GEN2_SRU 0 +#define RSND_GEN2_ADG 1 +#define RSND_GEN2_SSIU 2 +#define RSND_GEN2_SSI 3 + +#define RSND_BASE_MAX 4 + +/* + * flags + * + * 0xAB000000 + * + * A : clock sharing settings + * B : SSI direction + */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 31) +#define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ +#define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ +#define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */ + +#define RSND_SSI_PLAY (1 << 24) + +#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \ +{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI_UNUSED \ +{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 } + +struct rsnd_ssi_platform_info { + int dai_id; + int dma_id; + int pio_irq; + u32 flags; +}; + +/* + * flags + */ +#define RSND_SCU_USB_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ + +struct rsnd_scu_platform_info { + u32 flags; +}; + +/* + * flags + * + * 0x0000000A + * + * A : generation + */ +#define RSND_GEN1 (1 << 0) /* fixme */ +#define RSND_GEN2 (2 << 0) /* fixme */ + +struct rcar_snd_info { + u32 flags; + struct rsnd_ssi_platform_info *ssi_info; + int ssi_info_nr; + struct rsnd_scu_platform_info *scu_info; + int scu_info_nr; + int (*start)(int id); + int (*stop)(int id); +}; + +#endif diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c728d28ae9a..27a72d5d4b0 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -413,7 +413,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, struct snd_soc_dapm_widget *sink); /* dapm path setup */ -int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); +int snd_soc_dapm_new_widgets(struct snd_soc_card *card); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); diff --git a/include/sound/soc.h b/include/sound/soc.h index 6201c6ede8b..71848058ffd 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -234,7 +234,7 @@ .private_value = xdata } #define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_ext, \ + .info = snd_soc_info_enum_double, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = (unsigned long)&xenum } @@ -468,6 +468,8 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev); /* *Controls @@ -475,6 +477,8 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, void *data, const char *long_name, const char *prefix); +struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, + const char *name); int snd_soc_add_codec_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_add_platform_controls(struct snd_soc_platform *platform, @@ -485,8 +489,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, @@ -497,8 +499,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); #define snd_soc_info_bool_ext snd_ctl_boolean_mono_info int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); @@ -697,7 +697,6 @@ struct snd_soc_codec { unsigned int probed:1; /* Codec has been probed */ unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ - unsigned int sysfs_registered:1; /* codec has been sysfs registered */ unsigned int cache_init:1; /* codec cache has been initialized */ unsigned int using_regmap:1; /* using regmap access */ u32 cache_only; /* Suppress writes to hardware */ @@ -705,7 +704,6 @@ struct snd_soc_codec { /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ - enum snd_soc_control_type control_type; hw_write_t hw_write; unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int); @@ -724,7 +722,6 @@ struct snd_soc_codec { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_codec_root; struct dentry *debugfs_reg; - struct dentry *debugfs_dapm; #endif }; @@ -849,7 +846,6 @@ struct snd_soc_platform { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_platform_root; - struct dentry *debugfs_dapm; #endif }; diff --git a/include/uapi/linux/ip.h b/include/uapi/linux/ip.h index 6cf06bfd841..2fee45bdec0 100644 --- a/include/uapi/linux/ip.h +++ b/include/uapi/linux/ip.h @@ -133,4 +133,38 @@ struct ip_beet_phdr { __u8 reserved; }; +/* index values for the variables in ipv4_devconf */ +enum +{ + IPV4_DEVCONF_FORWARDING=1, + IPV4_DEVCONF_MC_FORWARDING, + IPV4_DEVCONF_PROXY_ARP, + IPV4_DEVCONF_ACCEPT_REDIRECTS, + IPV4_DEVCONF_SECURE_REDIRECTS, + IPV4_DEVCONF_SEND_REDIRECTS, + IPV4_DEVCONF_SHARED_MEDIA, + IPV4_DEVCONF_RP_FILTER, + IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, + IPV4_DEVCONF_BOOTP_RELAY, + IPV4_DEVCONF_LOG_MARTIANS, + IPV4_DEVCONF_TAG, + IPV4_DEVCONF_ARPFILTER, + IPV4_DEVCONF_MEDIUM_ID, + IPV4_DEVCONF_NOXFRM, + IPV4_DEVCONF_NOPOLICY, + IPV4_DEVCONF_FORCE_IGMP_VERSION, + IPV4_DEVCONF_ARP_ANNOUNCE, + IPV4_DEVCONF_ARP_IGNORE, + IPV4_DEVCONF_PROMOTE_SECONDARIES, + IPV4_DEVCONF_ARP_ACCEPT, + IPV4_DEVCONF_ARP_NOTIFY, + IPV4_DEVCONF_ACCEPT_LOCAL, + IPV4_DEVCONF_SRC_VMARK, + IPV4_DEVCONF_PROXY_ARP_PVLAN, + IPV4_DEVCONF_ROUTE_LOCALNET, + __IPV4_DEVCONF_MAX +}; + +#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1) + #endif /* _UAPI_LINUX_IP_H */ diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index dbd71b0c7d8..09d62b9228f 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -73,9 +73,17 @@ struct tc_estimator { #define TC_H_ROOT (0xFFFFFFFFU) #define TC_H_INGRESS (0xFFFFFFF1U) +/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */ +enum tc_link_layer { + TC_LINKLAYER_UNAWARE, /* Indicate unaware old iproute2 util */ + TC_LINKLAYER_ETHERNET, + TC_LINKLAYER_ATM, +}; +#define TC_LINKLAYER_MASK 0x0F /* limit use to lower 4 bits */ + struct tc_ratespec { unsigned char cell_log; - unsigned char __reserved; + __u8 linklayer; /* lower 4 bits */ unsigned short overhead; short cell_align; unsigned short mpu; diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index af0a674cc67..a1356d3b54d 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -253,7 +253,7 @@ enum LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */ LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */ LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */ - LINUX_MIB_LOWLATENCYRXPACKETS, /* LowLatencyRxPackets */ + LINUX_MIB_BUSYPOLLRXPACKETS, /* BusyPollRxPackets */ __LINUX_MIB_MAX }; diff --git a/include/uapi/sound/hdspm.h b/include/uapi/sound/hdspm.h index 1f59ea2a4a7..d956c3593f6 100644 --- a/include/uapi/sound/hdspm.h +++ b/include/uapi/sound/hdspm.h @@ -111,7 +111,7 @@ struct hdspm_ltc { enum hdspm_ltc_input_format input_format; }; -#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_mixer_ioctl) +#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_ltc) /** * The status data reflects the device's current state diff --git a/init/Kconfig b/init/Kconfig index 247084be059..fed81b576f2 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -955,7 +955,7 @@ config MEMCG_SWAP_ENABLED Memory Resource Controller Swap Extension comes with its price in a bigger memory consumption. General purpose distribution kernels which want to enable the feature but keep it disabled by default - and let the user enable it by swapaccount boot command line + and let the user enable it by swapaccount=1 boot command line parameter should have this option unselected. For those who want to have the feature enabled by default should select this option (if, for some reason, they need to disable it diff --git a/kernel/cpuset.c b/kernel/cpuset.c index e5657788fed..ea1966db34f 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -475,13 +475,17 @@ static int validate_change(const struct cpuset *cur, const struct cpuset *trial) /* * Cpusets with tasks - existing or newly being attached - can't - * have empty cpus_allowed or mems_allowed. + * be changed to have empty cpus_allowed or mems_allowed. */ ret = -ENOSPC; - if ((cgroup_task_count(cur->css.cgroup) || cur->attach_in_progress) && - (cpumask_empty(trial->cpus_allowed) && - nodes_empty(trial->mems_allowed))) - goto out; + if ((cgroup_task_count(cur->css.cgroup) || cur->attach_in_progress)) { + if (!cpumask_empty(cur->cpus_allowed) && + cpumask_empty(trial->cpus_allowed)) + goto out; + if (!nodes_empty(cur->mems_allowed) && + nodes_empty(trial->mems_allowed)) + goto out; + } ret = 0; out: @@ -1608,11 +1612,13 @@ static int cpuset_write_u64(struct cgroup *cgrp, struct cftype *cft, u64 val) { struct cpuset *cs = cgroup_cs(cgrp); cpuset_filetype_t type = cft->private; - int retval = -ENODEV; + int retval = 0; mutex_lock(&cpuset_mutex); - if (!is_cpuset_online(cs)) + if (!is_cpuset_online(cs)) { + retval = -ENODEV; goto out_unlock; + } switch (type) { case FILE_CPU_EXCLUSIVE: diff --git a/kernel/fork.c b/kernel/fork.c index 403d2bb8a96..e23bb19e2a3 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1679,6 +1679,12 @@ SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags, int __user *, parent_tidptr, int __user *, child_tidptr, int, tls_val) +#elif defined(CONFIG_CLONE_BACKWARDS3) +SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp, + int, stack_size, + int __user *, parent_tidptr, + int __user *, child_tidptr, + int, tls_val) #else SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, int __user *, parent_tidptr, diff --git a/kernel/mutex.c b/kernel/mutex.c index ff05f4bd86e..a52ee7bb830 100644 --- a/kernel/mutex.c +++ b/kernel/mutex.c @@ -686,7 +686,7 @@ __ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) might_sleep(); ret = __mutex_lock_common(&lock->base, TASK_UNINTERRUPTIBLE, 0, &ctx->dep_map, _RET_IP_, ctx); - if (!ret && ctx->acquired > 0) + if (!ret && ctx->acquired > 1) return ww_mutex_deadlock_injection(lock, ctx); return ret; @@ -702,7 +702,7 @@ __ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) ret = __mutex_lock_common(&lock->base, TASK_INTERRUPTIBLE, 0, &ctx->dep_map, _RET_IP_, ctx); - if (!ret && ctx->acquired > 0) + if (!ret && ctx->acquired > 1) return ww_mutex_deadlock_injection(lock, ctx); return ret; diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 06fe28589e9..a394297f8b2 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -296,6 +296,17 @@ int pm_qos_request_active(struct pm_qos_request *req) } EXPORT_SYMBOL_GPL(pm_qos_request_active); +static void __pm_qos_update_request(struct pm_qos_request *req, + s32 new_value) +{ + trace_pm_qos_update_request(req->pm_qos_class, new_value); + + if (new_value != req->node.prio) + pm_qos_update_target( + pm_qos_array[req->pm_qos_class]->constraints, + &req->node, PM_QOS_UPDATE_REQ, new_value); +} + /** * pm_qos_work_fn - the timeout handler of pm_qos_update_request_timeout * @work: work struct for the delayed work (timeout) @@ -308,7 +319,7 @@ static void pm_qos_work_fn(struct work_struct *work) struct pm_qos_request, work); - pm_qos_update_request(req, PM_QOS_DEFAULT_VALUE); + __pm_qos_update_request(req, PM_QOS_DEFAULT_VALUE); } /** @@ -364,12 +375,7 @@ void pm_qos_update_request(struct pm_qos_request *req, } cancel_delayed_work_sync(&req->work); - - trace_pm_qos_update_request(req->pm_qos_class, new_value); - if (new_value != req->node.prio) - pm_qos_update_target( - pm_qos_array[req->pm_qos_class]->constraints, - &req->node, PM_QOS_UPDATE_REQ, new_value); + __pm_qos_update_request(req, new_value); } EXPORT_SYMBOL_GPL(pm_qos_update_request); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index b7c32cb7bfe..05c39f03031 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -933,6 +933,8 @@ static int effective_prio(struct task_struct *p) /** * task_curr - is this task currently executing on a CPU? * @p: the task in question. + * + * Return: 1 if the task is currently executing. 0 otherwise. */ inline int task_curr(const struct task_struct *p) { @@ -1482,7 +1484,7 @@ static void ttwu_queue(struct task_struct *p, int cpu) * the simpler "current->state = TASK_RUNNING" to mark yourself * runnable without the overhead of this. * - * Returns %true if @p was woken up, %false if it was already running + * Return: %true if @p was woken up, %false if it was already running. * or @state didn't match @p's state. */ static int @@ -1491,7 +1493,13 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) unsigned long flags; int cpu, success = 0; - smp_wmb(); + /* + * If we are going to wake up a thread waiting for CONDITION we + * need to ensure that CONDITION=1 done by the caller can not be + * reordered with p->state check below. This pairs with mb() in + * set_current_state() the waiting thread does. + */ + smp_mb__before_spinlock(); raw_spin_lock_irqsave(&p->pi_lock, flags); if (!(p->state & state)) goto out; @@ -1577,8 +1585,9 @@ out: * @p: The process to be woken up. * * Attempt to wake up the nominated process and move it to the set of runnable - * processes. Returns 1 if the process was woken up, 0 if it was already - * running. + * processes. + * + * Return: 1 if the process was woken up, 0 if it was already running. * * It may be assumed that this function implies a write memory barrier before * changing the task state if and only if any tasks are woken up. @@ -2191,6 +2200,8 @@ void scheduler_tick(void) * This makes sure that uptime, CFS vruntime, load * balancing, etc... continue to move forward, even * with a very low granularity. + * + * Return: Maximum deferment in nanoseconds. */ u64 scheduler_tick_max_deferment(void) { @@ -2394,6 +2405,12 @@ need_resched: if (sched_feat(HRTICK)) hrtick_clear(rq); + /* + * Make sure that signal_pending_state()->signal_pending() below + * can't be reordered with __set_current_state(TASK_INTERRUPTIBLE) + * done by the caller to avoid the race with signal_wake_up(). + */ + smp_mb__before_spinlock(); raw_spin_lock_irq(&rq->lock); switch_count = &prev->nivcsw; @@ -2796,8 +2813,8 @@ EXPORT_SYMBOL(wait_for_completion); * specified timeout to expire. The timeout is in jiffies. It is not * interruptible. * - * The return value is 0 if timed out, and positive (at least 1, or number of - * jiffies left till timeout) if completed. + * Return: 0 if timed out, and positive (at least 1, or number of jiffies left + * till timeout) if completed. */ unsigned long __sched wait_for_completion_timeout(struct completion *x, unsigned long timeout) @@ -2829,8 +2846,8 @@ EXPORT_SYMBOL(wait_for_completion_io); * specified timeout to expire. The timeout is in jiffies. It is not * interruptible. The caller is accounted as waiting for IO. * - * The return value is 0 if timed out, and positive (at least 1, or number of - * jiffies left till timeout) if completed. + * Return: 0 if timed out, and positive (at least 1, or number of jiffies left + * till timeout) if completed. */ unsigned long __sched wait_for_completion_io_timeout(struct completion *x, unsigned long timeout) @@ -2846,7 +2863,7 @@ EXPORT_SYMBOL(wait_for_completion_io_timeout); * This waits for completion of a specific task to be signaled. It is * interruptible. * - * The return value is -ERESTARTSYS if interrupted, 0 if completed. + * Return: -ERESTARTSYS if interrupted, 0 if completed. */ int __sched wait_for_completion_interruptible(struct completion *x) { @@ -2865,8 +2882,8 @@ EXPORT_SYMBOL(wait_for_completion_interruptible); * This waits for either a completion of a specific task to be signaled or for a * specified timeout to expire. It is interruptible. The timeout is in jiffies. * - * The return value is -ERESTARTSYS if interrupted, 0 if timed out, - * positive (at least 1, or number of jiffies left till timeout) if completed. + * Return: -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1, + * or number of jiffies left till timeout) if completed. */ long __sched wait_for_completion_interruptible_timeout(struct completion *x, @@ -2883,7 +2900,7 @@ EXPORT_SYMBOL(wait_for_completion_interruptible_timeout); * This waits to be signaled for completion of a specific task. It can be * interrupted by a kill signal. * - * The return value is -ERESTARTSYS if interrupted, 0 if completed. + * Return: -ERESTARTSYS if interrupted, 0 if completed. */ int __sched wait_for_completion_killable(struct completion *x) { @@ -2903,8 +2920,8 @@ EXPORT_SYMBOL(wait_for_completion_killable); * signaled or for a specified timeout to expire. It can be * interrupted by a kill signal. The timeout is in jiffies. * - * The return value is -ERESTARTSYS if interrupted, 0 if timed out, - * positive (at least 1, or number of jiffies left till timeout) if completed. + * Return: -ERESTARTSYS if interrupted, 0 if timed out, positive (at least 1, + * or number of jiffies left till timeout) if completed. */ long __sched wait_for_completion_killable_timeout(struct completion *x, @@ -2918,7 +2935,7 @@ EXPORT_SYMBOL(wait_for_completion_killable_timeout); * try_wait_for_completion - try to decrement a completion without blocking * @x: completion structure * - * Returns: 0 if a decrement cannot be done without blocking + * Return: 0 if a decrement cannot be done without blocking * 1 if a decrement succeeded. * * If a completion is being used as a counting completion, @@ -2945,7 +2962,7 @@ EXPORT_SYMBOL(try_wait_for_completion); * completion_done - Test to see if a completion has any waiters * @x: completion structure * - * Returns: 0 if there are waiters (wait_for_completion() in progress) + * Return: 0 if there are waiters (wait_for_completion() in progress) * 1 if there are no waiters. * */ @@ -3182,7 +3199,7 @@ SYSCALL_DEFINE1(nice, int, increment) * task_prio - return the priority value of a given task. * @p: the task in question. * - * This is the priority value as seen by users in /proc. + * Return: The priority value as seen by users in /proc. * RT tasks are offset by -200. Normal tasks are centered * around 0, value goes from -16 to +15. */ @@ -3194,6 +3211,8 @@ int task_prio(const struct task_struct *p) /** * task_nice - return the nice value of a given task. * @p: the task in question. + * + * Return: The nice value [ -20 ... 0 ... 19 ]. */ int task_nice(const struct task_struct *p) { @@ -3204,6 +3223,8 @@ EXPORT_SYMBOL(task_nice); /** * idle_cpu - is a given cpu idle currently? * @cpu: the processor in question. + * + * Return: 1 if the CPU is currently idle. 0 otherwise. */ int idle_cpu(int cpu) { @@ -3226,6 +3247,8 @@ int idle_cpu(int cpu) /** * idle_task - return the idle task for a given cpu. * @cpu: the processor in question. + * + * Return: The idle task for the cpu @cpu. */ struct task_struct *idle_task(int cpu) { @@ -3235,6 +3258,8 @@ struct task_struct *idle_task(int cpu) /** * find_process_by_pid - find a process with a matching PID value. * @pid: the pid in question. + * + * The task of @pid, if found. %NULL otherwise. */ static struct task_struct *find_process_by_pid(pid_t pid) { @@ -3432,6 +3457,8 @@ recheck: * @policy: new policy. * @param: structure containing the new RT priority. * + * Return: 0 on success. An error code otherwise. + * * NOTE that the task may be already dead. */ int sched_setscheduler(struct task_struct *p, int policy, @@ -3451,6 +3478,8 @@ EXPORT_SYMBOL_GPL(sched_setscheduler); * current context has permission. For example, this is needed in * stop_machine(): we create temporary high priority worker threads, * but our caller might not have that capability. + * + * Return: 0 on success. An error code otherwise. */ int sched_setscheduler_nocheck(struct task_struct *p, int policy, const struct sched_param *param) @@ -3485,6 +3514,8 @@ do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param) * @pid: the pid in question. * @policy: new policy. * @param: structure containing the new RT priority. + * + * Return: 0 on success. An error code otherwise. */ SYSCALL_DEFINE3(sched_setscheduler, pid_t, pid, int, policy, struct sched_param __user *, param) @@ -3500,6 +3531,8 @@ SYSCALL_DEFINE3(sched_setscheduler, pid_t, pid, int, policy, * sys_sched_setparam - set/change the RT priority of a thread * @pid: the pid in question. * @param: structure containing the new RT priority. + * + * Return: 0 on success. An error code otherwise. */ SYSCALL_DEFINE2(sched_setparam, pid_t, pid, struct sched_param __user *, param) { @@ -3509,6 +3542,9 @@ SYSCALL_DEFINE2(sched_setparam, pid_t, pid, struct sched_param __user *, param) /** * sys_sched_getscheduler - get the policy (scheduling class) of a thread * @pid: the pid in question. + * + * Return: On success, the policy of the thread. Otherwise, a negative error + * code. */ SYSCALL_DEFINE1(sched_getscheduler, pid_t, pid) { @@ -3535,6 +3571,9 @@ SYSCALL_DEFINE1(sched_getscheduler, pid_t, pid) * sys_sched_getparam - get the RT priority of a thread * @pid: the pid in question. * @param: structure containing the RT priority. + * + * Return: On success, 0 and the RT priority is in @param. Otherwise, an error + * code. */ SYSCALL_DEFINE2(sched_getparam, pid_t, pid, struct sched_param __user *, param) { @@ -3659,6 +3698,8 @@ static int get_user_cpu_mask(unsigned long __user *user_mask_ptr, unsigned len, * @pid: pid of the process * @len: length in bytes of the bitmask pointed to by user_mask_ptr * @user_mask_ptr: user-space pointer to the new cpu mask + * + * Return: 0 on success. An error code otherwise. */ SYSCALL_DEFINE3(sched_setaffinity, pid_t, pid, unsigned int, len, unsigned long __user *, user_mask_ptr) @@ -3710,6 +3751,8 @@ out_unlock: * @pid: pid of the process * @len: length in bytes of the bitmask pointed to by user_mask_ptr * @user_mask_ptr: user-space pointer to hold the current cpu mask + * + * Return: 0 on success. An error code otherwise. */ SYSCALL_DEFINE3(sched_getaffinity, pid_t, pid, unsigned int, len, unsigned long __user *, user_mask_ptr) @@ -3744,6 +3787,8 @@ SYSCALL_DEFINE3(sched_getaffinity, pid_t, pid, unsigned int, len, * * This function yields the current CPU to other tasks. If there are no * other threads running on this CPU then this function will return. + * + * Return: 0. */ SYSCALL_DEFINE0(sched_yield) { @@ -3869,7 +3914,7 @@ EXPORT_SYMBOL(yield); * It's the caller's job to ensure that the target task struct * can't go away on us before we can do any checks. * - * Returns: + * Return: * true (>0) if we indeed boosted the target task. * false (0) if we failed to boost the target. * -ESRCH if there's no task to yield to. @@ -3972,8 +4017,9 @@ long __sched io_schedule_timeout(long timeout) * sys_sched_get_priority_max - return maximum RT priority. * @policy: scheduling class. * - * this syscall returns the maximum rt_priority that can be used - * by a given scheduling class. + * Return: On success, this syscall returns the maximum + * rt_priority that can be used by a given scheduling class. + * On failure, a negative error code is returned. */ SYSCALL_DEFINE1(sched_get_priority_max, int, policy) { @@ -3997,8 +4043,9 @@ SYSCALL_DEFINE1(sched_get_priority_max, int, policy) * sys_sched_get_priority_min - return minimum RT priority. * @policy: scheduling class. * - * this syscall returns the minimum rt_priority that can be used - * by a given scheduling class. + * Return: On success, this syscall returns the minimum + * rt_priority that can be used by a given scheduling class. + * On failure, a negative error code is returned. */ SYSCALL_DEFINE1(sched_get_priority_min, int, policy) { @@ -4024,6 +4071,9 @@ SYSCALL_DEFINE1(sched_get_priority_min, int, policy) * * this syscall writes the default timeslice value of a given process * into the user-space timespec buffer. A value of '0' means infinity. + * + * Return: On success, 0 and the timeslice is in @interval. Otherwise, + * an error code. */ SYSCALL_DEFINE2(sched_rr_get_interval, pid_t, pid, struct timespec __user *, interval) @@ -6632,6 +6682,8 @@ void normalize_rt_tasks(void) * @cpu: the processor in question. * * ONLY VALID WHEN THE WHOLE SYSTEM IS STOPPED! + * + * Return: The current task for @cpu. */ struct task_struct *curr_task(int cpu) { diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c index 1095e878a46..8b836b376d9 100644 --- a/kernel/sched/cpupri.c +++ b/kernel/sched/cpupri.c @@ -62,7 +62,7 @@ static int convert_prio(int prio) * any discrepancies created by racing against the uncertainty of the current * priority configuration. * - * Returns: (int)bool - CPUs were found + * Return: (int)bool - CPUs were found */ int cpupri_find(struct cpupri *cp, struct task_struct *p, struct cpumask *lowest_mask) @@ -203,7 +203,7 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri) * cpupri_init - initialize the cpupri structure * @cp: The cpupri context * - * Returns: -ENOMEM if memory fails. + * Return: -ENOMEM on memory allocation failure. */ int cpupri_init(struct cpupri *cp) { diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 9565645e320..68f1609ca14 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2032,6 +2032,7 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) */ update_entity_load_avg(curr, 1); update_cfs_rq_blocked_load(cfs_rq, 1); + update_cfs_shares(cfs_rq); #ifdef CONFIG_SCHED_HRTICK /* @@ -4280,6 +4281,8 @@ struct sg_lb_stats { * get_sd_load_idx - Obtain the load index for a given sched domain. * @sd: The sched_domain whose load_idx is to be obtained. * @idle: The Idle status of the CPU for whose sd load_icx is obtained. + * + * Return: The load index. */ static inline int get_sd_load_idx(struct sched_domain *sd, enum cpu_idle_type idle) @@ -4574,6 +4577,9 @@ static inline void update_sg_lb_stats(struct lb_env *env, * * Determine if @sg is a busier group than the previously selected * busiest group. + * + * Return: %true if @sg is a busier group than the previously selected + * busiest group. %false otherwise. */ static bool update_sd_pick_busiest(struct lb_env *env, struct sd_lb_stats *sds, @@ -4691,7 +4697,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, * assuming lower CPU number will be equivalent to lower a SMT thread * number. * - * Returns 1 when packing is required and a task should be moved to + * Return: 1 when packing is required and a task should be moved to * this CPU. The amount of the imbalance is returned in *imbalance. * * @env: The load balancing environment. @@ -4869,7 +4875,7 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s * @balance: Pointer to a variable indicating if this_cpu * is the appropriate cpu to perform load balancing at this_level. * - * Returns: - the busiest group if imbalance exists. + * Return: - The busiest group if imbalance exists. * - If no imbalance and user has opted for power-savings balance, * return the least loaded group whose CPUs can be * put to idle by rebalancing its tasks onto our group. diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c index a326f27d7f0..0b479a6a22b 100644 --- a/kernel/time/sched_clock.c +++ b/kernel/time/sched_clock.c @@ -121,7 +121,7 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) BUG_ON(bits > 32); WARN_ON(!irqs_disabled()); read_sched_clock = read; - sched_clock_mask = (1 << bits) - 1; + sched_clock_mask = (1ULL << bits) - 1; cd.rate = rate; /* calculate the mult/shift to convert counter ticks to ns. */ diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index e77edc97e03..e8a1516cc0a 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -182,7 +182,8 @@ static bool can_stop_full_tick(void) * Don't allow the user to think they can get * full NO_HZ with this machine. */ - WARN_ONCE(1, "NO_HZ FULL will not work with unstable sched clock"); + WARN_ONCE(have_nohz_full_mask, + "NO_HZ FULL will not work with unstable sched clock"); return false; } #endif @@ -343,8 +344,6 @@ static int tick_nohz_init_all(void) void __init tick_nohz_init(void) { - int cpu; - if (!have_nohz_full_mask) { if (tick_nohz_init_all() < 0) return; diff --git a/kernel/wait.c b/kernel/wait.c index dec68bd4e9d..d550920e040 100644 --- a/kernel/wait.c +++ b/kernel/wait.c @@ -363,8 +363,7 @@ EXPORT_SYMBOL(out_of_line_wait_on_atomic_t); /** * wake_up_atomic_t - Wake up a waiter on a atomic_t - * @word: The word being waited on, a kernel virtual address - * @bit: The bit of the word being waited on + * @p: The atomic_t being waited on, a kernel virtual address * * Wake up anyone waiting for the atomic_t to go to zero. * diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c index fd94058bd7f..28321d8f75e 100644 --- a/lib/lz4/lz4_compress.c +++ b/lib/lz4/lz4_compress.c @@ -437,7 +437,7 @@ int lz4_compress(const unsigned char *src, size_t src_len, exit: return ret; } -EXPORT_SYMBOL_GPL(lz4_compress); +EXPORT_SYMBOL(lz4_compress); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("LZ4 compressor"); diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index d3414eae73a..411be80ddb4 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -299,7 +299,7 @@ exit_0: return ret; } #ifndef STATIC -EXPORT_SYMBOL_GPL(lz4_decompress); +EXPORT_SYMBOL(lz4_decompress); #endif int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, @@ -319,8 +319,8 @@ exit_0: return ret; } #ifndef STATIC -EXPORT_SYMBOL_GPL(lz4_decompress_unknownoutputsize); +EXPORT_SYMBOL(lz4_decompress_unknownoutputsize); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("LZ4 Decompressor"); #endif diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c index eb1a74f5e36..f344f76b655 100644 --- a/lib/lz4/lz4hc_compress.c +++ b/lib/lz4/lz4hc_compress.c @@ -533,7 +533,7 @@ int lz4hc_compress(const unsigned char *src, size_t src_len, exit: return ret; } -EXPORT_SYMBOL_GPL(lz4hc_compress); +EXPORT_SYMBOL(lz4hc_compress); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("LZ4HC compressor"); diff --git a/mm/fremap.c b/mm/fremap.c index 87da3590c61..5bff0814776 100644 --- a/mm/fremap.c +++ b/mm/fremap.c @@ -57,17 +57,22 @@ static int install_file_pte(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, unsigned long pgoff, pgprot_t prot) { int err = -ENOMEM; - pte_t *pte; + pte_t *pte, ptfile; spinlock_t *ptl; pte = get_locked_pte(mm, addr, &ptl); if (!pte) goto out; - if (!pte_none(*pte)) + ptfile = pgoff_to_pte(pgoff); + + if (!pte_none(*pte)) { + if (pte_present(*pte) && pte_soft_dirty(*pte)) + pte_file_mksoft_dirty(ptfile); zap_pte(mm, vma, addr, pte); + } - set_pte_at(mm, addr, pte, pgoff_to_pte(pgoff)); + set_pte_at(mm, addr, pte, ptfile); /* * We don't need to run update_mmu_cache() here because the "file pte" * being installed by install_file_pte() is not a real pte - it's a diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 83aff0a4d09..b60f33080a2 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2490,7 +2490,7 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, mm = vma->vm_mm; - tlb_gather_mmu(&tlb, mm, 0); + tlb_gather_mmu(&tlb, mm, start, end); __unmap_hugepage_range(&tlb, vma, start, end, ref_page); tlb_finish_mmu(&tlb, start, end); } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index c290a1cf386..0878ff7c26a 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -3195,11 +3195,11 @@ int memcg_register_cache(struct mem_cgroup *memcg, struct kmem_cache *s, if (!s->memcg_params) return -ENOMEM; - INIT_WORK(&s->memcg_params->destroy, - kmem_cache_destroy_work_func); if (memcg) { s->memcg_params->memcg = memcg; s->memcg_params->root_cache = root_cache; + INIT_WORK(&s->memcg_params->destroy, + kmem_cache_destroy_work_func); } else s->memcg_params->is_root_cache = true; @@ -6969,7 +6969,6 @@ struct cgroup_subsys mem_cgroup_subsys = { #ifdef CONFIG_MEMCG_SWAP static int __init enable_swap_account(char *s) { - /* consider enabled if no parameter or 1 is given */ if (!strcmp(s, "1")) really_do_swap_account = 1; else if (!strcmp(s, "0")) diff --git a/mm/memory.c b/mm/memory.c index 1ce2e2a734f..af84bc0ec17 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -209,14 +209,15 @@ static int tlb_next_batch(struct mmu_gather *tlb) * tear-down from @mm. The @fullmm argument is used when @mm is without * users and we're going to destroy the full address space (exit/execve). */ -void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, bool fullmm) +void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) { tlb->mm = mm; - tlb->fullmm = fullmm; + /* Is it from 0 to ~0? */ + tlb->fullmm = !(start | (end+1)); tlb->need_flush_all = 0; - tlb->start = -1UL; - tlb->end = 0; + tlb->start = start; + tlb->end = end; tlb->need_flush = 0; tlb->local.next = NULL; tlb->local.nr = 0; @@ -256,8 +257,6 @@ void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long e { struct mmu_gather_batch *batch, *next; - tlb->start = start; - tlb->end = end; tlb_flush_mmu(tlb); /* keep the page table cache within bounds */ @@ -1099,7 +1098,6 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, spinlock_t *ptl; pte_t *start_pte; pte_t *pte; - unsigned long range_start = addr; again: init_rss_vec(rss); @@ -1141,9 +1139,12 @@ again: continue; if (unlikely(details) && details->nonlinear_vma && linear_page_index(details->nonlinear_vma, - addr) != page->index) - set_pte_at(mm, addr, pte, - pgoff_to_pte(page->index)); + addr) != page->index) { + pte_t ptfile = pgoff_to_pte(page->index); + if (pte_soft_dirty(ptent)) + pte_file_mksoft_dirty(ptfile); + set_pte_at(mm, addr, pte, ptfile); + } if (PageAnon(page)) rss[MM_ANONPAGES]--; else { @@ -1202,17 +1203,25 @@ again: * and page-free while holding it. */ if (force_flush) { + unsigned long old_end; + force_flush = 0; -#ifdef HAVE_GENERIC_MMU_GATHER - tlb->start = range_start; + /* + * Flush the TLB just for the previous segment, + * then update the range to be the remaining + * TLB range. + */ + old_end = tlb->end; tlb->end = addr; -#endif + tlb_flush_mmu(tlb); - if (addr != end) { - range_start = addr; + + tlb->start = addr; + tlb->end = old_end; + + if (addr != end) goto again; - } } return addr; @@ -1397,7 +1406,7 @@ void zap_page_range(struct vm_area_struct *vma, unsigned long start, unsigned long end = start + size; lru_add_drain(); - tlb_gather_mmu(&tlb, mm, 0); + tlb_gather_mmu(&tlb, mm, start, end); update_hiwater_rss(mm); mmu_notifier_invalidate_range_start(mm, start, end); for ( ; vma && vma->vm_start < end; vma = vma->vm_next) @@ -1423,7 +1432,7 @@ static void zap_page_range_single(struct vm_area_struct *vma, unsigned long addr unsigned long end = address + size; lru_add_drain(); - tlb_gather_mmu(&tlb, mm, 0); + tlb_gather_mmu(&tlb, mm, address, end); update_hiwater_rss(mm); mmu_notifier_invalidate_range_start(mm, address, end); unmap_single_vma(&tlb, vma, address, end, details); @@ -3115,6 +3124,8 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, exclusive = 1; } flush_icache_page(vma, page); + if (pte_swp_soft_dirty(orig_pte)) + pte = pte_mksoft_dirty(pte); set_pte_at(mm, address, page_table, pte); if (page == swapcache) do_page_add_anon_rmap(page, vma, address, exclusive); @@ -3408,6 +3419,8 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, entry = mk_pte(page, vma->vm_page_prot); if (flags & FAULT_FLAG_WRITE) entry = maybe_mkwrite(pte_mkdirty(entry), vma); + else if (pte_file(orig_pte) && pte_file_soft_dirty(orig_pte)) + pte_mksoft_dirty(entry); if (anon) { inc_mm_counter_fast(mm, MM_ANONPAGES); page_add_new_anon_rmap(page, vma, address); diff --git a/mm/mmap.c b/mm/mmap.c index 1edbaa3136c..f9c97d10b87 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2336,7 +2336,7 @@ static void unmap_region(struct mm_struct *mm, struct mmu_gather tlb; lru_add_drain(); - tlb_gather_mmu(&tlb, mm, 0); + tlb_gather_mmu(&tlb, mm, start, end); update_hiwater_rss(mm); unmap_vmas(&tlb, vma, start, end); free_pgtables(&tlb, vma, prev ? prev->vm_end : FIRST_USER_ADDRESS, @@ -2709,7 +2709,7 @@ void exit_mmap(struct mm_struct *mm) lru_add_drain(); flush_cache_mm(mm); - tlb_gather_mmu(&tlb, mm, 1); + tlb_gather_mmu(&tlb, mm, 0, -1); /* update_hiwater_rss(mm) here? but nobody should be looking */ /* Use -1 here to ensure all VMAs in the mm are unmapped */ unmap_vmas(&tlb, vma, 0, -1); diff --git a/mm/rmap.c b/mm/rmap.c index cd356df4f71..b2e29acd7e3 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -1236,6 +1236,7 @@ int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, swp_entry_to_pte(make_hwpoison_entry(page))); } else if (PageAnon(page)) { swp_entry_t entry = { .val = page_private(page) }; + pte_t swp_pte; if (PageSwapCache(page)) { /* @@ -1264,7 +1265,10 @@ int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, BUG_ON(TTU_ACTION(flags) != TTU_MIGRATION); entry = make_migration_entry(page, pte_write(pteval)); } - set_pte_at(mm, address, pte, swp_entry_to_pte(entry)); + swp_pte = swp_entry_to_pte(entry); + if (pte_soft_dirty(pteval)) + swp_pte = pte_swp_mksoft_dirty(swp_pte); + set_pte_at(mm, address, pte, swp_pte); BUG_ON(pte_file(*pte)); } else if (IS_ENABLED(CONFIG_MIGRATION) && (TTU_ACTION(flags) == TTU_MIGRATION)) { @@ -1401,8 +1405,12 @@ static int try_to_unmap_cluster(unsigned long cursor, unsigned int *mapcount, pteval = ptep_clear_flush(vma, address, pte); /* If nonlinear, store the file page offset in the pte. */ - if (page->index != linear_page_index(vma, address)) - set_pte_at(mm, address, pte, pgoff_to_pte(page->index)); + if (page->index != linear_page_index(vma, address)) { + pte_t ptfile = pgoff_to_pte(page->index); + if (pte_soft_dirty(pteval)) + pte_file_mksoft_dirty(ptfile); + set_pte_at(mm, address, pte, ptfile); + } /* Move the dirty bit to the physical page now the pte is gone. */ if (pte_dirty(pteval)) diff --git a/mm/shmem.c b/mm/shmem.c index 8335dbd3fc3..e43dc555069 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2909,14 +2909,8 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range); /* common code */ -static char *shmem_dname(struct dentry *dentry, char *buffer, int buflen) -{ - return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)", - dentry->d_name.name); -} - static struct dentry_operations anon_ops = { - .d_dname = shmem_dname + .d_dname = simple_dname }; /** diff --git a/mm/swapfile.c b/mm/swapfile.c index 36af6eeaa67..6cf2e60983b 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -866,6 +866,21 @@ unsigned int count_swap_pages(int type, int free) } #endif /* CONFIG_HIBERNATION */ +static inline int maybe_same_pte(pte_t pte, pte_t swp_pte) +{ +#ifdef CONFIG_MEM_SOFT_DIRTY + /* + * When pte keeps soft dirty bit the pte generated + * from swap entry does not has it, still it's same + * pte from logical point of view. + */ + pte_t swp_pte_dirty = pte_swp_mksoft_dirty(swp_pte); + return pte_same(pte, swp_pte) || pte_same(pte, swp_pte_dirty); +#else + return pte_same(pte, swp_pte); +#endif +} + /* * No need to decide whether this PTE shares the swap entry with others, * just let do_wp_page work it out if a write is requested later - to @@ -892,7 +907,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd, } pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); - if (unlikely(!pte_same(*pte, swp_entry_to_pte(entry)))) { + if (unlikely(!maybe_same_pte(*pte, swp_entry_to_pte(entry)))) { mem_cgroup_cancel_charge_swapin(memcg); ret = 0; goto out; @@ -947,7 +962,7 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd, * swapoff spends a _lot_ of time in this loop! * Test inline before going to call unuse_pte. */ - if (unlikely(pte_same(*pte, swp_pte))) { + if (unlikely(maybe_same_pte(*pte, swp_pte))) { pte_unmap(pte); ret = unuse_pte(vma, pmd, addr, entry, page); if (ret) diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 4a78c4de9f2..6ee48aac776 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -91,7 +91,12 @@ EXPORT_SYMBOL(__vlan_find_dev_deep); struct net_device *vlan_dev_real_dev(const struct net_device *dev) { - return vlan_dev_priv(dev)->real_dev; + struct net_device *ret = vlan_dev_priv(dev)->real_dev; + + while (is_vlan_dev(ret)) + ret = vlan_dev_priv(ret)->real_dev; + + return ret; } EXPORT_SYMBOL(vlan_dev_real_dev); diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index e14531f1ce1..264de88db32 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -1529,6 +1529,8 @@ out: * in these cases, the skb is further handled by this function and * returns 1, otherwise it returns 0 and the caller shall further * process the skb. + * + * This call might reallocate skb data. */ int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, unsigned short vid) diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index f105219f4a4..7614af31daf 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -508,6 +508,7 @@ out: return 0; } +/* this call might reallocate skb data */ static bool batadv_is_type_dhcprequest(struct sk_buff *skb, int header_len) { int ret = false; @@ -568,6 +569,7 @@ out: return ret; } +/* this call might reallocate skb data */ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) { struct ethhdr *ethhdr; @@ -619,6 +621,12 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr))) return false; + + /* skb->data might have been reallocated by pskb_may_pull() */ + ethhdr = (struct ethhdr *)skb->data; + if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) + ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN); + udphdr = (struct udphdr *)(skb->data + *header_len); *header_len += sizeof(*udphdr); @@ -634,12 +642,14 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) return true; } +/* this call might reallocate skb data */ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, - struct sk_buff *skb, struct ethhdr *ethhdr) + struct sk_buff *skb) { struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL; struct batadv_orig_node *orig_dst_node = NULL; struct batadv_gw_node *curr_gw = NULL; + struct ethhdr *ethhdr; bool ret, out_of_range = false; unsigned int header_len = 0; uint8_t curr_tq_avg; @@ -648,6 +658,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, if (!ret) goto out; + ethhdr = (struct ethhdr *)skb->data; orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source, ethhdr->h_dest); if (!orig_dst_node) diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h index 039902dca4a..1037d75da51 100644 --- a/net/batman-adv/gateway_client.h +++ b/net/batman-adv/gateway_client.h @@ -34,7 +34,6 @@ void batadv_gw_node_delete(struct batadv_priv *bat_priv, void batadv_gw_node_purge(struct batadv_priv *bat_priv); int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset); bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len); -bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, - struct sk_buff *skb, struct ethhdr *ethhdr); +bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct sk_buff *skb); #endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 700d0b49742..0f04e1c302b 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -180,6 +180,9 @@ static int batadv_interface_tx(struct sk_buff *skb, if (batadv_bla_tx(bat_priv, skb, vid)) goto dropped; + /* skb->data might have been reallocated by batadv_bla_tx() */ + ethhdr = (struct ethhdr *)skb->data; + /* Register the client MAC in the transtable */ if (!is_multicast_ether_addr(ethhdr->h_source)) batadv_tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); @@ -220,6 +223,10 @@ static int batadv_interface_tx(struct sk_buff *skb, default: break; } + + /* reminder: ethhdr might have become unusable from here on + * (batadv_gw_is_dhcp_target() might have reallocated skb data) + */ } /* ethernet packet should be broadcasted */ @@ -266,7 +273,7 @@ static int batadv_interface_tx(struct sk_buff *skb, /* unicast packet */ } else { if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_OFF) { - ret = batadv_gw_out_of_range(bat_priv, skb, ethhdr); + ret = batadv_gw_out_of_range(bat_priv, skb); if (ret) goto dropped; } diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index dc8b5d4dd63..857e1b8349e 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -326,7 +326,9 @@ static bool batadv_unicast_push_and_fill_skb(struct sk_buff *skb, int hdr_size, * @skb: the skb containing the payload to encapsulate * @orig_node: the destination node * - * Returns false if the payload could not be encapsulated or true otherwise + * Returns false if the payload could not be encapsulated or true otherwise. + * + * This call might reallocate skb data. */ static bool batadv_unicast_prepare_skb(struct sk_buff *skb, struct batadv_orig_node *orig_node) @@ -343,7 +345,9 @@ static bool batadv_unicast_prepare_skb(struct sk_buff *skb, * @orig_node: the destination node * @packet_subtype: the batman 4addr packet subtype to use * - * Returns false if the payload could not be encapsulated or true otherwise + * Returns false if the payload could not be encapsulated or true otherwise. + * + * This call might reallocate skb data. */ bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv, struct sk_buff *skb, @@ -401,7 +405,7 @@ int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv, struct batadv_neigh_node *neigh_node; int data_len = skb->len; int ret = NET_RX_DROP; - unsigned int dev_mtu; + unsigned int dev_mtu, header_len; /* get routing information */ if (is_multicast_ether_addr(ethhdr->h_dest)) { @@ -428,11 +432,17 @@ find_router: switch (packet_type) { case BATADV_UNICAST: - batadv_unicast_prepare_skb(skb, orig_node); + if (!batadv_unicast_prepare_skb(skb, orig_node)) + goto out; + + header_len = sizeof(struct batadv_unicast_packet); break; case BATADV_UNICAST_4ADDR: - batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node, - packet_subtype); + if (!batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node, + packet_subtype)) + goto out; + + header_len = sizeof(struct batadv_unicast_4addr_packet); break; default: /* this function supports UNICAST and UNICAST_4ADDR only. It @@ -441,6 +451,7 @@ find_router: goto out; } + ethhdr = (struct ethhdr *)(skb->data + header_len); unicast_packet = (struct batadv_unicast_packet *)skb->data; /* inform the destination node that we are still missing a correct route diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 60aca9109a5..ffd5874f259 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -161,7 +161,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) if (!pv) return; - for_each_set_bit_from(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) { + for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) { f = __br_fdb_get(br, br->dev->dev_addr, vid); if (f && f->is_local && !f->dst) fdb_delete(br, f); @@ -730,7 +730,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], /* VID was specified, so use it. */ err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); } else { - if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { + if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) { err = __br_fdb_add(ndm, p, addr, nlh_flags, 0); goto out; } @@ -739,7 +739,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], * specify a VLAN. To be nice, add/update entry for every * vlan on this port. */ - for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) { + for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); if (err) goto out; @@ -817,7 +817,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], err = __br_fdb_delete(p, addr, vid); } else { - if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { + if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) { err = __br_fdb_delete(p, addr, 0); goto out; } @@ -827,7 +827,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], * vlan on this port. */ err = -ENOENT; - for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) { + for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { err &= __br_fdb_delete(p, addr, vid); } } diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 61c5e819380..08e576ada0b 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1195,7 +1195,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, max_delay = msecs_to_jiffies(ntohs(mld->mld_maxdelay)); if (max_delay) group = &mld->mld_mca; - } else if (skb->len >= sizeof(*mld2q)) { + } else { if (!pskb_may_pull(skb, sizeof(*mld2q))) { err = -EINVAL; goto out; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 1fc30abd3a5..b9259efa636 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -132,7 +132,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, else pv = br_get_vlan_info(br); - if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) + if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) goto done; af = nla_nest_start(skb, IFLA_AF_SPEC); @@ -140,7 +140,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, goto nla_put_failure; pvid = br_get_pvid(pv); - for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) { + for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { vinfo.vid = vid; vinfo.flags = 0; if (vid == pvid) diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 394bb96b608..3b9637fb793 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -1,5 +1,5 @@ /* - * Sysfs attributes of bridge ports + * Sysfs attributes of bridge * Linux ethernet bridge * * Authors: diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index bd58b45f5f9..9a9ffe7e401 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -108,7 +108,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) clear_bit(vid, v->vlan_bitmap); v->num_vlans--; - if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { + if (bitmap_empty(v->vlan_bitmap, VLAN_N_VID)) { if (v->port_idx) rcu_assign_pointer(v->parent.port->vlan_info, NULL); else @@ -122,7 +122,7 @@ static void __vlan_flush(struct net_port_vlans *v) { smp_wmb(); v->pvid = 0; - bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN); + bitmap_zero(v->vlan_bitmap, VLAN_N_VID); if (v->port_idx) rcu_assign_pointer(v->parent.port->vlan_info, NULL); else diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 00ee068efc1..b84a1b155bc 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -65,6 +65,7 @@ ipv6: nhoff += sizeof(struct ipv6hdr); break; } + case __constant_htons(ETH_P_8021AD): case __constant_htons(ETH_P_8021Q): { const struct vlan_hdr *vlan; struct vlan_hdr _vlan; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 9232c68941a..60533db8b72 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1441,16 +1441,18 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, atomic_set(&p->refcnt, 1); p->reachable_time = neigh_rand_reach_time(p->base_reachable_time); + dev_hold(dev); + p->dev = dev; + write_pnet(&p->net, hold_net(net)); + p->sysctl_table = NULL; if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) { + release_net(net); + dev_put(dev); kfree(p); return NULL; } - dev_hold(dev); - p->dev = dev; - write_pnet(&p->net, hold_net(net)); - p->sysctl_table = NULL; write_lock_bh(&tbl->lock); p->next = tbl->parms.next; tbl->parms.next = p; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 3de740834d1..ca198c1d1d3 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2156,7 +2156,7 @@ int ndo_dflt_fdb_del(struct ndmsg *ndm, /* If aging addresses are supported device will need to * implement its own handler for this. */ - if (ndm->ndm_state & NUD_PERMANENT) { + if (!(ndm->ndm_state & NUD_PERMANENT)) { pr_info("%s: FDB only supports static addresses\n", dev->name); return -EINVAL; } @@ -2384,7 +2384,7 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) struct nlattr *extfilt; u32 filter_mask = 0; - extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct rtgenmsg), + extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg), IFLA_EXT_MASK); if (extfilt) filter_mask = nla_get_u32(extfilt); diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index ab3d814bc80..109ee89f123 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -477,7 +477,7 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu) } return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) - - net_adj) & ~(align - 1)) + (net_adj - 2); + net_adj) & ~(align - 1)) + net_adj - 2; } static void esp4_err(struct sk_buff *skb, u32 info) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 108a1e9c9ea..3df6d3edb2a 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -71,7 +71,6 @@ #include <linux/init.h> #include <linux/list.h> #include <linux/slab.h> -#include <linux/prefetch.h> #include <linux/export.h> #include <net/net_namespace.h> #include <net/ip.h> @@ -1761,10 +1760,8 @@ static struct leaf *leaf_walk_rcu(struct tnode *p, struct rt_trie_node *c) if (!c) continue; - if (IS_LEAF(c)) { - prefetch(rcu_dereference_rtnl(p->child[idx])); + if (IS_LEAF(c)) return (struct leaf *) c; - } /* Rescan start scanning in new node */ p = (struct tnode *) c; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 1f6eab66f7c..8d6939eeb49 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -383,7 +383,7 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev, if (daddr) memcpy(&iph->daddr, daddr, 4); if (iph->daddr) - return t->hlen; + return t->hlen + sizeof(*iph); return -(t->hlen + sizeof(*iph)); } diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 7167b08977d..850525b3489 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -76,9 +76,7 @@ int iptunnel_xmit(struct net *net, struct rtable *rt, iph->daddr = dst; iph->saddr = src; iph->ttl = ttl; - tunnel_ip_select_ident(skb, - (const struct iphdr *)skb_inner_network_header(skb), - &rt->dst); + __ip_select_ident(iph, &rt->dst, (skb_shinfo(skb)->gso_segs ?: 1) - 1); err = ip_local_out(skb); if (unlikely(net_xmit_eval(err))) diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 6577a1149a4..463bd127334 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -273,7 +273,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPFastOpenListenOverflow", LINUX_MIB_TCPFASTOPENLISTENOVERFLOW), SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD), SNMP_MIB_ITEM("TCPSpuriousRtxHostQueues", LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES), - SNMP_MIB_ITEM("LowLatencyRxPackets", LINUX_MIB_LOWLATENCYRXPACKETS), + SNMP_MIB_ITEM("BusyPollRxPackets", LINUX_MIB_BUSYPOLLRXPACKETS), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 5423223e93c..b2f6c74861a 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1121,6 +1121,13 @@ new_segment: goto wait_for_memory; /* + * All packets are restored as if they have + * already been sent. + */ + if (tp->repair) + TCP_SKB_CB(skb)->when = tcp_time_stamp; + + /* * Check whether we can use HW checksum. */ if (sk->sk_route_caps & NETIF_F_ALL_CSUM) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index a9077f441cb..b6ae92a51f5 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -206,8 +206,8 @@ static u32 cubic_root(u64 a) */ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) { - u64 offs; - u32 delta, t, bic_target, max_cnt; + u32 delta, bic_target, max_cnt; + u64 offs, t; ca->ack_cnt++; /* count the number of ACKs */ @@ -250,9 +250,11 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) * if the cwnd < 1 million packets !!! */ + t = (s32)(tcp_time_stamp - ca->epoch_start); + t += msecs_to_jiffies(ca->delay_min >> 3); /* change the unit from HZ to bictcp_HZ */ - t = ((tcp_time_stamp + msecs_to_jiffies(ca->delay_min>>3) - - ca->epoch_start) << BICTCP_HZ) / HZ; + t <<= BICTCP_HZ; + do_div(t, HZ); if (t < ca->bic_K) /* t - K */ offs = ca->bic_K - t; @@ -414,7 +416,7 @@ static void bictcp_acked(struct sock *sk, u32 cnt, s32 rtt_us) return; /* Discard delay samples right after fast recovery */ - if ((s32)(tcp_time_stamp - ca->epoch_start) < HZ) + if (ca->epoch_start && (s32)(tcp_time_stamp - ca->epoch_start) < HZ) return; delay = (rtt_us << 3) / USEC_PER_MSEC; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index da4241c8c7d..498ea99194a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1126,12 +1126,10 @@ retry: if (ifp->flags & IFA_F_OPTIMISTIC) addr_flags |= IFA_F_OPTIMISTIC; - ift = !max_addresses || - ipv6_count_addresses(idev) < max_addresses ? - ipv6_add_addr(idev, &addr, NULL, tmp_plen, - ipv6_addr_scope(&addr), addr_flags, - tmp_valid_lft, tmp_prefered_lft) : NULL; - if (IS_ERR_OR_NULL(ift)) { + ift = ipv6_add_addr(idev, &addr, NULL, tmp_plen, + ipv6_addr_scope(&addr), addr_flags, + tmp_valid_lft, tmp_prefered_lft); + if (IS_ERR(ift)) { in6_ifa_put(ifp); in6_dev_put(idev); pr_info("%s: retry temporary address regeneration\n", __func__); diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 40ffd72243a..aeac0dc3635 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -425,7 +425,7 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu) net_adj = 0; return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) - - net_adj) & ~(align - 1)) + (net_adj - 2); + net_adj) & ~(align - 1)) + net_adj - 2; } static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index bff3d821c7e..c4ff5bbb45c 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -993,14 +993,22 @@ static struct fib6_node * fib6_lookup_1(struct fib6_node *root, if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) { #ifdef CONFIG_IPV6_SUBTREES - if (fn->subtree) - fn = fib6_lookup_1(fn->subtree, args + 1); + if (fn->subtree) { + struct fib6_node *sfn; + sfn = fib6_lookup_1(fn->subtree, + args + 1); + if (!sfn) + goto backtrack; + fn = sfn; + } #endif - if (!fn || fn->fn_flags & RTN_RTINFO) + if (fn->fn_flags & RTN_RTINFO) return fn; } } - +#ifdef CONFIG_IPV6_SUBTREES +backtrack: +#endif if (fn->fn_flags & RTN_ROOT) break; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 79aa9652ed8..04d31c2fbef 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1369,8 +1369,10 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) return; - if (!ndopts.nd_opts_rh) + if (!ndopts.nd_opts_rh) { + ip6_redirect_no_header(skb, dev_net(skb->dev), 0, 0); return; + } hdr = (u8 *)ndopts.nd_opts_rh; hdr += 8; diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 790d9f4b8b0..1aeb473b2cc 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -490,6 +490,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, ipv6_hdr(head)->payload_len = htons(payload_len); ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn); IP6CB(head)->nhoff = nhoff; + IP6CB(head)->flags |= IP6SKB_FRAGMENTED; /* Yes, and fold redundant checksum back. 8) */ if (head->ip_summed == CHECKSUM_COMPLETE) @@ -524,6 +525,9 @@ static int ipv6_frag_rcv(struct sk_buff *skb) struct net *net = dev_net(skb_dst(skb)->dev); int evicted; + if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED) + goto fail_hdr; + IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMREQDS); /* Jumbo payload inhibits frag. header */ @@ -544,6 +548,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb) ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMOKS); IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb); + IP6CB(skb)->flags |= IP6SKB_FRAGMENTED; return 1; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b70f8979003..8d9a93ed9c5 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1178,6 +1178,27 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) } EXPORT_SYMBOL_GPL(ip6_redirect); +void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, + u32 mark) +{ + const struct ipv6hdr *iph = ipv6_hdr(skb); + const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); + struct dst_entry *dst; + struct flowi6 fl6; + + memset(&fl6, 0, sizeof(fl6)); + fl6.flowi6_oif = oif; + fl6.flowi6_mark = mark; + fl6.flowi6_flags = 0; + fl6.daddr = msg->dest; + fl6.saddr = iph->daddr; + + dst = ip6_route_output(net, NULL, &fl6); + if (!dst->error) + rt6_do_redirect(dst, NULL, skb); + dst_release(dst); +} + void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) { ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ae31968d42d..cc9e02d79b5 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -31,10 +31,12 @@ #include "led.h" #define IEEE80211_AUTH_TIMEOUT (HZ / 5) +#define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2) #define IEEE80211_AUTH_TIMEOUT_SHORT (HZ / 10) #define IEEE80211_AUTH_MAX_TRIES 3 #define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5) #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) +#define IEEE80211_ASSOC_TIMEOUT_LONG (HZ / 2) #define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10) #define IEEE80211_ASSOC_MAX_TRIES 3 @@ -209,8 +211,9 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel, const struct ieee80211_ht_operation *ht_oper, const struct ieee80211_vht_operation *vht_oper, - struct cfg80211_chan_def *chandef, bool verbose) + struct cfg80211_chan_def *chandef, bool tracking) { + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct cfg80211_chan_def vht_chandef; u32 ht_cfreq, ret; @@ -229,7 +232,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, channel->band); /* check that channel matches the right operating channel */ - if (channel->center_freq != ht_cfreq) { + if (!tracking && channel->center_freq != ht_cfreq) { /* * It's possible that some APs are confused here; * Netgear WNDR3700 sometimes reports 4 higher than @@ -237,11 +240,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, * since we look at probe response/beacon data here * it should be OK. */ - if (verbose) - sdata_info(sdata, - "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", - channel->center_freq, ht_cfreq, - ht_oper->primary_chan, channel->band); + sdata_info(sdata, + "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", + channel->center_freq, ht_cfreq, + ht_oper->primary_chan, channel->band); ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; goto out; } @@ -295,7 +297,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, channel->band); break; default: - if (verbose) + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) sdata_info(sdata, "AP VHT operation IE has invalid channel width (%d), disable VHT\n", vht_oper->chan_width); @@ -304,7 +306,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, } if (!cfg80211_chandef_valid(&vht_chandef)) { - if (verbose) + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) sdata_info(sdata, "AP VHT information is invalid, disable VHT\n"); ret = IEEE80211_STA_DISABLE_VHT; @@ -317,7 +319,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, } if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { - if (verbose) + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) sdata_info(sdata, "AP VHT information doesn't match HT, disable VHT\n"); ret = IEEE80211_STA_DISABLE_VHT; @@ -333,18 +335,27 @@ out: if (ret & IEEE80211_STA_DISABLE_VHT) vht_chandef = *chandef; + /* + * Ignore the DISABLED flag when we're already connected and only + * tracking the APs beacon for bandwidth changes - otherwise we + * might get disconnected here if we connect to an AP, update our + * regulatory information based on the AP's country IE and the + * information we have is wrong/outdated and disables the channel + * that we're actually using for the connection to the AP. + */ while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, - IEEE80211_CHAN_DISABLED)) { + tracking ? 0 : + IEEE80211_CHAN_DISABLED)) { if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; - goto out; + break; } ret |= chandef_downgrade(chandef); } - if (chandef->width != vht_chandef.width && verbose) + if (chandef->width != vht_chandef.width && !tracking) sdata_info(sdata, "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n"); @@ -384,7 +395,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, /* calculate new channel (type) based on HT/VHT operation IEs */ flags = ieee80211_determine_chantype(sdata, sband, chan, ht_oper, - vht_oper, &chandef, false); + vht_oper, &chandef, true); /* * Downgrade the new channel if we associated with restricted @@ -3394,10 +3405,13 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) if (tx_flags == 0) { auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; - ifmgd->auth_data->timeout_started = true; + auth_data->timeout_started = true; run_again(sdata, auth_data->timeout); } else { - auth_data->timeout_started = false; + auth_data->timeout = + round_jiffies_up(jiffies + IEEE80211_AUTH_TIMEOUT_LONG); + auth_data->timeout_started = true; + run_again(sdata, auth_data->timeout); } return 0; @@ -3434,7 +3448,11 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) assoc_data->timeout_started = true; run_again(sdata, assoc_data->timeout); } else { - assoc_data->timeout_started = false; + assoc_data->timeout = + round_jiffies_up(jiffies + + IEEE80211_ASSOC_TIMEOUT_LONG); + assoc_data->timeout_started = true; + run_again(sdata, assoc_data->timeout); } return 0; @@ -3829,7 +3847,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, cbss->channel, ht_oper, vht_oper, - &chandef, true); + &chandef, false); sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), local->rx_chains); diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 7dcc376eea5..2f8010707d0 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -526,7 +526,7 @@ static bool tcp_in_window(const struct nf_conn *ct, const struct nf_conntrack_tuple *tuple = &ct->tuplehash[dir].tuple; __u32 seq, ack, sack, end, win, swin; s16 receiver_offset; - bool res; + bool res, in_recv_win; /* * Get the required data from the packet. @@ -649,14 +649,18 @@ static bool tcp_in_window(const struct nf_conn *ct, receiver->td_end, receiver->td_maxend, receiver->td_maxwin, receiver->td_scale); + /* Is the ending sequence in the receive window (if available)? */ + in_recv_win = !receiver->td_maxwin || + after(end, sender->td_end - receiver->td_maxwin - 1); + pr_debug("tcp_in_window: I=%i II=%i III=%i IV=%i\n", before(seq, sender->td_maxend + 1), - after(end, sender->td_end - receiver->td_maxwin - 1), + (in_recv_win ? 1 : 0), before(sack, receiver->td_end + 1), after(sack, receiver->td_end - MAXACKWINDOW(sender) - 1)); if (before(seq, sender->td_maxend + 1) && - after(end, sender->td_end - receiver->td_maxwin - 1) && + in_recv_win && before(sack, receiver->td_end + 1) && after(sack, receiver->td_end - MAXACKWINDOW(sender) - 1)) { /* @@ -725,7 +729,7 @@ static bool tcp_in_window(const struct nf_conn *ct, nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, "nf_ct_tcp: %s ", before(seq, sender->td_maxend + 1) ? - after(end, sender->td_end - receiver->td_maxwin - 1) ? + in_recv_win ? before(sack, receiver->td_end + 1) ? after(sack, receiver->td_end - MAXACKWINDOW(sender) - 1) ? "BUG" : "ACK is under the lower bound (possible overly delayed ACK)" diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 962e9792e31..d92cc317bf8 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -419,6 +419,7 @@ __build_packet_message(struct nfnl_log_net *log, nfmsg->version = NFNETLINK_V0; nfmsg->res_id = htons(inst->group_num); + memset(&pmsg, 0, sizeof(pmsg)); pmsg.hw_protocol = skb->protocol; pmsg.hook = hooknum; @@ -498,7 +499,10 @@ __build_packet_message(struct nfnl_log_net *log, if (indev && skb->dev && skb->mac_header != skb->network_header) { struct nfulnl_msg_packet_hw phw; - int len = dev_parse_header(skb, phw.hw_addr); + int len; + + memset(&phw, 0, sizeof(phw)); + len = dev_parse_header(skb, phw.hw_addr); if (len > 0) { phw.hw_addrlen = htons(len); if (nla_put(inst->skb, NFULA_HWADDR, sizeof(phw), &phw)) diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 971ea145ab3..8a703c3dd31 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -463,7 +463,10 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, if (indev && entskb->dev && entskb->mac_header != entskb->network_header) { struct nfqnl_msg_packet_hw phw; - int len = dev_parse_header(entskb, phw.hw_addr); + int len; + + memset(&phw, 0, sizeof(phw)); + len = dev_parse_header(entskb, phw.hw_addr); if (len) { phw.hw_addrlen = htons(len); if (nla_put(skb, NFQA_HWADDR, sizeof(phw), &phw)) diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index 7011c71646f..6113cc7efff 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -52,7 +52,8 @@ tcpmss_mangle_packet(struct sk_buff *skb, { const struct xt_tcpmss_info *info = par->targinfo; struct tcphdr *tcph; - unsigned int tcplen, i; + int len, tcp_hdrlen; + unsigned int i; __be16 oldval; u16 newmss; u8 *opt; @@ -64,11 +65,14 @@ tcpmss_mangle_packet(struct sk_buff *skb, if (!skb_make_writable(skb, skb->len)) return -1; - tcplen = skb->len - tcphoff; + len = skb->len - tcphoff; + if (len < (int)sizeof(struct tcphdr)) + return -1; + tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); + tcp_hdrlen = tcph->doff * 4; - /* Header cannot be larger than the packet */ - if (tcplen < tcph->doff*4) + if (len < tcp_hdrlen) return -1; if (info->mss == XT_TCPMSS_CLAMP_PMTU) { @@ -87,9 +91,8 @@ tcpmss_mangle_packet(struct sk_buff *skb, newmss = info->mss; opt = (u_int8_t *)tcph; - for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { - if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && - opt[i+1] == TCPOLEN_MSS) { + for (i = sizeof(struct tcphdr); i <= tcp_hdrlen - TCPOLEN_MSS; i += optlen(opt, i)) { + if (opt[i] == TCPOPT_MSS && opt[i+1] == TCPOLEN_MSS) { u_int16_t oldmss; oldmss = (opt[i+2] << 8) | opt[i+3]; @@ -112,9 +115,10 @@ tcpmss_mangle_packet(struct sk_buff *skb, } /* There is data after the header so the option can't be added - without moving it, and doing so may make the SYN packet - itself too large. Accept the packet unmodified instead. */ - if (tcplen > tcph->doff*4) + * without moving it, and doing so may make the SYN packet + * itself too large. Accept the packet unmodified instead. + */ + if (len > tcp_hdrlen) return 0; /* @@ -143,10 +147,10 @@ tcpmss_mangle_packet(struct sk_buff *skb, newmss = min(newmss, (u16)1220); opt = (u_int8_t *)tcph + sizeof(struct tcphdr); - memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); + memmove(opt + TCPOLEN_MSS, opt, len - sizeof(struct tcphdr)); inet_proto_csum_replace2(&tcph->check, skb, - htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); + htons(len), htons(len + TCPOLEN_MSS), 1); opt[0] = TCPOPT_MSS; opt[1] = TCPOLEN_MSS; opt[2] = (newmss & 0xff00) >> 8; diff --git a/net/netfilter/xt_TCPOPTSTRIP.c b/net/netfilter/xt_TCPOPTSTRIP.c index b68fa191710..625fa1d636a 100644 --- a/net/netfilter/xt_TCPOPTSTRIP.c +++ b/net/netfilter/xt_TCPOPTSTRIP.c @@ -38,7 +38,7 @@ tcpoptstrip_mangle_packet(struct sk_buff *skb, struct tcphdr *tcph; u_int16_t n, o; u_int8_t *opt; - int len; + int len, tcp_hdrlen; /* This is a fragment, no TCP header is available */ if (par->fragoff != 0) @@ -52,7 +52,9 @@ tcpoptstrip_mangle_packet(struct sk_buff *skb, return NF_DROP; tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); - if (tcph->doff * 4 > len) + tcp_hdrlen = tcph->doff * 4; + + if (len < tcp_hdrlen) return NF_DROP; opt = (u_int8_t *)tcph; @@ -61,10 +63,10 @@ tcpoptstrip_mangle_packet(struct sk_buff *skb, * Walk through all TCP options - if we find some option to remove, * set all octets to %TCPOPT_NOP and adjust checksum. */ - for (i = sizeof(struct tcphdr); i < tcp_hdrlen(skb); i += optl) { + for (i = sizeof(struct tcphdr); i < tcp_hdrlen - 1; i += optl) { optl = optlen(opt, i); - if (i + optl > tcp_hdrlen(skb)) + if (i + optl > tcp_hdrlen) break; if (!tcpoptstrip_test_bit(info->strip_bmap, opt[i])) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 22c5f399f1c..ab101f71544 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -535,6 +535,7 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb) { struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts); + OVS_CB(skb)->tun_key = NULL; return do_execute_actions(dp, skb, acts->actions, acts->actions_len, false); } diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index f7e3a0d84c4..f2ed7600084 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -2076,9 +2076,6 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) ovs_notify(reply, info, &ovs_dp_vport_multicast_group); return 0; - rtnl_unlock(); - return 0; - exit_free: kfree_skb(reply); exit_unlock: diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 5c519b121e1..1aa84dc5877 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -240,7 +240,7 @@ static struct flex_array *alloc_buckets(unsigned int n_buckets) struct flex_array *buckets; int i, err; - buckets = flex_array_alloc(sizeof(struct hlist_head *), + buckets = flex_array_alloc(sizeof(struct hlist_head), n_buckets, GFP_KERNEL); if (!buckets) return NULL; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4b66c752eae..75c8bbf598c 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3259,9 +3259,11 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, if (po->tp_version == TPACKET_V3) { lv = sizeof(struct tpacket_stats_v3); + st.stats3.tp_packets += st.stats3.tp_drops; data = &st.stats3; } else { lv = sizeof(struct tpacket_stats); + st.stats1.tp_packets += st.stats1.tp_drops; data = &st.stats1; } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 281c1bded1f..51b968d3feb 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -285,6 +285,45 @@ static struct Qdisc_ops *qdisc_lookup_ops(struct nlattr *kind) return q; } +/* The linklayer setting were not transferred from iproute2, in older + * versions, and the rate tables lookup systems have been dropped in + * the kernel. To keep backward compatible with older iproute2 tc + * utils, we detect the linklayer setting by detecting if the rate + * table were modified. + * + * For linklayer ATM table entries, the rate table will be aligned to + * 48 bytes, thus some table entries will contain the same value. The + * mpu (min packet unit) is also encoded into the old rate table, thus + * starting from the mpu, we find low and high table entries for + * mapping this cell. If these entries contain the same value, when + * the rate tables have been modified for linklayer ATM. + * + * This is done by rounding mpu to the nearest 48 bytes cell/entry, + * and then roundup to the next cell, calc the table entry one below, + * and compare. + */ +static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab) +{ + int low = roundup(r->mpu, 48); + int high = roundup(low+1, 48); + int cell_low = low >> r->cell_log; + int cell_high = (high >> r->cell_log) - 1; + + /* rtab is too inaccurate at rates > 100Mbit/s */ + if ((r->rate > (100000000/8)) || (rtab[0] == 0)) { + pr_debug("TC linklayer: Giving up ATM detection\n"); + return TC_LINKLAYER_ETHERNET; + } + + if ((cell_high > cell_low) && (cell_high < 256) + && (rtab[cell_low] == rtab[cell_high])) { + pr_debug("TC linklayer: Detected ATM, low(%d)=high(%d)=%u\n", + cell_low, cell_high, rtab[cell_high]); + return TC_LINKLAYER_ATM; + } + return TC_LINKLAYER_ETHERNET; +} + static struct qdisc_rate_table *qdisc_rtab_list; struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab) @@ -308,6 +347,8 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *ta rtab->rate = *r; rtab->refcnt = 1; memcpy(rtab->data, nla_data(tab), 1024); + if (r->linklayer == TC_LINKLAYER_UNAWARE) + r->linklayer = __detect_linklayer(r, rtab->data); rtab->next = qdisc_rtab_list; qdisc_rtab_list = rtab; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 4626cef4b76..48be3d5c0d9 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -25,6 +25,7 @@ #include <linux/rcupdate.h> #include <linux/list.h> #include <linux/slab.h> +#include <linux/if_vlan.h> #include <net/sch_generic.h> #include <net/pkt_sched.h> #include <net/dst.h> @@ -207,15 +208,19 @@ void __qdisc_run(struct Qdisc *q) unsigned long dev_trans_start(struct net_device *dev) { - unsigned long val, res = dev->trans_start; + unsigned long val, res; unsigned int i; + if (is_vlan_dev(dev)) + dev = vlan_dev_real_dev(dev); + res = dev->trans_start; for (i = 0; i < dev->num_tx_queues; i++) { val = netdev_get_tx_queue(dev, i)->trans_start; if (val && time_after(val, res)) res = val; } dev->trans_start = res; + return res; } EXPORT_SYMBOL(dev_trans_start); @@ -904,6 +909,7 @@ void psched_ratecfg_precompute(struct psched_ratecfg *r, memset(r, 0, sizeof(*r)); r->overhead = conf->overhead; r->rate_bytes_ps = conf->rate; + r->linklayer = (conf->linklayer & TC_LINKLAYER_MASK); r->mult = 1; /* * The deal here is to replace a divide by a reciprocal one diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 45e751527df..c2178b15ca6 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1329,6 +1329,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = (struct htb_class *)*arg, *parent; struct nlattr *opt = tca[TCA_OPTIONS]; + struct qdisc_rate_table *rtab = NULL, *ctab = NULL; struct nlattr *tb[TCA_HTB_MAX + 1]; struct tc_htb_opt *hopt; @@ -1350,6 +1351,18 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, if (!hopt->rate.rate || !hopt->ceil.rate) goto failure; + /* Keeping backward compatible with rate_table based iproute2 tc */ + if (hopt->rate.linklayer == TC_LINKLAYER_UNAWARE) { + rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB]); + if (rtab) + qdisc_put_rtab(rtab); + } + if (hopt->ceil.linklayer == TC_LINKLAYER_UNAWARE) { + ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB]); + if (ctab) + qdisc_put_rtab(ctab); + } + if (!cl) { /* new class */ struct Qdisc *new_q; int prio; diff --git a/net/sctp/associola.c b/net/sctp/associola.c index bce5b79662a..ab67efc64b2 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -846,12 +846,12 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, else spc_state = SCTP_ADDR_AVAILABLE; /* Don't inform ULP about transition from PF to - * active state and set cwnd to 1, see SCTP + * active state and set cwnd to 1 MTU, see SCTP * Quick failover draft section 5.1, point 5 */ if (transport->state == SCTP_PF) { ulp_notify = false; - transport->cwnd = 1; + transport->cwnd = asoc->pathmtu; } transport->state = SCTP_ACTIVE; break; diff --git a/net/sctp/transport.c b/net/sctp/transport.c index bdbbc3fd7c1..8fdd16046d6 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -181,12 +181,12 @@ static void sctp_transport_destroy(struct sctp_transport *transport) return; } - call_rcu(&transport->rcu, sctp_transport_destroy_rcu); - sctp_packet_free(&transport->packet); if (transport->asoc) sctp_association_put(transport->asoc); + + call_rcu(&transport->rcu, sctp_transport_destroy_rcu); } /* Start T3_rtx timer if it is not already running and update the heartbeat diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index cb29ef7ba2f..609c30c8081 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -460,6 +460,7 @@ static void bearer_disable(struct tipc_bearer *b_ptr) { struct tipc_link *l_ptr; struct tipc_link *temp_l_ptr; + struct tipc_link_req *temp_req; pr_info("Disabling bearer <%s>\n", b_ptr->name); spin_lock_bh(&b_ptr->lock); @@ -468,9 +469,13 @@ static void bearer_disable(struct tipc_bearer *b_ptr) list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) { tipc_link_delete(l_ptr); } - if (b_ptr->link_req) - tipc_disc_delete(b_ptr->link_req); + temp_req = b_ptr->link_req; + b_ptr->link_req = NULL; spin_unlock_bh(&b_ptr->lock); + + if (temp_req) + tipc_disc_delete(temp_req); + memset(b_ptr, 0, sizeof(struct tipc_bearer)); } diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 593071dabd1..4d9334683f8 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -347,7 +347,7 @@ void vsock_for_each_connected_socket(void (*fn)(struct sock *sk)) for (i = 0; i < ARRAY_SIZE(vsock_connected_table); i++) { struct vsock_sock *vsk; list_for_each_entry(vsk, &vsock_connected_table[i], - connected_table); + connected_table) fn(sk_vsock(vsk)); } diff --git a/net/wireless/core.c b/net/wireless/core.c index 4f9f216665e..a8c29fa4f1b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -765,6 +765,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, cfg80211_leave_mesh(rdev, dev); break; case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: cfg80211_stop_ap(rdev, dev); break; default: diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 25d217d9080..5f6e982cdcf 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -441,10 +441,12 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, goto out_unlock; } *rdev = wiphy_to_dev((*wdev)->wiphy); - cb->args[0] = (*rdev)->wiphy_idx; + /* 0 is the first index - add 1 to parse only once */ + cb->args[0] = (*rdev)->wiphy_idx + 1; cb->args[1] = (*wdev)->identifier; } else { - struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0]); + /* subtract the 1 again here */ + struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1); struct wireless_dev *tmp; if (!wiphy) { @@ -2620,8 +2622,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_NEW_KEY); - if (IS_ERR(hdr)) - return PTR_ERR(hdr); + if (!hdr) + return -ENOBUFS; cookie.msg = msg; cookie.idx = key_idx; @@ -6505,6 +6507,9 @@ static int nl80211_testmode_dump(struct sk_buff *skb, NL80211_CMD_TESTMODE); struct nlattr *tmdata; + if (!hdr) + break; + if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) { genlmsg_cancel(skb, hdr); break; @@ -6949,9 +6954,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_REMAIN_ON_CHANNEL); - - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); + if (!hdr) { + err = -ENOBUFS; goto free_msg; } @@ -7249,9 +7253,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_FRAME); - - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); + if (!hdr) { + err = -ENOBUFS; goto free_msg; } } @@ -8130,9 +8133,8 @@ static int nl80211_probe_client(struct sk_buff *skb, hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_PROBE_CLIENT); - - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); + if (!hdr) { + err = -ENOBUFS; goto free_msg; } diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 81c8a10d743..20e86a95dc4 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -976,21 +976,19 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; + int err = 0; ASSERT_WDEV_LOCK(wdev); kfree(wdev->connect_keys); wdev->connect_keys = NULL; - if (wdev->conn) { + if (wdev->conn) err = cfg80211_sme_disconnect(wdev, reason); - } else if (!rdev->ops->disconnect) { + else if (!rdev->ops->disconnect) cfg80211_mlme_down(rdev, dev); - err = 0; - } else { + else if (wdev->current_bss) err = rdev_disconnect(rdev, dev, reason); - } return err; } diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index ce431e6e07c..5066a3768b2 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -14,12 +14,14 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/dmaengine.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> #include <sound/initval.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include <mach/regs-ac97.h> #include <mach/audio.h> @@ -41,20 +43,20 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = { - .name = "AC97 PCM out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_out_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = { - .name = "AC97 PCM in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_in_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_in_req, }; static struct snd_pcm *pxa2xx_ac97_pcm; diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 823359ed95e..a61d7a9a995 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -7,11 +7,13 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include <mach/dma.h> @@ -43,6 +45,35 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, size_t period = params_period_bytes(params); pxa_dma_desc *dma_desc; dma_addr_t dma_buff_phys, next_desc_phys; + u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + + /* temporary transition hack */ + switch (rtd->params->addr_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + dcmd |= DCMD_WIDTH1; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + dcmd |= DCMD_WIDTH2; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + dcmd |= DCMD_WIDTH4; + break; + default: + /* can't happen */ + break; + } + + switch (rtd->params->maxburst) { + case 8: + dcmd |= DCMD_BURST8; + break; + case 16: + dcmd |= DCMD_BURST16; + break; + case 32: + dcmd |= DCMD_BURST32; + break; + } snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = totsize; @@ -55,14 +86,14 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, dma_desc->ddadr = next_desc_phys; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_desc->dsadr = dma_buff_phys; - dma_desc->dtadr = rtd->params->dev_addr; + dma_desc->dtadr = rtd->params->addr; } else { - dma_desc->dsadr = rtd->params->dev_addr; + dma_desc->dsadr = rtd->params->addr; dma_desc->dtadr = dma_buff_phys; } if (period > totsize) period = totsize; - dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN; + dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN; dma_desc++; dma_buff_phys += period; } while (totsize -= period); @@ -76,8 +107,10 @@ int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - if (rtd && rtd->params && rtd->params->drcmr) - *rtd->params->drcmr = 0; + if (rtd && rtd->params && rtd->params->filter_data) { + unsigned long req = *(unsigned long *) rtd->params->filter_data; + DRCMR(req) = 0; + } snd_pcm_set_runtime_buffer(substream, NULL); return 0; @@ -136,6 +169,7 @@ EXPORT_SYMBOL(pxa2xx_pcm_pointer); int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + unsigned long req; if (!prtd || !prtd->params) return 0; @@ -146,7 +180,8 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) DCSR(prtd->dma_ch) &= ~DCSR_RUN; DCSR(prtd->dma_ch) = 0; DCMD(prtd->dma_ch) = 0; - *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; + req = *(unsigned long *) prtd->params->filter_data; + DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD; return 0; } @@ -155,7 +190,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_prepare); void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) { struct snd_pcm_substream *substream = dev_id; - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; int dcsr; dcsr = DCSR(dma_ch); @@ -164,8 +198,8 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) if (dcsr & DCSR_ENDINTR) { snd_pcm_period_elapsed(substream); } else { - printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", - rtd->params->name, dma_ch, dcsr); + printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", + dma_ch, dcsr); snd_pcm_stream_lock(substream); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(substream); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index 26422a3584e..69a2455b447 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -11,8 +11,11 @@ */ #include <linux/module.h> +#include <linux/dmaengine.h> + #include <sound/core.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include "pxa2xx-pcm.h" @@ -40,7 +43,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? client->playback_params : client->capture_params; - ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("dma", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) goto err2; diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h index 65f86b56ba4..2a8fc08d52a 100644 --- a/sound/arm/pxa2xx-pcm.h +++ b/sound/arm/pxa2xx-pcm.h @@ -13,14 +13,14 @@ struct pxa2xx_runtime_data { int dma_ch; - struct pxa2xx_pcm_dma_params *params; + struct snd_dmaengine_dai_dma_data *params; pxa_dma_desc *dma_desc_array; dma_addr_t dma_desc_array_phys; }; struct pxa2xx_pcm_client { - struct pxa2xx_pcm_dma_params *playback_params; - struct pxa2xx_pcm_dma_params *capture_params; + struct snd_dmaengine_dai_dma_data *playback_params; + struct snd_dmaengine_dai_dma_data *capture_params; int (*startup)(struct snd_pcm_substream *); void (*shutdown)(struct snd_pcm_substream *); int (*prepare)(struct snd_pcm_substream *); diff --git a/sound/core/Kconfig b/sound/core/Kconfig index c0c2f57a0d6..313f22e9d92 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -6,6 +6,9 @@ config SND_PCM tristate select SND_TIMER +config SND_DMAENGINE_PCM + tristate + config SND_HWDEP tristate diff --git a/sound/core/Makefile b/sound/core/Makefile index 43d4117428a..5e890cfed42 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -13,6 +13,8 @@ snd-$(CONFIG_SND_JACK) += jack.o snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o +snd-pcm-dmaengine-objs := pcm_dmaengine.o + snd-page-alloc-y := memalloc.o snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o @@ -30,6 +32,7 @@ obj-$(CONFIG_SND_TIMER) += snd-timer.o obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o +obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o obj-$(CONFIG_SND_OSSEMUL) += oss/ diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/core/pcm_dmaengine.c index aa924d9b798..aa924d9b798 100644 --- a/sound/soc/soc-dmaengine-pcm.c +++ b/sound/core/pcm_dmaengine.c diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 82bb029d441..6e03b465e44 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -184,7 +184,7 @@ static void xrun(struct snd_pcm_substream *substream) do { \ if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ xrun_log_show(substream); \ - if (printk_ratelimit()) { \ + if (snd_printd_ratelimit()) { \ snd_printd("PCM: " fmt, ##args); \ } \ dump_stack_on_xrun(substream); \ @@ -342,7 +342,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, return -EPIPE; } if (pos >= runtime->buffer_size) { - if (printk_ratelimit()) { + if (snd_printd_ratelimit()) { char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); xrun_log_show(substream); diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 11048cc744d..915b4d7fbb2 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -1022,7 +1022,7 @@ static void dummy_proc_write(struct snd_info_entry *entry, if (i >= ARRAY_SIZE(fields)) continue; snd_info_get_str(item, ptr, sizeof(item)); - if (strict_strtoull(item, 0, &val)) + if (kstrtoull(item, 0, &val)) continue; if (fields[i].size == sizeof(int)) *get_dummy_int_ptr(dummy, fields[i].offset) = val; diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index 2c638650394..fe9e6e2f2c5 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -49,7 +49,6 @@ struct fwspk { struct snd_card *card; struct fw_unit *unit; const struct device_info *device_info; - struct snd_pcm_substream *pcm; struct mutex mutex; struct cmp_connection connection; struct amdtp_out_stream stream; @@ -363,8 +362,7 @@ static int fwspk_create_pcm(struct fwspk *fwspk) return err; pcm->private_data = fwspk; strcpy(pcm->name, fwspk->device_info->short_name); - fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - fwspk->pcm->ops = &ops; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); return 0; } diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 9942691cc0c..afef0d73807 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -443,8 +443,7 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus) for (i = 0; i < 8; ++i) iwave[i] = snd_gf1_peek(gus, bank_pos + i); #ifdef CONFIG_SND_DEBUG_ROM - printk(KERN_DEBUG "ROM at 0x%06x = %*phC\n", bank_pos, - 8, iwave); + printk(KERN_DEBUG "ROM at 0x%06x = %8phC\n", bank_pos, iwave); #endif if (strncmp(iwave, "INTRWAVE", 8)) continue; /* first check */ diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c index a59c88818f4..461d94cfecb 100644 --- a/sound/oss/dmabuf.c +++ b/sound/oss/dmabuf.c @@ -557,7 +557,6 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) unsigned long flags; int err = 0, n = 0; struct dma_buffparms *dmap = adev->dmap_in; - int go; if (!(adev->open_mode & OPEN_READ)) return -EIO; @@ -584,7 +583,7 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) spin_unlock_irqrestore(&dmap->lock,flags); return -EAGAIN; } - if ((go = adev->go)) + if (adev->go) timeout = dmabuf_timeout(dmap); spin_unlock_irqrestore(&dmap->lock,flags); diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 59c5e9c03d5..8de66ccd727 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -152,14 +152,9 @@ config SND_HDA_CODEC_HDMI This module is automatically loaded at probing. config SND_HDA_I915 - bool "Build Display HD-audio controller/codec power well support for i915 cards" + bool + default y depends on DRM_I915 - help - Say Y here to include full HDMI and DisplayPort HD-audio controller/codec - power-well support for Intel Haswell graphics cards based on the i915 driver. - - Note that this option must be enabled for Intel Haswell C+ stepping machines, otherwise - the GPU audio controller/codecs will not be initialized or damaged when exit from S3 mode. config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8a005f0e5ca..5b6c4e3c92c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -666,6 +666,64 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, } EXPORT_SYMBOL_HDA(snd_hda_get_conn_index); + +/* return DEVLIST_LEN parameter of the given widget */ +static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int parm; + + if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) || + get_wcaps_type(wcaps) != AC_WID_PIN) + return 0; + + parm = snd_hda_param_read(codec, nid, AC_PAR_DEVLIST_LEN); + if (parm == -1 && codec->bus->rirb_error) + parm = 0; + return parm & AC_DEV_LIST_LEN_MASK; +} + +/** + * snd_hda_get_devices - copy device list without cache + * @codec: the HDA codec + * @nid: NID of the pin to parse + * @dev_list: device list array + * @max_devices: max. number of devices to store + * + * Copy the device list. This info is dynamic and so not cached. + * Currently called only from hda_proc.c, so not exported. + */ +int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, + u8 *dev_list, int max_devices) +{ + unsigned int parm; + int i, dev_len, devices; + + parm = get_num_devices(codec, nid); + if (!parm) /* not multi-stream capable */ + return 0; + + dev_len = parm + 1; + dev_len = dev_len < max_devices ? dev_len : max_devices; + + devices = 0; + while (devices < dev_len) { + parm = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DEVICE_LIST, devices); + if (parm == -1 && codec->bus->rirb_error) + break; + + for (i = 0; i < 8; i++) { + dev_list[devices] = (u8)parm; + parm >>= 4; + devices++; + if (devices >= dev_len) + break; + } + } + return devices; +} + /** * snd_hda_queue_unsol_event - add an unsolicited event to queue * @bus: the BUS @@ -1216,11 +1274,13 @@ static void hda_jackpoll_work(struct work_struct *work) { struct hda_codec *codec = container_of(work, struct hda_codec, jackpoll_work.work); - if (!codec->jackpoll_interval) - return; snd_hda_jack_set_dirty_all(codec); snd_hda_jack_poll_all(codec); + + if (!codec->jackpoll_interval) + return; + queue_delayed_work(codec->bus->workq, &codec->jackpoll_work, codec->jackpoll_interval); } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 701c2e069b1..7aa9870040c 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -94,6 +94,8 @@ enum { #define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32 #define AC_VERB_GET_HDMI_CP_CTRL 0x0f33 #define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34 +#define AC_VERB_GET_DEVICE_SEL 0xf35 +#define AC_VERB_GET_DEVICE_LIST 0xf36 /* * SET verbs @@ -133,6 +135,7 @@ enum { #define AC_VERB_SET_HDMI_DIP_XMIT 0x732 #define AC_VERB_SET_HDMI_CP_CTRL 0x733 #define AC_VERB_SET_HDMI_CHAN_SLOT 0x734 +#define AC_VERB_SET_DEVICE_SEL 0x735 /* * Parameter IDs @@ -154,6 +157,7 @@ enum { #define AC_PAR_GPIO_CAP 0x11 #define AC_PAR_AMP_OUT_CAP 0x12 #define AC_PAR_VOL_KNB_CAP 0x13 +#define AC_PAR_DEVLIST_LEN 0x15 #define AC_PAR_HDMI_LPCM_CAP 0x20 /* @@ -251,6 +255,11 @@ enum { #define AC_UNSOL_RES_TAG_SHIFT 26 #define AC_UNSOL_RES_SUBTAG (0x1f<<21) #define AC_UNSOL_RES_SUBTAG_SHIFT 21 +#define AC_UNSOL_RES_DE (0x3f<<15) /* Device Entry + * (for DP1.2 MST) + */ +#define AC_UNSOL_RES_DE_SHIFT 15 +#define AC_UNSOL_RES_IA (1<<2) /* Inactive (for DP1.2 MST) */ #define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */ #define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */ #define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */ @@ -352,6 +361,10 @@ enum { #define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */ #define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */ +/* Display pin's device list length */ +#define AC_DEV_LIST_LEN_MASK 0x3f +#define AC_MAX_DEV_LIST_LEN 64 + /* * Control Parameters */ @@ -460,6 +473,11 @@ enum { #define AC_DEFCFG_PORT_CONN (0x3<<30) #define AC_DEFCFG_PORT_CONN_SHIFT 30 +/* Display pin's device list entry */ +#define AC_DE_PD (1<<0) +#define AC_DE_ELDV (1<<1) +#define AC_DE_IA (1<<2) + /* device device types (0x0-0xf) */ enum { AC_JACK_LINE_OUT, @@ -885,6 +903,7 @@ struct hda_codec { 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 */ + unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */ #ifdef CONFIG_PM unsigned int power_on :1; /* current (global) power-state */ unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ @@ -972,6 +991,8 @@ 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, hda_nid_t nid, int recursive); +int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, + u8 *dev_list, int max_devices); int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8e77cbbad87..ac41e9cdc97 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -142,6 +142,9 @@ static void parse_user_hints(struct hda_codec *codec) 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_io"); + if (val >= 0) + spec->no_multi_io = !val; val = snd_hda_get_bool_hint(codec, "multi_cap_vol"); if (val >= 0) spec->multi_cap_vol = !!val; @@ -522,7 +525,7 @@ static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1, } #define nid_has_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) + check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) #define nid_has_volume(codec, nid, dir) \ check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) @@ -624,7 +627,7 @@ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, if (enable) val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; } - if (caps & AC_AMPCAP_MUTE) { + if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { if (!enable) val |= HDA_AMP_MUTE; } @@ -648,7 +651,7 @@ static unsigned int get_amp_mask_to_modify(struct hda_codec *codec, { unsigned int mask = 0xff; - if (caps & AC_AMPCAP_MUTE) { + if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) { if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL)) mask &= ~0x80; } @@ -813,6 +816,8 @@ static void resume_path_from_idx(struct hda_codec *codec, int path_idx) static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); enum { HDA_CTL_WIDGET_VOL, @@ -830,7 +835,13 @@ static const struct snd_kcontrol_new control_templates[] = { .put = hda_gen_mixer_mute_put, /* replaced */ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), }, - HDA_BIND_MUTE(NULL, 0, 0, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_bind_switch_get, + .put = hda_gen_bind_mute_put, /* replaced */ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), + }, }; /* add dynamic controls from template */ @@ -937,8 +948,8 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx, } /* playback mute control with the software mute bit check */ -static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void sync_auto_mute_bits(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; @@ -949,10 +960,22 @@ static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] &= enabled; ucontrol->value.integer.value[1] &= enabled; } +} +static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sync_auto_mute_bits(kcontrol, ucontrol); return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); } +static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sync_auto_mute_bits(kcontrol, ucontrol); + return snd_hda_mixer_bind_switch_put(kcontrol, ucontrol); +} + /* 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) { @@ -1541,7 +1564,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec, cfg->speaker_pins, spec->multiout.extra_out_nid, spec->speaker_paths); - if (fill_mio_first && cfg->line_outs == 1 && + if (!spec->no_multi_io && + 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) @@ -1554,7 +1578,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->private_dac_nids, spec->out_paths, spec->main_out_badness); - if (fill_mio_first && + if (!spec->no_multi_io && 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); @@ -1582,7 +1606,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec, return err; badness += err; } - if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + if (!spec->no_multi_io && + 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; @@ -1600,7 +1625,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec, check_aamix_out_path(codec, spec->speaker_paths[0]); } - if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + if (!spec->no_multi_io && + 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 */ @@ -3724,7 +3750,8 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, /* 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; + int i; + bool present = false; for (i = 0; i < num_pins; i++) { hda_nid_t nid = pins[i]; @@ -3733,14 +3760,15 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) /* 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); + if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT) + present = true; } 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) + int *paths, bool mute) { struct hda_gen_spec *spec = codec->spec; int i; @@ -3752,10 +3780,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, break; if (spec->auto_mute_via_amp) { + struct nid_path *path; + hda_nid_t mute_nid; + + path = snd_hda_get_path_from_idx(codec, paths[i]); + if (!path) + continue; + mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]); + if (!mute_nid) + continue; if (mute) - spec->mute_bits |= (1ULL << nid); + spec->mute_bits |= (1ULL << mute_nid); else - spec->mute_bits &= ~(1ULL << nid); + spec->mute_bits &= ~(1ULL << mute_nid); set_pin_eapd(codec, nid, !mute); continue; } @@ -3786,14 +3823,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, void snd_hda_gen_update_outputs(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; + int *paths; 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->autocfg.line_out_type == AUTO_PIN_HP_OUT) + paths = spec->out_paths; + else + paths = spec->hp_paths; do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins, spec->master_mute); + spec->autocfg.hp_pins, paths, spec->master_mute); if (!spec->automute_speaker) on = 0; @@ -3801,8 +3843,12 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) on = spec->hp_jack_present | spec->line_jack_present; on |= spec->master_mute; spec->speaker_muted = on; + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) + paths = spec->out_paths; + else + paths = spec->speaker_paths; do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), - spec->autocfg.speaker_pins, on); + spec->autocfg.speaker_pins, paths, on); /* toggle line-out mutes if needed, too */ /* if LO is a copy of either HP or Speaker, don't need to handle it */ @@ -3815,8 +3861,9 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) on = spec->hp_jack_present; on |= spec->master_mute; spec->line_out_muted = on; + paths = spec->out_paths; do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins, on); + spec->autocfg.line_out_pins, paths, on); } EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs); @@ -3887,7 +3934,7 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja /* 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)) { + if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) { mux_select(codec, 0, spec->am_entry[i].idx); return; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index e199a852388..48d44026705 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -220,6 +220,7 @@ struct hda_gen_spec { unsigned int hp_mic:1; /* Allow HP as a mic-in */ unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ + unsigned int no_multi_io:1; /* Don't try multi I/O config */ 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 */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index ce67608734b..fe0bda19de1 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -295,7 +295,7 @@ static ssize_t type##_store(struct device *dev, \ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ struct hda_codec *codec = hwdep->private_data; \ unsigned long val; \ - int err = strict_strtoul(buf, 0, &val); \ + int err = kstrtoul(buf, 0, &val); \ if (err < 0) \ return err; \ codec->type = val; \ @@ -654,7 +654,7 @@ int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) p = snd_hda_get_hint(codec, key); if (!p) ret = -ENOENT; - else if (strict_strtoul(p, 0, &val)) + else if (kstrtoul(p, 0, &val)) ret = -EINVAL; else { *valp = val; @@ -751,7 +751,7 @@ static void parse_##name##_mode(char *buf, struct hda_bus *bus, \ struct hda_codec **codecp) \ { \ unsigned long val; \ - if (!strict_strtoul(buf, 0, &val)) \ + if (!kstrtoul(buf, 0, &val)) \ (*codecp)->name = val; \ } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 8860dd52952..c6c98298ac3 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1160,7 +1160,7 @@ static int azx_reset(struct azx *chip, int full_reset) goto __skip; /* clear STATESTS */ - azx_writeb(chip, STATESTS, STATESTS_INT_MASK); + azx_writew(chip, STATESTS, STATESTS_INT_MASK); /* reset controller */ azx_enter_link_reset(chip); @@ -1242,7 +1242,7 @@ static void azx_int_clear(struct azx *chip) } /* clear STATESTS */ - azx_writeb(chip, STATESTS, STATESTS_INT_MASK); + azx_writew(chip, STATESTS, STATESTS_INT_MASK); /* clear rirb status */ azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); @@ -1451,8 +1451,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) #if 0 /* clear state status int */ - if (azx_readb(chip, STATESTS) & 0x04) - azx_writeb(chip, STATESTS, 0x04); + if (azx_readw(chip, STATESTS) & 0x04) + azx_writew(chip, STATESTS, 0x04); #endif spin_unlock(&chip->reg_lock); @@ -2971,6 +2971,10 @@ static int azx_runtime_suspend(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; + /* enable controller wake up event */ + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | + STATESTS_INT_MASK); + azx_stop_chip(chip); azx_enter_link_reset(chip); azx_clear_irq_pending(chip); @@ -2983,11 +2987,31 @@ static int azx_runtime_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; + struct hda_bus *bus; + struct hda_codec *codec; + int status; if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) hda_display_power(true); + + /* Read STATESTS before controller reset */ + status = azx_readw(chip, STATESTS); + azx_init_pci(chip); azx_init_chip(chip, 1); + + bus = chip->bus; + if (status && bus) { + list_for_each_entry(codec, &bus->codec_list, list) + if (status & (1 << codec->addr)) + queue_delayed_work(codec->bus->workq, + &codec->jackpoll_work, codec->jackpoll_interval); + } + + /* disable controller Wake Up event*/ + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & + ~STATESTS_INT_MASK); + return 0; } @@ -3831,11 +3855,13 @@ static int azx_probe_continue(struct azx *chip) /* Request power well for Haswell HDA controller and codec */ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { +#ifdef CONFIG_SND_HDA_I915 err = hda_i915_init(); if (err < 0) { snd_printk(KERN_ERR SFX "Error request power-well from i915\n"); goto out_free; } +#endif hda_display_power(true); } diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 3fd2973183e..05b3e3e9108 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -194,18 +194,24 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) EXPORT_SYMBOL_HDA(snd_hda_pin_sense); /** - * snd_hda_jack_detect - query pin Presence Detect status + * snd_hda_jack_detect_state - query pin Presence Detect status * @codec: the CODEC to sense * @nid: the pin NID to sense * - * Query and return the pin's Presence Detect status. + * Query and return the pin's Presence Detect status, as either + * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM. */ -int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) +int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid) { - u32 sense = snd_hda_pin_sense(codec, nid); - return get_jack_plug_state(sense); + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); + if (jack && jack->phantom_jack) + return HDA_JACK_PHANTOM; + else if (snd_hda_pin_sense(codec, nid) & AC_PINSENSE_PRESENCE) + return HDA_JACK_PRESENT; + else + return HDA_JACK_NOT_PRESENT; } -EXPORT_SYMBOL_HDA(snd_hda_jack_detect); +EXPORT_SYMBOL_HDA(snd_hda_jack_detect_state); /** * snd_hda_jack_detect_enable - enable the jack-detection @@ -247,8 +253,8 @@ EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, hda_nid_t gating_nid) { - struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid); - struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid); + struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid); + struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, gating_nid); if (!gated || !gating) return -EINVAL; diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index ec12abd4526..379420c44ee 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -75,7 +75,18 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, hda_nid_t gating_nid); u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); + +/* the jack state returned from snd_hda_jack_detect_state() */ +enum { + HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM, +}; + +int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid); + +static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT; +} bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 9760f001916..a8cb22eec89 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -582,6 +582,36 @@ static void print_gpio(struct snd_info_buffer *buffer, print_nid_array(buffer, codec, nid, &codec->nids); } +static void print_device_list(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int i, curr = -1; + u8 dev_list[AC_MAX_DEV_LIST_LEN]; + int devlist_len; + + devlist_len = snd_hda_get_devices(codec, nid, dev_list, + AC_MAX_DEV_LIST_LEN); + snd_iprintf(buffer, " Devices: %d\n", devlist_len); + if (devlist_len <= 0) + return; + + curr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DEVICE_SEL, 0); + + for (i = 0; i < devlist_len; i++) { + if (i == curr) + snd_iprintf(buffer, " *"); + else + snd_iprintf(buffer, " "); + + snd_iprintf(buffer, + "Dev %02d: PD = %d, ELDV = %d, IA = %d\n", i, + !!(dev_list[i] & AC_DE_PD), + !!(dev_list[i] & AC_DE_ELDV), + !!(dev_list[i] & AC_DE_IA)); + } +} + static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -751,6 +781,9 @@ static void print_codec_info(struct snd_info_entry *entry, (wid_caps & AC_WCAP_DELAY) >> AC_WCAP_DELAY_SHIFT); + if (wid_type == AC_WID_PIN && codec->dp_mst) + print_device_list(buffer, codec, nid); + if (wid_caps & AC_WCAP_CONN_LIST) print_conn_list(buffer, codec, nid, wid_type, conn, conn_len); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d97f0d61a15..0cbdd87dde6 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -32,7 +32,6 @@ #include "hda_jack.h" #include "hda_generic.h" -#define ENABLE_AD_STATIC_QUIRKS struct ad198x_spec { struct hda_gen_spec gen; @@ -43,114 +42,8 @@ struct ad198x_spec { hda_nid_t eapd_nid; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - -#ifdef ENABLE_AD_STATIC_QUIRKS - const struct snd_kcontrol_new *mixers[6]; - int num_mixers; - const struct hda_verb *init_verbs[6]; /* initialization verbs - * don't forget NULL termination! - */ - unsigned int num_init_verbs; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - unsigned int cur_eapd; - unsigned int need_dac_fix; - - /* capture */ - unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - - /* capture source */ - const struct hda_input_mux *input_mux; - const hda_nid_t *capsrc_nids; - unsigned int cur_mux[3]; - - /* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; - - /* PCM information */ - struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ - - unsigned int spdif_route; - - unsigned int jack_present: 1; - unsigned int inv_jack_detect: 1;/* inverted jack-detection */ - unsigned int analog_beep: 1; /* analog beep input present */ - unsigned int avoid_init_slave_vol:1; - -#ifdef CONFIG_PM - struct hda_loopback_check loopback; -#endif - /* for virtual master */ - 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) - */ -static int ad198x_mux_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; - - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int ad198x_mux_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; - 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 ad198x_mux_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 adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->capsrc_nids[adc_idx], - &spec->cur_mux[adc_idx]); -} - -/* - * initialization (common callbacks) - */ -static int ad198x_init(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - return 0; -} - -static const char * const ad_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Mono", "Speaker", "IEC958", - NULL }; -static const char * const ad1988_6stack_fp_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", "IEC958", - NULL -}; -#endif /* ENABLE_AD_STATIC_QUIRKS */ #ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ @@ -160,12 +53,6 @@ static const struct snd_kcontrol_new ad_beep_mixer[] = { { } /* end */ }; -static const struct snd_kcontrol_new ad_beep2_mixer[] = { - HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT), - { } /* end */ -}; - #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ #else @@ -181,8 +68,7 @@ static int create_beep_ctls(struct hda_codec *codec) if (!spec->beep_amp) return 0; - knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; - for ( ; knew->name; knew++) { + for (knew = ad_beep_mixer ; knew->name; knew++) { int err; struct snd_kcontrol *kctl; kctl = snd_ctl_new1(knew, codec); @@ -199,268 +85,6 @@ static int create_beep_ctls(struct hda_codec *codec) #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; - struct snd_kcontrol *kctl; - unsigned int i; - int err; - - 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; - } - - /* create beep controls if needed */ - err = create_beep_ctls(codec); - 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->vmaster_nid, - HDA_OUTPUT, vmaster_tlv); - err = __snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, - (spec->slave_vols ? - spec->slave_vols : ad_slave_pfxs), - "Playback Volume", - !spec->avoid_init_slave_vol, NULL); - 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, - (spec->slave_sws ? - spec->slave_sws : ad_slave_pfxs), - "Playback Switch"); - if (err < 0) - return err; - } - - /* assign Capture Source enums to NID */ - 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, spec->capsrc_nids[i]); - if (err < 0) - return err; - } - - /* assign IEC958 enums to NID */ - kctl = snd_hda_find_mixer_ctl(codec, - SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source"); - if (kctl) { - err = snd_hda_add_nid(codec, kctl, 0, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - } - - return 0; -} - -#ifdef CONFIG_PM -static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - -/* - * Analog playback callbacks - */ -static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int ad198x_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 ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int ad198x_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 ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -/* - * Analog capture - */ -static int ad198x_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 ad198x_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); - return 0; -} - -static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - return 0; -} - -/* - */ -static const struct hda_pcm_stream ad198x_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 6, /* changed later */ - .nid = 0, /* fill later */ - .ops = { - .open = ad198x_playback_pcm_open, - .prepare = ad198x_playback_pcm_prepare, - .cleanup = ad198x_playback_pcm_cleanup, - }, -}; - -static const struct hda_pcm_stream ad198x_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = ad198x_capture_pcm_prepare, - .cleanup = ad198x_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ad198x_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .open = ad198x_dig_playback_pcm_open, - .close = ad198x_dig_playback_pcm_close, - .prepare = ad198x_dig_playback_pcm_prepare, - .cleanup = ad198x_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ad198x_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static int ad198x_build_pcms(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "AD198x Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - - if (spec->multiout.dig_out_nid) { - info++; - codec->num_pcms++; - codec->spdif_status_reset = 1; - info->name = "AD198x Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - if (spec->dig_in_nid) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - } - - return 0; -} -#endif /* ENABLE_AD_STATIC_QUIRKS */ static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, hda_nid_t hp) @@ -507,18 +131,6 @@ static void ad198x_shutup(struct hda_codec *codec) ad198x_power_eapd(codec); } -static void ad198x_free(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - if (!spec) - return; - - snd_hda_gen_spec_free(&spec->gen); - kfree(spec); - snd_hda_detach_beep_device(codec); -} - #ifdef CONFIG_PM static int ad198x_suspend(struct hda_codec *codec) { @@ -527,65 +139,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, - .init = ad198x_init, - .free = ad198x_free, -#ifdef CONFIG_PM - .check_power_status = ad198x_check_power_status, - .suspend = ad198x_suspend, -#endif - .reboot_notify = ad198x_shutup, -}; - - -/* - * EAPD control - * the private value = nid - */ -#define ad198x_eapd_info snd_ctl_boolean_mono_info - -static int ad198x_eapd_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; - if (codec->inv_eapd) - ucontrol->value.integer.value[0] = ! spec->cur_eapd; - else - ucontrol->value.integer.value[0] = spec->cur_eapd; - return 0; -} - -static int ad198x_eapd_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; - hda_nid_t nid = kcontrol->private_value & 0xff; - unsigned int eapd; - eapd = !!ucontrol->value.integer.value[0]; - if (codec->inv_eapd) - eapd = !eapd; - if (eapd == spec->cur_eapd) - return 0; - spec->cur_eapd = eapd; - snd_hda_codec_write_cache(codec, nid, - 0, AC_VERB_SET_EAPD_BTLENABLE, - eapd ? 0x02 : 0x00); - return 1; -} - -static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -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 @@ -646,537 +199,6 @@ static int ad198x_parse_auto_config(struct hda_codec *codec) * AD1986A specific */ -#ifdef ENABLE_AD_STATIC_QUIRKS -#define AD1986A_SPDIF_OUT 0x02 -#define AD1986A_FRONT_DAC 0x03 -#define AD1986A_SURR_DAC 0x04 -#define AD1986A_CLFE_DAC 0x05 -#define AD1986A_ADC 0x06 - -static const hda_nid_t ad1986a_dac_nids[3] = { - AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC -}; -static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC }; -static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 }; - -static const struct hda_input_mux ad1986a_capture_source = { - .num_items = 7, - .items = { - { "Mic", 0x0 }, - { "CD", 0x1 }, - { "Aux", 0x3 }, - { "Line", 0x4 }, - { "Mix", 0x5 }, - { "Mono", 0x6 }, - { "Phone", 0x7 }, - }, -}; - - -static const struct hda_bind_ctls ad1986a_bind_pcm_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct hda_bind_ctls ad1986a_bind_pcm_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -/* - * mixers - */ -static const struct snd_kcontrol_new ad1986a_mixers[] = { - /* - * bind volumes/mutes of 3 DACs as a single PCM control for simplicity - */ - HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol), - HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw), - HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* additional mixers for 3stack mode */ -static const struct snd_kcontrol_new ad1986a_3st_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ad198x_ch_mode_info, - .get = ad198x_ch_mode_get, - .put = ad198x_ch_mode_put, - }, - { } /* end */ -}; - -/* laptop model - 2ch only */ -static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC }; - -/* master controls both pins 0x1a and 0x1b */ -static const struct hda_bind_ctls ad1986a_laptop_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - 0, - }, -}; - -static const struct hda_bind_ctls ad1986a_laptop_master_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - 0, - }, -}; - -static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT), - /* - HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */ - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -/* laptop-eapd model - 2ch only */ - -static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x4 }, - { "Mix", 0x5 }, - }, -}; - -static const struct hda_input_mux ad1986a_automic_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Mix", 0x5 }, - }, -}; - -static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "External Amplifier", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad198x_eapd_put, - .private_value = 0x1b, /* port-D */ - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = { - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT), - { } /* end */ -}; - -/* re-connect the mic boost input according to the jack sensing */ -static void ad1986a_automic(struct hda_codec *codec) -{ - unsigned int present; - present = snd_hda_jack_detect(codec, 0x1f); - /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */ - snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, - present ? 0 : 2); -} - -#define AD1986A_MIC_EVENT 0x36 - -static void ad1986a_automic_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != AD1986A_MIC_EVENT) - return; - ad1986a_automic(codec); -} - -static int ad1986a_automic_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1986a_automic(codec); - return 0; -} - -/* laptop-automute - 2ch only */ - -static void ad1986a_update_hp(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - unsigned int mute; - - if (spec->jack_present) - mute = HDA_AMP_MUTE; /* mute internal speaker */ - else - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); -} - -static void ad1986a_hp_automute(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - spec->jack_present = snd_hda_jack_detect(codec, 0x1a); - if (spec->inv_jack_detect) - spec->jack_present = !spec->jack_present; - ad1986a_update_hp(codec); -} - -#define AD1986A_HP_EVENT 0x37 - -static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) != AD1986A_HP_EVENT) - return; - ad1986a_hp_automute(codec); -} - -static int ad1986a_hp_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1986a_hp_automute(codec); - return 0; -} - -/* bind hp and internal speaker mute (with plug check) */ -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); - if (change) - ad1986a_update_hp(codec); - return change; -} - -static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1986a_hp_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - }, - { } /* end */ -}; - - -/* - * initialization verbs - */ -static const struct hda_verb ad1986a_init_verbs[] = { - /* Front, Surround, CLFE DAC; mute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Downmix - off */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* HP, Line-Out, Surround, CLFE selectors */ - {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mono selector */ - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic selector: Mic 1/2 pin */ - {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Line-in selector: Line-in */ - {0x10, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic 1/2 swap */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Record selector: mic */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic, Phone, CD, Aux, Line-In amp; mute as default */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* PC beep */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* HP Pin */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* Front, Surround, CLFE Pins */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Mono Pin */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Mic Pin */ - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* Line, Aux, CD, Beep-In Pin */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - { } /* end */ -}; - -static const struct hda_verb ad1986a_ch2_init[] = { - /* Surround out -> Line In */ - { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* Line-in selectors */ - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 }, - /* CLFE -> Mic in */ - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 }, - { } /* end */ -}; - -static const struct hda_verb ad1986a_ch4_init[] = { - /* Surround out -> Surround */ - { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* CLFE -> Mic in */ - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 }, - { } /* end */ -}; - -static const struct hda_verb ad1986a_ch6_init[] = { - /* Surround out -> Surround out */ - { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* CLFE -> CLFE */ - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 }, - { } /* end */ -}; - -static const struct hda_channel_mode ad1986a_modes[3] = { - { 2, ad1986a_ch2_init }, - { 4, ad1986a_ch4_init }, - { 6, ad1986a_ch6_init }, -}; - -/* eapd initialization */ -static const struct hda_verb ad1986a_eapd_init_verbs[] = { - {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, - {} -}; - -static const struct hda_verb ad1986a_automic_verbs[] = { - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/ - {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT}, - {} -}; - -/* Ultra initialization */ -static const struct hda_verb ad1986a_ultra_init[] = { - /* eapd initialization */ - { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, - /* CLFE -> Mic in */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 }, - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, - { } /* end */ -}; - -/* pin sensing on HP jack */ -static const struct hda_verb ad1986a_hp_init_verbs[] = { - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT}, - {} -}; - -static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case AD1986A_HP_EVENT: - ad1986a_hp_automute(codec); - break; - case AD1986A_MIC_EVENT: - ad1986a_automic(codec); - break; - } -} - -static int ad1986a_samsung_p50_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1986a_hp_automute(codec); - ad1986a_automic(codec); - return 0; -} - - -/* models */ -enum { - AD1986A_AUTO, - AD1986A_6STACK, - AD1986A_3STACK, - AD1986A_LAPTOP, - AD1986A_LAPTOP_EAPD, - AD1986A_LAPTOP_AUTOMUTE, - AD1986A_ULTRA, - AD1986A_SAMSUNG, - AD1986A_SAMSUNG_P50, - AD1986A_MODELS -}; - -static const char * const ad1986a_models[AD1986A_MODELS] = { - [AD1986A_AUTO] = "auto", - [AD1986A_6STACK] = "6stack", - [AD1986A_3STACK] = "3stack", - [AD1986A_LAPTOP] = "laptop", - [AD1986A_LAPTOP_EAPD] = "laptop-eapd", - [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", - [AD1986A_ULTRA] = "ultra", - [AD1986A_SAMSUNG] = "samsung", - [AD1986A_SAMSUNG_P50] = "samsung-p50", -}; - -static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), - SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), - SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK), - SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), - SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50), - SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), - SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG), - SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), - SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), - SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE), - SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP), - {} -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1986a_loopbacks[] = { - { 0x13, HDA_OUTPUT, 0 }, /* Mic */ - { 0x14, HDA_OUTPUT, 0 }, /* Phone */ - { 0x15, HDA_OUTPUT, 0 }, /* CD */ - { 0x16, HDA_OUTPUT, 0 }, /* Aux */ - { 0x17, HDA_OUTPUT, 0 }, /* Line */ - { } /* end */ -}; -#endif - -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) { struct ad198x_spec *spec; @@ -1203,6 +225,11 @@ static void ad_fixup_inv_jack_detect(struct hda_codec *codec, enum { AD1986A_FIXUP_INV_JACK_DETECT, + AD1986A_FIXUP_ULTRA, + AD1986A_FIXUP_SAMSUNG, + AD1986A_FIXUP_3STACK, + AD1986A_FIXUP_LAPTOP, + AD1986A_FIXUP_LAPTOP_IMIC, }; static const struct hda_fixup ad1986a_fixups[] = { @@ -1210,16 +237,86 @@ static const struct hda_fixup ad1986a_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = ad_fixup_inv_jack_detect, }, + [AD1986A_FIXUP_ULTRA] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170110 }, /* speaker */ + { 0x1d, 0x90a7013e }, /* int mic */ + {} + }, + }, + [AD1986A_FIXUP_SAMSUNG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170110 }, /* speaker */ + { 0x1d, 0x90a7013e }, /* int mic */ + { 0x20, 0x411111f0 }, /* N/A */ + { 0x24, 0x411111f0 }, /* N/A */ + {} + }, + }, + [AD1986A_FIXUP_3STACK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x02214021 }, /* headphone */ + { 0x1b, 0x01014011 }, /* front */ + { 0x1c, 0x01013012 }, /* surround */ + { 0x1d, 0x01019015 }, /* clfe */ + { 0x1e, 0x411111f0 }, /* N/A */ + { 0x1f, 0x02a190f0 }, /* mic */ + { 0x20, 0x018130f0 }, /* line-in */ + {} + }, + }, + [AD1986A_FIXUP_LAPTOP] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x02214021 }, /* headphone */ + { 0x1b, 0x90170110 }, /* speaker */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x411111f0 }, /* N/A */ + { 0x1f, 0x02a191f0 }, /* mic */ + { 0x20, 0x411111f0 }, /* N/A */ + {} + }, + }, + [AD1986A_FIXUP_LAPTOP_IMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1d, 0x90a7013e }, /* int mic */ + {} + }, + .chained_before = 1, + .chain_id = AD1986A_FIXUP_LAPTOP, + }, }; static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC), + SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK), + SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK), + SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK), + SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP), + SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG), + SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT), + SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK), + SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK), + {} +}; + +static const struct hda_model_fixup ad1986a_fixup_models[] = { + { .id = AD1986A_FIXUP_3STACK, .name = "3stack" }, + { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" }, + { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" }, + { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */ {} }; /* */ -static int ad1986a_parse_auto_config(struct hda_codec *codec) +static int patch_ad1986a(struct hda_codec *codec) { int err; struct ad198x_spec *spec; @@ -1244,7 +341,8 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec) */ spec->gen.multiout.no_share_stream = 1; - snd_hda_pick_fixup(codec, NULL, ad1986a_fixup_tbl, ad1986a_fixups); + snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl, + ad1986a_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = ad198x_parse_auto_config(codec); @@ -1258,330 +356,11 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec) 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; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x19); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids); - spec->multiout.dac_nids = ad1986a_dac_nids; - spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = ad1986a_adc_nids; - spec->capsrc_nids = ad1986a_capsrc_nids; - spec->input_mux = &ad1986a_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1986a_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1986a_init_verbs; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1986a_loopbacks; -#endif - spec->vmaster_nid = 0x1b; - codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ - - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - switch (board_config) { - case AD1986A_3STACK: - spec->num_mixers = 2; - spec->mixers[1] = ad1986a_3st_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1986a_ch2_init; - spec->channel_mode = ad1986a_modes; - spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes); - spec->need_dac_fix = 1; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - break; - case AD1986A_LAPTOP: - spec->mixers[0] = ad1986a_laptop_mixers; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - break; - case AD1986A_LAPTOP_EAPD: - spec->num_mixers = 3; - spec->mixers[0] = ad1986a_laptop_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->mixers[2] = ad1986a_laptop_intmic_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - if (!is_jack_available(codec, 0x25)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_laptop_eapd_capture_source; - break; - case AD1986A_SAMSUNG: - spec->num_mixers = 2; - spec->mixers[0] = ad1986a_laptop_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 3; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->init_verbs[2] = ad1986a_automic_verbs; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - if (!is_jack_available(codec, 0x25)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_automic_capture_source; - codec->patch_ops.unsol_event = ad1986a_automic_unsol_event; - codec->patch_ops.init = ad1986a_automic_init; - break; - case AD1986A_SAMSUNG_P50: - spec->num_mixers = 2; - spec->mixers[0] = ad1986a_automute_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 4; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->init_verbs[2] = ad1986a_automic_verbs; - spec->init_verbs[3] = ad1986a_hp_init_verbs; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - if (!is_jack_available(codec, 0x25)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_automic_capture_source; - codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event; - codec->patch_ops.init = ad1986a_samsung_p50_init; - break; - case AD1986A_LAPTOP_AUTOMUTE: - spec->num_mixers = 3; - spec->mixers[0] = ad1986a_automute_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->mixers[2] = ad1986a_laptop_intmic_mixers; - spec->num_init_verbs = 3; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->init_verbs[2] = ad1986a_hp_init_verbs; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - if (!is_jack_available(codec, 0x25)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_laptop_eapd_capture_source; - codec->patch_ops.unsol_event = ad1986a_hp_unsol_event; - codec->patch_ops.init = ad1986a_hp_init; - /* Lenovo N100 seems to report the reversed bit - * for HP jack-sensing - */ - spec->inv_jack_detect = 1; - break; - case AD1986A_ULTRA: - spec->mixers[0] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1986a_ultra_init; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - spec->multiout.dig_out_nid = 0; - break; - } - - /* 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->multiout.no_share_stream = 1; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - 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 - -static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC }; -static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC }; -static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 }; - -static const struct hda_input_mux ad1983_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x1 }, - { "Mix", 0x2 }, - { "Mix Mono", 0x3 }, - }, -}; - -/* - * SPDIF playback route - */ -static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { "PCM", "ADC" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int ad1983_spdif_route_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->spdif_route; - return 0; -} - -static int ad1983_spdif_route_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; - - if (ucontrol->value.enumerated.item[0] > 1) - return -EINVAL; - if (spec->spdif_route != ucontrol->value.enumerated.item[0]) { - spec->spdif_route = ucontrol->value.enumerated.item[0]; - snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0, - AC_VERB_SET_CONNECT_SEL, - spec->spdif_route); - return 1; - } - return 0; -} - -static const struct snd_kcontrol_new ad1983_mixers[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct hda_verb ad1983_init_verbs[] = { - /* Front, HP, Mono; mute as default */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Beep, PCM, Mic, Line-In: mute */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Front, HP selectors; from Mix */ - {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x06, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* Mono selector; from Mix */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic selector; Mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Line-in selector: Line-in */ - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic boost: 0dB */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* Record selector: mic */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* SPDIF route: PCM */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Front Pin */ - {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* HP Pin */ - {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* Mono Pin */ - {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Mic Pin */ - {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* Line Pin */ - {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1983_loopbacks[] = { - { 0x12, HDA_OUTPUT, 0 }, /* Mic */ - { 0x13, HDA_OUTPUT, 0 }, /* Line */ - { } /* end */ -}; -#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 */ @@ -1656,7 +435,7 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) return 0; } -static int ad1983_parse_auto_config(struct hda_codec *codec) +static int patch_ad1983(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -1681,432 +460,11 @@ static int ad1983_parse_auto_config(struct hda_codec *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; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids); - spec->multiout.dac_nids = ad1983_dac_nids; - spec->multiout.dig_out_nid = AD1983_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = ad1983_adc_nids; - spec->capsrc_nids = ad1983_capsrc_nids; - spec->input_mux = &ad1983_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1983_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1983_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1983_loopbacks; -#endif - spec->vmaster_nid = 0x05; - - codec->patch_ops = ad198x_patch_ops; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - 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 - -static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC }; -static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC }; -static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 }; - -/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */ -static const struct hda_input_mux ad1981_capture_source = { - .num_items = 7, - .items = { - { "Front Mic", 0x0 }, - { "Line", 0x1 }, - { "Mix", 0x2 }, - { "Mix Mono", 0x3 }, - { "CD", 0x4 }, - { "Mic", 0x6 }, - { "Aux", 0x7 }, - }, -}; - -static const struct snd_kcontrol_new ad1981_mixers[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* identical with AD1983 */ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct hda_verb ad1981_init_verbs[] = { - /* Front, HP, Mono; mute as default */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Front, HP selectors; from Mix */ - {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x06, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* Mono selector; from Mix */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic Mixer; select Front Mic */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Mic boost: 0dB */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Record selector: Front mic */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* SPDIF route: PCM */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Front Pin */ - {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* HP Pin */ - {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* Mono Pin */ - {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Front & Rear Mic Pins */ - {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* Line Pin */ - {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* Digital Beep */ - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Line-Out as Input: disabled */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1981_loopbacks[] = { - { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */ - { 0x13, HDA_OUTPUT, 0 }, /* Line */ - { 0x1b, HDA_OUTPUT, 0 }, /* Aux */ - { 0x1c, HDA_OUTPUT, 0 }, /* Mic */ - { 0x1d, HDA_OUTPUT, 0 }, /* CD */ - { } /* end */ -}; -#endif - -/* - * Patch for HP nx6320 - * - * nx6320 uses EAPD in the reverse way - EAPD-on means the internal - * speaker output enabled _and_ mute-LED off. - */ - -#define AD1981_HP_EVENT 0x37 -#define AD1981_MIC_EVENT 0x38 - -static const struct hda_verb ad1981_hp_init_verbs[] = { - {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */ - /* pin sensing on HP and Mic jacks */ - {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT}, - {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT}, - {} -}; - -/* turn on/off EAPD (+ mute HP) as a master switch */ -static int ad1981_hp_master_sw_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; - - if (! ad198x_eapd_put(kcontrol, ucontrol)) - return 0; - /* change speaker pin appropriately */ - snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0); - /* toggle HP mute appropriately */ - snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - spec->cur_eapd ? 0 : HDA_AMP_MUTE); - return 1; -} - -/* bind volumes of both NID 0x05 and 0x06 */ -static const struct hda_bind_ctls ad1981_hp_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -/* mute internal speaker if HP is plugged */ -static void ad1981_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x06); - snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - -/* toggle input of built-in and mic jack appropriately */ -static void ad1981_hp_automic(struct hda_codec *codec) -{ - static const struct hda_verb mic_jack_on[] = { - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - static const struct hda_verb mic_jack_off[] = { - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x08); - if (present) - snd_hda_sequence_write(codec, mic_jack_on); - else - snd_hda_sequence_write(codec, mic_jack_off); -} - -/* unsolicited event for HP jack sensing */ -static void ad1981_hp_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - res >>= 26; - switch (res) { - case AD1981_HP_EVENT: - ad1981_hp_automute(codec); - break; - case AD1981_MIC_EVENT: - ad1981_hp_automic(codec); - break; - } -} - -static const struct hda_input_mux ad1981_hp_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Dock Mic", 0x1 }, - { "Mix", 0x2 }, - }, -}; - -static const struct snd_kcontrol_new ad1981_hp_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .subdevice = HDA_SUBDEV_NID_FLAG | 0x05, - .name = "Master Playback Switch", - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad1981_hp_master_sw_put, - .private_value = 0x05, - }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), -#if 0 - /* FIXME: analog mic/line loopback doesn't work with my tests... - * (although recording is OK) - */ - HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), - /* FIXME: does this laptop have analog CD connection? */ - HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), -#endif - HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -/* initialize jack-sensing, too */ -static int ad1981_hp_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1981_hp_automute(codec); - ad1981_hp_automic(codec); - return 0; -} - -/* configuration for Toshiba Laptops */ -static const struct hda_verb ad1981_toshiba_init_verbs[] = { - {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */ - /* pin sensing on HP and Mic jacks */ - {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT}, - {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT}, - {} -}; - -static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = { - HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT), - { } -}; - -/* configuration for Lenovo Thinkpad T60 */ -static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* identical with AD1983 */ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct hda_input_mux ad1981_thinkpad_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Mix", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* models */ -enum { - AD1981_AUTO, - AD1981_BASIC, - AD1981_HP, - AD1981_THINKPAD, - AD1981_TOSHIBA, - AD1981_MODELS -}; - -static const char * const ad1981_models[AD1981_MODELS] = { - [AD1981_AUTO] = "auto", - [AD1981_HP] = "hp", - [AD1981_THINKPAD] = "thinkpad", - [AD1981_BASIC] = "basic", - [AD1981_TOSHIBA] = "toshiba" -}; - -static const struct snd_pci_quirk ad1981_cfg_tbl[] = { - SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), - SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD), - /* All HP models */ - SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP), - SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), - /* Lenovo Thinkpad T60/X60/Z6xx */ - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD), - /* HP nx6320 (reversed SSID, H/W bug) */ - 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) { @@ -2172,7 +530,7 @@ static const struct snd_pci_quirk ad1981_fixup_tbl[] = { {} }; -static int ad1981_parse_auto_config(struct hda_codec *codec) +static int patch_ad1981(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -2205,110 +563,6 @@ static int ad1981_parse_auto_config(struct hda_codec *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; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids); - spec->multiout.dac_nids = ad1981_dac_nids; - spec->multiout.dig_out_nid = AD1981_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = ad1981_adc_nids; - spec->capsrc_nids = ad1981_capsrc_nids; - spec->input_mux = &ad1981_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1981_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1981_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1981_loopbacks; -#endif - spec->vmaster_nid = 0x05; - - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - switch (board_config) { - case AD1981_HP: - spec->mixers[0] = ad1981_hp_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1981_hp_init_verbs; - if (!is_jack_available(codec, 0x0a)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1981_hp_capture_source; - - codec->patch_ops.init = ad1981_hp_init; - codec->patch_ops.unsol_event = ad1981_hp_unsol_event; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - 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)); - break; - case AD1981_THINKPAD: - spec->mixers[0] = ad1981_thinkpad_mixers; - spec->input_mux = &ad1981_thinkpad_capture_source; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - 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)); - break; - case AD1981_TOSHIBA: - spec->mixers[0] = ad1981_hp_mixers; - spec->mixers[1] = ad1981_toshiba_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1981_toshiba_init_verbs; - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1981_hp_capture_source; - codec->patch_ops.init = ad1981_hp_init; - codec->patch_ops.unsol_event = ad1981_hp_unsol_event; - break; - } - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1981 ad1981_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - /* * AD1988 @@ -2395,90 +649,7 @@ static int patch_ad1981(struct hda_codec *codec) * E/F quad mic array */ - #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_MODEL_LAST, -}; - -/* reivision id to check workarounds */ -#define AD1988A_REV2 0x100200 - -#define is_rev2(codec) \ - ((codec)->vendor_id == 0x11d41988 && \ - (codec)->revision_id == AD1988A_REV2) - -/* - * mixers - */ - -static const hda_nid_t ad1988_6stack_dac_nids[4] = { - 0x04, 0x06, 0x05, 0x0a -}; - -static const hda_nid_t ad1988_3stack_dac_nids[3] = { - 0x04, 0x05, 0x0a -}; - -/* for AD1988A revision-2, DAC2-4 are swapped */ -static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = { - 0x04, 0x05, 0x0a, 0x06 -}; - -static const hda_nid_t ad1988_alt_dac_nid[1] = { - 0x03 -}; - -static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = { - 0x04, 0x0a, 0x06 -}; - -static const hda_nid_t ad1988_adc_nids[3] = { - 0x08, 0x09, 0x0f -}; - -static const hda_nid_t ad1988_capsrc_nids[3] = { - 0x0c, 0x0d, 0x0e -}; - -#define AD1988_SPDIF_OUT 0x02 -#define AD1988_SPDIF_OUT_HDMI 0x0b -#define AD1988_SPDIF_IN 0x07 - -static const hda_nid_t ad1989b_slave_dig_outs[] = { - AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0 -}; - -static const struct hda_input_mux ad1988_6stack_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x1 }, /* port-B */ - { "Line", 0x2 }, /* port-C */ - { "Mic", 0x4 }, /* port-E */ - { "CD", 0x5 }, - { "Mix", 0x9 }, - }, -}; - -static const struct hda_input_mux ad1988_laptop_capture_source = { - .num_items = 3, - .items = { - { "Mic/Line", 0x1 }, /* port-B */ - { "CD", 0x5 }, - { "Mix", 0x9 }, - }, -}; - -/* - */ static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2509,569 +680,6 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, spec->multiout.num_dacs = spec->multiout.max_channels / 2; return err; } - -/* 6-stack mode */ -static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* 3-stack mode */ -static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ad198x_ch_mode_info, - .get = ad198x_ch_mode_get, - .put = ad198x_ch_mode_put, - }, - - { } /* end */ -}; - -/* laptop mode */ -static const struct snd_kcontrol_new ad1988_laptop_mixers[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "External Amplifier", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x12, - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad198x_eapd_put, - .private_value = 0x12, /* port-D */ - }, - - { } /* end */ -}; - -/* capture */ -static const struct snd_kcontrol_new ad1988_capture_mixers[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 3, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "PCM", "ADC1", "ADC2", "ADC3" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= 4) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int sel; - - sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT); - if (!(sel & 0x80)) - ucontrol->value.enumerated.item[0] = 0; - else { - sel = snd_hda_codec_read(codec, 0x0b, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (sel < 3) - sel++; - else - sel = 0; - ucontrol->value.enumerated.item[0] = sel; - } - return 0; -} - -static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int val, sel; - int change; - - val = ucontrol->value.enumerated.item[0]; - if (val > 3) - return -EINVAL; - if (!val) { - sel = snd_hda_codec_read(codec, 0x1d, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT); - change = sel & 0x80; - if (change) { - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(1)); - } - } else { - sel = snd_hda_codec_read(codec, 0x1d, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT | 0x01); - change = sel & 0x80; - if (change) { - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } - sel = snd_hda_codec_read(codec, 0x0b, 0, - AC_VERB_GET_CONNECT_SEL, 0) + 1; - change |= sel != val; - if (change) - snd_hda_codec_write_cache(codec, 0x0b, 0, - AC_VERB_SET_CONNECT_SEL, - val - 1); - } - return change; -} - -static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = { - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, - .info = ad1988_spdif_playback_source_info, - .get = ad1988_spdif_playback_source_get, - .put = ad1988_spdif_playback_source_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = { - HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = { - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* - * initialization verbs - */ - -/* - * for 6-stack (+dig) - */ -static const struct hda_verb ad1988_6stack_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-D line-out path */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-F surround path */ - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-G CLFE path */ - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-H side path */ - {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B front mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C line-in path */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Port-E mic-in path */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Analog CD Input */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - - { } -}; - -static const struct hda_verb ad1988_6stack_fp_init_verbs[] = { - /* Headphone; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - - { } -}; - -static const struct hda_verb ad1988_capture_init_verbs[] = { - /* mute analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* select ADCs - front-mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - - { } -}; - -static const struct hda_verb ad1988_spdif_init_verbs[] = { - /* SPDIF out sel */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */ - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* SPDIF out pin */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - - { } -}; - -static const struct hda_verb ad1988_spdif_in_init_verbs[] = { - /* unmute SPDIF input pin */ - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { } -}; - -/* AD1989 has no ADC -> SPDIF route */ -static const struct hda_verb ad1989_spdif_init_verbs[] = { - /* SPDIF-1 out pin */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - /* SPDIF-2/HDMI out pin */ - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } -}; - -/* - * verbs for 3stack (+dig) - */ -static const struct hda_verb ad1988_3stack_ch2_init[] = { - /* set port-C to line-in */ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* set port-E to mic-in */ - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { } /* end */ -}; - -static const struct hda_verb ad1988_3stack_ch6_init[] = { - /* set port-C to surround out */ - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - /* set port-E to CLFE out */ - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -static const struct hda_channel_mode ad1988_3stack_modes[2] = { - { 2, ad1988_3stack_ch2_init }, - { 6, ad1988_3stack_ch6_init }, -}; - -static const struct hda_verb ad1988_3stack_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-D line-out path */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B front mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C line-in/surround path - 6ch mode as default */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */ - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Port-E mic-in/CLFE path - 6ch mode as default */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* mute analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* select ADCs - front-mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - { } -}; - -/* - * verbs for laptop mode (+dig) - */ -static const struct hda_verb ad1988_laptop_hp_on[] = { - /* unmute port-A and mute port-D */ - { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; -static const struct hda_verb ad1988_laptop_hp_off[] = { - /* mute port-A and unmute port-D */ - { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -#define AD1988_HP_EVENT 0x01 - -static const struct hda_verb ad1988_laptop_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT }, - /* Port-D line-out path + EAPD */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */ - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C docking station - try to output */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* mute analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* select ADCs - mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - { } -}; - -static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) != AD1988_HP_EVENT) - return; - 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); -} - -#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 #endif /* ENABLE_AD_STATIC_QUIRKS */ static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol, @@ -3220,7 +828,34 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) /* */ -static int ad1988_parse_auto_config(struct hda_codec *codec) +enum { + AD1988_FIXUP_6STACK_DIG, +}; + +static const struct hda_fixup ad1988_fixups[] = { + [AD1988_FIXUP_6STACK_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x11, 0x02214130 }, /* front-hp */ + { 0x12, 0x01014010 }, /* line-out */ + { 0x14, 0x02a19122 }, /* front-mic */ + { 0x15, 0x01813021 }, /* line-in */ + { 0x16, 0x01011012 }, /* line-out */ + { 0x17, 0x01a19020 }, /* mic */ + { 0x1b, 0x0145f1f0 }, /* SPDIF */ + { 0x24, 0x01016011 }, /* line-out */ + { 0x25, 0x01012013 }, /* line-out */ + { } + } + }, +}; + +static const struct hda_model_fixup ad1988_fixup_models[] = { + { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" }, + {} +}; + +static int patch_ad1988(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -3234,12 +869,19 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) spec->gen.mixer_merge_nid = 0x21; spec->gen.beep_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); + + snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + err = ad198x_parse_auto_config(codec); if (err < 0) goto error; err = ad1988_add_spdif_mux_ctl(codec); if (err < 0) goto error; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: @@ -3247,169 +889,6 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) return err; } -/* - */ - -#ifdef ENABLE_AD_STATIC_QUIRKS -static const char * const ad1988_models[AD1988_MODEL_LAST] = { - [AD1988_6STACK] = "6stack", - [AD1988_6STACK_DIG] = "6stack-dig", - [AD1988_3STACK] = "3stack", - [AD1988_3STACK_DIG] = "3stack-dig", - [AD1988_LAPTOP] = "laptop", - [AD1988_LAPTOP_DIG] = "laptop-dig", - [AD1988_AUTO] = "auto", -}; - -static const struct snd_pci_quirk ad1988_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG), - {} -}; - -static int patch_ad1988(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, - ad1988_models, ad1988_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - 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"); - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = ad1988_alt_dac_nid[0]; - switch (board_config) { - case AD1988_6STACK: - case AD1988_6STACK_DIG: - spec->multiout.max_channels = 8; - spec->multiout.num_dacs = 4; - if (is_rev2(codec)) - spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2; - else - spec->multiout.dac_nids = ad1988_6stack_dac_nids; - spec->input_mux = &ad1988_6stack_capture_source; - spec->num_mixers = 2; - if (is_rev2(codec)) - spec->mixers[0] = ad1988_6stack_mixers1_rev2; - else - spec->mixers[0] = ad1988_6stack_mixers1; - spec->mixers[1] = ad1988_6stack_mixers2; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_6stack_init_verbs; - if (board_config == AD1988_6STACK_DIG) { - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - spec->dig_in_nid = AD1988_SPDIF_IN; - } - break; - case AD1988_3STACK: - case AD1988_3STACK_DIG: - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = 3; - if (is_rev2(codec)) - spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2; - else - spec->multiout.dac_nids = ad1988_3stack_dac_nids; - spec->input_mux = &ad1988_6stack_capture_source; - spec->channel_mode = ad1988_3stack_modes; - spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes); - spec->num_mixers = 2; - if (is_rev2(codec)) - spec->mixers[0] = ad1988_3stack_mixers1_rev2; - else - spec->mixers[0] = ad1988_3stack_mixers1; - spec->mixers[1] = ad1988_3stack_mixers2; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_3stack_init_verbs; - if (board_config == AD1988_3STACK_DIG) - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - break; - case AD1988_LAPTOP: - case AD1988_LAPTOP_DIG: - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1988_3stack_dac_nids; - spec->input_mux = &ad1988_laptop_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1988_laptop_mixers; - codec->inv_eapd = 1; /* inverted EAPD */ - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_laptop_init_verbs; - if (board_config == AD1988_LAPTOP_DIG) - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - break; - } - - spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids); - spec->adc_nids = ad1988_adc_nids; - spec->capsrc_nids = ad1988_capsrc_nids; - spec->mixers[spec->num_mixers++] = ad1988_capture_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs; - if (spec->multiout.dig_out_nid) { - if (codec->vendor_id >= 0x11d4989a) { - spec->mixers[spec->num_mixers++] = - ad1989_spdif_out_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1989_spdif_init_verbs; - codec->slave_dig_outs = ad1989b_slave_dig_outs; - } else { - spec->mixers[spec->num_mixers++] = - ad1988_spdif_out_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1988_spdif_init_verbs; - } - } - if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) { - spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1988_spdif_in_init_verbs; - } - - codec->patch_ops = ad198x_patch_ops; - switch (board_config) { - case AD1988_LAPTOP: - case AD1988_LAPTOP_DIG: - codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; - break; - } -#ifdef CONFIG_PM - spec->loopback.amplist = ad1988_loopbacks; -#endif - spec->vmaster_nid = 0x04; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1988 ad1988_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - /* * AD1884 / AD1984 @@ -3423,167 +902,19 @@ static int patch_ad1988(struct hda_codec *codec) * * AD1984 = AD1884 + two digital mic-ins * - * FIXME: - * For simplicity, we share the single DAC for both HP and line-outs - * right now. The inidividual playbacks could be easily implemented, - * but no build-up framework is given, so far. - */ - -#ifdef ENABLE_AD_STATIC_QUIRKS -static const hda_nid_t ad1884_dac_nids[1] = { - 0x04, -}; - -static const hda_nid_t ad1884_adc_nids[2] = { - 0x08, 0x09, -}; - -static const hda_nid_t ad1884_capsrc_nids[2] = { - 0x0c, 0x0d, -}; - -#define AD1884_SPDIF_OUT 0x02 - -static const struct hda_input_mux ad1884_capture_source = { - .num_items = 4, - .items = { - { "Front Mic", 0x0 }, - { "Mic", 0x1 }, - { "CD", 0x2 }, - { "Mix", 0x3 }, - }, -}; - -static const struct snd_kcontrol_new ad1884_base_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984_dmic_mixers[] = { - HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0, - HDA_INPUT), - HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0, - HDA_INPUT), - { } /* end */ -}; - -/* - * initialization verbs + * AD1883 / AD1884A / AD1984A / AD1984B + * + * port-B (0x14) - front mic-in + * port-E (0x1c) - rear mic-in + * port-F (0x16) - CD / ext out + * port-C (0x15) - rear line-in + * port-D (0x12) - rear line-out + * port-A (0x11) - front hp-out + * + * AD1984A = AD1884A + digital-mic + * AD1883 = equivalent with AD1984A + * AD1984B = AD1984A + extra SPDIF-out */ -static const struct hda_verb ad1884_init_verbs[] = { - /* DACs; mute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-A (HP) mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* HP selector - select DAC2 */ - {0x22, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Port-D (Line-out) mixer */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-D pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono-out mixer */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Mono-out pin */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono selector */ - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Port-B (front mic) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Port-C (rear mic) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - /* SPDIF output selector */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1884_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Mic */ - { 0x20, HDA_INPUT, 2 }, /* CD */ - { 0x20, HDA_INPUT, 4 }, /* Docking */ - { } /* end */ -}; -#endif - -static const char * const ad1884_slave_vols[] = { - "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD", - "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958", - 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 @@ -3599,14 +930,34 @@ static void ad1884_fixup_amp_override(struct hda_codec *codec, (1 << AC_AMPCAP_MUTE_SHIFT)); } +/* toggle GPIO1 according to the mute state */ +static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct ad198x_spec *spec = codec->spec; + + if (spec->eapd_nid) + ad_vmaster_eapd_hook(private_data, enabled); + snd_hda_codec_update_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, + enabled ? 0x00 : 0x02); +} + static void ad1884_fixup_hp_eapd(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct ad198x_spec *spec = codec->spec; + static const struct hda_verb gpio_init_verbs[] = { + {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, + {}, + }; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; + spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; + snd_hda_sequence_write_cache(codec, gpio_init_verbs); break; case HDA_FIXUP_ACT_PROBE: if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -3617,9 +968,18 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec, } } +/* set magic COEFs for dmic */ +static const struct hda_verb ad1884_dmic_init_verbs[] = { + {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, + {0x01, AC_VERB_SET_PROC_COEF, 0x08}, + {} +}; + enum { AD1884_FIXUP_AMP_OVERRIDE, AD1884_FIXUP_HP_EAPD, + AD1884_FIXUP_DMIC_COEF, + AD1884_FIXUP_HP_TOUCHSMART, }; static const struct hda_fixup ad1884_fixups[] = { @@ -3633,15 +993,27 @@ static const struct hda_fixup ad1884_fixups[] = { .chained = true, .chain_id = AD1884_FIXUP_AMP_OVERRIDE, }, + [AD1884_FIXUP_DMIC_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = ad1884_dmic_init_verbs, + }, + [AD1884_FIXUP_HP_TOUCHSMART] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = ad1884_dmic_init_verbs, + .chained = true, + .chain_id = AD1884_FIXUP_HP_EAPD, + }, }; static const struct snd_pci_quirk ad1884_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART), SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF), {} }; -static int ad1884_parse_auto_config(struct hda_codec *codec) +static int patch_ad1884(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -3674,1170 +1046,6 @@ static int ad1884_parse_auto_config(struct hda_codec *codec) return err; } -#ifdef ENABLE_AD_STATIC_QUIRKS -static int patch_ad1884_basic(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids); - spec->multiout.dac_nids = ad1884_dac_nids; - spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; - spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids); - spec->adc_nids = ad1884_adc_nids; - spec->capsrc_nids = ad1884_capsrc_nids; - spec->input_mux = &ad1884_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1884_base_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1884_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1884_loopbacks; -#endif - spec->vmaster_nid = 0x04; - /* we need to cover all playback volumes */ - spec->slave_vols = ad1884_slave_vols; - /* slaves may contain input volumes, so we can't raise to 0dB blindly */ - spec->avoid_init_slave_vol = 1; - - codec->patch_ops = ad198x_patch_ops; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - 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 - */ -static const struct hda_input_mux ad1984_thinkpad_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "Mix", 0x3 }, - { "Dock Mic", 0x4 }, - }, -}; - - -/* - * Dell Precision T3400 - */ -static const struct hda_input_mux ad1984_dell_desktop_capture_source = { - .num_items = 3, - .items = { - { "Front Mic", 0x0 }, - { "Line-In", 0x1 }, - { "Mix", 0x3 }, - }, -}; - - -static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -/* additional verbs */ -static const struct hda_verb ad1984_thinkpad_init_verbs[] = { - /* Port-E (docking station mic) pin */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* docking mic boost */ - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Analog PC Beeper - allow firmware/ACPI beeps */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a}, - /* Analog mixer - docking mic; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* enable EAPD bit */ - {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - { } /* end */ -}; - -/* - * Dell Precision T3400 - */ -static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -/* Digial MIC ADC NID 0x05 + 0x06 */ -static int ad1984_pcm_dmic_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, 0x05 + substream->number, - stream_tag, 0, format); - return 0; -} - -static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number); - return 0; -} - -static const struct hda_pcm_stream ad1984_pcm_dmic_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x05, - .ops = { - .prepare = ad1984_pcm_dmic_prepare, - .cleanup = ad1984_pcm_dmic_cleanup - }, -}; - -static int ad1984_build_pcms(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct hda_pcm *info; - int err; - - err = ad198x_build_pcms(codec); - if (err < 0) - return err; - - info = spec->pcm_rec + codec->num_pcms; - codec->num_pcms++; - info->name = "AD1984 Digital Mic"; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture; - return 0; -} - -/* models */ -enum { - AD1984_AUTO, - AD1984_BASIC, - AD1984_THINKPAD, - AD1984_DELL_DESKTOP, - AD1984_MODELS -}; - -static const char * const ad1984_models[AD1984_MODELS] = { - [AD1984_AUTO] = "auto", - [AD1984_BASIC] = "basic", - [AD1984_THINKPAD] = "thinkpad", - [AD1984_DELL_DESKTOP] = "dell_desktop", -}; - -static const struct snd_pci_quirk ad1984_cfg_tbl[] = { - /* Lenovo Thinkpad T61/X61 */ - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD), - SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP), - SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP), - {} -}; - -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); - if (err < 0) - return err; - spec = codec->spec; - - switch (board_config) { - case AD1984_BASIC: - /* additional digital mics */ - spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers; - codec->patch_ops.build_pcms = ad1984_build_pcms; - break; - case AD1984_THINKPAD: - if (codec->subsystem_id == 0x17aa20fb) { - /* Thinpad X300 does not have the ability to do SPDIF, - or attach to docking station to use SPDIF */ - spec->multiout.dig_out_nid = 0; - } else - spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; - spec->input_mux = &ad1984_thinkpad_capture_source; - spec->mixers[0] = ad1984_thinkpad_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; - spec->analog_beep = 1; - break; - case AD1984_DELL_DESKTOP: - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1984_dell_desktop_capture_source; - spec->mixers[0] = ad1984_dell_desktop_mixers; - break; - } - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1984 ad1884_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - -/* - * AD1883 / AD1884A / AD1984A / AD1984B - * - * port-B (0x14) - front mic-in - * port-E (0x1c) - rear mic-in - * port-F (0x16) - CD / ext out - * port-C (0x15) - rear line-in - * port-D (0x12) - rear line-out - * port-A (0x11) - front hp-out - * - * AD1984A = AD1884A + digital-mic - * AD1883 = equivalent with AD1984A - * AD1984B = AD1984A + extra SPDIF-out - * - * FIXME: - * We share the single DAC for both HP and line-outs (see AD1884/1984). - */ - -#ifdef ENABLE_AD_STATIC_QUIRKS -static const hda_nid_t ad1884a_dac_nids[1] = { - 0x03, -}; - -#define ad1884a_adc_nids ad1884_adc_nids -#define ad1884a_capsrc_nids ad1884_capsrc_nids - -#define AD1884A_SPDIF_OUT 0x02 - -static const struct hda_input_mux ad1884a_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x0 }, - { "Mic", 0x4 }, - { "Line", 0x1 }, - { "CD", 0x2 }, - { "Mix", 0x3 }, - }, -}; - -static const struct snd_kcontrol_new ad1884a_base_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -/* - * initialization verbs - */ -static const struct hda_verb ad1884a_init_verbs[] = { - /* DACs; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - /* Port-A (HP) mixer - route only from analog mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-D (Line-out) mixer - route only from analog mixer */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-D pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono-out mixer - route only from analog mixer */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Mono-out pin */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-B (front mic) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Port-C (rear line-in) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Port-E (rear mic) pin */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */ - /* Port-F (CD) pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* capture sources */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* SPDIF output amp */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1884a_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Mic */ - { 0x20, HDA_INPUT, 2 }, /* CD */ - { 0x20, HDA_INPUT, 4 }, /* Docking */ - { } /* end */ -}; -#endif - -/* - * Laptop model - * - * Port A: Headphone jack - * Port B: MIC jack - * Port C: Internal MIC - * Port D: Dock Line Out (if enabled) - * Port E: Dock Line In (if enabled) - * Port F: Internal speakers - */ - -static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - int mute = (!ucontrol->value.integer.value[0] && - !ucontrol->value.integer.value[1]); - /* toggle GPIO1 according to the mute state */ - snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - mute ? 0x02 : 0x0); - return ret; -} - -static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1884a_mobile_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1884a_mobile_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* mute internal speaker if HP is plugged */ -static void ad1884a_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x11); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, - present ? 0x00 : 0x02); -} - -/* switch to external mic if plugged */ -static void ad1884a_hp_automic(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x14); - snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, - present ? 0 : 1); -} - -#define AD1884A_HP_EVENT 0x37 -#define AD1884A_MIC_EVENT 0x36 - -/* unsolicited event for HP jack sensing */ -static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (res >> 26) { - case AD1884A_HP_EVENT: - ad1884a_hp_automute(codec); - break; - case AD1884A_MIC_EVENT: - ad1884a_hp_automic(codec); - break; - } -} - -/* initialize jack-sensing, too */ -static int ad1884a_hp_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1884a_hp_automute(codec); - ad1884a_hp_automic(codec); - return 0; -} - -/* mute internal speaker if HP or docking HP is plugged */ -static void ad1884a_laptop_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x11); - if (!present) - present = snd_hda_jack_detect(codec, 0x12); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, - present ? 0x00 : 0x02); -} - -/* switch to external mic if plugged */ -static void ad1884a_laptop_automic(struct hda_codec *codec) -{ - unsigned int idx; - - if (snd_hda_jack_detect(codec, 0x14)) - idx = 0; - else if (snd_hda_jack_detect(codec, 0x1c)) - idx = 4; - else - idx = 1; - snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx); -} - -/* unsolicited event for HP jack sensing */ -static void ad1884a_laptop_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case AD1884A_HP_EVENT: - ad1884a_laptop_automute(codec); - break; - case AD1884A_MIC_EVENT: - ad1884a_laptop_automic(codec); - break; - } -} - -/* initialize jack-sensing, too */ -static int ad1884a_laptop_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1884a_laptop_automute(codec); - ad1884a_laptop_automic(codec); - return 0; -} - -/* additional verbs for laptop model */ -static const struct hda_verb ad1884a_laptop_verbs[] = { - /* Port-A (HP) pin - always unmuted */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-F (int speaker) mixer - route only from analog mixer */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F (int speaker) pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* required for compaq 6530s/6531s speaker output */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-C pin - internal mic-in */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* Port-D (docking line-out) pin - default unmuted */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - /* allow to touch GPIO1 (for mute control) */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ - { } /* end */ -}; - -static const struct hda_verb ad1884a_mobile_verbs[] = { - /* DACs; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - /* Port-A (HP) mixer - route only from analog mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-A (HP) pin - always unmuted */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-B (mic jack) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* Port-C (int mic) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* Port-F (int speaker) mixer - route only from analog mixer */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* capture sources */ - /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - /* allow to touch GPIO1 (for mute control) */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ - { } /* end */ -}; - -/* - * Thinkpad X300 - * 0x11 - HP - * 0x12 - speaker - * 0x14 - mic-in - * 0x17 - built-in mic - */ - -static const struct hda_verb ad1984a_thinkpad_verbs[] = { - /* HP unmute */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* turn on EAPD */ - {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - /* internal mic - dmic */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* set magic COEFs for dmic */ - {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, - {0x01, AC_VERB_SET_PROC_COEF, 0x08}, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -static const struct hda_input_mux ad1984a_thinkpad_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x5 }, - { "Mix", 0x3 }, - }, -}; - -/* mute internal speaker if HP is plugged */ -static void ad1984a_thinkpad_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x11); - snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - -/* unsolicited event for HP jack sensing */ -static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != AD1884A_HP_EVENT) - return; - ad1984a_thinkpad_automute(codec); -} - -/* initialize jack-sensing, too */ -static int ad1984a_thinkpad_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1984a_thinkpad_automute(codec); - return 0; -} - -/* - * Precision R5500 - * 0x12 - HP/line-out - * 0x13 - speaker (mono) - * 0x15 - mic-in - */ - -static const struct hda_verb ad1984a_precision_verbs[] = { - /* Unmute main output path */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */ - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Select mic as input */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */ - /* Configure as mic */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* HP unmute */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* turn on EAPD */ - {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - /* unsolicited event for pin-sense */ - {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984a_precision_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - - -/* mute internal speaker if HP is plugged */ -static void ad1984a_precision_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x12); - snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - - -/* unsolicited event for HP jack sensing */ -static void ad1984a_precision_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != AD1884A_HP_EVENT) - return; - ad1984a_precision_automute(codec); -} - -/* initialize jack-sensing, too */ -static int ad1984a_precision_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1984a_precision_automute(codec); - return 0; -} - - -/* - * HP Touchsmart - * port-A (0x11) - front hp-out - * port-B (0x14) - unused - * port-C (0x15) - unused - * port-D (0x12) - rear line out - * port-E (0x1c) - front mic-in - * port-F (0x16) - Internal speakers - * digital-mic (0x17) - Internal mic - */ - -static const struct hda_verb ad1984a_touchsmart_verbs[] = { - /* DACs; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - /* Port-A (HP) mixer - route only from analog mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-A (HP) pin - always unmuted */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-E (int speaker) mixer - route only from analog mixer */ - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03}, - /* Port-E pin */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /* Port-F (int speaker) mixer - route only from analog mixer */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* capture sources */ - /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - /* allow to touch GPIO1 (for mute control) */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ - /* internal mic - dmic */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* set magic COEFs for dmic */ - {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, - {0x01, AC_VERB_SET_PROC_COEF, 0x08}, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), -/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .subdevice = HDA_SUBDEV_AMP_FLAG, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1884a_mobile_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT), - { } /* end */ -}; - -/* switch to external mic if plugged */ -static void ad1984a_touchsmart_automic(struct hda_codec *codec) -{ - if (snd_hda_jack_detect(codec, 0x1c)) - snd_hda_codec_write(codec, 0x0c, 0, - AC_VERB_SET_CONNECT_SEL, 0x4); - else - snd_hda_codec_write(codec, 0x0c, 0, - AC_VERB_SET_CONNECT_SEL, 0x5); -} - - -/* unsolicited event for HP jack sensing */ -static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case AD1884A_HP_EVENT: - ad1884a_hp_automute(codec); - break; - case AD1884A_MIC_EVENT: - ad1984a_touchsmart_automic(codec); - break; - } -} - -/* initialize jack-sensing, too */ -static int ad1984a_touchsmart_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1884a_hp_automute(codec); - ad1984a_touchsmart_automic(codec); - return 0; -} - - -/* - */ - -enum { - AD1884A_AUTO, - AD1884A_DESKTOP, - AD1884A_LAPTOP, - AD1884A_MOBILE, - AD1884A_THINKPAD, - AD1984A_TOUCHSMART, - AD1984A_PRECISION, - AD1884A_MODELS -}; - -static const char * const ad1884a_models[AD1884A_MODELS] = { - [AD1884A_AUTO] = "auto", - [AD1884A_DESKTOP] = "desktop", - [AD1884A_LAPTOP] = "laptop", - [AD1884A_MOBILE] = "mobile", - [AD1884A_THINKPAD] = "thinkpad", - [AD1984A_TOUCHSMART] = "touchsmart", - [AD1984A_PRECISION] = "precision", -}; - -static const struct snd_pci_quirk ad1884a_cfg_tbl[] = { - SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION), - SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE), - SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), - SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART), - {} -}; - -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; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids); - spec->multiout.dac_nids = ad1884a_dac_nids; - spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT; - spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids); - spec->adc_nids = ad1884a_adc_nids; - spec->capsrc_nids = ad1884a_capsrc_nids; - spec->input_mux = &ad1884a_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1884a_base_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1884a_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1884a_loopbacks; -#endif - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - switch (board_config) { - case AD1884A_LAPTOP: - spec->mixers[0] = ad1884a_laptop_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event; - codec->patch_ops.init = ad1884a_laptop_init; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - 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)); - break; - case AD1884A_MOBILE: - spec->mixers[0] = ad1884a_mobile_mixers; - spec->init_verbs[0] = ad1884a_mobile_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; - codec->patch_ops.init = ad1884a_hp_init; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - 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)); - break; - case AD1884A_THINKPAD: - spec->mixers[0] = ad1984a_thinkpad_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1984a_thinkpad_verbs; - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1984a_thinkpad_capture_source; - codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; - codec->patch_ops.init = ad1984a_thinkpad_init; - break; - case AD1984A_PRECISION: - spec->mixers[0] = ad1984a_precision_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1984a_precision_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1984a_precision_unsol_event; - codec->patch_ops.init = ad1984a_precision_init; - break; - case AD1984A_TOUCHSMART: - spec->mixers[0] = ad1984a_touchsmart_mixers; - spec->init_verbs[0] = ad1984a_touchsmart_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event; - codec->patch_ops.init = ad1984a_touchsmart_init; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - 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)); - break; - } - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1884a ad1884_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - /* * AD1882 / AD1882A * @@ -4850,299 +1058,7 @@ 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 -}; - -static const hda_nid_t ad1882_adc_nids[2] = { - 0x08, 0x09, -}; - -static const hda_nid_t ad1882_capsrc_nids[2] = { - 0x0c, 0x0d, -}; - -#define AD1882_SPDIF_OUT 0x02 - -/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */ -static const struct hda_input_mux ad1882_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x1 }, - { "Mic", 0x4 }, - { "Line", 0x2 }, - { "CD", 0x3 }, - { "Mix", 0x7 }, - }, -}; - -/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */ -static const struct hda_input_mux ad1882a_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x1 }, - { "Mic", 0x4}, - { "Line", 0x2 }, - { "Digital Mic", 0x06 }, - { "Mix", 0x7 }, - }, -}; - -static const struct snd_kcontrol_new ad1882_base_mixers[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882_loopback_mixers[] = { - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = { - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), - HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882_3stack_mixers[] = { - HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ad198x_ch_mode_info, - .get = ad198x_ch_mode_get, - .put = ad198x_ch_mode_put, - }, - { } /* end */ -}; - -/* simple auto-mute control for AD1882 3-stack board */ -#define AD1882_HP_EVENT 0x01 - -static void ad1882_3stack_automute(struct hda_codec *codec) -{ - bool mute = snd_hda_jack_detect(codec, 0x11); - snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - mute ? 0 : PIN_OUT); -} - -static int ad1882_3stack_automute_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1882_3stack_automute(codec); - return 0; -} - -static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (res >> 26) { - case AD1882_HP_EVENT: - ad1882_3stack_automute(codec); - break; - } -} - -static const struct snd_kcontrol_new ad1882_6stack_mixers[] = { - HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct hda_verb ad1882_ch2_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } /* end */ -}; - -static const struct hda_verb ad1882_ch4_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } /* end */ -}; - -static const struct hda_verb ad1882_ch6_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - { } /* end */ -}; - -static const struct hda_channel_mode ad1882_modes[3] = { - { 2, ad1882_ch2_init }, - { 4, ad1882_ch4_init }, - { 6, ad1882_ch6_init }, -}; - -/* - * initialization verbs - */ -static const struct hda_verb ad1882_init_verbs[] = { - /* DACs; mute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-A (HP) mixer */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* HP selector - select DAC2 */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Port-D (Line-out) mixer */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-D pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono-out mixer */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Mono-out pin */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-B (front mic) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ - /* Port-C (line-in) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ - /* Port-C mixer - mute as input */ - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Port-E (mic-in) pin */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ - /* Port-E mixer - mute as input */ - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Port-F (surround) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-G (CLFE) */ - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - /* SPDIF output selector */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } /* end */ -}; - -static const struct hda_verb ad1882_3stack_automute_verbs[] = { - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT}, - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1882_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Mic */ - { 0x20, HDA_INPUT, 4 }, /* Line */ - { 0x20, HDA_INPUT, 6 }, /* CD */ - { } /* end */ -}; -#endif - -/* models */ -enum { - AD1882_AUTO, - AD1882_3STACK, - AD1882_6STACK, - AD1882_3STACK_AUTOMUTE, - AD1882_MODELS -}; - -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) +static int patch_ad1882(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -5169,110 +1085,20 @@ static int ad1882_parse_auto_config(struct hda_codec *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; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = 3; - spec->multiout.dac_nids = ad1882_dac_nids; - spec->multiout.dig_out_nid = AD1882_SPDIF_OUT; - spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids); - spec->adc_nids = ad1882_adc_nids; - spec->capsrc_nids = ad1882_capsrc_nids; - if (codec->vendor_id == 0x11d41882) - spec->input_mux = &ad1882_capture_source; - else - spec->input_mux = &ad1882a_capture_source; - spec->num_mixers = 2; - spec->mixers[0] = ad1882_base_mixers; - if (codec->vendor_id == 0x11d41882) - spec->mixers[1] = ad1882_loopback_mixers; - else - spec->mixers[1] = ad1882a_loopback_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1882_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1882_loopbacks; -#endif - spec->vmaster_nid = 0x04; - - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - switch (board_config) { - default: - case AD1882_3STACK: - case AD1882_3STACK_AUTOMUTE: - spec->num_mixers = 3; - spec->mixers[2] = ad1882_3stack_mixers; - spec->channel_mode = ad1882_modes; - spec->num_channel_mode = ARRAY_SIZE(ad1882_modes); - spec->need_dac_fix = 1; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - if (board_config != AD1882_3STACK) { - spec->init_verbs[spec->num_init_verbs++] = - ad1882_3stack_automute_verbs; - codec->patch_ops.unsol_event = ad1882_3stack_unsol_event; - codec->patch_ops.init = ad1882_3stack_automute_init; - } - break; - case AD1882_6STACK: - spec->num_mixers = 3; - spec->mixers[2] = ad1882_6stack_mixers; - break; - } - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1882 ad1882_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - /* * patch entries */ static const struct hda_codec_preset snd_hda_preset_analog[] = { - { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, + { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 }, { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, - { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, + { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 }, { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 }, - { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a }, - { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a }, + { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 }, + { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 }, { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, - { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 }, + { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 }, { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index de00ce16647..4edd2d0f9a3 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -66,6 +66,8 @@ struct conexant_spec { hda_nid_t eapds[4]; bool dynamic_eapd; + unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ + #ifdef ENABLE_CXT_STATIC_QUIRKS const struct snd_kcontrol_new *mixers[5]; int num_mixers; @@ -3200,6 +3202,9 @@ static int cx_auto_init(struct hda_codec *codec) snd_hda_gen_init(codec); if (!spec->dynamic_eapd) cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + return 0; } @@ -3224,6 +3229,8 @@ enum { CXT_PINCFG_LEMOTE_A1205, CXT_FIXUP_STEREO_DMIC, CXT_FIXUP_INC_MIC_BOOST, + CXT_FIXUP_HEADPHONE_MIC_PIN, + CXT_FIXUP_HEADPHONE_MIC, }; static void cxt_fixup_stereo_dmic(struct hda_codec *codec, @@ -3246,6 +3253,59 @@ static void cxt5066_increase_mic_boost(struct hda_codec *codec, (0 << AC_AMPCAP_MUTE_SHIFT)); } +static void cxt_update_headset_mode(struct hda_codec *codec) +{ + /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */ + int i; + bool mic_mode = false; + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + + hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; + + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].pin == mux_pin) { + mic_mode = !!cfg->inputs[i].is_headphone_mic; + break; + } + + if (mic_mode) { + snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */ + spec->gen.hp_jack_present = false; + } else { + snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */ + spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]); + } + + snd_hda_gen_update_outputs(codec); +} + +static void cxt_update_headset_mode_hook(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol) +{ + cxt_update_headset_mode(codec); +} + +static void cxt_fixup_headphone_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC; + break; + case HDA_FIXUP_ACT_PROBE: + spec->gen.cap_sync_hook = cxt_update_headset_mode_hook; + spec->gen.automute_hook = cxt_update_headset_mode; + break; + case HDA_FIXUP_ACT_INIT: + cxt_update_headset_mode(codec); + break; + } +} + + /* ThinkPad X200 & co with cxt5051 */ static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ @@ -3302,6 +3362,19 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cxt5066_increase_mic_boost, }, + [CXT_FIXUP_HEADPHONE_MIC_PIN] = { + .type = HDA_FIXUP_PINS, + .chained = true, + .chain_id = CXT_FIXUP_HEADPHONE_MIC, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { } + } + }, + [CXT_FIXUP_HEADPHONE_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_headphone_mic, + }, }; static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -3311,6 +3384,7 @@ static const struct snd_pci_quirk cxt5051_fixups[] = { static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), @@ -3395,7 +3469,8 @@ static int patch_conexant_auto(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, + spec->parse_flags); if (err < 0) goto error; @@ -3416,6 +3491,8 @@ static int patch_conexant_auto(struct hda_codec *codec) codec->bus->allow_bus_reset = 1; } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 030ca8652a1..895a0d3320b 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -959,6 +959,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) int pin_nid; int pin_idx; struct hda_jack_tbl *jack; + int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT; jack = snd_hda_jack_tbl_get_from_tag(codec, tag); if (!jack) @@ -967,8 +968,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) jack->jack_dirty = 1; _snd_printd(SND_PR_VERBOSE, - "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, pin_nid, + "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n", + codec->addr, pin_nid, dev_entry, !!(res & AC_UNSOL_RES_IA), !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); pin_idx = pin_nid_to_pin_index(spec, pin_nid); @@ -1989,8 +1990,10 @@ static int patch_generic_hdmi(struct hda_codec *codec) return -EINVAL; } codec->patch_ops = generic_hdmi_patch_ops; - if (codec->vendor_id == 0x80862807) + if (codec->vendor_id == 0x80862807) { codec->patch_ops.set_power_state = haswell_set_power_state; + codec->dp_mst = true; + } generic_hdmi_init_per_pins(codec); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8bd22614986..4a909170b59 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -282,6 +282,7 @@ static void alc_eapd_shutup(struct hda_codec *codec) { alc_auto_setup_eapd(codec, false); msleep(200); + snd_hda_shutup_pins(codec); } /* generic EAPD initialization */ @@ -826,7 +827,8 @@ static inline void alc_shutup(struct hda_codec *codec) if (spec && spec->shutup) spec->shutup(codec); - snd_hda_shutup_pins(codec); + else + snd_hda_shutup_pins(codec); } #define alc_free snd_hda_gen_free @@ -1031,6 +1033,7 @@ enum { ALC880_FIXUP_GPIO2, ALC880_FIXUP_MEDION_RIM, ALC880_FIXUP_LG, + ALC880_FIXUP_LG_LW25, ALC880_FIXUP_W810, ALC880_FIXUP_EAPD_COEF, ALC880_FIXUP_TCL_S700, @@ -1089,6 +1092,14 @@ static const struct hda_fixup alc880_fixups[] = { { } } }, + [ALC880_FIXUP_LG_LW25] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x0181344f }, /* line-in */ + { 0x1b, 0x0321403f }, /* headphone */ + { } + } + }, [ALC880_FIXUP_W810] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -1341,6 +1352,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25), SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), /* Below is the copied entries from alc880_quirks.c. @@ -1843,8 +1855,10 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gen.no_primary_hp = 1; + spec->gen.no_multi_io = 1; + } } static const struct hda_fixup alc882_fixups[] = { @@ -2523,6 +2537,7 @@ enum { ALC269_TYPE_ALC269VD, ALC269_TYPE_ALC280, ALC269_TYPE_ALC282, + ALC269_TYPE_ALC283, ALC269_TYPE_ALC284, ALC269_TYPE_ALC286, }; @@ -2548,6 +2563,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC269VB: case ALC269_TYPE_ALC269VD: case ALC269_TYPE_ALC282: + case ALC269_TYPE_ALC283: case ALC269_TYPE_ALC286: ssids = alc269_ssids; break; @@ -2573,15 +2589,81 @@ static void alc269_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (spec->codec_variant != ALC269_TYPE_ALC269VB) - return; - if (spec->codec_variant == ALC269_TYPE_ALC269VB) alc269vb_toggle_power_output(codec, 0); if (spec->codec_variant == ALC269_TYPE_ALC269VB && (alc_get_coef0(codec) & 0x00ff) == 0x018) { msleep(150); } + snd_hda_shutup_pins(codec); +} + +static void alc283_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + bool hp_pin_sense; + int val; + + if (!hp_pin) + return; + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + /* Index 0x43 Direct Drive HP AMP LPM Control 1 */ + /* Headphone capless set to high power mode */ + alc_write_coef_idx(codec, 0x43, 0x9004); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + if (hp_pin_sense) + msleep(85); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + + if (hp_pin_sense) + msleep(85); + /* Index 0x46 Combo jack auto switch control 2 */ + /* 3k pull low control for Headset jack. */ + val = alc_read_coef_idx(codec, 0x46); + alc_write_coef_idx(codec, 0x46, val & ~(3 << 12)); + /* Headphone capless set to normal mode */ + alc_write_coef_idx(codec, 0x43, 0x9614); +} + +static void alc283_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + bool hp_pin_sense; + int val; + + if (!hp_pin) { + alc269_shutup(codec); + return; + } + + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + alc_write_coef_idx(codec, 0x43, 0x9004); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + if (hp_pin_sense) + msleep(85); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + val = alc_read_coef_idx(codec, 0x46); + alc_write_coef_idx(codec, 0x46, val | (3 << 12)); + + if (hp_pin_sense) + msleep(85); + snd_hda_shutup_pins(codec); + alc_write_coef_idx(codec, 0x43, 0x9614); } static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, @@ -2712,6 +2794,7 @@ static int alc269_resume(struct hda_codec *codec) hda_call_check_power_status(codec, 0x01); if (spec->has_alc5505_dsp) alc5505_dsp_resume(codec); + return 0; } #endif /* CONFIG_PM */ @@ -3251,6 +3334,28 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, alc_fixup_headset_mode(codec, fix, action); } +/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */ +static int find_ext_mic_pin(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + hda_nid_t nid; + unsigned int defcfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + nid = cfg->inputs[i].pin; + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) + continue; + return nid; + } + + return 0; +} + static void alc271_hp_gate_mic_jack(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -3258,11 +3363,12 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, 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])) + int mic_pin = find_ext_mic_pin(codec); + int hp_pin = spec->gen.autocfg.hp_pins[0]; + + if (snd_BUG_ON(!mic_pin || !hp_pin)) return; - snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin, - spec->gen.autocfg.hp_pins[0]); + snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin); } } @@ -3298,6 +3404,45 @@ static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec, } } +static void alc283_hp_automute_hook(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + struct alc_spec *spec = codec->spec; + int vref; + + msleep(200); + snd_hda_gen_hp_automute(codec, jack); + + vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; + + msleep(600); + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + vref); +} + +static void alc283_chromebook_caps(struct hda_codec *codec) +{ + snd_hda_override_wcaps(codec, 0x03, 0); +} + +static void alc283_fixup_chromebook(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + int val; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + alc283_chromebook_caps(codec); + spec->gen.hp_automute_hook = alc283_hp_automute_hook; + /* MIC2-VREF control */ + /* Set to manual mode */ + val = alc_read_coef_idx(codec, 0x06); + alc_write_coef_idx(codec, 0x06, val & ~0x000c); + break; + } +} + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -3334,6 +3479,7 @@ enum { ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_LIMIT_INT_MIC_BOOST, ALC269VB_FIXUP_ORDISSIMO_EVE2, + ALC283_FIXUP_CHROME_BOOK, }; static const struct hda_fixup alc269_fixups[] = { @@ -3585,11 +3731,20 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, + [ALC283_FIXUP_CHROME_BOOK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc283_fixup_chromebook, + }, }; 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(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700), + SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), + SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), + SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), + SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), @@ -3627,6 +3782,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK), SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -3645,11 +3801,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), - SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), - SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700), - SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), - SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), - SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), @@ -3660,8 +3811,16 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ @@ -3830,11 +3989,15 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0290: spec->codec_variant = ALC269_TYPE_ALC280; break; - case 0x10ec0233: case 0x10ec0282: - case 0x10ec0283: spec->codec_variant = ALC269_TYPE_ALC282; break; + case 0x10ec0233: + case 0x10ec0283: + spec->codec_variant = ALC269_TYPE_ALC283; + spec->shutup = alc283_shutup; + spec->init_hook = alc283_init; + break; case 0x10ec0284: case 0x10ec0292: spec->codec_variant = ALC269_TYPE_ALC284; @@ -3862,7 +4025,8 @@ static int patch_alc269(struct hda_codec *codec) codec->patch_ops.suspend = alc269_suspend; codec->patch_ops.resume = alc269_resume; #endif - spec->shutup = alc269_shutup; + if (!spec->shutup) + spec->shutup = alc269_shutup; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -4329,6 +4493,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 6d1924c19ab..fba0cef1c47 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -158,6 +158,7 @@ enum { STAC_D965_VERBS, STAC_DELL_3ST, STAC_DELL_BIOS, + STAC_DELL_BIOS_AMIC, STAC_DELL_BIOS_SPDIF, STAC_927X_DELL_DMIC, STAC_927X_VOLKNOB, @@ -3231,8 +3232,6 @@ static const struct hda_fixup stac927x_fixups[] = { [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, 0x0221101f }, /* correct the front input jack as a mic */ @@ -3242,6 +3241,16 @@ static const struct hda_fixup stac927x_fixups[] = { .chained = true, .chain_id = STAC_927X_DELL_DMIC, }, + [STAC_DELL_BIOS_AMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* configure the analog microphone on some laptops */ + { 0x0c, 0x90a79130 }, + {} + }, + .chained = true, + .chain_id = STAC_DELL_BIOS, + }, [STAC_DELL_BIOS_SPDIF] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -3270,6 +3279,7 @@ static const struct hda_model_fixup stac927x_models[] = { { .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_DELL_BIOS_AMIC, .name = "dell-bios-amic" }, { .id = STAC_927X_VOLKNOB, .name = "volknob" }, {} }; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e2481baddc7..0bc20ef5687 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -207,9 +207,9 @@ static void vt1708_stop_hp_work(struct hda_codec *codec) return; if (spec->hp_work_active) { snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); + codec->jackpoll_interval = 0; cancel_delayed_work_sync(&codec->jackpoll_work); spec->hp_work_active = false; - codec->jackpoll_interval = 0; } } diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 2a8ad9d1a2a..bb9ebc5543d 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -28,6 +28,7 @@ #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/module.h> +#include <linux/vmalloc.h> #include <sound/core.h> #include <sound/info.h> @@ -198,6 +199,31 @@ MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard."); #define RME96_AD1852_VOL_BITS 14 #define RME96_AD1855_VOL_BITS 10 +/* Defines for snd_rme96_trigger */ +#define RME96_TB_START_PLAYBACK 1 +#define RME96_TB_START_CAPTURE 2 +#define RME96_TB_STOP_PLAYBACK 4 +#define RME96_TB_STOP_CAPTURE 8 +#define RME96_TB_RESET_PLAYPOS 16 +#define RME96_TB_RESET_CAPTUREPOS 32 +#define RME96_TB_CLEAR_PLAYBACK_IRQ 64 +#define RME96_TB_CLEAR_CAPTURE_IRQ 128 +#define RME96_RESUME_PLAYBACK (RME96_TB_START_PLAYBACK) +#define RME96_RESUME_CAPTURE (RME96_TB_START_CAPTURE) +#define RME96_RESUME_BOTH (RME96_RESUME_PLAYBACK \ + | RME96_RESUME_CAPTURE) +#define RME96_START_PLAYBACK (RME96_TB_START_PLAYBACK \ + | RME96_TB_RESET_PLAYPOS) +#define RME96_START_CAPTURE (RME96_TB_START_CAPTURE \ + | RME96_TB_RESET_CAPTUREPOS) +#define RME96_START_BOTH (RME96_START_PLAYBACK \ + | RME96_START_CAPTURE) +#define RME96_STOP_PLAYBACK (RME96_TB_STOP_PLAYBACK \ + | RME96_TB_CLEAR_PLAYBACK_IRQ) +#define RME96_STOP_CAPTURE (RME96_TB_STOP_CAPTURE \ + | RME96_TB_CLEAR_CAPTURE_IRQ) +#define RME96_STOP_BOTH (RME96_STOP_PLAYBACK \ + | RME96_STOP_CAPTURE) struct rme96 { spinlock_t lock; @@ -214,6 +240,13 @@ struct rme96 { u8 rev; /* card revision number */ +#ifdef CONFIG_PM + u32 playback_pointer; + u32 capture_pointer; + void *playback_suspend_buffer; + void *capture_suspend_buffer; +#endif + struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; @@ -344,6 +377,8 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -373,6 +408,8 @@ static struct snd_pcm_hardware snd_rme96_capture_spdif_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -402,6 +439,8 @@ static struct snd_pcm_hardware snd_rme96_playback_adat_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -427,6 +466,8 @@ static struct snd_pcm_hardware snd_rme96_capture_adat_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -1045,54 +1086,35 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream, } static void -snd_rme96_playback_start(struct rme96 *rme96, - int from_pause) +snd_rme96_trigger(struct rme96 *rme96, + int op) { - if (!from_pause) { + if (op & RME96_TB_RESET_PLAYPOS) writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); - } - - rme96->wcreg |= RME96_WCR_START; - writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); -} - -static void -snd_rme96_capture_start(struct rme96 *rme96, - int from_pause) -{ - if (!from_pause) { + if (op & RME96_TB_RESET_CAPTUREPOS) writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); - } - - rme96->wcreg |= RME96_WCR_START_2; + if (op & RME96_TB_CLEAR_PLAYBACK_IRQ) { + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ) + writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); + } + if (op & RME96_TB_CLEAR_CAPTURE_IRQ) { + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ_2) + writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); + } + if (op & RME96_TB_START_PLAYBACK) + rme96->wcreg |= RME96_WCR_START; + if (op & RME96_TB_STOP_PLAYBACK) + rme96->wcreg &= ~RME96_WCR_START; + if (op & RME96_TB_START_CAPTURE) + rme96->wcreg |= RME96_WCR_START_2; + if (op & RME96_TB_STOP_CAPTURE) + rme96->wcreg &= ~RME96_WCR_START_2; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); } -static void -snd_rme96_playback_stop(struct rme96 *rme96) -{ - /* - * Check if there is an unconfirmed IRQ, if so confirm it, or else - * the hardware will not stop generating interrupts - */ - rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); - if (rme96->rcreg & RME96_RCR_IRQ) { - writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); - } - rme96->wcreg &= ~RME96_WCR_START; - writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); -} -static void -snd_rme96_capture_stop(struct rme96 *rme96) -{ - rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); - if (rme96->rcreg & RME96_RCR_IRQ_2) { - writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); - } - rme96->wcreg &= ~RME96_WCR_START_2; - writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); -} static irqreturn_t snd_rme96_interrupt(int irq, @@ -1155,6 +1177,7 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream) struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_set_sync(substream); spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { spin_unlock_irq(&rme96->lock); @@ -1191,6 +1214,7 @@ snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream) struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_set_sync(substream); runtime->hw = snd_rme96_capture_spdif_info; if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) @@ -1222,6 +1246,7 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream) struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_set_sync(substream); spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { spin_unlock_irq(&rme96->lock); @@ -1253,6 +1278,7 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream) struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_set_sync(substream); runtime->hw = snd_rme96_capture_adat_info; if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { /* makes no sense to use analog input. Note that analog @@ -1288,7 +1314,7 @@ snd_rme96_playback_close(struct snd_pcm_substream *substream) spin_lock_irq(&rme96->lock); if (RME96_ISPLAYING(rme96)) { - snd_rme96_playback_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK); } rme96->playback_substream = NULL; rme96->playback_periodsize = 0; @@ -1309,7 +1335,7 @@ snd_rme96_capture_close(struct snd_pcm_substream *substream) spin_lock_irq(&rme96->lock); if (RME96_ISRECORDING(rme96)) { - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_CAPTURE); } rme96->capture_substream = NULL; rme96->capture_periodsize = 0; @@ -1324,7 +1350,7 @@ snd_rme96_playback_prepare(struct snd_pcm_substream *substream) spin_lock_irq(&rme96->lock); if (RME96_ISPLAYING(rme96)) { - snd_rme96_playback_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK); } writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); spin_unlock_irq(&rme96->lock); @@ -1338,7 +1364,7 @@ snd_rme96_capture_prepare(struct snd_pcm_substream *substream) spin_lock_irq(&rme96->lock); if (RME96_ISRECORDING(rme96)) { - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_CAPTURE); } writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); spin_unlock_irq(&rme96->lock); @@ -1350,41 +1376,55 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *s; + bool sync; + + snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) == rme96) + snd_pcm_trigger_done(s, substream); + } + + sync = (rme96->playback_substream && rme96->capture_substream) && + (rme96->playback_substream->group == + rme96->capture_substream->group); switch (cmd) { case SNDRV_PCM_TRIGGER_START: if (!RME96_ISPLAYING(rme96)) { - if (substream != rme96->playback_substream) { + if (substream != rme96->playback_substream) return -EBUSY; - } - snd_rme96_playback_start(rme96, 0); + snd_rme96_trigger(rme96, sync ? RME96_START_BOTH + : RME96_START_PLAYBACK); } break; + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: if (RME96_ISPLAYING(rme96)) { - if (substream != rme96->playback_substream) { + if (substream != rme96->playback_substream) return -EBUSY; - } - snd_rme96_playback_stop(rme96); + snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH + : RME96_STOP_PLAYBACK); } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (RME96_ISPLAYING(rme96)) { - snd_rme96_playback_stop(rme96); - } + if (RME96_ISPLAYING(rme96)) + snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH + : RME96_STOP_PLAYBACK); break; + case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!RME96_ISPLAYING(rme96)) { - snd_rme96_playback_start(rme96, 1); - } + if (!RME96_ISPLAYING(rme96)) + snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH + : RME96_RESUME_PLAYBACK); break; - + default: return -EINVAL; } + return 0; } @@ -1393,38 +1433,51 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *s; + bool sync; + + snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) == rme96) + snd_pcm_trigger_done(s, substream); + } + + sync = (rme96->playback_substream && rme96->capture_substream) && + (rme96->playback_substream->group == + rme96->capture_substream->group); switch (cmd) { case SNDRV_PCM_TRIGGER_START: if (!RME96_ISRECORDING(rme96)) { - if (substream != rme96->capture_substream) { + if (substream != rme96->capture_substream) return -EBUSY; - } - snd_rme96_capture_start(rme96, 0); + snd_rme96_trigger(rme96, sync ? RME96_START_BOTH + : RME96_START_CAPTURE); } break; + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: if (RME96_ISRECORDING(rme96)) { - if (substream != rme96->capture_substream) { + if (substream != rme96->capture_substream) return -EBUSY; - } - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH + : RME96_STOP_CAPTURE); } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (RME96_ISRECORDING(rme96)) { - snd_rme96_capture_stop(rme96); - } + if (RME96_ISRECORDING(rme96)) + snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH + : RME96_STOP_CAPTURE); break; + case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!RME96_ISRECORDING(rme96)) { - snd_rme96_capture_start(rme96, 1); - } + if (!RME96_ISRECORDING(rme96)) + snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH + : RME96_RESUME_CAPTURE); break; - + default: return -EINVAL; } @@ -1505,8 +1558,7 @@ snd_rme96_free(void *private_data) return; } if (rme96->irq >= 0) { - snd_rme96_playback_stop(rme96); - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_BOTH); rme96->areg &= ~RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); free_irq(rme96->irq, (void *)rme96); @@ -1520,6 +1572,10 @@ snd_rme96_free(void *private_data) pci_release_regions(rme96->pci); rme96->port = 0; } +#ifdef CONFIG_PM + vfree(rme96->playback_suspend_buffer); + vfree(rme96->capture_suspend_buffer); +#endif pci_disable_device(rme96->pci); } @@ -1606,8 +1662,7 @@ snd_rme96_create(struct rme96 *rme96) rme96->capture_periodsize = 0; /* make sure playback/capture is stopped, if by some reason active */ - snd_rme96_playback_stop(rme96); - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_BOTH); /* set default values in registers */ rme96->wcreg = @@ -2319,6 +2374,87 @@ snd_rme96_create_switches(struct snd_card *card, * Card initialisation */ +#ifdef CONFIG_PM + +static int +snd_rme96_suspend(struct pci_dev *pci, + pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct rme96 *rme96 = card->private_data; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend(rme96->playback_substream); + snd_pcm_suspend(rme96->capture_substream); + + /* save capture & playback pointers */ + rme96->playback_pointer = readl(rme96->iobase + RME96_IO_GET_PLAY_POS) + & RME96_RCR_AUDIO_ADDR_MASK; + rme96->capture_pointer = readl(rme96->iobase + RME96_IO_GET_REC_POS) + & RME96_RCR_AUDIO_ADDR_MASK; + + /* save playback and capture buffers */ + memcpy_fromio(rme96->playback_suspend_buffer, + rme96->iobase + RME96_IO_PLAY_BUFFER, RME96_BUFFER_SIZE); + memcpy_fromio(rme96->capture_suspend_buffer, + rme96->iobase + RME96_IO_REC_BUFFER, RME96_BUFFER_SIZE); + + /* disable the DAC */ + rme96->areg &= ~RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + pci_disable_device(pci); + pci_save_state(pci); + + return 0; +} + +static int +snd_rme96_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct rme96 *rme96 = card->private_data; + + pci_restore_state(pci); + if (pci_enable_device(pci) < 0) { + printk(KERN_ERR "rme96: pci_enable_device failed, disabling device\n"); + snd_card_disconnect(card); + return -EIO; + } + + /* reset playback and record buffer pointers */ + writel(0, rme96->iobase + RME96_IO_SET_PLAY_POS + + rme96->playback_pointer); + writel(0, rme96->iobase + RME96_IO_SET_REC_POS + + rme96->capture_pointer); + + /* restore playback and capture buffers */ + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER, + rme96->playback_suspend_buffer, RME96_BUFFER_SIZE); + memcpy_toio(rme96->iobase + RME96_IO_REC_BUFFER, + rme96->capture_suspend_buffer, RME96_BUFFER_SIZE); + + /* reset the ADC */ + writel(rme96->areg | RME96_AR_PD2, + rme96->iobase + RME96_IO_ADDITIONAL_REG); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset and enable DAC, restore analog volume */ + snd_rme96_reset_dac(rme96); + rme96->areg |= RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + if (RME96_HAS_ANALOG_OUT(rme96)) { + usleep_range(3000, 10000); + snd_rme96_apply_dac_volume(rme96); + } + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + return 0; +} + +#endif + static void snd_rme96_card_free(struct snd_card *card) { snd_rme96_free(card->private_data); @@ -2355,6 +2491,23 @@ snd_rme96_probe(struct pci_dev *pci, return err; } +#ifdef CONFIG_PM + rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->playback_suspend_buffer) { + snd_printk(KERN_ERR + "Failed to allocate playback suspend buffer!\n"); + snd_card_free(card); + return -ENOMEM; + } + rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->capture_suspend_buffer) { + snd_printk(KERN_ERR + "Failed to allocate capture suspend buffer!\n"); + snd_card_free(card); + return -ENOMEM; + } +#endif + strcpy(card->driver, "Digi96"); switch (rme96->pci->device) { case PCI_DEVICE_ID_RME_DIGI96: @@ -2397,6 +2550,10 @@ static struct pci_driver rme96_driver = { .id_table = snd_rme96_ids, .probe = snd_rme96_probe, .remove = snd_rme96_remove, +#ifdef CONFIG_PM + .suspend = snd_rme96_suspend, + .resume = snd_rme96_resume, +#endif }; module_pci_driver(rme96_driver); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index bd501931ee2..3cde55b753e 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -38,6 +38,97 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ + +/* ************* Register Documentation ******************************************************* + * + * Work in progress! Documentation is based on the code in this file. + * + * --------- HDSPM_controlRegister --------- + * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :3322.2222:2222.1111:1111.1100:0000.0000: bit number + * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31 + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit + * : . : . : . : x . : HDSPM_AudioInterruptEnable \_ setting both bits + * : . : . : . : . x: HDSPM_Start / enables audio IO + * : . : . : . : x. : HDSPM_ClockModeMaster - 1: Master, 0: Slave + * : . : . : . : .210 : HDSPM_LatencyMask - 3 Bit value for latency + * : . : . : . : . : 0:64, 1:128, 2:256, 3:512, + * : . : . : . : . : 4:1024, 5:2048, 6:4096, 7:8192 + * :x . : . : . x:xx . : HDSPM_FrequencyMask + * : . : . : . :10 . : HDSPM_Frequency1|HDSPM_Frequency0: 1=32K,2=44.1K,3=48K,0=?? + * : . : . : . x: . : <MADI> HDSPM_DoubleSpeed + * :x . : . : . : . : <MADI> HDSPM_QuadSpeed + * : . 3 : . 10: 2 . : . : HDSPM_SyncRefMask : + * : . : . x: . : . : HDSPM_SyncRef0 + * : . : . x : . : . : HDSPM_SyncRef1 + * : . : . : x . : . : <AES32> HDSPM_SyncRef2 + * : . x : . : . : . : <AES32> HDSPM_SyncRef3 + * : . : . 10: . : . : <MADI> sync ref: 0:WC, 1:Madi, 2:TCO, 3:SyncIn + * : . 3 : . 10: 2 . : . : <AES32> 0:WC, 1:AES1 ... 8:AES8, 9: TCO, 10:SyncIn? + * : . x : . : . : . : <MADIe> HDSPe_FLOAT_FORMAT + * : . : . : x . : . : <MADI> HDSPM_InputSelect0 : 0=optical,1=coax + * : . : . :x . : . : <MADI> HDSPM_InputSelect1 + * : . : .x : . : . : <MADI> HDSPM_clr_tms + * : . : . : . x : . : <MADI> HDSPM_TX_64ch + * : . : . : . x : . : <AES32> HDSPM_Emphasis + * : . : . : .x : . : <MADI> HDSPM_AutoInp + * : . : . x : . : . : <MADI> HDSPM_SMUX + * : . : .x : . : . : <MADI> HDSPM_clr_tms + * : . : x. : . : . : <MADI> HDSPM_taxi_reset + * : . x: . : . : . : <MADI> HDSPM_LineOut + * : . x: . : . : . : <AES32> ?????????????????? + * : . : x. : . : . : <AES32> HDSPM_WCK48 + * : . : . : .x : . : <AES32> HDSPM_Dolby + * : . : x . : . : . : HDSPM_Midi0InterruptEnable + * : . :x . : . : . : HDSPM_Midi1InterruptEnable + * : . : x . : . : . : HDSPM_Midi2InterruptEnable + * : . x : . : . : . : <MADI> HDSPM_Midi3InterruptEnable + * : . x : . : . : . : <AES32> HDSPM_DS_DoubleWire + * : .x : . : . : . : <AES32> HDSPM_QS_DoubleWire + * : x. : . : . : . : <AES32> HDSPM_QS_QuadWire + * : . : . : . x : . : <AES32> HDSPM_Professional + * : x . : . : . : . : HDSPM_wclk_sel + * : . : . : . : . : + * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :3322.2222:2222.1111:1111.1100:0000.0000: bit number + * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31 + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :8421.8421:8421.8421:8421.8421:8421.8421:hex digit + * + * + * + * AIO / RayDAT only + * + * ------------ HDSPM_WR_SETTINGS ---------- + * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte + * :1098.7654:3210.9876:5432.1098:7654.3210: + * :||||.||||:||||.||||:||||.||||:||||.||||: bit number + * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31 + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit + * : . : . : . : . x: HDSPM_c0Master 1: Master, 0: Slave + * : . : . : . : . x : HDSPM_c0_SyncRef0 + * : . : . : . : . x : HDSPM_c0_SyncRef1 + * : . : . : . : .x : HDSPM_c0_SyncRef2 + * : . : . : . : x. : HDSPM_c0_SyncRef3 + * : . : . : . : 3.210 : HDSPM_c0_SyncRefMask: + * : . : . : . : . : RayDat: 0:WC, 1:AES, 2:SPDIF, 3..6: ADAT1..4, + * : . : . : . : . : 9:TCO, 10:SyncIn + * : . : . : . : . : AIO: 0:WC, 1:AES, 2: SPDIF, 3: ATAT, + * : . : . : . : . : 9:TCO, 10:SyncIn + * : . : . : . : . : + * : . : . : . : . : + * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte + * :1098.7654:3210.9876:5432.1098:7654.3210: + * :||||.||||:||||.||||:||||.||||:||||.||||: bit number + * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31 + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit + * + */ #include <linux/init.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -95,7 +186,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_controlRegister 64 #define HDSPM_interruptConfirmation 96 #define HDSPM_control2Reg 256 /* not in specs ???????? */ -#define HDSPM_freqReg 256 /* for AES32 */ +#define HDSPM_freqReg 256 /* for setting arbitrary clock values (DDS feature) */ #define HDSPM_midiDataOut0 352 /* just believe in old code */ #define HDSPM_midiDataOut1 356 #define HDSPM_eeprom_wr 384 /* for AES32 */ @@ -258,6 +349,25 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_wclk_sel (1<<30) +/* additional control register bits for AIO*/ +#define HDSPM_c0_Wck48 0x20 /* also RayDAT */ +#define HDSPM_c0_Input0 0x1000 +#define HDSPM_c0_Input1 0x2000 +#define HDSPM_c0_Spdif_Opt 0x4000 +#define HDSPM_c0_Pro 0x8000 +#define HDSPM_c0_clr_tms 0x10000 +#define HDSPM_c0_AEB1 0x20000 +#define HDSPM_c0_AEB2 0x40000 +#define HDSPM_c0_LineOut 0x80000 +#define HDSPM_c0_AD_GAIN0 0x100000 +#define HDSPM_c0_AD_GAIN1 0x200000 +#define HDSPM_c0_DA_GAIN0 0x400000 +#define HDSPM_c0_DA_GAIN1 0x800000 +#define HDSPM_c0_PH_GAIN0 0x1000000 +#define HDSPM_c0_PH_GAIN1 0x2000000 +#define HDSPM_c0_Sym6db 0x4000000 + + /* --- bit helper defines */ #define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2) #define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1|\ @@ -341,11 +451,11 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_madiLock (1<<3) /* MADI Locked =1, no=0 */ #define HDSPM_madiSync (1<<18) /* MADI is in sync */ -#define HDSPM_tcoLock 0x00000020 /* Optional TCO locked status FOR HDSPe MADI! */ -#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status */ +#define HDSPM_tcoLockMadi 0x00000020 /* Optional TCO locked status for HDSPe MADI*/ +#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status for HDSPe MADI and AES32!*/ -#define HDSPM_syncInLock 0x00010000 /* Sync In lock status FOR HDSPe MADI! */ -#define HDSPM_syncInSync 0x00020000 /* Sync In sync status FOR HDSPe MADI! */ +#define HDSPM_syncInLock 0x00010000 /* Sync In lock status for HDSPe MADI! */ +#define HDSPM_syncInSync 0x00020000 /* Sync In sync status for HDSPe MADI! */ #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */ /* since 64byte accurate, last 6 bits are not used */ @@ -363,7 +473,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); * Interrupt */ #define HDSPM_tco_detect 0x08000000 -#define HDSPM_tco_lock 0x20000000 +#define HDSPM_tcoLockAes 0x20000000 /* Optional TCO locked status for HDSPe AES */ #define HDSPM_s2_tco_detect 0x00000040 #define HDSPM_s2_AEBO_D 0x00000080 @@ -461,7 +571,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_AES32_AUTOSYNC_FROM_AES6 6 #define HDSPM_AES32_AUTOSYNC_FROM_AES7 7 #define HDSPM_AES32_AUTOSYNC_FROM_AES8 8 -#define HDSPM_AES32_AUTOSYNC_FROM_NONE 9 +#define HDSPM_AES32_AUTOSYNC_FROM_TCO 9 +#define HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN 10 +#define HDSPM_AES32_AUTOSYNC_FROM_NONE 11 /* status2 */ /* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */ @@ -537,36 +649,39 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); /* names for speed modes */ static char *hdspm_speed_names[] = { "single", "double", "quad" }; -static char *texts_autosync_aes_tco[] = { "Word Clock", +static const char *const texts_autosync_aes_tco[] = { "Word Clock", "AES1", "AES2", "AES3", "AES4", "AES5", "AES6", "AES7", "AES8", - "TCO" }; -static char *texts_autosync_aes[] = { "Word Clock", + "TCO", "Sync In" +}; +static const char *const texts_autosync_aes[] = { "Word Clock", "AES1", "AES2", "AES3", "AES4", - "AES5", "AES6", "AES7", "AES8" }; -static char *texts_autosync_madi_tco[] = { "Word Clock", + "AES5", "AES6", "AES7", "AES8", + "Sync In" +}; +static const char *const texts_autosync_madi_tco[] = { "Word Clock", "MADI", "TCO", "Sync In" }; -static char *texts_autosync_madi[] = { "Word Clock", +static const char *const texts_autosync_madi[] = { "Word Clock", "MADI", "Sync In" }; -static char *texts_autosync_raydat_tco[] = { +static const char *const texts_autosync_raydat_tco[] = { "Word Clock", "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", "AES", "SPDIF", "TCO", "Sync In" }; -static char *texts_autosync_raydat[] = { +static const char *const texts_autosync_raydat[] = { "Word Clock", "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", "AES", "SPDIF", "Sync In" }; -static char *texts_autosync_aio_tco[] = { +static const char *const texts_autosync_aio_tco[] = { "Word Clock", "ADAT", "AES", "SPDIF", "TCO", "Sync In" }; -static char *texts_autosync_aio[] = { "Word Clock", +static const char *const texts_autosync_aio[] = { "Word Clock", "ADAT", "AES", "SPDIF", "Sync In" }; -static char *texts_freq[] = { +static const char *const texts_freq[] = { "No Lock", "32 kHz", "44.1 kHz", @@ -629,7 +744,8 @@ static char *texts_ports_aio_in_ss[] = { "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", - "ADAT.7", "ADAT.8" + "ADAT.7", "ADAT.8", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_out_ss[] = { @@ -638,14 +754,16 @@ static char *texts_ports_aio_out_ss[] = { "SPDIF.L", "SPDIF.R", "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", "ADAT.7", "ADAT.8", - "Phone.L", "Phone.R" + "Phone.L", "Phone.R", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_in_ds[] = { "Analogue.L", "Analogue.R", "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", - "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_out_ds[] = { @@ -653,14 +771,16 @@ static char *texts_ports_aio_out_ds[] = { "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", - "Phone.L", "Phone.R" + "Phone.L", "Phone.R", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_in_qs[] = { "Analogue.L", "Analogue.R", "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", - "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_out_qs[] = { @@ -668,7 +788,8 @@ static char *texts_ports_aio_out_qs[] = { "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", - "Phone.L", "Phone.R" + "Phone.L", "Phone.R", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aes32[] = { @@ -745,8 +866,8 @@ static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = { 8, 9, /* aes in, */ 10, 11, /* spdif in */ 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */ - -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -760,7 +881,8 @@ static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = { 10, 11, /* spdif out */ 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */ 6, 7, /* phone out */ - -1, -1, -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -773,7 +895,8 @@ static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = { 8, 9, /* aes in */ 10, 11, /* spdif in */ 12, 14, 16, 18, /* adat in */ - -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -788,7 +911,7 @@ static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = { 10, 11, /* spdif out */ 12, 14, 16, 18, /* adat out */ 6, 7, /* phone out */ - -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -802,7 +925,8 @@ static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = { 8, 9, /* aes in */ 10, 11, /* spdif in */ 12, 16, /* adat in */ - -1, -1, -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -817,7 +941,8 @@ static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = { 10, 11, /* spdif out */ 12, 16, /* adat out */ 6, 7, /* phone out */ - -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -856,11 +981,11 @@ struct hdspm_midi { }; struct hdspm_tco { - int input; - int framerate; - int wordclock; - int samplerate; - int pull; + int input; /* 0: LTC, 1:Video, 2: WC*/ + int framerate; /* 0=24, 1=25, 2=29.97, 3=29.97d, 4=30, 5=30d */ + int wordclock; /* 0=1:1, 1=44.1->48, 2=48->44.1 */ + int samplerate; /* 0=44.1, 1=48, 2= freq from app */ + int pull; /* 0=0, 1=+0.1%, 2=-0.1%, 3=+4%, 4=-4%*/ int term; /* 0 = off, 1 = on */ }; @@ -879,7 +1004,7 @@ struct hdspm { u32 control_register; /* cached value */ u32 control2_register; /* cached value */ - u32 settings_register; + u32 settings_register; /* cached value for AIO / RayDat (sync reference, master/slave) */ struct hdspm_midi midi[4]; struct tasklet_struct midi_tasklet; @@ -941,7 +1066,7 @@ struct hdspm { struct hdspm_tco *tco; /* NULL if no TCO detected */ - char **texts_autosync; + const char *const *texts_autosync; int texts_autosync_items; cycles_t last_interrupt; @@ -976,12 +1101,24 @@ static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm); static inline int hdspm_get_pll_freq(struct hdspm *hdspm); static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm); static int hdspm_autosync_ref(struct hdspm *hdspm); +static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out); static int snd_hdspm_set_defaults(struct hdspm *hdspm); static int hdspm_system_clock_mode(struct hdspm *hdspm); static void hdspm_set_sgbuf(struct hdspm *hdspm, struct snd_pcm_substream *substream, unsigned int reg, int channels); +static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx); +static int hdspm_wc_sync_check(struct hdspm *hdspm); +static int hdspm_tco_sync_check(struct hdspm *hdspm); +static int hdspm_sync_in_sync_check(struct hdspm *hdspm); + +static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index); +static int hdspm_get_tco_sample_rate(struct hdspm *hdspm); +static int hdspm_get_wc_sample_rate(struct hdspm *hdspm); + + + static inline int HDSPM_bit2freq(int n) { static const int bit2freq_tab[] = { @@ -992,6 +1129,12 @@ static inline int HDSPM_bit2freq(int n) return bit2freq_tab[n]; } +static bool hdspm_is_raydat_or_aio(struct hdspm *hdspm) +{ + return ((AIO == hdspm->io_type) || (RayDAT == hdspm->io_type)); +} + + /* Write/read to/from HDSPM with Adresses in Bytes not words but only 32Bit writes are allowed */ @@ -1107,14 +1250,11 @@ static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate) else if (hdspm->control_register & HDSPM_DoubleSpeed) return rate * 2; - }; + } return rate; } -static int hdspm_tco_sync_check(struct hdspm *hdspm); -static int hdspm_sync_in_sync_check(struct hdspm *hdspm); - -/* check for external sample rate */ +/* check for external sample rate, returns the sample rate in Hz*/ static int hdspm_external_sample_rate(struct hdspm *hdspm) { unsigned int status, status2, timecode; @@ -1127,17 +1267,36 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); syncref = hdspm_autosync_ref(hdspm); + switch (syncref) { + case HDSPM_AES32_AUTOSYNC_FROM_WORD: + /* Check WC sync and get sample rate */ + if (hdspm_wc_sync_check(hdspm)) + return HDSPM_bit2freq(hdspm_get_wc_sample_rate(hdspm)); + break; - if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD && - status & HDSPM_AES32_wcLock) - return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF); + case HDSPM_AES32_AUTOSYNC_FROM_AES1: + case HDSPM_AES32_AUTOSYNC_FROM_AES2: + case HDSPM_AES32_AUTOSYNC_FROM_AES3: + case HDSPM_AES32_AUTOSYNC_FROM_AES4: + case HDSPM_AES32_AUTOSYNC_FROM_AES5: + case HDSPM_AES32_AUTOSYNC_FROM_AES6: + case HDSPM_AES32_AUTOSYNC_FROM_AES7: + case HDSPM_AES32_AUTOSYNC_FROM_AES8: + /* Check AES sync and get sample rate */ + if (hdspm_aes_sync_check(hdspm, syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)) + return HDSPM_bit2freq(hdspm_get_aes_sample_rate(hdspm, + syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)); + break; - if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 && - syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 && - status2 & (HDSPM_LockAES >> - (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))) - return HDSPM_bit2freq((timecode >> (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF); - return 0; + + case HDSPM_AES32_AUTOSYNC_FROM_TCO: + /* Check TCO sync and get sample rate */ + if (hdspm_tco_sync_check(hdspm)) + return HDSPM_bit2freq(hdspm_get_tco_sample_rate(hdspm)); + break; + default: + return 0; + } /* end switch(syncref) */ break; case MADIface: @@ -2129,6 +2288,9 @@ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); return (status >> 16) & 0xF; break; + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + return (status >> HDSPM_AES32_wcFreq_bit) & 0xF; default: break; } @@ -2152,6 +2314,9 @@ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); return (status >> 20) & 0xF; break; + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + return (status >> 1) & 0xF; default: break; } @@ -2183,6 +2348,23 @@ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) return 0; } +/** + * Returns the AES sample rate class for the given card. + **/ +static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index) +{ + int timecode; + + switch (hdspm->io_type) { + case AES32: + timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); + return (timecode >> (4*index)) & 0xF; + break; + default: + break; + } + return 0; +} /** * Returns the sample rate class for input source <idx> for @@ -2196,15 +2378,23 @@ static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx) } #define ENUMERATED_CTL_INFO(info, texts) \ -{ \ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; \ - uinfo->count = 1; \ - uinfo->value.enumerated.items = ARRAY_SIZE(texts); \ - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) \ - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; \ - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); \ -} + snd_ctl_enum_info(info, 1, ARRAY_SIZE(texts), texts) + +/* Helper function to query the external sample rate and return the + * corresponding enum to be returned to userspace. + */ +static int hdspm_external_rate_to_enum(struct hdspm *hdspm) +{ + int rate = hdspm_external_sample_rate(hdspm); + int i, selected_rate = 0; + for (i = 1; i < 10; i++) + if (HDSPM_bit2freq(i) == rate) { + selected_rate = i; + break; + } + return selected_rate; +} #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \ @@ -2270,7 +2460,7 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, default: ucontrol->value.enumerated.item[0] = hdspm_get_s1_sample_rate(hdspm, - ucontrol->id.index-1); + kcontrol->private_value-1); } break; @@ -2289,28 +2479,24 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, ucontrol->value.enumerated.item[0] = hdspm_get_sync_in_sample_rate(hdspm); break; + case 11: /* External Rate */ + ucontrol->value.enumerated.item[0] = + hdspm_external_rate_to_enum(hdspm); + break; default: /* AES1 to AES8 */ ucontrol->value.enumerated.item[0] = - hdspm_get_s1_sample_rate(hdspm, - kcontrol->private_value-1); + hdspm_get_aes_sample_rate(hdspm, + kcontrol->private_value - + HDSPM_AES32_AUTOSYNC_FROM_AES1); break; } break; case MADI: case MADIface: - { - int rate = hdspm_external_sample_rate(hdspm); - int i, selected_rate = 0; - for (i = 1; i < 10; i++) - if (HDSPM_bit2freq(i) == rate) { - selected_rate = i; - break; - } - ucontrol->value.enumerated.item[0] = selected_rate; - } + ucontrol->value.enumerated.item[0] = + hdspm_external_rate_to_enum(hdspm); break; - default: break; } @@ -2359,33 +2545,17 @@ static int hdspm_system_clock_mode(struct hdspm *hdspm) **/ static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode) { - switch (hdspm->io_type) { - case AIO: - case RayDAT: - if (0 == mode) - hdspm->settings_register |= HDSPM_c0Master; - else - hdspm->settings_register &= ~HDSPM_c0Master; - - hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); - break; - - default: - if (0 == mode) - hdspm->control_register |= HDSPM_ClockModeMaster; - else - hdspm->control_register &= ~HDSPM_ClockModeMaster; - - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - } + hdspm_set_toggle_setting(hdspm, + (hdspm_is_raydat_or_aio(hdspm)) ? + HDSPM_c0Master : HDSPM_ClockModeMaster, + (0 == mode)); } static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Master", "AutoSync" }; + static const char *const texts[] = { "Master", "AutoSync" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -2809,16 +2979,7 @@ static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = hdspm->texts_autosync_items; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - - strcpy(uinfo->value.enumerated.name, - hdspm->texts_autosync[uinfo->value.enumerated.item]); + snd_ctl_enum_info(uinfo, 1, hdspm->texts_autosync_items, hdspm->texts_autosync); return 0; } @@ -2873,19 +3034,20 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, static int hdspm_autosync_ref(struct hdspm *hdspm) { + /* This looks at the autosync selected sync reference */ if (AES32 == hdspm->io_type) { + unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int syncref = - (status >> HDSPM_AES32_syncref_bit) & 0xF; - if (syncref == 0) - return HDSPM_AES32_AUTOSYNC_FROM_WORD; - if (syncref <= 8) + unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF; + if ((syncref >= HDSPM_AES32_AUTOSYNC_FROM_WORD) && + (syncref <= HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN)) { return syncref; + } return HDSPM_AES32_AUTOSYNC_FROM_NONE; + } else if (MADI == hdspm->io_type) { - /* This looks at the autosync selected sync reference */ - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); switch (status2 & HDSPM_SelSyncRefMask) { case HDSPM_SelSyncRef_WORD: return HDSPM_AUTOSYNC_FROM_WORD; @@ -2898,7 +3060,7 @@ static int hdspm_autosync_ref(struct hdspm *hdspm) case HDSPM_SelSyncRef_NVALID: return HDSPM_AUTOSYNC_FROM_NONE; default: - return 0; + return HDSPM_AUTOSYNC_FROM_NONE; } } @@ -2912,31 +3074,15 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); if (AES32 == hdspm->io_type) { - static char *texts[] = { "WordClock", "AES1", "AES2", "AES3", - "AES4", "AES5", "AES6", "AES7", "AES8", "None"}; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 10; - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + static const char *const texts[] = { "WordClock", "AES1", "AES2", "AES3", + "AES4", "AES5", "AES6", "AES7", "AES8", "TCO", "Sync In", "None"}; + + ENUMERATED_CTL_INFO(uinfo, texts); } else if (MADI == hdspm->io_type) { - static char *texts[] = {"Word Clock", "MADI", "TCO", + static const char *const texts[] = {"Word Clock", "MADI", "TCO", "Sync In", "None" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + ENUMERATED_CTL_INFO(uinfo, texts); } return 0; } @@ -2964,7 +3110,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"No video", "NTSC", "PAL"}; + static const char *const texts[] = {"No video", "NTSC", "PAL"}; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3010,7 +3156,7 @@ static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps", + static const char *const texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps", "30 fps"}; ENUMERATED_CTL_INFO(uinfo, texts); return 0; @@ -3027,19 +3173,19 @@ static int hdspm_tco_ltc_frames(struct hdspm *hdspm) HDSPM_TCO1_LTC_Format_MSB)) { case 0: /* 24 fps */ - ret = 1; + ret = fps_24; break; case HDSPM_TCO1_LTC_Format_LSB: /* 25 fps */ - ret = 2; + ret = fps_25; break; case HDSPM_TCO1_LTC_Format_MSB: - /* 25 fps */ - ret = 3; + /* 29.97 fps */ + ret = fps_2997; break; default: /* 30 fps */ - ret = 4; + ret = fps_30; break; } } @@ -3067,16 +3213,35 @@ static int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol, static int hdspm_toggle_setting(struct hdspm *hdspm, u32 regmask) { - return (hdspm->control_register & regmask) ? 1 : 0; + u32 reg; + + if (hdspm_is_raydat_or_aio(hdspm)) + reg = hdspm->settings_register; + else + reg = hdspm->control_register; + + return (reg & regmask) ? 1 : 0; } static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out) { + u32 *reg; + u32 target_reg; + + if (hdspm_is_raydat_or_aio(hdspm)) { + reg = &(hdspm->settings_register); + target_reg = HDSPM_WR_SETTINGS; + } else { + reg = &(hdspm->control_register); + target_reg = HDSPM_controlRegister; + } + if (out) - hdspm->control_register |= regmask; + *reg |= regmask; else - hdspm->control_register &= ~regmask; - hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + *reg &= ~regmask; + + hdspm_write(hdspm, target_reg, *reg); return 0; } @@ -3141,7 +3306,7 @@ static int hdspm_set_input_select(struct hdspm * hdspm, int out) static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "optical", "coaxial" }; + static const char *const texts[] = { "optical", "coaxial" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3203,7 +3368,7 @@ static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds) static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Single", "Double" }; + static const char *const texts[] = { "Single", "Double" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3276,7 +3441,7 @@ static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode) static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Single", "Double", "Quad" }; + static const char *const texts[] = { "Single", "Double", "Quad" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3313,6 +3478,84 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol, return change; } +#define HDSPM_CONTROL_TRISTATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = xindex, \ + .info = snd_hdspm_info_tristate, \ + .get = snd_hdspm_get_tristate, \ + .put = snd_hdspm_put_tristate \ +} + +static int hdspm_tristate(struct hdspm *hdspm, u32 regmask) +{ + u32 reg = hdspm->settings_register & (regmask * 3); + return reg / regmask; +} + +static int hdspm_set_tristate(struct hdspm *hdspm, int mode, u32 regmask) +{ + hdspm->settings_register &= ~(regmask * 3); + hdspm->settings_register |= (regmask * mode); + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); + + return 0; +} + +static int snd_hdspm_info_tristate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + u32 regmask = kcontrol->private_value; + + static const char *const texts_spdif[] = { "Optical", "Coaxial", "Internal" }; + static const char *const texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" }; + + switch (regmask) { + case HDSPM_c0_Input0: + ENUMERATED_CTL_INFO(uinfo, texts_spdif); + break; + default: + ENUMERATED_CTL_INFO(uinfo, texts_levels); + break; + } + return 0; +} + +static int snd_hdspm_get_tristate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + u32 regmask = kcontrol->private_value; + + spin_lock_irq(&hdspm->lock); + ucontrol->value.enumerated.item[0] = hdspm_tristate(hdspm, regmask); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_tristate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + u32 regmask = kcontrol->private_value; + int change; + int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0]; + if (val < 0) + val = 0; + if (val > 2) + val = 2; + + spin_lock_irq(&hdspm->lock); + change = val != hdspm_tristate(hdspm, regmask); + hdspm_set_tristate(hdspm, val, regmask); + spin_unlock_irq(&hdspm->lock); + return change; +} + #define HDSPM_MADI_SPEEDMODE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -3352,7 +3595,7 @@ static int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode) static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Single", "Double", "Quad" }; + static const char *const texts[] = { "Single", "Double", "Quad" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3587,7 +3830,7 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" }; + static const char *const texts[] = { "No Lock", "Lock", "Sync", "N/A" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3595,7 +3838,7 @@ static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol, static int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "No Lock", "Lock" }; + static const char *const texts[] = { "No Lock", "Lock" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3745,9 +3988,18 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm) if (hdspm->tco) { switch (hdspm->io_type) { case MADI: + status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_tcoLockMadi) { + if (status & HDSPM_tcoSync) + return 2; + else + return 1; + } + return 0; + break; case AES32: status = hdspm_read(hdspm, HDSPM_statusRegister); - if (status & HDSPM_tcoLock) { + if (status & HDSPM_tcoLockAes) { if (status & HDSPM_tcoSync) return 2; else @@ -3807,7 +4059,8 @@ static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol, case 5: /* SYNC IN */ val = hdspm_sync_in_sync_check(hdspm); break; default: - val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1); + val = hdspm_s1_sync_check(hdspm, + kcontrol->private_value-1); } break; @@ -3975,7 +4228,8 @@ static void hdspm_tco_write(struct hdspm *hdspm) static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "44.1 kHz", "48 kHz" }; + /* TODO freq from app could be supported here, see tco->samplerate */ + static const char *const texts[] = { "44.1 kHz", "48 kHz" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -4021,7 +4275,8 @@ static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" }; + static const char *const texts[] = { "0", "+ 0.1 %", "- 0.1 %", + "+ 4 %", "- 4 %" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -4066,7 +4321,7 @@ static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" }; + static const char *const texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -4112,7 +4367,7 @@ static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "24 fps", "25 fps", "29.97fps", + static const char *const texts[] = { "24 fps", "25 fps", "29.97fps", "29.97 dfps", "30 fps", "30 dfps" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; @@ -4159,7 +4414,7 @@ static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "LTC", "Video", "WCK" }; + static const char *const texts[] = { "LTC", "Video", "WCK" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -4284,7 +4539,6 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = { HDSPM_INTERNAL_CLOCK("Internal Clock", 0), HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), - HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), HDSPM_SYNC_CHECK("WC SyncCheck", 0), @@ -4298,7 +4552,16 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = { HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2), HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3), HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4), - HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5) + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5), + HDSPM_CONTROL_TRISTATE("S/PDIF Input", HDSPM_c0_Input0), + HDSPM_TOGGLE_SETTING("S/PDIF Out Optical", HDSPM_c0_Spdif_Opt), + HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro), + HDSPM_TOGGLE_SETTING("ADAT internal (AEB/TEB)", HDSPM_c0_AEB1), + HDSPM_TOGGLE_SETTING("XLR Breakout Cable", HDSPM_c0_Sym6db), + HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48), + HDSPM_CONTROL_TRISTATE("Input Level", HDSPM_c0_AD_GAIN0), + HDSPM_CONTROL_TRISTATE("Output Level", HDSPM_c0_DA_GAIN0), + HDSPM_CONTROL_TRISTATE("Phones Level", HDSPM_c0_PH_GAIN0) /* HDSPM_INPUT_SELECT("Input Select", 0), @@ -4335,7 +4598,9 @@ static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = { HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5), HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6), HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7), - HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8) + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8), + HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro), + HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48) }; static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { @@ -4345,7 +4610,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), - HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 11), HDSPM_SYNC_CHECK("WC Sync Check", 0), HDSPM_SYNC_CHECK("AES1 Sync Check", 1), HDSPM_SYNC_CHECK("AES2 Sync Check", 2), @@ -4501,77 +4766,22 @@ static int snd_hdspm_create_controls(struct snd_card *card, ------------------------------------------------------------*/ static void -snd_hdspm_proc_read_madi(struct snd_info_entry * entry, - struct snd_info_buffer *buffer) +snd_hdspm_proc_read_tco(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; - unsigned int status, status2, control, freq; - - char *pref_sync_ref; - char *autosync_ref; - char *system_clock_mode; - char *insel; - int x, x2; - - /* TCO stuff */ + unsigned int status, control; int a, ltc, frames, seconds, minutes, hours; unsigned int period; u64 freq_const = 0; u32 rate; + snd_iprintf(buffer, "--- TCO ---\n"); + status = hdspm_read(hdspm, HDSPM_statusRegister); - status2 = hdspm_read(hdspm, HDSPM_statusRegister2); control = hdspm->control_register; - freq = hdspm_read(hdspm, HDSPM_timecodeRegister); - snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", - hdspm->card_name, hdspm->card->number + 1, - hdspm->firmware_rev, - (status2 & HDSPM_version0) | - (status2 & HDSPM_version1) | (status2 & - HDSPM_version2)); - snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n", - (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF, - hdspm->serial); - - snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", - hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); - - snd_iprintf(buffer, "--- System ---\n"); - - snd_iprintf(buffer, - "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", - status & HDSPM_audioIRQPending, - (status & HDSPM_midi0IRQPending) ? 1 : 0, - (status & HDSPM_midi1IRQPending) ? 1 : 0, - hdspm->irq_count); - snd_iprintf(buffer, - "HW pointer: id = %d, rawptr = %d (%d->%d) " - "estimated= %ld (bytes)\n", - ((status & HDSPM_BufferID) ? 1 : 0), - (status & HDSPM_BufferPositionMask), - (status & HDSPM_BufferPositionMask) % - (2 * (int)hdspm->period_bytes), - ((status & HDSPM_BufferPositionMask) - 64) % - (2 * (int)hdspm->period_bytes), - (long) hdspm_hw_pointer(hdspm) * 4); - - snd_iprintf(buffer, - "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", - hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); - snd_iprintf(buffer, - "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", - hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); - snd_iprintf(buffer, - "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " - "status2=0x%x\n", - hdspm->control_register, hdspm->control2_register, - status, status2); if (status & HDSPM_tco_detect) { snd_iprintf(buffer, "TCO module detected.\n"); a = hdspm_read(hdspm, HDSPM_RD_TCO+4); @@ -4665,6 +4875,75 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, } else { snd_iprintf(buffer, "No TCO module detected.\n"); } +} + +static void +snd_hdspm_proc_read_madi(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + unsigned int status, status2, control, freq; + + char *pref_sync_ref; + char *autosync_ref; + char *system_clock_mode; + char *insel; + int x, x2; + + status = hdspm_read(hdspm, HDSPM_statusRegister); + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + control = hdspm->control_register; + freq = hdspm_read(hdspm, HDSPM_timecodeRegister); + + snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", + hdspm->card_name, hdspm->card->number + 1, + hdspm->firmware_rev, + (status2 & HDSPM_version0) | + (status2 & HDSPM_version1) | (status2 & + HDSPM_version2)); + + snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n", + (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF, + hdspm->serial); + + snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", + hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); + + snd_iprintf(buffer, "--- System ---\n"); + + snd_iprintf(buffer, + "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", + status & HDSPM_audioIRQPending, + (status & HDSPM_midi0IRQPending) ? 1 : 0, + (status & HDSPM_midi1IRQPending) ? 1 : 0, + hdspm->irq_count); + snd_iprintf(buffer, + "HW pointer: id = %d, rawptr = %d (%d->%d) " + "estimated= %ld (bytes)\n", + ((status & HDSPM_BufferID) ? 1 : 0), + (status & HDSPM_BufferPositionMask), + (status & HDSPM_BufferPositionMask) % + (2 * (int)hdspm->period_bytes), + ((status & HDSPM_BufferPositionMask) - 64) % + (2 * (int)hdspm->period_bytes), + (long) hdspm_hw_pointer(hdspm) * 4); + + snd_iprintf(buffer, + "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); + snd_iprintf(buffer, + "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " + "status2=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2); + snd_iprintf(buffer, "--- Settings ---\n"); @@ -4768,6 +5047,9 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, (status & HDSPM_RX_64ch) ? "64 channels" : "56 channels"); + /* call readout function for TCO specific status */ + snd_hdspm_proc_read_tco(entry, buffer); + snd_iprintf(buffer, "\n"); } @@ -4909,11 +5191,18 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, autosync_ref = "AES7"; break; case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref = "AES8"; break; + case HDSPM_AES32_AUTOSYNC_FROM_TCO: + autosync_ref = "TCO"; break; + case HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN: + autosync_ref = "Sync In"; break; default: autosync_ref = "---"; break; } snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref); + /* call readout function for TCO specific status */ + snd_hdspm_proc_read_tco(entry, buffer); + snd_iprintf(buffer, "\n"); } @@ -5097,7 +5386,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) case AES32: hdspm->control_register = - HDSPM_ClockModeMaster | /* Master Cloack Mode on */ + HDSPM_ClockModeMaster | /* Master Clock Mode on */ hdspm_encode_latency(7) | /* latency max=8192samples */ HDSPM_SyncRef0 | /* AES1 is syncclock */ HDSPM_LineOut | /* Analog output in */ @@ -5123,9 +5412,8 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) all_in_all_mixer(hdspm, 0 * UNITY_GAIN); - if (hdspm->io_type == AIO || hdspm->io_type == RayDAT) { + if (hdspm_is_raydat_or_aio(hdspm)) hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); - } /* set a default rate so that the channel map is set up. */ hdspm_set_rate(hdspm, 48000, 1); @@ -5371,6 +5659,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, */ + /* For AES cards, the float format bit is the same as the + * preferred sync reference. Since we don't want to break + * sync settings, we have to skip the remaining part of this + * function. + */ + if (hdspm->io_type == AES32) { + return 0; + } + + /* Switch to native float format if requested */ if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) { if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT)) @@ -6013,7 +6311,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, ltc.format = fps_2997; break; default: - ltc.format = 30; + ltc.format = fps_30; break; } if (i & HDSPM_TCO1_set_drop_frame_flag) { @@ -6479,10 +6777,6 @@ static int snd_hdspm_create(struct snd_card *card, break; case AIO: - if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) { - snd_printk(KERN_INFO "HDSPM: AEB input board found, but not supported\n"); - } - hdspm->ss_in_channels = AIO_IN_SS_CHANNELS; hdspm->ds_in_channels = AIO_IN_DS_CHANNELS; hdspm->qs_in_channels = AIO_IN_QS_CHANNELS; @@ -6490,6 +6784,20 @@ static int snd_hdspm_create(struct snd_card *card, hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS; hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS; + if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) { + snd_printk(KERN_INFO "HDSPM: AEB input board found\n"); + hdspm->ss_in_channels += 4; + hdspm->ds_in_channels += 4; + hdspm->qs_in_channels += 4; + } + + if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBO_D)) { + snd_printk(KERN_INFO "HDSPM: AEB output board found\n"); + hdspm->ss_out_channels += 4; + hdspm->ds_out_channels += 4; + hdspm->qs_out_channels += 4; + } + hdspm->channel_map_out_ss = channel_map_aio_out_ss; hdspm->channel_map_out_ds = channel_map_aio_out_ds; hdspm->channel_map_out_qs = channel_map_aio_out_qs; @@ -6558,6 +6866,7 @@ static int snd_hdspm_create(struct snd_card *card, break; case MADI: + case AES32: if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) { hdspm->midiPorts++; hdspm->tco = kzalloc(sizeof(struct hdspm_tco), @@ -6565,7 +6874,7 @@ static int snd_hdspm_create(struct snd_card *card, if (NULL != hdspm->tco) { hdspm_tco_write(hdspm); } - snd_printk(KERN_INFO "HDSPM: MADI TCO module found\n"); + snd_printk(KERN_INFO "HDSPM: MADI/AES TCO module found\n"); } else { hdspm->tco = NULL; } @@ -6580,10 +6889,12 @@ static int snd_hdspm_create(struct snd_card *card, case AES32: if (hdspm->tco) { hdspm->texts_autosync = texts_autosync_aes_tco; - hdspm->texts_autosync_items = 10; + hdspm->texts_autosync_items = + ARRAY_SIZE(texts_autosync_aes_tco); } else { hdspm->texts_autosync = texts_autosync_aes; - hdspm->texts_autosync_items = 9; + hdspm->texts_autosync_items = + ARRAY_SIZE(texts_autosync_aes); } break; diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 45eeaa9f7fe..5138b849305 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -26,12 +26,9 @@ if SND_SOC config SND_SOC_AC97_BUS bool -config SND_SOC_DMAENGINE_PCM - bool - config SND_SOC_GENERIC_DMAENGINE_PCM bool - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM # All the supported SoCs source "sound/soc/atmel/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index bc0261476d7..61a64d28190 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,10 +1,6 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o -ifneq ($(CONFIG_SND_SOC_DMAENGINE_PCM),) -snd-soc-core-objs += soc-dmaengine-pcm.o -endif - ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o endif diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 3fdd87fa18a..e48d38a1b95 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -13,6 +13,7 @@ config SND_ATMEL_SOC_PDC config SND_ATMEL_SOC_DMA tristate depends on SND_ATMEL_SOC + select SND_SOC_GENERIC_DMAENGINE_PCM config SND_ATMEL_SOC_SSC tristate @@ -32,6 +33,26 @@ config SND_AT91_SOC_SAM9G20_WM8731 Say Y if you want to add support for SoC audio on WM8731-based AT91sam9g20 evaluation board. +config SND_ATMEL_SOC_WM8904 + tristate "Atmel ASoC driver for boards using WM8904 codec" + depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC + select SND_ATMEL_SOC_SSC + select SND_ATMEL_SOC_DMA + select SND_SOC_WM8904 + help + Say Y if you want to add support for Atmel ASoC driver for boards using + WM8904 codec. + +config SND_AT91_SOC_SAM9X5_WM8731 + tristate "SoC Audio support for WM8731-based at91sam9x5 board" + depends on ATMEL_SSC && SND_ATMEL_SOC && SOC_AT91SAM9X5 + select SND_ATMEL_SOC_SSC + select SND_ATMEL_SOC_DMA + select SND_SOC_WM8731 + help + Say Y if you want to add support for audio SoC on an + at91sam9x5 based board that is using WM8731 codec. + config SND_AT91_SOC_AFEB9260 tristate "SoC Audio support for AFEB9260 board" depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 41967ccb6f4..5baabc8bde3 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -11,6 +11,10 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o # AT91 Machine Support snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o +snd-atmel-soc-wm8904-objs := atmel_wm8904.o +snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o +obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o +obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index d1282652679..06082e5e5dc 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -91,138 +91,52 @@ static void atmel_pcm_dma_irq(u32 ssc_sr, } } -/*--------------------------------------------------------------------------*\ - * DMAENGINE operations -\*--------------------------------------------------------------------------*/ -static bool filter(struct dma_chan *chan, void *slave) -{ - struct at_dma_slave *sl = slave; - - if (sl->dma_dev == chan->device->dev) { - chan->private = sl; - return true; - } else { - return false; - } -} - static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct atmel_pcm_dma_params *prtd) + struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_pcm_dma_params *prtd; struct ssc_device *ssc; - struct dma_chan *dma_chan; - struct dma_slave_config slave_config; int ret; + prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); ssc = prtd->ssc; - ret = snd_hwparams_to_dma_slave_config(substream, params, - &slave_config); + ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); if (ret) { pr_err("atmel-pcm: hwparams to dma slave configure failed\n"); return ret; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.dst_addr = (dma_addr_t)ssc->phybase + SSC_THR; - slave_config.dst_maxburst = 1; + slave_config->dst_addr = ssc->phybase + SSC_THR; + slave_config->dst_maxburst = 1; } else { - slave_config.src_addr = (dma_addr_t)ssc->phybase + SSC_RHR; - slave_config.src_maxburst = 1; - } - - dma_chan = snd_dmaengine_pcm_get_chan(substream); - if (dmaengine_slave_config(dma_chan, &slave_config)) { - pr_err("atmel-pcm: failed to configure dma channel\n"); - ret = -EBUSY; - return ret; - } - - return 0; -} - -static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct atmel_pcm_dma_params *prtd; - struct ssc_device *ssc; - struct at_dma_slave *sdata = NULL; - int ret; - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - - prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - ssc = prtd->ssc; - if (ssc->pdev) - sdata = ssc->pdev->dev.platform_data; - - ret = snd_dmaengine_pcm_open_request_chan(substream, filter, sdata); - if (ret) { - pr_err("atmel-pcm: dmaengine pcm open failed\n"); - return -EINVAL; - } - - ret = atmel_pcm_configure_dma(substream, params, prtd); - if (ret) { - pr_err("atmel-pcm: failed to configure dmai\n"); - goto err; + slave_config->src_addr = ssc->phybase + SSC_RHR; + slave_config->src_maxburst = 1; } prtd->dma_intr_handler = atmel_pcm_dma_irq; return 0; -err: - snd_dmaengine_pcm_close_release_chan(substream); - return ret; } -static int atmel_pcm_dma_prepare(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct atmel_pcm_dma_params *prtd; - - prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - ssc_writex(prtd->ssc->regs, SSC_IER, prtd->mask->ssc_error); - ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_enable); - - return 0; -} - -static int atmel_pcm_open(struct snd_pcm_substream *substream) -{ - snd_soc_set_runtime_hwparams(substream, &atmel_pcm_dma_hardware); - - return 0; -} - -static struct snd_pcm_ops atmel_pcm_ops = { - .open = atmel_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = atmel_pcm_hw_params, - .prepare = atmel_pcm_dma_prepare, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer_no_residue, - .mmap = atmel_pcm_mmap, -}; - -static struct snd_soc_platform_driver atmel_soc_platform = { - .ops = &atmel_pcm_ops, - .pcm_new = atmel_pcm_new, - .pcm_free = atmel_pcm_free, +static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = { + .prepare_slave_config = atmel_pcm_configure_dma, + .pcm_hardware = &atmel_pcm_dma_hardware, + .prealloc_buffer_size = ATMEL_SSC_DMABUF_SIZE, }; int atmel_pcm_dma_platform_register(struct device *dev) { - return snd_soc_register_platform(dev, &atmel_soc_platform); + return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); } EXPORT_SYMBOL(atmel_pcm_dma_platform_register); void atmel_pcm_dma_platform_unregister(struct device *dev) { - snd_soc_unregister_platform(dev); + snd_dmaengine_pcm_unregister(dev); } EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister); diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index f3fdfa07fcb..0ecf356027f 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -73,6 +73,7 @@ static struct atmel_ssc_mask ssc_tx_mask = { .ssc_disable = SSC_BIT(CR_TXDIS), .ssc_endx = SSC_BIT(SR_ENDTX), .ssc_endbuf = SSC_BIT(SR_TXBUFE), + .ssc_error = SSC_BIT(SR_OVRUN), .pdc_enable = ATMEL_PDC_TXTEN, .pdc_disable = ATMEL_PDC_TXTDIS, }; @@ -82,6 +83,7 @@ static struct atmel_ssc_mask ssc_rx_mask = { .ssc_disable = SSC_BIT(CR_RXDIS), .ssc_endx = SSC_BIT(SR_ENDRX), .ssc_endbuf = SSC_BIT(SR_RXBUFF), + .ssc_error = SSC_BIT(SR_OVRUN), .pdc_enable = ATMEL_PDC_RXTEN, .pdc_disable = ATMEL_PDC_RXTDIS, }; @@ -196,15 +198,27 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; - int dir_mask; + struct atmel_pcm_dma_params *dma_params; + int dir, dir_mask; pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", ssc_readl(ssc_p->ssc->regs, SR)); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dir = 0; dir_mask = SSC_DIR_MASK_PLAYBACK; - else + } else { + dir = 1; dir_mask = SSC_DIR_MASK_CAPTURE; + } + + dma_params = &ssc_dma_params[dai->id][dir]; + dma_params->ssc = ssc_p->ssc; + dma_params->substream = substream; + + ssc_p->dma_params[dir] = dma_params; + + snd_soc_dai_set_dma_data(dai, substream, dma_params); spin_lock_irq(&ssc_p->lock); if (ssc_p->dir_mask & dir_mask) { @@ -325,7 +339,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); int id = dai->id; struct atmel_ssc_info *ssc_p = &ssc_info[id]; struct atmel_pcm_dma_params *dma_params; @@ -344,19 +357,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, else dir = 1; - dma_params = &ssc_dma_params[id][dir]; - dma_params->ssc = ssc_p->ssc; - dma_params->substream = substream; - - ssc_p->dma_params[dir] = dma_params; - - /* - * The snd_soc_pcm_stream->dma_data field is only used to communicate - * the appropriate DMA parameters to the pcm driver hw_params() - * function. It should not be used for other purposes - * as it is common to all substreams. - */ - snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_params); + dma_params = ssc_p->dma_params[dir]; channels = params_channels(params); @@ -648,6 +649,7 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream, dma_params = ssc_p->dma_params[dir]; ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable); + ssc_writel(ssc_p->ssc->regs, IER, dma_params->mask->ssc_error); pr_debug("%s enabled SSC_SR=0x%08x\n", dir ? "receive" : "transmit", diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c new file mode 100644 index 00000000000..7222380131e --- /dev/null +++ b/sound/soc/atmel/atmel_wm8904.c @@ -0,0 +1,254 @@ +/* + * atmel_wm8904 - Atmel ASoC driver for boards with WM8904 codec. + * + * Copyright (C) 2012 Atmel + * + * Author: Bo Shen <voice.shen@atmel.com> + * + * GPLv2 or later + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> + +#include <sound/soc.h> + +#include "../codecs/wm8904.h" +#include "atmel_ssc_dai.h" + +#define MCLK_RATE 32768 + +static struct clk *mclk; + +static const struct snd_soc_dapm_widget atmel_asoc_wm8904_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), +}; + +static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK, + 32768, params_rate(params) * 256); + if (ret < 0) { + pr_err("%s - failed to set wm8904 codec PLL.", __func__); + return ret; + } + + /* + * As here wm8904 use FLL output as its system clock + * so calling set_sysclk won't care freq parameter + * then we pass 0 + */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8904_CLK_FLL, + 0, SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("%s -failed to set wm8904 SYSCLK\n", __func__); + return ret; + } + + return 0; +} + +static struct snd_soc_ops atmel_asoc_wm8904_ops = { + .hw_params = atmel_asoc_wm8904_hw_params, +}; + +static int atmel_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + switch (level) { + case SND_SOC_BIAS_PREPARE: + clk_prepare_enable(mclk); + break; + case SND_SOC_BIAS_OFF: + clk_disable_unprepare(mclk); + break; + default: + break; + } + } + + return 0; +}; + +static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = { + .name = "WM8904", + .stream_name = "WM8904 PCM", + .codec_dai_name = "wm8904-hifi", + .dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .ops = &atmel_asoc_wm8904_ops, +}; + +static struct snd_soc_card atmel_asoc_wm8904_card = { + .name = "atmel_asoc_wm8904", + .owner = THIS_MODULE, + .set_bias_level = atmel_set_bias_level, + .dai_link = &atmel_asoc_wm8904_dailink, + .num_links = 1, + .dapm_widgets = atmel_asoc_wm8904_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(atmel_asoc_wm8904_dapm_widgets), + .fully_routed = true, +}; + +static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *codec_np, *cpu_np; + struct snd_soc_card *card = &atmel_asoc_wm8904_card; + struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; + int ret; + + if (!np) { + dev_err(&pdev->dev, "only device tree supported\n"); + return -EINVAL; + } + + ret = snd_soc_of_parse_card_name(card, "atmel,model"); + if (ret) { + dev_err(&pdev->dev, "failed to parse card name\n"); + return ret; + } + + ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "failed to parse audio routing\n"); + return ret; + } + + cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "failed to get dai and pcm info\n"); + ret = -EINVAL; + return ret; + } + dailink->cpu_of_node = cpu_np; + dailink->platform_of_node = cpu_np; + of_node_put(cpu_np); + + codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "failed to get codec info\n"); + ret = -EINVAL; + return ret; + } + dailink->codec_of_node = codec_np; + of_node_put(codec_np); + + return 0; +} + +static int atmel_asoc_wm8904_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &atmel_asoc_wm8904_card; + struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; + struct clk *clk_src; + struct pinctrl *pinctrl; + int id, ret; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + dev_err(&pdev->dev, "failed to request pinctrl\n"); + return PTR_ERR(pinctrl); + } + + card->dev = &pdev->dev; + ret = atmel_asoc_wm8904_dt_init(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to init dt info\n"); + return ret; + } + + id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc"); + ret = atmel_ssc_set_audio(id); + if (ret != 0) { + dev_err(&pdev->dev, "failed to set SSC %d for audio\n", id); + return ret; + } + + mclk = clk_get(NULL, "pck0"); + if (IS_ERR(mclk)) { + dev_err(&pdev->dev, "failed to get pck0\n"); + ret = PTR_ERR(mclk); + goto err_set_audio; + } + + clk_src = clk_get(NULL, "clk32k"); + if (IS_ERR(clk_src)) { + dev_err(&pdev->dev, "failed to get clk32k\n"); + ret = PTR_ERR(clk_src); + goto err_set_audio; + } + + ret = clk_set_parent(mclk, clk_src); + clk_put(clk_src); + if (ret != 0) { + dev_err(&pdev->dev, "failed to set MCLK parent\n"); + goto err_set_audio; + } + + dev_info(&pdev->dev, "setting pck0 to %dHz\n", MCLK_RATE); + clk_set_rate(mclk, MCLK_RATE); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed\n"); + goto err_set_audio; + } + + return 0; + +err_set_audio: + atmel_ssc_put_audio(id); + return ret; +} + +static int atmel_asoc_wm8904_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; + int id; + + id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc"); + + snd_soc_unregister_card(card); + atmel_ssc_put_audio(id); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = { + { .compatible = "atmel,asoc-wm8904", }, + { } +}; +#endif + +static struct platform_driver atmel_asoc_wm8904_driver = { + .driver = { + .name = "atmel-wm8904-audio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids), + }, + .probe = atmel_asoc_wm8904_probe, + .remove = atmel_asoc_wm8904_remove, +}; + +module_platform_driver(atmel_asoc_wm8904_driver); + +/* Module information */ +MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>"); +MODULE_DESCRIPTION("ALSA SoC machine driver for Atmel EK with WM8904 codec"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c new file mode 100644 index 00000000000..992ae38d5a1 --- /dev/null +++ b/sound/soc/atmel/sam9x5_wm8731.c @@ -0,0 +1,208 @@ +/* + * sam9x5_wm8731 -- SoC audio for AT91SAM9X5-based boards + * that are using WM8731 as codec. + * + * Copyright (C) 2011 Atmel, + * Nicolas Ferre <nicolas.ferre@atmel.com> + * + * Copyright (C) 2013 Paratronic, + * Richard Genoud <richard.genoud@gmail.com> + * + * Based on sam9g20_wm8731.c by: + * Sedji Gaouaou <sedji.gaouaou@atmel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include <linux/of.h> +#include <linux/export.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/device.h> + +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/soc-dapm.h> + +#include "../codecs/wm8731.h" +#include "atmel_ssc_dai.h" + + +#define MCLK_RATE 12288000 + +#define DRV_NAME "sam9x5-snd-wm8731" + +struct sam9x5_drvdata { + int ssc_id; +}; + +/* + * Logic for a wm8731 as connected on a at91sam9x5ek based board. + */ +static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct device *dev = rtd->dev; + int ret; + + dev_dbg(dev, "ASoC: %s called\n", __func__); + + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, + MCLK_RATE, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Audio paths on at91sam9x5ek board: + * + * |A| ------------> | | ---R----> Headphone Jack + * |T| <----\ | WM | ---L--/ + * |9| ---> CLK <--> | 8731 | <--R----- Line In Jack + * |1| <------------ | | <--L--/ + */ +static const struct snd_soc_dapm_widget sam9x5_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), +}; + +static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *codec_np, *cpu_np; + struct snd_soc_card *card; + struct snd_soc_dai_link *dai; + struct sam9x5_drvdata *priv; + int ret; + + if (!np) { + dev_err(&pdev->dev, "No device node supplied\n"); + return -EINVAL; + } + + card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL); + if (!dai || !card || !priv) { + ret = -ENOMEM; + goto out; + } + + card->dev = &pdev->dev; + card->owner = THIS_MODULE; + card->dai_link = dai; + card->num_links = 1; + card->dapm_widgets = sam9x5_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sam9x5_dapm_widgets); + dai->name = "WM8731"; + dai->stream_name = "WM8731 PCM"; + dai->codec_dai_name = "wm8731-hifi"; + dai->init = sam9x5_wm8731_init; + dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM; + + ret = snd_soc_of_parse_card_name(card, "atmel,model"); + if (ret) { + dev_err(&pdev->dev, "atmel,model node missing\n"); + goto out; + } + + ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "atmel,audio-routing node missing\n"); + goto out; + } + + codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "atmel,audio-codec node missing\n"); + ret = -EINVAL; + goto out; + } + + dai->codec_of_node = codec_np; + + cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "atmel,ssc-controller node missing\n"); + ret = -EINVAL; + goto out; + } + dai->cpu_of_node = cpu_np; + dai->platform_of_node = cpu_np; + + priv->ssc_id = of_alias_get_id(cpu_np, "ssc"); + + ret = atmel_ssc_set_audio(priv->ssc_id); + if (ret != 0) { + dev_err(&pdev->dev, + "ASoC: Failed to set SSC %d for audio: %d\n", + ret, priv->ssc_id); + goto out; + } + + of_node_put(codec_np); + of_node_put(cpu_np); + + platform_set_drvdata(pdev, card); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, + "ASoC: Platform device allocation failed\n"); + goto out_put_audio; + } + + dev_dbg(&pdev->dev, "ASoC: %s ok\n", __func__); + + return ret; + +out_put_audio: + atmel_ssc_put_audio(priv->ssc_id); +out: + return ret; +} + +static int sam9x5_wm8731_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct sam9x5_drvdata *priv = card->drvdata; + + snd_soc_unregister_card(card); + atmel_ssc_put_audio(priv->ssc_id); + + return 0; +} + +static const struct of_device_id sam9x5_wm8731_of_match[] = { + { .compatible = "atmel,sam9x5-wm8731-audio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sam9x5_wm8731_of_match); + +static struct platform_driver sam9x5_wm8731_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sam9x5_wm8731_of_match), + }, + .probe = sam9x5_wm8731_driver_probe, + .remove = sam9x5_wm8731_driver_remove, +}; +module_platform_driver(sam9x5_wm8731_driver); + +/* Module information */ +MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>"); +MODULE_AUTHOR("Richard Genoud <richard.genoud@gmail.com>"); +MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index a497a0cfeba..decba87a074 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -73,12 +73,14 @@ static struct snd_soc_dai_link db1300_ac97_dai = { static struct snd_soc_card db1300_ac97_machine = { .name = "DB1300_AC97", + .owner = THIS_MODULE, .dai_link = &db1300_ac97_dai, .num_links = 1, }; static struct snd_soc_card db1550_ac97_machine = { .name = "DB1550_AC97", + .owner = THIS_MODULE, .dai_link = &db1200_ac97_dai, .num_links = 1, }; @@ -145,6 +147,7 @@ static struct snd_soc_dai_link db1300_i2s_dai = { static struct snd_soc_card db1300_i2s_machine = { .name = "DB1300_I2S", + .owner = THIS_MODULE, .dai_link = &db1300_i2s_dai, .num_links = 1, }; @@ -161,6 +164,7 @@ static struct snd_soc_dai_link db1550_i2s_dai = { static struct snd_soc_card db1550_i2s_machine = { .name = "DB1550_I2S", + .owner = THIS_MODULE, .dai_link = &db1550_i2s_dai, .num_links = 1, }; diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index a822ab822bb..986dcec79fa 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -379,9 +379,6 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev) mutex_init(&wd->lock); iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -ENODEV; - wd->mmio = devm_ioremap_resource(&pdev->dev, iores); if (IS_ERR(wd->mmio)) return PTR_ERR(wd->mmio); diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h index 0c3e22d90a8..a680fdc9bb4 100644 --- a/sound/soc/blackfin/bf5xx-ac97.h +++ b/sound/soc/blackfin/bf5xx-ac97.h @@ -9,7 +9,6 @@ #ifndef _BF5XX_AC97_H #define _BF5XX_AC97_H -extern struct snd_ac97 *ac97; /* Frame format in memory, only support stereo currently */ struct ac97_frame { u16 ac97_tag; /* slot 0 */ diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index 04491f0e8d1..efa75b5086a 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -363,9 +363,6 @@ static int ep93xx_ac97_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - info->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(info->regs)) return PTR_ERR(info->regs); diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 17ad70bca9f..a57643d6402 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -376,9 +376,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - info->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(info->regs)) return PTR_ERR(info->regs); @@ -411,7 +408,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) return 0; fail_put_lrclk: - dev_set_drvdata(&pdev->dev, NULL); clk_put(info->lrclk); fail_put_sclk: clk_put(info->sclk); @@ -426,7 +422,6 @@ static int ep93xx_i2s_remove(struct platform_device *pdev) struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - dev_set_drvdata(&pdev->dev, NULL); clk_put(info->lrclk); clk_put(info->sclk); clk_put(info->mclk); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 01d112b48e7..15106c04547 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -21,6 +21,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AD73311 select SND_SOC_ADAU1373 if I2C select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI + select SND_SOC_ADAU1701 if I2C select SND_SOC_ADS117X select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C @@ -55,6 +56,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C select SND_SOC_HDMI_CODEC + select SND_SOC_PCM1681 if I2C + select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C @@ -123,6 +126,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8994 if MFD_WM8994 select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8996 if I2C + select SND_SOC_WM8997 if MFD_WM8997 select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS @@ -146,8 +150,10 @@ config SND_SOC_ARIZONA tristate default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y + default y if SND_SOC_WM8997=y default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m + default m if SND_SOC_WM8997=m config SND_SOC_WM_HUBS tristate @@ -199,6 +205,9 @@ config SND_SOC_AK4104 config SND_SOC_AK4535 tristate +config SND_SOC_AK4554 + tristate + config SND_SOC_AK4641 tristate @@ -293,6 +302,12 @@ config SND_SOC_MAX9850 config SND_SOC_HDMI_CODEC tristate +config SND_SOC_PCM1681 + tristate + +config SND_SOC_PCM1792A + tristate + config SND_SOC_PCM3008 tristate @@ -501,6 +516,9 @@ config SND_SOC_WM8995 config SND_SOC_WM8996 tristate +config SND_SOC_WM8997 + tristate + config SND_SOC_WM9081 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 70fd8066f54..bc126764a44 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -11,6 +11,7 @@ snd-soc-adav80x-objs := adav80x.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o +snd-soc-ak4554-objs := ak4554.o snd-soc-ak4641-objs := ak4641.o snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o @@ -42,6 +43,8 @@ snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o snd-soc-hdmi-codec-objs := hdmi.o +snd-soc-pcm1681-objs := pcm1681.o +snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o @@ -114,6 +117,7 @@ snd-soc-wm8991-objs := wm8991.o snd-soc-wm8993-objs := wm8993.o snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o snd-soc-wm8995-objs := wm8995.o +snd-soc-wm8997-objs := wm8997.o snd-soc-wm9081-objs := wm9081.o snd-soc-wm9090-objs := wm9090.o snd-soc-wm9705-objs := wm9705.o @@ -138,6 +142,7 @@ obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o +obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o @@ -171,6 +176,8 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o +obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o +obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o @@ -239,6 +246,7 @@ obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o +obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index ec7351803c2..8d9ba4ba4bf 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -23,6 +23,16 @@ #include <sound/initval.h> #include <sound/soc.h> +static const struct snd_soc_dapm_widget ac97_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route ac97_routes[] = { + { "AC97 Capture", NULL, "RX" }, + { "TX", NULL, "AC97 Playback" }, +}; + static int ac97_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -117,6 +127,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = { .probe = ac97_soc_probe, .suspend = ac97_soc_suspend, .resume = ac97_soc_resume, + + .dapm_widgets = ac97_widgets, + .num_dapm_widgets = ARRAY_SIZE(ac97_widgets), + .dapm_routes = ac97_routes, + .num_dapm_routes = ARRAY_SIZE(ac97_routes), }; static int ac97_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 89fcf7d6e7b..7257a8885f4 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -96,6 +96,44 @@ SOC_ENUM("Capture Source", ad1980_cap_src), SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0), }; +static const struct snd_soc_dapm_widget ad1980_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_INPUT("CD_L"), +SND_SOC_DAPM_INPUT("CD_R"), +SND_SOC_DAPM_INPUT("AUX_L"), +SND_SOC_DAPM_INPUT("AUX_R"), +SND_SOC_DAPM_INPUT("LINE_IN_L"), +SND_SOC_DAPM_INPUT("LINE_IN_R"), + +SND_SOC_DAPM_OUTPUT("LFE_OUT"), +SND_SOC_DAPM_OUTPUT("CENTER_OUT"), +SND_SOC_DAPM_OUTPUT("LINE_OUT_L"), +SND_SOC_DAPM_OUTPUT("LINE_OUT_R"), +SND_SOC_DAPM_OUTPUT("MONO_OUT"), +SND_SOC_DAPM_OUTPUT("HP_OUT_L"), +SND_SOC_DAPM_OUTPUT("HP_OUT_R"), +}; + +static const struct snd_soc_dapm_route ad1980_dapm_routes[] = { + { "Capture", NULL, "MIC1" }, + { "Capture", NULL, "MIC2" }, + { "Capture", NULL, "CD_L" }, + { "Capture", NULL, "CD_R" }, + { "Capture", NULL, "AUX_L" }, + { "Capture", NULL, "AUX_R" }, + { "Capture", NULL, "LINE_IN_L" }, + { "Capture", NULL, "LINE_IN_R" }, + + { "LFE_OUT", NULL, "Playback" }, + { "CENTER_OUT", NULL, "Playback" }, + { "LINE_OUT_L", NULL, "Playback" }, + { "LINE_OUT_R", NULL, "Playback" }, + { "MONO_OUT", NULL, "Playback" }, + { "HP_OUT_L", NULL, "Playback" }, + { "HP_OUT_R", NULL, "Playback" }, +}; + static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -253,6 +291,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1980 = { .reg_cache_step = 2, .write = ac97_write, .read = ac97_read, + + .dapm_widgets = ad1980_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets), + .dapm_routes = ad1980_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes), }; static int ad1980_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c index b1f2baf42b4..5fac8adbc13 100644 --- a/sound/soc/codecs/ad73311.c +++ b/sound/soc/codecs/ad73311.c @@ -23,6 +23,21 @@ #include "ad73311.h" +static const struct snd_soc_dapm_widget ad73311_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("VINP"), +SND_SOC_DAPM_INPUT("VINN"), +SND_SOC_DAPM_OUTPUT("VOUTN"), +SND_SOC_DAPM_OUTPUT("VOUTP"), +}; + +static const struct snd_soc_dapm_route ad73311_dapm_routes[] = { + { "Capture", NULL, "VINP" }, + { "Capture", NULL, "VINN" }, + + { "VOUTN", NULL, "Playback" }, + { "VOUTP", NULL, "Playback" }, +}; + static struct snd_soc_dai_driver ad73311_dai = { .name = "ad73311-hifi", .playback = { @@ -39,7 +54,12 @@ static struct snd_soc_dai_driver ad73311_dai = { .formats = SNDRV_PCM_FMTBIT_S16_LE, }, }; -static struct snd_soc_codec_driver soc_codec_dev_ad73311; +static struct snd_soc_codec_driver soc_codec_dev_ad73311 = { + .dapm_widgets = ad73311_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets), + .dapm_routes = ad73311_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes), +}; static int ad73311_probe(struct platform_device *pdev) { diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index d1124a5b347..ebff1128be5 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -91,7 +91,7 @@ #define ADAU1701_OSCIPOW_OPD 0x04 #define ADAU1701_DACSET_DACINIT 1 -#define ADAU1707_CLKDIV_UNSET (-1UL) +#define ADAU1707_CLKDIV_UNSET (-1U) #define ADAU1701_FIRMWARE "adau1701.bin" @@ -247,21 +247,21 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv) gpio_is_valid(adau1701->gpio_pll_mode[1])) { switch (clkdiv) { case 64: - gpio_set_value(adau1701->gpio_pll_mode[0], 0); - gpio_set_value(adau1701->gpio_pll_mode[1], 0); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); break; case 256: - gpio_set_value(adau1701->gpio_pll_mode[0], 0); - gpio_set_value(adau1701->gpio_pll_mode[1], 1); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); break; case 384: - gpio_set_value(adau1701->gpio_pll_mode[0], 1); - gpio_set_value(adau1701->gpio_pll_mode[1], 0); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); break; case 0: /* fallback */ case 512: - gpio_set_value(adau1701->gpio_pll_mode[0], 1); - gpio_set_value(adau1701->gpio_pll_mode[1], 1); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); break; } } @@ -269,10 +269,10 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv) adau1701->pll_clkdiv = clkdiv; if (gpio_is_valid(adau1701->gpio_nreset)) { - gpio_set_value(adau1701->gpio_nreset, 0); + gpio_set_value_cansleep(adau1701->gpio_nreset, 0); /* minimum reset time is 20ns */ udelay(1); - gpio_set_value(adau1701->gpio_nreset, 1); + gpio_set_value_cansleep(adau1701->gpio_nreset, 1); /* power-up time may be as long as 85ms */ mdelay(85); } @@ -734,7 +734,10 @@ static int adau1701_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id adau1701_i2c_id[] = { + { "adau1401", 0 }, + { "adau1401a", 0 }, { "adau1701", 0 }, + { "adau1702", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id); diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 3c839cc4e00..15b012d0f22 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -868,6 +868,12 @@ static int adav80x_bus_remove(struct device *dev) } #if defined(CONFIG_SPI_MASTER) +static const struct spi_device_id adav80x_spi_id[] = { + { "adav801", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adav80x_spi_id); + static int adav80x_spi_probe(struct spi_device *spi) { return adav80x_bus_probe(&spi->dev, SND_SOC_SPI); @@ -885,15 +891,16 @@ static struct spi_driver adav80x_spi_driver = { }, .probe = adav80x_spi_probe, .remove = adav80x_spi_remove, + .id_table = adav80x_spi_id, }; #endif #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -static const struct i2c_device_id adav80x_id[] = { +static const struct i2c_device_id adav80x_i2c_id[] = { { "adav803", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, adav80x_id); +MODULE_DEVICE_TABLE(i2c, adav80x_i2c_id); static int adav80x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -913,7 +920,7 @@ static struct i2c_driver adav80x_i2c_driver = { }, .probe = adav80x_i2c_probe, .remove = adav80x_i2c_remove, - .id_table = adav80x_id, + .id_table = adav80x_i2c_id, }; #endif diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c index 506d474c4d2..8f388edff58 100644 --- a/sound/soc/codecs/ads117x.c +++ b/sound/soc/codecs/ads117x.c @@ -23,6 +23,28 @@ #define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000) #define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) +static const struct snd_soc_dapm_widget ads117x_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("Input1"), +SND_SOC_DAPM_INPUT("Input2"), +SND_SOC_DAPM_INPUT("Input3"), +SND_SOC_DAPM_INPUT("Input4"), +SND_SOC_DAPM_INPUT("Input5"), +SND_SOC_DAPM_INPUT("Input6"), +SND_SOC_DAPM_INPUT("Input7"), +SND_SOC_DAPM_INPUT("Input8"), +}; + +static const struct snd_soc_dapm_route ads117x_dapm_routes[] = { + { "Capture", NULL, "Input1" }, + { "Capture", NULL, "Input2" }, + { "Capture", NULL, "Input3" }, + { "Capture", NULL, "Input4" }, + { "Capture", NULL, "Input5" }, + { "Capture", NULL, "Input6" }, + { "Capture", NULL, "Input7" }, + { "Capture", NULL, "Input8" }, +}; + static struct snd_soc_dai_driver ads117x_dai = { /* ADC */ .name = "ads117x-hifi", @@ -34,7 +56,12 @@ static struct snd_soc_dai_driver ads117x_dai = { .formats = ADS117X_FORMATS,}, }; -static struct snd_soc_codec_driver soc_codec_dev_ads117x; +static struct snd_soc_codec_driver soc_codec_dev_ads117x = { + .dapm_widgets = ads117x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets), + .dapm_routes = ads117x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes), +}; static int ads117x_probe(struct platform_device *pdev) { diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index c7cfdf957e4..71059c07ae7 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -51,6 +51,17 @@ struct ak4104_private { struct regmap *regmap; }; +static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = { +SND_SOC_DAPM_PGA("TXE", AK4104_REG_TX, AK4104_TX_TXE, 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route ak4104_dapm_routes[] = { + { "TXE", NULL, "Playback" }, + { "TX", NULL, "TXE" }, +}; + static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int format) { @@ -138,29 +149,11 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* enable transmitter */ - ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX, - AK4104_TX_TXE, AK4104_TX_TXE); - if (ret < 0) - return ret; - return 0; } -static int ak4104_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_codec *codec = dai->codec; - struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec); - - /* disable transmitter */ - return regmap_update_bits(ak4104->regmap, AK4104_REG_TX, - AK4104_TX_TXE, 0); -} - static const struct snd_soc_dai_ops ak4101_dai_ops = { .hw_params = ak4104_hw_params, - .hw_free = ak4104_hw_free, .set_fmt = ak4104_set_dai_fmt, }; @@ -214,6 +207,11 @@ static int ak4104_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_device_ak4104 = { .probe = ak4104_probe, .remove = ak4104_remove, + + .dapm_widgets = ak4104_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets), + .dapm_routes = ak4104_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes), }; static const struct regmap_config ak4104_regmap = { diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c new file mode 100644 index 00000000000..79e9555766c --- /dev/null +++ b/sound/soc/codecs/ak4554.c @@ -0,0 +1,106 @@ +/* + * ak4554.c + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <sound/soc.h> + +/* + * ak4554 is very simple DA/AD converter which has no setting register. + * + * CAUTION + * + * ak4554 playback format is SND_SOC_DAIFMT_RIGHT_J, + * and, capture format is SND_SOC_DAIFMT_LEFT_J + * on same bit clock, LR clock. + * But, this driver doesn't have snd_soc_dai_ops :: set_fmt + * + * CPU/Codec DAI image + * + * CPU-DAI1 (plaback only fmt = RIGHT_J) --+-- ak4554 + * | + * CPU-DAI2 (capture only fmt = LEFT_J) ---+ + */ + +static const struct snd_soc_dapm_widget ak4554_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), + +SND_SOC_DAPM_OUTPUT("AOUTL"), +SND_SOC_DAPM_OUTPUT("AOUTR"), +}; + +static const struct snd_soc_dapm_route ak4554_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, + + { "AOUTL", NULL, "Playback" }, + { "AOUTR", NULL, "Playback" }, +}; + +static struct snd_soc_dai_driver ak4554_dai = { + .name = "ak4554-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .symmetric_rates = 1, +}; + +static struct snd_soc_codec_driver soc_codec_dev_ak4554 = { + .dapm_widgets = ak4554_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets), + .dapm_routes = ak4554_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes), +}; + +static int ak4554_soc_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_ak4554, + &ak4554_dai, 1); +} + +static int ak4554_soc_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct of_device_id ak4554_of_match[] = { + { .compatible = "asahi-kasei,ak4554" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4554_of_match); + +static struct platform_driver ak4554_driver = { + .driver = { + .name = "ak4554-adc-dac", + .owner = THIS_MODULE, + .of_match_table = ak4554_of_match, + }, + .probe = ak4554_soc_probe, + .remove = ak4554_soc_remove, +}; +module_platform_driver(ak4554_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SoC AK4554 driver"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c index 1f303983ae0..72e953b2cb4 100644 --- a/sound/soc/codecs/ak5386.c +++ b/sound/soc/codecs/ak5386.c @@ -22,7 +22,22 @@ struct ak5386_priv { int reset_gpio; }; -static struct snd_soc_codec_driver soc_codec_ak5386; +static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), +}; + +static const struct snd_soc_dapm_route ak5386_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, +}; + +static struct snd_soc_codec_driver soc_codec_ak5386 = { + .dapm_widgets = ak5386_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets), + .dapm_routes = ak5386_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes), +}; static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int format) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index de625813c0e..657808ba141 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -19,6 +19,7 @@ #include <sound/tlv.h> #include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/gpio.h> #include <linux/mfd/arizona/registers.h> #include "arizona.h" @@ -199,9 +200,16 @@ int arizona_init_spk(struct snd_soc_codec *codec) if (ret != 0) return ret; - ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkr, 1); - if (ret != 0) - return ret; + switch (arizona->type) { + case WM8997: + break; + default: + ret = snd_soc_dapm_new_controls(&codec->dapm, + &arizona_spkr, 1); + if (ret != 0) + return ret; + break; + } ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_SHUTDOWN_WARN, "Thermal warning", arizona_thermal_warn, @@ -223,6 +231,41 @@ int arizona_init_spk(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(arizona_init_spk); +int arizona_init_gpio(struct snd_soc_codec *codec) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + int i; + + switch (arizona->type) { + case WM5110: + snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity"); + break; + default: + break; + } + + snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity"); + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { + switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) { + case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT: + snd_soc_dapm_enable_pin(&codec->dapm, + "DRC1 Signal Activity"); + break; + case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT: + snd_soc_dapm_enable_pin(&codec->dapm, + "DRC2 Signal Activity"); + break; + default: + break; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_gpio); + const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", "Tone Generator 1", @@ -517,6 +560,26 @@ const struct soc_enum arizona_ng_hold = 4, arizona_ng_hold_text); EXPORT_SYMBOL_GPL(arizona_ng_hold); +static const char * const arizona_in_dmic_osr_text[] = { + "1.536MHz", "3.072MHz", "6.144MHz", +}; + +const struct soc_enum arizona_in_dmic_osr[] = { + SOC_ENUM_SINGLE(ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), + SOC_ENUM_SINGLE(ARIZONA_IN2L_CONTROL, ARIZONA_IN2_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), + SOC_ENUM_SINGLE(ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), + SOC_ENUM_SINGLE(ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), +}; +EXPORT_SYMBOL_GPL(arizona_in_dmic_osr); + static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena) { struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index b60b08ccc1d..9e81b639269 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -150,7 +150,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \ ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux) -#define ARIZONA_MUX_ROUTES(name) \ +#define ARIZONA_MUX_ROUTES(widget, name) \ + { widget, NULL, name " Input" }, \ ARIZONA_MIXER_INPUT_ROUTES(name " Input") #define ARIZONA_MIXER_ROUTES(widget, name) \ @@ -198,6 +199,7 @@ extern const struct soc_enum arizona_lhpf3_mode; extern const struct soc_enum arizona_lhpf4_mode; extern const struct soc_enum arizona_ng_hold; +extern const struct soc_enum arizona_in_dmic_osr[]; extern int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, @@ -242,6 +244,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source, unsigned int Fref, unsigned int Fout); extern int arizona_init_spk(struct snd_soc_codec *codec); +extern int arizona_init_gpio(struct snd_soc_codec *codec); extern int arizona_init_dai(struct arizona_priv *priv, int dai); diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index a081d9fcb16..c4cf0699e77 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -15,15 +15,27 @@ #include <sound/soc.h> +static const struct snd_soc_dapm_widget bt_sco_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route bt_sco_routes[] = { + { "Capture", NULL, "RX" }, + { "TX", NULL, "Playback" }, +}; + static struct snd_soc_dai_driver bt_sco_dai = { .name = "bt-sco-pcm", .playback = { + .stream_name = "Playback", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { + .stream_name = "Capture", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000, @@ -31,7 +43,12 @@ static struct snd_soc_dai_driver bt_sco_dai = { }, }; -static struct snd_soc_codec_driver soc_codec_dev_bt_sco; +static struct snd_soc_codec_driver soc_codec_dev_bt_sco = { + .dapm_widgets = bt_sco_widgets, + .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets), + .dapm_routes = bt_sco_routes, + .num_dapm_routes = ARRAY_SIZE(bt_sco_routes), +}; static int bt_sco_probe(struct platform_device *pdev) { @@ -50,6 +67,9 @@ static struct platform_device_id bt_sco_driver_ids[] = { { .name = "dfbmcs320", }, + { + .name = "bt-sco", + }, {}, }; MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 8e4779812b9..83c835d9fd8 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -139,6 +139,22 @@ struct cs4270_private { struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; +static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), + +SND_SOC_DAPM_OUTPUT("AOUTL"), +SND_SOC_DAPM_OUTPUT("AOUTR"), +}; + +static const struct snd_soc_dapm_route cs4270_dapm_routes[] = { + { "Capture", NULL, "AINA" }, + { "Capture", NULL, "AINB" }, + + { "AOUTA", NULL, "Playback" }, + { "AOUTB", NULL, "Playback" }, +}; + /** * struct cs4270_mode_ratios - clock ratio tables * @ratio: the ratio of MCLK to the sample rate @@ -612,6 +628,10 @@ static const struct snd_soc_codec_driver soc_codec_device_cs4270 = { .controls = cs4270_snd_controls, .num_controls = ARRAY_SIZE(cs4270_snd_controls), + .dapm_widgets = cs4270_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets), + .dapm_routes = cs4270_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes), }; /* diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 03036b32673..a20f1bb8f07 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -173,6 +173,26 @@ struct cs4271_private { bool enable_soft_reset; }; +static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINA"), +SND_SOC_DAPM_INPUT("AINB"), + +SND_SOC_DAPM_OUTPUT("AOUTA+"), +SND_SOC_DAPM_OUTPUT("AOUTA-"), +SND_SOC_DAPM_OUTPUT("AOUTB+"), +SND_SOC_DAPM_OUTPUT("AOUTB-"), +}; + +static const struct snd_soc_dapm_route cs4271_dapm_routes[] = { + { "Capture", NULL, "AINA" }, + { "Capture", NULL, "AINB" }, + + { "AOUTA+", NULL, "Playback" }, + { "AOUTA-", NULL, "Playback" }, + { "AOUTB+", NULL, "Playback" }, + { "AOUTB-", NULL, "Playback" }, +}; + /* * @freq is the desired MCLK rate * MCLK rate should (c) be the sample rate, multiplied by one of the @@ -576,8 +596,7 @@ static int cs4271_probe(struct snd_soc_codec *codec) CS4271_MODE2_MUTECAEQUB, CS4271_MODE2_MUTECAEQUB); - return snd_soc_add_codec_controls(codec, cs4271_snd_controls, - ARRAY_SIZE(cs4271_snd_controls)); + return 0; } static int cs4271_remove(struct snd_soc_codec *codec) @@ -596,6 +615,13 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { .remove = cs4271_remove, .suspend = cs4271_soc_suspend, .resume = cs4271_soc_resume, + + .controls = cs4271_snd_controls, + .num_controls = ARRAY_SIZE(cs4271_snd_controls), + .dapm_widgets = cs4271_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets), + .dapm_routes = cs4271_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes), }; #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 987f728718c..be2ba1b6fe4 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -195,6 +195,8 @@ static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0); static DECLARE_TLV_DB_SCALE(mix_tlv, -50, 50, 0); +static DECLARE_TLV_DB_SCALE(beep_tlv, -56, 200, 0); + static const unsigned int limiter_tlv[] = { TLV_DB_RANGE_HEAD(2), 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), @@ -451,7 +453,8 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = { SOC_ENUM("Beep Pitch", beep_pitch_enum), SOC_ENUM("Beep on Time", beep_ontime_enum), SOC_ENUM("Beep off Time", beep_offtime_enum), - SOC_SINGLE_TLV("Beep Volume", CS42L52_BEEP_VOL, 0, 0x1f, 0x07, hl_tlv), + SOC_SINGLE_SX_TLV("Beep Volume", CS42L52_BEEP_VOL, + 0, 0x07, 0x1f, beep_tlv), SOC_SINGLE("Beep Mixer Switch", CS42L52_BEEP_TONE_CTL, 5, 1, 1), SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum), SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum), diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index 66967ba6f75..b2090b2a5e2 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -50,20 +50,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"DMIC AIF", NULL, "DMic"}, }; -static int dmic_probe(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets, - ARRAY_SIZE(dmic_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(dapm); - - return 0; -} - static struct snd_soc_codec_driver soc_dmic = { - .probe = dmic_probe, + .dapm_widgets = dmic_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), }; static int dmic_dev_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c index 2bcae2b40c9..68342b121c9 100644 --- a/sound/soc/codecs/hdmi.c +++ b/sound/soc/codecs/hdmi.c @@ -23,11 +23,20 @@ #define DRV_NAME "hdmi-audio-codec" -static struct snd_soc_codec_driver hdmi_codec; +static const struct snd_soc_dapm_widget hdmi_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route hdmi_routes[] = { + { "Capture", NULL, "RX" }, + { "TX", NULL, "Playback" }, +}; static struct snd_soc_dai_driver hdmi_codec_dai = { .name = "hdmi-hifi", .playback = { + .stream_name = "Playback", .channels_min = 2, .channels_max = 8, .rates = SNDRV_PCM_RATE_32000 | @@ -37,6 +46,25 @@ static struct snd_soc_dai_driver hdmi_codec_dai = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + +}; + +static struct snd_soc_codec_driver hdmi_codec = { + .dapm_widgets = hdmi_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), + .dapm_routes = hdmi_routes, + .num_dapm_routes = ARRAY_SIZE(hdmi_routes), }; static int hdmi_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index 9f9f59573f7..0e5743ea79d 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <sound/core.h> @@ -23,12 +24,15 @@ #include <sound/tlv.h> struct lm4857 { - struct i2c_client *i2c; + struct regmap *regmap; uint8_t mode; }; -static const uint8_t lm4857_default_regs[] = { - 0x00, 0x00, 0x00, 0x00, +static const struct reg_default lm4857_default_regs[] = { + { 0x0, 0x00 }, + { 0x1, 0x00 }, + { 0x2, 0x00 }, + { 0x3, 0x00 }, }; /* The register offsets in the cache array */ @@ -42,39 +46,6 @@ static const uint8_t lm4857_default_regs[] = { #define LM4857_WAKEUP 5 #define LM4857_EPGAIN 4 -static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - uint8_t data; - int ret; - - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return ret; - - data = (reg << 6) | value; - ret = i2c_master_send(codec->control_data, &data, 1); - if (ret != 1) { - dev_err(codec->dev, "Failed to write register: %d\n", ret); - return ret; - } - - return 0; -} - -static unsigned int lm4857_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - unsigned int val; - int ret; - - ret = snd_soc_cache_read(codec, reg, &val); - if (ret) - return -1; - - return val; -} - static int lm4857_get_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -96,7 +67,7 @@ static int lm4857_set_mode(struct snd_kcontrol *kcontrol, lm4857->mode = value; if (codec->dapm.bias_level == SND_SOC_BIAS_ON) - snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6); + regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6); return 1; } @@ -108,10 +79,11 @@ static int lm4857_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6); + regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, + lm4857->mode + 6); break; case SND_SOC_BIAS_STANDBY: - snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0); + regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0); break; default: break; @@ -171,49 +143,32 @@ static const struct snd_soc_dapm_route lm4857_routes[] = { {"EP", NULL, "IN"}, }; -static int lm4857_probe(struct snd_soc_codec *codec) -{ - struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; - int ret; - - codec->control_data = lm4857->i2c; - - ret = snd_soc_add_codec_controls(codec, lm4857_controls, - ARRAY_SIZE(lm4857_controls)); - if (ret) - return ret; - - ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets, - ARRAY_SIZE(lm4857_dapm_widgets)); - if (ret) - return ret; +static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { + .set_bias_level = lm4857_set_bias_level, - ret = snd_soc_dapm_add_routes(dapm, lm4857_routes, - ARRAY_SIZE(lm4857_routes)); - if (ret) - return ret; + .controls = lm4857_controls, + .num_controls = ARRAY_SIZE(lm4857_controls), + .dapm_widgets = lm4857_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets), + .dapm_routes = lm4857_routes, + .num_dapm_routes = ARRAY_SIZE(lm4857_routes), +}; - snd_soc_dapm_new_widgets(dapm); +static const struct regmap_config lm4857_regmap_config = { + .val_bits = 6, + .reg_bits = 2, - return 0; -} + .max_register = LM4857_CTRL, -static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { - .write = lm4857_write, - .read = lm4857_read, - .probe = lm4857_probe, - .reg_cache_size = ARRAY_SIZE(lm4857_default_regs), - .reg_word_size = sizeof(uint8_t), - .reg_cache_default = lm4857_default_regs, - .set_bias_level = lm4857_set_bias_level, + .cache_type = REGCACHE_FLAT, + .reg_defaults = lm4857_default_regs, + .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs), }; static int lm4857_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct lm4857 *lm4857; - int ret; lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL); if (!lm4857) @@ -221,11 +176,11 @@ static int lm4857_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, lm4857); - lm4857->i2c = i2c; - - ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0); + lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); + if (IS_ERR(lm4857->regmap)) + return PTR_ERR(lm4857->regmap); - return ret; + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0); } static int lm4857_i2c_remove(struct i2c_client *i2c) diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c index a6ac2313047..31f91560e9f 100644 --- a/sound/soc/codecs/max9768.c +++ b/sound/soc/codecs/max9768.c @@ -118,6 +118,18 @@ static const struct snd_kcontrol_new max9768_mute[] = { SOC_SINGLE_BOOL_EXT("Playback Switch", 0, max9768_get_gpio, max9768_set_gpio), }; +static const struct snd_soc_dapm_widget max9768_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN"), + +SND_SOC_DAPM_OUTPUT("OUT+"), +SND_SOC_DAPM_OUTPUT("OUT-"), +}; + +static const struct snd_soc_dapm_route max9768_dapm_routes[] = { + { "OUT+", NULL, "IN" }, + { "OUT-", NULL, "IN" }, +}; + static int max9768_probe(struct snd_soc_codec *codec) { struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec); @@ -148,6 +160,10 @@ static struct snd_soc_codec_driver max9768_codec_driver = { .probe = max9768_probe, .controls = max9768_volume, .num_controls = ARRAY_SIZE(max9768_volume), + .dapm_widgets = max9768_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9768_dapm_widgets), + .dapm_routes = max9768_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9768_dapm_routes), }; static const struct regmap_config max9768_i2c_regmap_config = { diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index ad5313f98f2..0569a4c3ae0 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2084,8 +2084,9 @@ static irqreturn_t max98090_interrupt(int irq, void *data) pm_wakeup_event(codec->dev, 100); - schedule_delayed_work(&max98090->jack_work, - msecs_to_jiffies(100)); + queue_delayed_work(system_power_efficient_wq, + &max98090->jack_work, + msecs_to_jiffies(100)); } if (active & M98090_DRCACT_MASK) @@ -2132,8 +2133,9 @@ int max98090_mic_detect(struct snd_soc_codec *codec, snd_soc_jack_report(max98090->jack, 0, SND_JACK_HEADSET | SND_JACK_BTN_0); - schedule_delayed_work(&max98090->jack_work, - msecs_to_jiffies(100)); + queue_delayed_work(system_power_efficient_wq, + &max98090->jack_work, + msecs_to_jiffies(100)); return 0; } diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 6b6c74cd83e..29549cdbf4c 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -14,170 +14,21 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include <sound/soc.h> #include <sound/tlv.h> #include "max9877.h" -static struct i2c_client *i2c; +static struct regmap *regmap; -static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 }; - -static void max9877_write_regs(void) -{ - unsigned int i; - u8 data[6]; - - data[0] = MAX9877_INPUT_MODE; - for (i = 0; i < ARRAY_SIZE(max9877_regs); i++) - data[i + 1] = max9877_regs[i]; - - if (i2c_master_send(i2c, data, 6) != 6) - dev_err(&i2c->dev, "i2c write failed\n"); -} - -static int max9877_get_reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = mc->max; - unsigned int invert = mc->invert; - - ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask; - - if (invert) - ucontrol->value.integer.value[0] = - mask - ucontrol->value.integer.value[0]; - - return 0; -} - -static int max9877_set_reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = mc->max; - unsigned int invert = mc->invert; - unsigned int val = (ucontrol->value.integer.value[0] & mask); - - if (invert) - val = mask - val; - - if (((max9877_regs[reg] >> shift) & mask) == val) - return 0; - - max9877_regs[reg] &= ~(mask << shift); - max9877_regs[reg] |= val << shift; - max9877_write_regs(); - - return 1; -} - -static int max9877_get_2reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int mask = mc->max; - - ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask; - ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask; - - return 0; -} - -static int max9877_set_2reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int mask = mc->max; - unsigned int val = (ucontrol->value.integer.value[0] & mask); - unsigned int val2 = (ucontrol->value.integer.value[1] & mask); - unsigned int change = 0; - - if (((max9877_regs[reg] >> shift) & mask) != val) - change = 1; - - if (((max9877_regs[reg2] >> shift) & mask) != val2) - change = 1; - - if (change) { - max9877_regs[reg] &= ~(mask << shift); - max9877_regs[reg] |= val << shift; - max9877_regs[reg2] &= ~(mask << shift); - max9877_regs[reg2] |= val2 << shift; - max9877_write_regs(); - } - - return change; -} - -static int max9877_get_out_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK; - - if (value) - value -= 1; - - ucontrol->value.integer.value[0] = value; - return 0; -} - -static int max9877_set_out_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = ucontrol->value.integer.value[0]; - - value += 1; - - if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value) - return 0; - - max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OUTMODE_MASK; - max9877_regs[MAX9877_OUTPUT_MODE] |= value; - max9877_write_regs(); - return 1; -} - -static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK); - - value = value >> MAX9877_OSC_OFFSET; - - ucontrol->value.integer.value[0] = value; - return 0; -} - -static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = ucontrol->value.integer.value[0]; - - value = value << MAX9877_OSC_OFFSET; - if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value) - return 0; - - max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OSC_MASK; - max9877_regs[MAX9877_OUTPUT_MODE] |= value; - max9877_write_regs(); - return 1; -} +static struct reg_default max9877_regs[] = { + { 0, 0x40 }, + { 1, 0x00 }, + { 2, 0x00 }, + { 3, 0x00 }, + { 4, 0x49 }, +}; static const unsigned int max9877_pgain_tlv[] = { TLV_DB_RANGE_HEAD(2), @@ -212,65 +63,104 @@ static const char *max9877_osc_mode[] = { }; static const struct soc_enum max9877_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode), - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode), + SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, 0, ARRAY_SIZE(max9877_out_mode), + max9877_out_mode), + SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, MAX9877_OSC_OFFSET, + ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode), }; static const struct snd_kcontrol_new max9877_controls[] = { - SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume", - MAX9877_INPUT_MODE, 0, 2, 0, - max9877_get_reg, max9877_set_reg, max9877_pgain_tlv), - SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume", - MAX9877_INPUT_MODE, 2, 2, 0, - max9877_get_reg, max9877_set_reg, max9877_pgain_tlv), - SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume", - MAX9877_SPK_VOLUME, 0, 31, 0, - max9877_get_reg, max9877_set_reg, max9877_output_tlv), - SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume", - MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0, - max9877_get_2reg, max9877_set_2reg, max9877_output_tlv), - SOC_SINGLE_EXT("MAX9877 INB Stereo Switch", - MAX9877_INPUT_MODE, 4, 1, 1, - max9877_get_reg, max9877_set_reg), - SOC_SINGLE_EXT("MAX9877 INA Stereo Switch", - MAX9877_INPUT_MODE, 5, 1, 1, - max9877_get_reg, max9877_set_reg), - SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch", - MAX9877_INPUT_MODE, 6, 1, 0, - max9877_get_reg, max9877_set_reg), - SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch", - MAX9877_OUTPUT_MODE, 6, 1, 0, - max9877_get_reg, max9877_set_reg), - SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch", - MAX9877_OUTPUT_MODE, 7, 1, 1, - max9877_get_reg, max9877_set_reg), - SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0], - max9877_get_out_mode, max9877_set_out_mode), - SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1], - max9877_get_osc_mode, max9877_set_osc_mode), + SOC_SINGLE_TLV("MAX9877 PGAINA Playback Volume", + MAX9877_INPUT_MODE, 0, 2, 0, max9877_pgain_tlv), + SOC_SINGLE_TLV("MAX9877 PGAINB Playback Volume", + MAX9877_INPUT_MODE, 2, 2, 0, max9877_pgain_tlv), + SOC_SINGLE_TLV("MAX9877 Amp Speaker Playback Volume", + MAX9877_SPK_VOLUME, 0, 31, 0, max9877_output_tlv), + SOC_DOUBLE_R_TLV("MAX9877 Amp HP Playback Volume", + MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0, + max9877_output_tlv), + SOC_SINGLE("MAX9877 INB Stereo Switch", + MAX9877_INPUT_MODE, 4, 1, 1), + SOC_SINGLE("MAX9877 INA Stereo Switch", + MAX9877_INPUT_MODE, 5, 1, 1), + SOC_SINGLE("MAX9877 Zero-crossing detection Switch", + MAX9877_INPUT_MODE, 6, 1, 0), + SOC_SINGLE("MAX9877 Bypass Mode Switch", + MAX9877_OUTPUT_MODE, 6, 1, 0), + SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]), + SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]), }; -/* This function is called from ASoC machine driver */ -int max9877_add_controls(struct snd_soc_codec *codec) -{ - return snd_soc_add_codec_controls(codec, max9877_controls, - ARRAY_SIZE(max9877_controls)); -} -EXPORT_SYMBOL_GPL(max9877_add_controls); +static const struct snd_soc_dapm_widget max9877_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("INA1"), +SND_SOC_DAPM_INPUT("INA2"), +SND_SOC_DAPM_INPUT("INB1"), +SND_SOC_DAPM_INPUT("INB2"), +SND_SOC_DAPM_INPUT("RXIN+"), +SND_SOC_DAPM_INPUT("RXIN-"), + +SND_SOC_DAPM_PGA("SHDN", MAX9877_OUTPUT_MODE, 7, 1, NULL, 0), + +SND_SOC_DAPM_OUTPUT("OUT+"), +SND_SOC_DAPM_OUTPUT("OUT-"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +}; + +static const struct snd_soc_dapm_route max9877_dapm_routes[] = { + { "SHDN", NULL, "INA1" }, + { "SHDN", NULL, "INA2" }, + { "SHDN", NULL, "INB1" }, + { "SHDN", NULL, "INB2" }, + + { "OUT+", NULL, "RXIN+" }, + { "OUT+", NULL, "SHDN" }, + + { "OUT-", NULL, "SHDN" }, + { "OUT-", NULL, "RXIN-" }, + + { "HPL", NULL, "SHDN" }, + { "HPR", NULL, "SHDN" }, +}; + +static const struct snd_soc_codec_driver max9877_codec = { + .controls = max9877_controls, + .num_controls = ARRAY_SIZE(max9877_controls), + + .dapm_widgets = max9877_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9877_dapm_widgets), + .dapm_routes = max9877_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9877_dapm_routes), +}; + +static const struct regmap_config max9877_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = max9877_regs, + .num_reg_defaults = ARRAY_SIZE(max9877_regs), + .cache_type = REGCACHE_RBTREE, +}; static int max9877_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { - i2c = client; + int i; - max9877_write_regs(); + regmap = devm_regmap_init_i2c(client, &max9877_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - return 0; + /* Ensure the device is in reset state */ + for (i = 0; i < ARRAY_SIZE(max9877_regs); i++) + regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def); + + return snd_soc_register_codec(&client->dev, &max9877_codec, NULL, 0); } static int max9877_i2c_remove(struct i2c_client *client) { - i2c = NULL; + snd_soc_unregister_codec(&client->dev); return 0; } diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 5402dfbbb71..4d3c8fd8c5d 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -94,7 +94,6 @@ #define AUDIO_DAC_CFS_DLY_B (1 << 10) struct mc13783_priv { - struct snd_soc_codec codec; struct mc13xxx *mc13xxx; enum mc13783_ssi_port adc_ssi_port; diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c new file mode 100644 index 00000000000..651ce092367 --- /dev/null +++ b/sound/soc/codecs/pcm1681.c @@ -0,0 +1,339 @@ +/* + * PCM1681 ASoC codec driver + * + * Copyright (c) StreamUnlimited GmbH 2013 + * Marek Belisko <marek.belisko@streamunlimited.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define PCM1681_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define PCM1681_SOFT_MUTE_ALL 0xff +#define PCM1681_DEEMPH_RATE_MASK 0x18 +#define PCM1681_DEEMPH_MASK 0x01 + +#define PCM1681_ATT_CONTROL(X) (X <= 6 ? X : X + 9) /* Attenuation level */ +#define PCM1681_SOFT_MUTE 0x07 /* Soft mute control register */ +#define PCM1681_DAC_CONTROL 0x08 /* DAC operation control */ +#define PCM1681_FMT_CONTROL 0x09 /* Audio interface data format */ +#define PCM1681_DEEMPH_CONTROL 0x0a /* De-emphasis control */ +#define PCM1681_ZERO_DETECT_STATUS 0x0e /* Zero detect status reg */ + +static const struct reg_default pcm1681_reg_defaults[] = { + { 0x01, 0xff }, + { 0x02, 0xff }, + { 0x03, 0xff }, + { 0x04, 0xff }, + { 0x05, 0xff }, + { 0x06, 0xff }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x06 }, + { 0x0A, 0x00 }, + { 0x0B, 0xff }, + { 0x0C, 0x0f }, + { 0x0D, 0x00 }, + { 0x10, 0xff }, + { 0x11, 0xff }, + { 0x12, 0x00 }, + { 0x13, 0x00 }, +}; + +static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg) +{ + return !((reg == 0x00) || (reg == 0x0f)); +} + +static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg) +{ + return pcm1681_accessible_reg(dev, reg) && + (reg != PCM1681_ZERO_DETECT_STATUS); +} + +struct pcm1681_private { + struct regmap *regmap; + unsigned int format; + /* Current deemphasis status */ + unsigned int deemph; + /* Current rate for deemphasis control */ + unsigned int rate; +}; + +static const int pcm1681_deemph[] = { 44100, 48000, 32000 }; + +static int pcm1681_set_deemph(struct snd_soc_codec *codec) +{ + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + int i = 0, val = -1, enable = 0; + + if (priv->deemph) + for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++) + if (pcm1681_deemph[i] == priv->rate) + val = i; + + if (val != -1) { + regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL, + PCM1681_DEEMPH_RATE_MASK, val); + enable = 1; + } else + enable = 0; + + /* enable/disable deemphasis functionality */ + return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL, + PCM1681_DEEMPH_MASK, enable); +} + +static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = priv->deemph; + + return 0; +} + +static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + + priv->deemph = ucontrol->value.enumerated.item[0]; + + return pcm1681_set_deemph(codec); +} + +static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + + /* The PCM1681 can only be slave to all clocks */ + if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "Invalid clocking mode\n"); + return -EINVAL; + } + + priv->format = format; + + return 0; +} + +static int pcm1681_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + int val; + + if (mute) + val = PCM1681_SOFT_MUTE_ALL; + else + val = 0; + + return regmap_write(priv->regmap, PCM1681_SOFT_MUTE, val); +} + +static int pcm1681_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + int val = 0, ret; + int pcm_format = params_format(params); + + priv->rate = params_rate(params); + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + if (pcm_format == SNDRV_PCM_FORMAT_S24_LE) + val = 0x00; + else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE) + val = 0x03; + break; + case SND_SOC_DAIFMT_I2S: + val = 0x04; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = 0x05; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + ret = regmap_update_bits(priv->regmap, PCM1681_FMT_CONTROL, 0x0f, val); + if (ret < 0) + return ret; + + return pcm1681_set_deemph(codec); +} + +static const struct snd_soc_dai_ops pcm1681_dai_ops = { + .set_fmt = pcm1681_set_dai_fmt, + .hw_params = pcm1681_hw_params, + .digital_mute = pcm1681_digital_mute, +}; + +static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("VOUT1"), +SND_SOC_DAPM_OUTPUT("VOUT2"), +SND_SOC_DAPM_OUTPUT("VOUT3"), +SND_SOC_DAPM_OUTPUT("VOUT4"), +SND_SOC_DAPM_OUTPUT("VOUT5"), +SND_SOC_DAPM_OUTPUT("VOUT6"), +SND_SOC_DAPM_OUTPUT("VOUT7"), +SND_SOC_DAPM_OUTPUT("VOUT8"), +}; + +static const struct snd_soc_dapm_route pcm1681_dapm_routes[] = { + { "VOUT1", NULL, "Playback" }, + { "VOUT2", NULL, "Playback" }, + { "VOUT3", NULL, "Playback" }, + { "VOUT4", NULL, "Playback" }, + { "VOUT5", NULL, "Playback" }, + { "VOUT6", NULL, "Playback" }, + { "VOUT7", NULL, "Playback" }, + { "VOUT8", NULL, "Playback" }, +}; + +static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1); + +static const struct snd_kcontrol_new pcm1681_controls[] = { + SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume", + PCM1681_ATT_CONTROL(1), PCM1681_ATT_CONTROL(2), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume", + PCM1681_ATT_CONTROL(3), PCM1681_ATT_CONTROL(4), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume", + PCM1681_ATT_CONTROL(5), PCM1681_ATT_CONTROL(6), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 7/8 Playback Volume", + PCM1681_ATT_CONTROL(7), PCM1681_ATT_CONTROL(8), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + pcm1681_get_deemph, pcm1681_put_deemph), +}; + +static struct snd_soc_dai_driver pcm1681_dai = { + .name = "pcm1681-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = PCM1681_PCM_RATES, + .formats = PCM1681_PCM_FORMATS, + }, + .ops = &pcm1681_dai_ops, +}; + +#ifdef CONFIG_OF +static const struct of_device_id pcm1681_dt_ids[] = { + { .compatible = "ti,pcm1681", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm1681_dt_ids); +#endif + +static const struct regmap_config pcm1681_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ARRAY_SIZE(pcm1681_reg_defaults) + 1, + .reg_defaults = pcm1681_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm1681_reg_defaults), + .writeable_reg = pcm1681_writeable_reg, + .readable_reg = pcm1681_accessible_reg, +}; + +static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = { + .controls = pcm1681_controls, + .num_controls = ARRAY_SIZE(pcm1681_controls), + .dapm_widgets = pcm1681_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets), + .dapm_routes = pcm1681_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes), +}; + +static const struct i2c_device_id pcm1681_i2c_id[] = { + {"pcm1681", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id); + +static int pcm1681_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct pcm1681_private *priv; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&client->dev, "Failed to create regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, priv); + + return snd_soc_register_codec(&client->dev, &soc_codec_dev_pcm1681, + &pcm1681_dai, 1); +} + +static int pcm1681_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver pcm1681_i2c_driver = { + .driver = { + .name = "pcm1681", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pcm1681_dt_ids), + }, + .id_table = pcm1681_i2c_id, + .probe = pcm1681_i2c_probe, + .remove = pcm1681_i2c_remove, +}; + +module_i2c_driver(pcm1681_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments PCM1681 ALSA SoC Codec Driver"); +MODULE_AUTHOR("Marek Belisko <marek.belisko@streamunlimited.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c new file mode 100644 index 00000000000..2a8eccf64c7 --- /dev/null +++ b/sound/soc/codecs/pcm1792a.c @@ -0,0 +1,257 @@ +/* + * PCM1792A ASoC codec driver + * + * Copyright (c) Amarula Solutions B.V. 2013 + * + * Michael Trimarchi <michael@amarulasolutions.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/spi/spi.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <linux/of_device.h> + +#include "pcm1792a.h" + +#define PCM1792A_DAC_VOL_LEFT 0x10 +#define PCM1792A_DAC_VOL_RIGHT 0x11 +#define PCM1792A_FMT_CONTROL 0x12 +#define PCM1792A_SOFT_MUTE PCM1792A_FMT_CONTROL + +#define PCM1792A_FMT_MASK 0x70 +#define PCM1792A_FMT_SHIFT 4 +#define PCM1792A_MUTE_MASK 0x01 +#define PCM1792A_MUTE_SHIFT 0 +#define PCM1792A_ATLD_ENABLE (1 << 7) + +static const struct reg_default pcm1792a_reg_defaults[] = { + { 0x10, 0xff }, + { 0x11, 0xff }, + { 0x12, 0x50 }, + { 0x13, 0x00 }, + { 0x14, 0x00 }, + { 0x15, 0x01 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, +}; + +static bool pcm1792a_accessible_reg(struct device *dev, unsigned int reg) +{ + return reg >= 0x10 && reg <= 0x17; +} + +static bool pcm1792a_writeable_reg(struct device *dev, unsigned register reg) +{ + bool accessible; + + accessible = pcm1792a_accessible_reg(dev, reg); + + return accessible && reg != 0x16 && reg != 0x17; +} + +struct pcm1792a_private { + struct regmap *regmap; + unsigned int format; + unsigned int rate; +}; + +static int pcm1792a_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec); + + priv->format = format; + + return 0; +} + +static int pcm1792a_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = regmap_update_bits(priv->regmap, PCM1792A_SOFT_MUTE, + PCM1792A_MUTE_MASK, !!mute); + if (ret < 0) + return ret; + + return 0; +} + +static int pcm1792a_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec); + int val = 0, ret; + int pcm_format = params_format(params); + + priv->rate = params_rate(params); + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + if (pcm_format == SNDRV_PCM_FORMAT_S24_LE || + pcm_format == SNDRV_PCM_FORMAT_S32_LE) + val = 0x02; + else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE) + val = 0x00; + break; + case SND_SOC_DAIFMT_I2S: + if (pcm_format == SNDRV_PCM_FORMAT_S24_LE || + pcm_format == SNDRV_PCM_FORMAT_S32_LE) + val = 0x05; + else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE) + val = 0x04; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + val = val << PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE; + + ret = regmap_update_bits(priv->regmap, PCM1792A_FMT_CONTROL, + PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE, val); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_soc_dai_ops pcm1792a_dai_ops = { + .set_fmt = pcm1792a_set_dai_fmt, + .hw_params = pcm1792a_hw_params, + .digital_mute = pcm1792a_digital_mute, +}; + +static const DECLARE_TLV_DB_SCALE(pcm1792a_dac_tlv, -12000, 50, 1); + +static const struct snd_kcontrol_new pcm1792a_controls[] = { + SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT, + PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0, + pcm1792a_dac_tlv), +}; + +static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("IOUTL+"), +SND_SOC_DAPM_OUTPUT("IOUTL-"), +SND_SOC_DAPM_OUTPUT("IOUTR+"), +SND_SOC_DAPM_OUTPUT("IOUTR-"), +}; + +static const struct snd_soc_dapm_route pcm1792a_dapm_routes[] = { + { "IOUTL+", NULL, "Playback" }, + { "IOUTL-", NULL, "Playback" }, + { "IOUTR+", NULL, "Playback" }, + { "IOUTR-", NULL, "Playback" }, +}; + +static struct snd_soc_dai_driver pcm1792a_dai = { + .name = "pcm1792a-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = PCM1792A_RATES, + .formats = PCM1792A_FORMATS, }, + .ops = &pcm1792a_dai_ops, +}; + +static const struct of_device_id pcm1792a_of_match[] = { + { .compatible = "ti,pcm1792a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm1792a_of_match); + +static const struct regmap_config pcm1792a_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 24, + .reg_defaults = pcm1792a_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm1792a_reg_defaults), + .writeable_reg = pcm1792a_writeable_reg, + .readable_reg = pcm1792a_accessible_reg, +}; + +static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = { + .controls = pcm1792a_controls, + .num_controls = ARRAY_SIZE(pcm1792a_controls), + .dapm_widgets = pcm1792a_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1792a_dapm_widgets), + .dapm_routes = pcm1792a_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1792a_dapm_routes), +}; + +static int pcm1792a_spi_probe(struct spi_device *spi) +{ + struct pcm1792a_private *pcm1792a; + int ret; + + pcm1792a = devm_kzalloc(&spi->dev, sizeof(struct pcm1792a_private), + GFP_KERNEL); + if (!pcm1792a) + return -ENOMEM; + + spi_set_drvdata(spi, pcm1792a); + + pcm1792a->regmap = devm_regmap_init_spi(spi, &pcm1792a_regmap); + if (IS_ERR(pcm1792a->regmap)) { + ret = PTR_ERR(pcm1792a->regmap); + dev_err(&spi->dev, "Failed to register regmap: %d\n", ret); + return ret; + } + + return snd_soc_register_codec(&spi->dev, + &soc_codec_dev_pcm1792a, &pcm1792a_dai, 1); +} + +static int pcm1792a_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static const struct spi_device_id pcm1792a_spi_ids[] = { + { "pcm1792a", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids); + +static struct spi_driver pcm1792a_codec_driver = { + .driver = { + .name = "pcm1792a", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pcm1792a_of_match), + }, + .id_table = pcm1792a_spi_ids, + .probe = pcm1792a_spi_probe, + .remove = pcm1792a_spi_remove, +}; + +module_spi_driver(pcm1792a_codec_driver); + +MODULE_DESCRIPTION("ASoC PCM1792A driver"); +MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm1792a.h b/sound/soc/codecs/pcm1792a.h new file mode 100644 index 00000000000..7a83d1fc102 --- /dev/null +++ b/sound/soc/codecs/pcm1792a.h @@ -0,0 +1,26 @@ +/* + * definitions for PCM1792A + * + * Copyright 2013 Amarula Solutions + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PCM1792A_H__ +#define __PCM1792A_H__ + +#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S16_LE) + +#endif diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index f2a6282b41f..b6618c4a759 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -28,7 +28,54 @@ #include "pcm3008.h" -#define PCM3008_VERSION "0.2" +static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = w->codec; + struct pcm3008_setup_data *setup = codec->dev->platform_data; + + gpio_set_value_cansleep(setup->pdda_pin, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = w->codec; + struct pcm3008_setup_data *setup = codec->dev->platform_data; + + gpio_set_value_cansleep(setup->pdad_pin, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("VINL"), +SND_SOC_DAPM_INPUT("VINR"), + +SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_OUTPUT("VOUTL"), +SND_SOC_DAPM_OUTPUT("VOUTR"), +}; + +static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = { + { "PCM3008 Capture", NULL, "ADC" }, + { "ADC", NULL, "VINL" }, + { "ADC", NULL, "VINR" }, + + { "DAC", NULL, "PCM3008 Playback" }, + { "VOUTL", NULL, "DAC" }, + { "VOUTR", NULL, "DAC" }, +}; #define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000) @@ -51,20 +98,20 @@ static struct snd_soc_dai_driver pcm3008_dai = { }, }; -static void pcm3008_gpio_free(struct pcm3008_setup_data *setup) -{ - gpio_free(setup->dem0_pin); - gpio_free(setup->dem1_pin); - gpio_free(setup->pdad_pin); - gpio_free(setup->pdda_pin); -} +static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = { + .dapm_widgets = pcm3008_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets), + .dapm_routes = pcm3008_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes), +}; -static int pcm3008_soc_probe(struct snd_soc_codec *codec) +static int pcm3008_codec_probe(struct platform_device *pdev) { - struct pcm3008_setup_data *setup = codec->dev->platform_data; - int ret = 0; + struct pcm3008_setup_data *setup = pdev->dev.platform_data; + int ret; - printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION); + if (!setup) + return -EINVAL; /* DEM1 DEM0 DE-EMPHASIS_MODE * Low Low De-emphasis 44.1 kHz ON @@ -74,83 +121,29 @@ static int pcm3008_soc_probe(struct snd_soc_codec *codec) */ /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */ - ret = gpio_request(setup->dem0_pin, "codec_dem0"); - if (ret == 0) - ret = gpio_direction_output(setup->dem0_pin, 1); + ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin, + GPIOF_OUT_INIT_HIGH, "codec_dem0"); if (ret != 0) - goto gpio_err; + return ret; /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */ - ret = gpio_request(setup->dem1_pin, "codec_dem1"); - if (ret == 0) - ret = gpio_direction_output(setup->dem1_pin, 0); + ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin, + GPIOF_OUT_INIT_LOW, "codec_dem1"); if (ret != 0) - goto gpio_err; + return ret; /* Configure PDAD GPIO. */ - ret = gpio_request(setup->pdad_pin, "codec_pdad"); - if (ret == 0) - ret = gpio_direction_output(setup->pdad_pin, 1); + ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin, + GPIOF_OUT_INIT_LOW, "codec_pdad"); if (ret != 0) - goto gpio_err; + return ret; /* Configure PDDA GPIO. */ - ret = gpio_request(setup->pdda_pin, "codec_pdda"); - if (ret == 0) - ret = gpio_direction_output(setup->pdda_pin, 1); + ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin, + GPIOF_OUT_INIT_LOW, "codec_pdda"); if (ret != 0) - goto gpio_err; - - return ret; - -gpio_err: - pcm3008_gpio_free(setup); + return ret; - return ret; -} - -static int pcm3008_soc_remove(struct snd_soc_codec *codec) -{ - struct pcm3008_setup_data *setup = codec->dev->platform_data; - - pcm3008_gpio_free(setup); - return 0; -} - -#ifdef CONFIG_PM -static int pcm3008_soc_suspend(struct snd_soc_codec *codec) -{ - struct pcm3008_setup_data *setup = codec->dev->platform_data; - - gpio_set_value(setup->pdad_pin, 0); - gpio_set_value(setup->pdda_pin, 0); - - return 0; -} - -static int pcm3008_soc_resume(struct snd_soc_codec *codec) -{ - struct pcm3008_setup_data *setup = codec->dev->platform_data; - - gpio_set_value(setup->pdad_pin, 1); - gpio_set_value(setup->pdda_pin, 1); - - return 0; -} -#else -#define pcm3008_soc_suspend NULL -#define pcm3008_soc_resume NULL -#endif - -static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = { - .probe = pcm3008_soc_probe, - .remove = pcm3008_soc_remove, - .suspend = pcm3008_soc_suspend, - .resume = pcm3008_soc_resume, -}; - -static int pcm3008_codec_probe(struct platform_device *pdev) -{ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm3008, &pcm3008_dai, 1); } @@ -158,6 +151,7 @@ static int pcm3008_codec_probe(struct platform_device *pdev) static int pcm3008_codec_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); + return 0; } diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 6c8a9e7bee2..1f4093f3f3a 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -153,6 +153,8 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w, static int power_vag_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + const u32 mask = SGTL5000_DAC_POWERUP | SGTL5000_ADC_POWERUP; + switch (event) { case SND_SOC_DAPM_POST_PMU: snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, @@ -160,9 +162,17 @@ static int power_vag_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_PRE_PMD: - snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, - SGTL5000_VAG_POWERUP, 0); - msleep(400); + /* + * Don't clear VAG_POWERUP, when both DAC and ADC are + * operational to prevent inadvertently starving the + * other one of them. + */ + if ((snd_soc_read(w->codec, SGTL5000_CHIP_ANA_POWER) & + mask) != mask) { + snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, 0); + msleep(400); + } break; default: break; @@ -388,7 +398,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0), SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)", SGTL5000_CHIP_ANA_ADC_CTRL, - 8, 2, 0, capture_6db_attenuate), + 8, 1, 0, capture_6db_attenuate), SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0), SOC_DOUBLE_TLV("Headphone Playback Volume", @@ -644,16 +654,19 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP); + + /* if using pll, clk_ctrl must be set after pll power up */ + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); } else { + /* otherwise, clk_ctrl must be set before pll power down */ + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); + /* power down pll */ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, 0); } - /* if using pll, clk_ctrl must be set after pll power up */ - snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); - return 0; } @@ -1470,6 +1483,7 @@ static struct snd_soc_codec_driver sgtl5000_driver = { static const struct regmap_config sgtl5000_regmap = { .reg_bits = 16, .val_bits = 16, + .reg_stride = 2, .max_register = SGTL5000_MAX_REG_OFFSET, .volatile_reg = sgtl5000_volatile, diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index 73e205c892a..38f3b105c17 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -102,6 +102,16 @@ static int si476x_codec_write(struct snd_soc_codec *codec, return err; } +static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +}; + +static const struct snd_soc_dapm_route si476x_dapm_routes[] = { + { "Capture", NULL, "LOUT" }, + { "Capture", NULL, "ROUT" }, +}; + static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { @@ -260,6 +270,10 @@ static struct snd_soc_codec_driver soc_codec_dev_si476x = { .probe = si476x_codec_probe, .read = si476x_codec_read, .write = si476x_codec_write, + .dapm_widgets = si476x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets), + .dapm_routes = si476x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes), }; static int si476x_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c index e9d7881ed2c..e3501f40c7b 100644 --- a/sound/soc/codecs/spdif_receiver.c +++ b/sound/soc/codecs/spdif_receiver.c @@ -23,11 +23,26 @@ #include <sound/initval.h> #include <linux/of.h> +static const struct snd_soc_dapm_widget dir_widgets[] = { + SND_SOC_DAPM_INPUT("spdif-in"), +}; + +static const struct snd_soc_dapm_route dir_routes[] = { + { "Capture", NULL, "spdif-in" }, +}; + #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) -static struct snd_soc_codec_driver soc_codec_spdif_dir; +static struct snd_soc_codec_driver soc_codec_spdif_dir = { + .dapm_widgets = dir_widgets, + .num_dapm_widgets = ARRAY_SIZE(dir_widgets), + .dapm_routes = dir_routes, + .num_dapm_routes = ARRAY_SIZE(dir_routes), +}; static struct snd_soc_dai_driver dir_stub_dai = { .name = "dir-hifi", diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c index 18280499fd5..a078aa31052 100644 --- a/sound/soc/codecs/spdif_transmitter.c +++ b/sound/soc/codecs/spdif_transmitter.c @@ -25,10 +25,24 @@ #define DRV_NAME "spdif-dit" #define STUB_RATES SNDRV_PCM_RATE_8000_96000 -#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) +static const struct snd_soc_dapm_widget dit_widgets[] = { + SND_SOC_DAPM_OUTPUT("spdif-out"), +}; + +static const struct snd_soc_dapm_route dit_routes[] = { + { "spdif-out", NULL, "Playback" }, +}; -static struct snd_soc_codec_driver soc_codec_spdif_dit; +static struct snd_soc_codec_driver soc_codec_spdif_dit = { + .dapm_widgets = dit_widgets, + .num_dapm_widgets = ARRAY_SIZE(dit_widgets), + .dapm_routes = dit_routes, + .num_dapm_routes = ARRAY_SIZE(dit_routes), +}; static struct snd_soc_dai_driver dit_stub_dai = { .name = "dit-hifi", diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index cfb55fe35e9..06edb396e73 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -363,16 +363,18 @@ static void sta32x_watchdog(struct work_struct *work) } if (!sta32x->shutdown) - schedule_delayed_work(&sta32x->watchdog_work, - round_jiffies_relative(HZ)); + queue_delayed_work(system_power_efficient_wq, + &sta32x->watchdog_work, + round_jiffies_relative(HZ)); } static void sta32x_watchdog_start(struct sta32x_priv *sta32x) { if (sta32x->pdata->needs_esd_watchdog) { sta32x->shutdown = 0; - schedule_delayed_work(&sta32x->watchdog_work, - round_jiffies_relative(HZ)); + queue_delayed_work(system_power_efficient_wq, + &sta32x->watchdog_work, + round_jiffies_relative(HZ)); } } diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index b1f6982c7c9..7b8f3d965f4 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -29,7 +29,7 @@ MODULE_LICENSE("GPL"); /* AIC26 driver private data */ struct aic26 { struct spi_device *spi; - struct snd_soc_codec codec; + struct snd_soc_codec *codec; int master; int datfm; int mclk; @@ -119,6 +119,22 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg, return 0; } +static const struct snd_soc_dapm_widget tlv320aic26_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("MICIN"), +SND_SOC_DAPM_INPUT("AUX"), + +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +}; + +static const struct snd_soc_dapm_route tlv320aic26_dapm_routes[] = { + { "Capture", NULL, "MICIN" }, + { "Capture", NULL, "AUX" }, + + { "HPL", NULL, "Playback" }, + { "HPR", NULL, "Playback" }, +}; + /* --------------------------------------------------------------------- * Digital Audio Interface Operations */ @@ -174,9 +190,9 @@ static int aic26_hw_params(struct snd_pcm_substream *substream, dev_dbg(&aic26->spi->dev, "Setting PLLM to %d.%04d\n", jval, dval); qval = 0; reg = 0x8000 | qval << 11 | pval << 8 | jval << 2; - aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg); + snd_soc_write(codec, AIC26_REG_PLL_PROG1, reg); reg = dval << 2; - aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg); + snd_soc_write(codec, AIC26_REG_PLL_PROG2, reg); /* Audio Control 3 (master mode, fsref rate) */ reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3); @@ -185,13 +201,13 @@ static int aic26_hw_params(struct snd_pcm_substream *substream, reg |= 0x0800; if (fsref == 48000) reg |= 0x2000; - aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg); + snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg); /* Audio Control 1 (FSref divisor) */ reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1); reg &= ~0x0fff; reg |= wlen | aic26->datfm | (divisor << 3) | divisor; - aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg); + snd_soc_write(codec, AIC26_REG_AUDIO_CTRL1, reg); return 0; } @@ -212,7 +228,7 @@ static int aic26_mute(struct snd_soc_dai *dai, int mute) reg |= 0x8080; else reg &= ~0x8080; - aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg); + snd_soc_write(codec, AIC26_REG_DAC_GAIN, reg); return 0; } @@ -330,7 +346,7 @@ static ssize_t aic26_keyclick_show(struct device *dev, struct aic26 *aic26 = dev_get_drvdata(dev); int val, amp, freq, len; - val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2); + val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2); amp = (val >> 12) & 0x7; freq = (125 << ((val >> 8) & 0x7)) >> 1; len = 2 * (1 + ((val >> 4) & 0xf)); @@ -346,9 +362,9 @@ static ssize_t aic26_keyclick_set(struct device *dev, struct aic26 *aic26 = dev_get_drvdata(dev); int val; - val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2); + val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2); val |= 0x8000; - aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val); + snd_soc_write(aic26->codec, AIC26_REG_AUDIO_CTRL2, val); return count; } @@ -360,25 +376,26 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set); */ static int aic26_probe(struct snd_soc_codec *codec) { + struct aic26 *aic26 = dev_get_drvdata(codec->dev); int ret, err, i, reg; - dev_info(codec->dev, "Probing AIC26 SoC CODEC driver\n"); + aic26->codec = codec; /* Reset the codec to power on defaults */ - aic26_reg_write(codec, AIC26_REG_RESET, 0xBB00); + snd_soc_write(codec, AIC26_REG_RESET, 0xBB00); /* Power up CODEC */ - aic26_reg_write(codec, AIC26_REG_POWER_CTRL, 0); + snd_soc_write(codec, AIC26_REG_POWER_CTRL, 0); /* Audio Control 3 (master mode, fsref rate) */ - reg = aic26_reg_read(codec, AIC26_REG_AUDIO_CTRL3); + reg = snd_soc_read(codec, AIC26_REG_AUDIO_CTRL3); reg &= ~0xf800; reg |= 0x0800; /* set master mode */ - aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg); + snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg); /* Fill register cache */ for (i = 0; i < codec->driver->reg_cache_size; i++) - aic26_reg_read(codec, i); + snd_soc_read(codec, i); /* Register the sysfs files for debugging */ /* Create SysFS files */ @@ -401,6 +418,10 @@ static struct snd_soc_codec_driver aic26_soc_codec_dev = { .write = aic26_reg_write, .reg_cache_size = AIC26_NUM_REGS, .reg_word_size = sizeof(u16), + .dapm_widgets = tlv320aic26_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets), + .dapm_routes = tlv320aic26_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes), }; /* --------------------------------------------------------------------- diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 17df4e32fea..2ed57d4aa44 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -338,18 +338,6 @@ static inline int aic32x4_get_divs(int mclk, int rate) return -EINVAL; } -static int aic32x4_add_widgets(struct snd_soc_codec *codec) -{ - snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets, - ARRAY_SIZE(aic32x4_dapm_widgets)); - - snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes, - ARRAY_SIZE(aic32x4_dapm_routes)); - - snd_soc_dapm_new_widgets(&codec->dapm); - return 0; -} - static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { @@ -683,9 +671,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec) } aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_codec_controls(codec, aic32x4_snd_controls, - ARRAY_SIZE(aic32x4_snd_controls)); - aic32x4_add_widgets(codec); /* * Workaround: for an unknown reason, the ADC needs to be powered up @@ -714,6 +699,13 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { .suspend = aic32x4_suspend, .resume = aic32x4_resume, .set_bias_level = aic32x4_set_bias_level, + + .controls = aic32x4_snd_controls, + .num_controls = ARRAY_SIZE(aic32x4_snd_controls), + .dapm_widgets = aic32x4_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), + .dapm_routes = aic32x4_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), }; static int aic32x4_i2c_probe(struct i2c_client *i2c, diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index fec0db04262..6e3f269243e 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1474,6 +1474,7 @@ static const struct i2c_device_id aic3x_i2c_id[] = { { "tlv320aic3x", AIC3X_MODEL_3X }, { "tlv320aic33", AIC3X_MODEL_33 }, { "tlv320aic3007", AIC3X_MODEL_3007 }, + { "tlv320aic3106", AIC3X_MODEL_3X }, { } }; MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); @@ -1564,6 +1565,9 @@ static int aic3x_i2c_remove(struct i2c_client *client) #if defined(CONFIG_OF) static const struct of_device_id tlv320aic3x_of_match[] = { { .compatible = "ti,tlv320aic3x", }, + { .compatible = "ti,tlv320aic33" }, + { .compatible = "ti,tlv320aic3007" }, + { .compatible = "ti,tlv320aic3106" }, {}, }; MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match); diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 8e6e5b01602..1e3884d6b3f 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -137,8 +137,6 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { /* codec private data */ struct twl4030_priv { - struct snd_soc_codec codec; - unsigned int codec_powered; /* reference counts of AIF/APLL users */ diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index d6c5bf14179..3c79dbb6c32 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -429,7 +429,8 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data) struct snd_soc_codec *codec = data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - schedule_delayed_work(&priv->hs_jack.work, msecs_to_jiffies(200)); + queue_delayed_work(system_power_efficient_wq, + &priv->hs_jack.work, msecs_to_jiffies(200)); return IRQ_HANDLED; } diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 6d0aa44c375..c94d4c1e3da 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -325,7 +325,6 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai, static int uda134x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u8 reg; struct uda134x_platform_data *pd = codec->control_data; int i; u8 *cache = codec->reg_cache; @@ -334,23 +333,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - /* ADC, DAC on */ - switch (pd->model) { - case UDA134X_UDA1340: - case UDA134X_UDA1344: - case UDA134X_UDA1345: - reg = uda134x_read_reg_cache(codec, UDA134X_DATA011); - uda134x_write(codec, UDA134X_DATA011, reg | 0x03); - break; - case UDA134X_UDA1341: - reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1); - uda134x_write(codec, UDA134X_STATUS1, reg | 0x03); - break; - default: - printk(KERN_ERR "UDA134X SoC codec: " - "unsupported model %d\n", pd->model); - return -EINVAL; - } break; case SND_SOC_BIAS_PREPARE: /* power on */ @@ -362,23 +344,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, } break; case SND_SOC_BIAS_STANDBY: - /* ADC, DAC power off */ - switch (pd->model) { - case UDA134X_UDA1340: - case UDA134X_UDA1344: - case UDA134X_UDA1345: - reg = uda134x_read_reg_cache(codec, UDA134X_DATA011); - uda134x_write(codec, UDA134X_DATA011, reg & ~(0x03)); - break; - case UDA134X_UDA1341: - reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1); - uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03)); - break; - default: - printk(KERN_ERR "UDA134X SoC codec: " - "unsupported model %d\n", pd->model); - return -EINVAL; - } break; case SND_SOC_BIAS_OFF: /* power off */ @@ -450,6 +415,37 @@ SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), }; +/* UDA1341 has the DAC/ADC power down in STATUS1 */ +static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0), + SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0), +}; + +/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */ +static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0), + SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0), +}; + +/* Common DAPM widgets */ +static const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("VINL1"), + SND_SOC_DAPM_INPUT("VINR1"), + SND_SOC_DAPM_INPUT("VINL2"), + SND_SOC_DAPM_INPUT("VINR2"), + SND_SOC_DAPM_OUTPUT("VOUTL"), + SND_SOC_DAPM_OUTPUT("VOUTR"), +}; + +static const struct snd_soc_dapm_route uda134x_dapm_routes[] = { + { "ADC", NULL, "VINL1" }, + { "ADC", NULL, "VINR1" }, + { "ADC", NULL, "VINL2" }, + { "ADC", NULL, "VINR2" }, + { "VOUTL", NULL, "DAC" }, + { "VOUTR", NULL, "DAC" }, +}; + static const struct snd_soc_dai_ops uda134x_dai_ops = { .startup = uda134x_startup, .shutdown = uda134x_shutdown, @@ -485,6 +481,8 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) { struct uda134x_priv *uda134x; struct uda134x_platform_data *pd = codec->card->dev->platform_data; + const struct snd_soc_dapm_widget *widgets; + unsigned num_widgets; int ret; @@ -526,6 +524,22 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) else uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + if (pd->model == UDA134X_UDA1341) { + widgets = uda1341_dapm_widgets; + num_widgets = ARRAY_SIZE(uda1341_dapm_widgets); + } else { + widgets = uda1340_dapm_widgets; + num_widgets = ARRAY_SIZE(uda1340_dapm_widgets); + } + + ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets); + if (ret) { + printk(KERN_ERR "%s failed to register dapm controls: %d", + __func__, ret); + kfree(uda134x); + return ret; + } + switch (pd->model) { case UDA134X_UDA1340: case UDA134X_UDA1344: @@ -599,6 +613,10 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = { .read = uda134x_read_reg_cache, .write = uda134x_write, .set_bias_level = uda134x_set_bias_level, + .dapm_widgets = uda134x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets), + .dapm_routes = uda134x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes), }; static int uda134x_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 54cd3da09ab..b7ab2ef567c 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -290,6 +290,18 @@ static const struct snd_kcontrol_new wl1273_controls[] = { snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put), }; +static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route wl1273_dapm_routes[] = { + { "Capture", NULL, "RX" }, + + { "TX", NULL, "Playback" }, +}; + static int wl1273_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -483,6 +495,11 @@ static int wl1273_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_wl1273 = { .probe = wl1273_probe, .remove = wl1273_remove, + + .dapm_widgets = wl1273_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets), + .dapm_routes = wl1273_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes), }; static int wl1273_platform_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 10adc4145d4..d5ebcb00019 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -420,7 +420,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec) xfer->codec = codec; list_add_tail(&xfer->list, &xfer_list); - out = kzalloc(len, GFP_KERNEL); + out = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!out) { dev_err(codec->dev, "Failed to allocate RX buffer\n"); @@ -429,7 +429,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec) } xfer->t.rx_buf = out; - img = kzalloc(len, GFP_KERNEL); + img = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!img) { dev_err(codec->dev, "Failed to allocate image buffer\n"); @@ -523,14 +523,14 @@ static int wm0010_stage2_load(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size); /* Copy to local buffer first as vmalloc causes problems for dma */ - img = kzalloc(fw->size, GFP_KERNEL); + img = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); if (!img) { dev_err(codec->dev, "Failed to allocate image buffer\n"); ret = -ENOMEM; goto abort2; } - out = kzalloc(fw->size, GFP_KERNEL); + out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); if (!out) { dev_err(codec->dev, "Failed to allocate output buffer\n"); ret = -ENOMEM; @@ -670,14 +670,14 @@ static int wm0010_boot(struct snd_soc_codec *codec) ret = -ENOMEM; len = pll_rec.length + 8; - out = kzalloc(len, GFP_KERNEL); + out = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!out) { dev_err(codec->dev, "Failed to allocate RX buffer\n"); goto abort; } - img_swap = kzalloc(len, GFP_KERNEL); + img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!img_swap) { dev_err(codec->dev, "Failed to allocate image buffer\n"); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 282fd232cdf..8bbddc151aa 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -998,6 +998,8 @@ SND_SOC_DAPM_INPUT("IN2R"), SND_SOC_DAPM_INPUT("IN3L"), SND_SOC_DAPM_INPUT("IN3R"), +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), + SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, 0, NULL, 0, arizona_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | @@ -1421,9 +1423,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "Tone Generator 1", NULL, "TONE" }, { "Tone Generator 2", NULL, "TONE" }, - { "Mic Mute Mixer", NULL, "Noise Mixer" }, - { "Mic Mute Mixer", NULL, "Mic Mixer" }, - { "AIF1 Capture", NULL, "AIF1TX1" }, { "AIF1 Capture", NULL, "AIF1TX2" }, { "AIF1 Capture", NULL, "AIF1TX3" }, @@ -1499,23 +1498,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "IN3L PGA", NULL, "IN3L" }, { "IN3R PGA", NULL, "IN3R" }, - { "ASRC1L", NULL, "ASRC1L Input" }, - { "ASRC1R", NULL, "ASRC1R Input" }, - { "ASRC2L", NULL, "ASRC2L Input" }, - { "ASRC2R", NULL, "ASRC2R Input" }, - - { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" }, - { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" }, - - { "ISRC1INT1", NULL, "ISRC1INT1 Input" }, - { "ISRC1INT2", NULL, "ISRC1INT2 Input" }, - - { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" }, - { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" }, - - { "ISRC2INT1", NULL, "ISRC2INT1 Input" }, - { "ISRC2INT2", NULL, "ISRC2INT2 Input" }, - ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), @@ -1567,22 +1549,25 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), - ARIZONA_MUX_ROUTES("ASRC1L"), - ARIZONA_MUX_ROUTES("ASRC1R"), - ARIZONA_MUX_ROUTES("ASRC2L"), - ARIZONA_MUX_ROUTES("ASRC2R"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"), - ARIZONA_MUX_ROUTES("ISRC1INT1"), - ARIZONA_MUX_ROUTES("ISRC1INT2"), + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), - ARIZONA_MUX_ROUTES("ISRC1DEC1"), - ARIZONA_MUX_ROUTES("ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), - ARIZONA_MUX_ROUTES("ISRC2INT1"), - ARIZONA_MUX_ROUTES("ISRC2INT2"), + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), - ARIZONA_MUX_ROUTES("ISRC2DEC1"), - ARIZONA_MUX_ROUTES("ISRC2DEC2"), + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), ARIZONA_DSP_ROUTES("DSP1"), @@ -1614,6 +1599,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "SPKDAT1R", NULL, "OUT5R" }, { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, }; static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source, @@ -1781,6 +1769,7 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) return ret; arizona_init_spk(codec); + arizona_init_gpio(codec); snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 2e7cb4ba161..bbd64384ca1 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -58,14 +58,10 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0) static const struct snd_kcontrol_new wm5110_snd_controls[] = { -SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1_OSR_SHIFT, 1, 0), -SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2_OSR_SHIFT, 1, 0), -SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3_OSR_SHIFT, 1, 0), -SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL, - ARIZONA_IN4_OSR_SHIFT, 1, 0), +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), +SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]), +SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]), SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), @@ -432,6 +428,9 @@ SND_SOC_DAPM_INPUT("IN3R"), SND_SOC_DAPM_INPUT("IN4L"), SND_SOC_DAPM_INPUT("IN4R"), +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, 0, NULL, 0, arizona_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | @@ -842,9 +841,6 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "Tone Generator 1", NULL, "TONE" }, { "Tone Generator 2", NULL, "TONE" }, - { "Mic Mute Mixer", NULL, "Noise Mixer" }, - { "Mic Mute Mixer", NULL, "Mic Mixer" }, - { "AIF1 Capture", NULL, "AIF1TX1" }, { "AIF1 Capture", NULL, "AIF1TX2" }, { "AIF1 Capture", NULL, "AIF1TX3" }, @@ -979,10 +975,13 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), - ARIZONA_MUX_ROUTES("ASRC1L"), - ARIZONA_MUX_ROUTES("ASRC1R"), - ARIZONA_MUX_ROUTES("ASRC2L"), - ARIZONA_MUX_ROUTES("ASRC2R"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), { "HPOUT1L", NULL, "OUT1L" }, { "HPOUT1R", NULL, "OUT1R" }, @@ -1006,6 +1005,11 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "SPKDAT2R", NULL, "OUT6R" }, { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, + { "DRC2 Signal Activity", NULL, "DRC2L" }, + { "DRC2 Signal Activity", NULL, "DRC2R" }, }; static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source, @@ -1170,6 +1174,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) return ret; arizona_init_spk(codec); + arizona_init_gpio(codec); snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 0e8b3aaf6c8..af1318ddb06 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1301,7 +1301,8 @@ static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data) if (device_may_wakeup(wm8350->dev)) pm_wakeup_event(wm8350->dev, 250); - schedule_delayed_work(&priv->hpl.work, msecs_to_jiffies(200)); + queue_delayed_work(system_power_efficient_wq, + &priv->hpl.work, msecs_to_jiffies(200)); return IRQ_HANDLED; } @@ -1318,7 +1319,8 @@ static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data) if (device_may_wakeup(wm8350->dev)) pm_wakeup_event(wm8350->dev, 250); - schedule_delayed_work(&priv->hpr.work, msecs_to_jiffies(200)); + queue_delayed_work(system_power_efficient_wq, + &priv->hpr.work, msecs_to_jiffies(200)); return IRQ_HANDLED; } diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c index 462f5e4d5c0..7b1a6d5c11c 100644 --- a/sound/soc/codecs/wm8727.c +++ b/sound/soc/codecs/wm8727.c @@ -23,6 +23,16 @@ #include <sound/initval.h> #include <sound/soc.h> +static const struct snd_soc_dapm_widget wm8727_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("VOUTL"), +SND_SOC_DAPM_OUTPUT("VOUTR"), +}; + +static const struct snd_soc_dapm_route wm8727_dapm_routes[] = { + { "VOUTL", NULL, "Playback" }, + { "VOUTR", NULL, "Playback" }, +}; + /* * Note this is a simple chip with no configuration interface, sample rate is * determined automatically by examining the Master clock and Bit clock ratios @@ -43,7 +53,12 @@ static struct snd_soc_dai_driver wm8727_dai = { }, }; -static struct snd_soc_codec_driver soc_codec_dev_wm8727; +static struct snd_soc_codec_driver soc_codec_dev_wm8727 = { + .dapm_widgets = wm8727_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets), + .dapm_routes = wm8727_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes), +}; static int wm8727_probe(struct platform_device *pdev) { diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 5276062d6c7..456bb8c6d75 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -45,6 +45,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { struct wm8731_priv { struct regmap *regmap; struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; + const struct snd_pcm_hw_constraint_list *constraints; unsigned int sysclk; int sysclk_type; int playback_fs; @@ -290,6 +291,36 @@ static const struct _coeff_div coeff_div[] = { {12000000, 88200, 136, 0xf, 0x1, 0x1}, }; +/* rates constraints */ +static const unsigned int wm8731_rates_12000000[] = { + 8000, 32000, 44100, 48000, 96000, 88200, +}; + +static const unsigned int wm8731_rates_12288000_18432000[] = { + 8000, 32000, 48000, 96000, +}; + +static const unsigned int wm8731_rates_11289600_16934400[] = { + 8000, 44100, 88200, +}; + +static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = { + .list = wm8731_rates_12000000, + .count = ARRAY_SIZE(wm8731_rates_12000000), +}; + +static const +struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = { + .list = wm8731_rates_12288000_18432000, + .count = ARRAY_SIZE(wm8731_rates_12288000_18432000), +}; + +static const +struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = { + .list = wm8731_rates_11289600_16934400, + .count = ARRAY_SIZE(wm8731_rates_11289600_16934400), +}; + static inline int get_coeff(int mclk, int rate) { int i; @@ -362,17 +393,26 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, } switch (freq) { - case 11289600: + case 0: + wm8731->constraints = NULL; + break; case 12000000: + wm8731->constraints = &wm8731_constraints_12000000; + break; case 12288000: - case 16934400: case 18432000: - wm8731->sysclk = freq; + wm8731->constraints = &wm8731_constraints_12288000_18432000; + break; + case 16934400: + case 11289600: + wm8731->constraints = &wm8731_constraints_11289600_16934400; break; default: return -EINVAL; } + wm8731->sysclk = freq; + snd_soc_dapm_sync(&codec->dapm); return 0; @@ -475,12 +515,26 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, return 0; } +static int wm8731_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(dai->codec); + + if (wm8731->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + wm8731->constraints); + + return 0; +} + #define WM8731_RATES SNDRV_PCM_RATE_8000_96000 #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) static const struct snd_soc_dai_ops wm8731_dai_ops = { + .startup = wm8731_startup, .hw_params = wm8731_hw_params, .digital_mute = wm8731_mute, .set_sysclk = wm8731_set_dai_sysclk, diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 0a4ab4c423d..d96ebf52d95 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1456,8 +1456,9 @@ static int wm8753_resume(struct snd_soc_codec *codec) if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); codec->dapm.bias_level = SND_SOC_BIAS_ON; - schedule_delayed_work(&codec->dapm.delayed_work, - msecs_to_jiffies(caps_charge)); + queue_delayed_work(system_power_efficient_wq, + &codec->dapm.delayed_work, + msecs_to_jiffies(caps_charge)); } return 0; diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c index f1fdbf63abb..8092495605c 100644 --- a/sound/soc/codecs/wm8782.c +++ b/sound/soc/codecs/wm8782.c @@ -26,6 +26,16 @@ #include <sound/initval.h> #include <sound/soc.h> +static const struct snd_soc_dapm_widget wm8782_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), +}; + +static const struct snd_soc_dapm_route wm8782_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, +}; + static struct snd_soc_dai_driver wm8782_dai = { .name = "wm8782", .capture = { @@ -40,7 +50,12 @@ static struct snd_soc_dai_driver wm8782_dai = { }, }; -static struct snd_soc_codec_driver soc_codec_dev_wm8782; +static struct snd_soc_codec_driver soc_codec_dev_wm8782 = { + .dapm_widgets = wm8782_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets), + .dapm_routes = wm8782_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes), +}; static int wm8782_probe(struct platform_device *pdev) { diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 4c9fb142cb2..4dfa8dceeab 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1012,7 +1012,7 @@ static const struct soc_enum liner_enum = SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 0, 2, out_mux_text); static const struct snd_kcontrol_new liner_mux = - SOC_DAPM_ENUM("LINEL Mux", liner_enum); + SOC_DAPM_ENUM("LINER Mux", liner_enum); static const char *sidetone_text[] = { "None", "Left", "Right" @@ -1202,7 +1202,6 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec) break; } - snd_soc_dapm_new_widgets(dapm); return 0; } diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 0a4ffdd1d2a..f156010e52b 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -263,8 +263,8 @@ SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0), SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0), -SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH, - 0, 127, 0), +SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC, + 0, 255, 0, adc_tlv), SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume", WM8960_BYPASS1, 4, 7, 1, bypass_tlv), @@ -857,9 +857,9 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, if (pll_div.k) { reg |= 0x20; - snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f); - snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff); - snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff); + snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff); + snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff); + snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff); } snd_soc_write(codec, WM8960_PLL1, reg); diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index e2de9ecfd64..11d80f3b613 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2621,8 +2621,6 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, wm8962->sysclk_rate = freq; - wm8962_configure_bclk(codec); - return 0; } @@ -3046,8 +3044,9 @@ static irqreturn_t wm8962_irq(int irq, void *data) pm_wakeup_event(dev, 300); - schedule_delayed_work(&wm8962->mic_work, - msecs_to_jiffies(250)); + queue_delayed_work(system_power_efficient_wq, + &wm8962->mic_work, + msecs_to_jiffies(250)); } return IRQ_HANDLED; @@ -3175,7 +3174,7 @@ static ssize_t wm8962_beep_set(struct device *dev, long int time; int ret; - ret = strict_strtol(buf, 10, &time); + ret = kstrtol(buf, 10, &time); if (ret != 0) return ret; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index eee2a01f269..86426a117b0 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -819,8 +819,9 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w, * don't want false reports. */ if (wm8994->jackdet && !wm8994->clk_has_run) { - schedule_delayed_work(&wm8994->jackdet_bootstrap, - msecs_to_jiffies(1000)); + queue_delayed_work(system_power_efficient_wq, + &wm8994->jackdet_bootstrap, + msecs_to_jiffies(1000)); wm8994->clk_has_run = true; } break; @@ -1432,7 +1433,7 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, #define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \ - snd_soc_get_volsw, wm8994_put_class_w) + snd_soc_dapm_get_volsw, wm8994_put_class_w) static int wm8994_put_class_w(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -3485,7 +3486,8 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data) pm_wakeup_event(codec->dev, 300); - schedule_delayed_work(&priv->mic_work, msecs_to_jiffies(250)); + queue_delayed_work(system_power_efficient_wq, + &priv->mic_work, msecs_to_jiffies(250)); return IRQ_HANDLED; } @@ -3573,8 +3575,9 @@ static void wm8958_mic_id(void *data, u16 status) /* If nothing present then clear our statuses */ dev_dbg(codec->dev, "Detected open circuit\n"); - schedule_delayed_work(&wm8994->open_circuit_work, - msecs_to_jiffies(2500)); + queue_delayed_work(system_power_efficient_wq, + &wm8994->open_circuit_work, + msecs_to_jiffies(2500)); return; } @@ -3688,8 +3691,9 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) WM1811_JACKDET_DB, 0); delay = control->pdata.micdet_delay; - schedule_delayed_work(&wm8994->mic_work, - msecs_to_jiffies(delay)); + queue_delayed_work(system_power_efficient_wq, + &wm8994->mic_work, + msecs_to_jiffies(delay)); } else { dev_dbg(codec->dev, "Jack not detected\n"); @@ -3934,8 +3938,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) id_delay = wm8994->wm8994->pdata.mic_id_delay; if (wm8994->mic_detecting) - schedule_delayed_work(&wm8994->mic_complete_work, - msecs_to_jiffies(id_delay)); + queue_delayed_work(system_power_efficient_wq, + &wm8994->mic_complete_work, + msecs_to_jiffies(id_delay)); else wm8958_button_det(codec, reg); @@ -4008,9 +4013,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->micdet_irq = control->pdata.micdet_irq; - pm_runtime_enable(codec->dev); - pm_runtime_idle(codec->dev); - /* By default use idle_bias_off, will override for WM8994 */ codec->dapm.idle_bias_off = 1; @@ -4383,8 +4385,6 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); - pm_runtime_disable(codec->dev); - for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, &wm8994->fll_locked[i]); @@ -4443,6 +4443,9 @@ static int wm8994_probe(struct platform_device *pdev) wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent); + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994, wm8994_dai, ARRAY_SIZE(wm8994_dai)); } @@ -4450,6 +4453,8 @@ static int wm8994_probe(struct platform_device *pdev) static int wm8994_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return 0; } diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c new file mode 100644 index 00000000000..6ec3de3efa4 --- /dev/null +++ b/sound/soc/codecs/wm8997.c @@ -0,0 +1,1175 @@ +/* + * wm8997.c -- WM8997 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/registers.h> + +#include "arizona.h" +#include "wm8997.h" + +struct wm8997_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +static const struct reg_default wm8997_sysclk_reva_patch[] = { + { 0x301D, 0x7B15 }, + { 0x301B, 0x0050 }, + { 0x305D, 0x7B17 }, + { 0x305B, 0x0050 }, + { 0x3001, 0x08FE }, + { 0x3003, 0x00F4 }, + { 0x3041, 0x08FF }, + { 0x3043, 0x0005 }, + { 0x3020, 0x0225 }, + { 0x3021, 0x0A00 }, + { 0x3022, 0xE24D }, + { 0x3023, 0x0800 }, + { 0x3024, 0xE24D }, + { 0x3025, 0xF000 }, + { 0x3060, 0x0226 }, + { 0x3061, 0x0A00 }, + { 0x3062, 0xE252 }, + { 0x3063, 0x0800 }, + { 0x3064, 0xE252 }, + { 0x3065, 0xF000 }, + { 0x3116, 0x022B }, + { 0x3117, 0xFA00 }, + { 0x3110, 0x246C }, + { 0x3111, 0x0A03 }, + { 0x3112, 0x246E }, + { 0x3113, 0x0A03 }, + { 0x3114, 0x2470 }, + { 0x3115, 0x0A03 }, + { 0x3126, 0x246C }, + { 0x3127, 0x0A02 }, + { 0x3128, 0x246E }, + { 0x3129, 0x0A02 }, + { 0x312A, 0x2470 }, + { 0x312B, 0xFA02 }, + { 0x3125, 0x0800 }, +}; + +static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + struct regmap *regmap = codec->control_data; + const struct reg_default *patch = NULL; + int i, patch_size; + + switch (arizona->rev) { + case 0: + patch = wm8997_sysclk_reva_patch; + patch_size = ARRAY_SIZE(wm8997_sysclk_reva_patch); + break; + default: + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (patch) + for (i = 0; i < patch_size; i++) + regmap_write(regmap, patch[i].reg, + patch[i].def); + break; + default: + break; + } + + return 0; +} + +static const char *wm8997_osr_text[] = { + "Low power", "Normal", "High performance", +}; + +static const unsigned int wm8997_osr_val[] = { + 0x0, 0x3, 0x5, +}; + +static const struct soc_enum wm8997_hpout_osr[] = { + SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUT1_OSR_SHIFT, 0x7, 3, + wm8997_osr_text, wm8997_osr_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3_OSR_SHIFT, 0x7, 3, + wm8997_osr_text, wm8997_osr_val), +}; + +#define WM8997_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + +static const struct snd_kcontrol_new wm8997_snd_controls[] = { +SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2_OSR_SHIFT, 1, 0), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21, + ARIZONA_EQ1_ENA_MASK), +SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21, + ARIZONA_EQ2_ENA_MASK), +SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21, + ARIZONA_EQ3_ENA_MASK), +SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21, + ARIZONA_EQ4_ENA_MASK), + +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1), +SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1), + +SOC_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), + +ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L, + ARIZONA_OUT4_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L, + ARIZONA_OUT5_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_VALUE_ENUM("HPOUT1 OSR", wm8997_hpout_osr[0]), +SOC_VALUE_ENUM("EPOUT OSR", wm8997_hpout_osr[1]), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM8997_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM8997_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM8997_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM8997_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L), +WM8997_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM8997_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + +static const char *wm8997_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "EPOUT", "SPKOUT", "SPKDAT1L", "SPKDAT1R", +}; + +static const unsigned int wm8997_aec_loopback_values[] = { + 0, 1, 4, 6, 8, 9, +}; + +static const struct soc_enum wm8997_aec_loopback = + SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(wm8997_aec_loopback_texts), + wm8997_aec_loopback_texts, + wm8997_aec_loopback_values); + +static const struct snd_kcontrol_new wm8997_aec_loopback_mux = + SOC_DAPM_VALUE_ENUM("AEC Loopback", wm8997_aec_loopback); + +static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, + 0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1, + ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8997_aec_loopback_mux), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), +ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"), +ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(Mic, "Mic"), +ARIZONA_MIXER_WIDGETS(Noise, "Noise"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"), +ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), + +ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("EPOUTN"), +SND_SOC_DAPM_OUTPUT("EPOUTP"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" } + +static const struct snd_soc_dapm_route wm8997_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT3L", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDD" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + + ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + ARIZONA_MIXER_ROUTES("EQ3", "EQ3"), + ARIZONA_MIXER_ROUTES("EQ4", "EQ4"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + { "AEC Loopback", "HPOUT1L", "OUT1L" }, + { "AEC Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC Loopback", "EPOUT", "OUT3L" }, + { "EPOUTN", NULL, "OUT3L" }, + { "EPOUTP", NULL, "OUT3L" }, + + { "AEC Loopback", "SPKOUT", "OUT4L" }, + { "SPKOUTN", NULL, "OUT4L" }, + { "SPKOUTP", NULL, "OUT4L" }, + + { "AEC Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "MICSUPP", NULL, "SYSCLK" }, +}; + +static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm8997_priv *wm8997 = snd_soc_codec_get_drvdata(codec); + + switch (fll_id) { + case WM8997_FLL1: + return arizona_set_fll(&wm8997->fll[0], source, Fref, Fout); + case WM8997_FLL2: + return arizona_set_fll(&wm8997->fll[1], source, Fref, Fout); + case WM8997_FLL1_REFCLK: + return arizona_set_fll_refclk(&wm8997->fll[0], source, Fref, + Fout); + case WM8997_FLL2_REFCLK: + return arizona_set_fll_refclk(&wm8997->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +#define WM8997_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8997_dai[] = { + { + .name = "wm8997-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "wm8997-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "wm8997-slim1", + .id = 3, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8997-slim2", + .id = 4, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8997-slim3", + .id = 5, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, +}; + +static int wm8997_codec_probe(struct snd_soc_codec *codec) +{ + struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + codec->control_data = priv->core.arizona->regmap; + + ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP); + if (ret != 0) + return ret; + + arizona_init_spk(codec); + + snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); + + priv->core.arizona->dapm = &codec->dapm; + + return 0; +} + +static int wm8997_codec_remove(struct snd_soc_codec *codec) +{ + struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); + + priv->core.arizona->dapm = NULL; + + return 0; +} + +#define WM8997_DIG_VU 0x0200 + +static unsigned int wm8997_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static struct snd_soc_codec_driver soc_codec_dev_wm8997 = { + .probe = wm8997_codec_probe, + .remove = wm8997_codec_remove, + + .idle_bias_off = true, + + .set_sysclk = arizona_set_sysclk, + .set_pll = wm8997_set_fll, + + .controls = wm8997_snd_controls, + .num_controls = ARRAY_SIZE(wm8997_snd_controls), + .dapm_widgets = wm8997_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets), + .dapm_routes = wm8997_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes), +}; + +static int wm8997_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm8997_priv *wm8997; + int i; + + wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv), + GFP_KERNEL); + if (wm8997 == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, wm8997); + + wm8997->core.arizona = arizona; + wm8997->core.num_inputs = 4; + + for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++) + wm8997->fll[i].vco_mult = 1; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm8997->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm8997->fll[1]); + + /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */ + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2, + ARIZONA_SAMPLE_RATE_2_MASK, 0x11); + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3, + ARIZONA_SAMPLE_RATE_3_MASK, 0x12); + + for (i = 0; i < ARRAY_SIZE(wm8997_dai); i++) + arizona_init_dai(&wm8997->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm8997_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm8997_digital_vu[i], + WM8997_DIG_VU, WM8997_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997, + wm8997_dai, ARRAY_SIZE(wm8997_dai)); +} + +static int wm8997_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver wm8997_codec_driver = { + .driver = { + .name = "wm8997-codec", + .owner = THIS_MODULE, + }, + .probe = wm8997_probe, + .remove = wm8997_remove, +}; + +module_platform_driver(wm8997_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8997 driver"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8997-codec"); diff --git a/sound/soc/codecs/wm8997.h b/sound/soc/codecs/wm8997.h new file mode 100644 index 00000000000..5e91c6a7d56 --- /dev/null +++ b/sound/soc/codecs/wm8997.h @@ -0,0 +1,23 @@ +/* + * wm8997.h -- WM8997 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8997_H +#define _WM8997_H + +#include "arizona.h" + +#define WM8997_FLL1 1 +#define WM8997_FLL2 2 +#define WM8997_FLL1_REFCLK 3 +#define WM8997_FLL2_REFCLK 4 + +#endif diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 05252ac936a..b38f3506418 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -225,15 +225,8 @@ struct wm_coeff_ctl_ops { struct snd_ctl_elem_info *uinfo); }; -struct wm_coeff { - struct device *dev; - struct list_head ctl_list; - struct regmap *regmap; -}; - struct wm_coeff_ctl { const char *name; - struct snd_card *card; struct wm_adsp_alg_region region; struct wm_coeff_ctl_ops ops; struct wm_adsp *adsp; @@ -378,7 +371,6 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol, static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, const void *buf, size_t len) { - struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *region = &ctl->region; const struct wm_adsp_region *mem; @@ -401,7 +393,7 @@ static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, if (!scratch) return -ENOMEM; - ret = regmap_raw_write(wm_coeff->regmap, reg, scratch, + ret = regmap_raw_write(adsp->regmap, reg, scratch, ctl->len); if (ret) { adsp_err(adsp, "Failed to write %zu bytes to %x\n", @@ -434,7 +426,6 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol, static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, void *buf, size_t len) { - struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *region = &ctl->region; const struct wm_adsp_region *mem; @@ -457,7 +448,7 @@ static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, if (!scratch) return -ENOMEM; - ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len); + ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len); if (ret) { adsp_err(adsp, "Failed to read %zu bytes from %x\n", ctl->len, reg); @@ -481,37 +472,18 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol, return 0; } -static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff, - struct wm_coeff_ctl *ctl, - const struct snd_kcontrol_new *kctl) -{ - int ret; - struct snd_kcontrol *kcontrol; - - kcontrol = snd_ctl_new1(kctl, wm_coeff); - ret = snd_ctl_add(ctl->card, kcontrol); - if (ret < 0) { - dev_err(wm_coeff->dev, "Failed to add %s: %d\n", - kctl->name, ret); - return ret; - } - ctl->kcontrol = kcontrol; - return 0; -} - struct wmfw_ctl_work { - struct wm_coeff *wm_coeff; + struct wm_adsp *adsp; struct wm_coeff_ctl *ctl; struct work_struct work; }; -static int wmfw_add_ctl(struct wm_coeff *wm_coeff, - struct wm_coeff_ctl *ctl) +static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) { struct snd_kcontrol_new *kcontrol; int ret; - if (!wm_coeff || !ctl || !ctl->name || !ctl->card) + if (!ctl || !ctl->name) return -EINVAL; kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); @@ -525,14 +497,17 @@ static int wmfw_add_ctl(struct wm_coeff *wm_coeff, kcontrol->put = wm_coeff_put; kcontrol->private_value = (unsigned long)ctl; - ret = wm_coeff_add_kcontrol(wm_coeff, - ctl, kcontrol); + ret = snd_soc_add_card_controls(adsp->card, + kcontrol, 1); if (ret < 0) goto err_kcontrol; kfree(kcontrol); - list_add(&ctl->list, &wm_coeff->ctl_list); + ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card, + ctl->name); + + list_add(&ctl->list, &adsp->ctl_list); return 0; err_kcontrol: @@ -753,13 +728,12 @@ out: return ret; } -static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff) +static int wm_coeff_init_control_caches(struct wm_adsp *adsp) { struct wm_coeff_ctl *ctl; int ret; - list_for_each_entry(ctl, &wm_coeff->ctl_list, - list) { + list_for_each_entry(ctl, &adsp->ctl_list, list) { if (!ctl->enabled || ctl->set) continue; ret = wm_coeff_read_control(ctl->kcontrol, @@ -772,13 +746,12 @@ static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff) return 0; } -static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff) +static int wm_coeff_sync_controls(struct wm_adsp *adsp) { struct wm_coeff_ctl *ctl; int ret; - list_for_each_entry(ctl, &wm_coeff->ctl_list, - list) { + list_for_each_entry(ctl, &adsp->ctl_list, list) { if (!ctl->enabled) continue; if (ctl->set) { @@ -799,15 +772,14 @@ static void wm_adsp_ctl_work(struct work_struct *work) struct wmfw_ctl_work, work); - wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl); + wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl); kfree(ctl_work); } -static int wm_adsp_create_control(struct snd_soc_codec *codec, +static int wm_adsp_create_control(struct wm_adsp *dsp, const struct wm_adsp_alg_region *region) { - struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); struct wm_coeff_ctl *ctl; struct wmfw_ctl_work *ctl_work; char *name; @@ -842,7 +814,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, snprintf(name, PAGE_SIZE, "DSP%d %s %x", dsp->num, region_name, region->alg); - list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, + list_for_each_entry(ctl, &dsp->ctl_list, list) { if (!strcmp(ctl->name, name)) { if (!ctl->enabled) @@ -866,7 +838,6 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, ctl->set = 0; ctl->ops.xget = wm_coeff_get; ctl->ops.xput = wm_coeff_put; - ctl->card = codec->card->snd_card; ctl->adsp = dsp; ctl->len = region->len; @@ -882,7 +853,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, goto err_ctl_cache; } - ctl_work->wm_coeff = dsp->wm_coeff; + ctl_work->adsp = dsp; ctl_work->ctl = ctl; INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); schedule_work(&ctl_work->work); @@ -903,7 +874,7 @@ err_name: return ret; } -static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) +static int wm_adsp_setup_algs(struct wm_adsp *dsp) { struct regmap *regmap = dsp->regmap; struct wmfw_adsp1_id_hdr adsp1_id; @@ -1091,7 +1062,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].dm); region->len -= be32_to_cpu(adsp1_alg[i].dm); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1108,7 +1079,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].zm); region->len -= be32_to_cpu(adsp1_alg[i].zm); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1137,7 +1108,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].xm); region->len -= be32_to_cpu(adsp2_alg[i].xm); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1154,7 +1125,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].ym); region->len -= be32_to_cpu(adsp2_alg[i].ym); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1171,7 +1142,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].zm); region->len -= be32_to_cpu(adsp2_alg[i].zm); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1391,6 +1362,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, int ret; int val; + dsp->card = codec->card; + switch (event) { case SND_SOC_DAPM_POST_PMU: regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, @@ -1425,7 +1398,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp, codec); + ret = wm_adsp_setup_algs(dsp); if (ret != 0) goto err; @@ -1434,12 +1407,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, goto err; /* Initialize caches for enabled and unset controls */ - ret = wm_coeff_init_control_caches(dsp->wm_coeff); + ret = wm_coeff_init_control_caches(dsp); if (ret != 0) goto err; /* Sync set controls */ - ret = wm_coeff_sync_controls(dsp->wm_coeff); + ret = wm_coeff_sync_controls(dsp); if (ret != 0) goto err; @@ -1460,10 +1433,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, 0); - list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, - list) { + list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; - } break; default: @@ -1520,6 +1491,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, unsigned int val; int ret; + dsp->card = codec->card; + switch (event) { case SND_SOC_DAPM_POST_PMU: /* @@ -1582,7 +1555,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp, codec); + ret = wm_adsp_setup_algs(dsp); if (ret != 0) goto err; @@ -1591,12 +1564,12 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, goto err; /* Initialize caches for enabled and unset controls */ - ret = wm_coeff_init_control_caches(dsp->wm_coeff); + ret = wm_coeff_init_control_caches(dsp); if (ret != 0) goto err; /* Sync set controls */ - ret = wm_coeff_sync_controls(dsp->wm_coeff); + ret = wm_coeff_sync_controls(dsp); if (ret != 0) goto err; @@ -1637,10 +1610,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ret); } - list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, - list) { + list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; - } while (!list_empty(&dsp->alg_regions)) { alg_region = list_first_entry(&dsp->alg_regions, @@ -1679,49 +1650,38 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) } INIT_LIST_HEAD(&adsp->alg_regions); - - adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff), - GFP_KERNEL); - if (!adsp->wm_coeff) - return -ENOMEM; - adsp->wm_coeff->regmap = adsp->regmap; - adsp->wm_coeff->dev = adsp->dev; - INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list); + INIT_LIST_HEAD(&adsp->ctl_list); if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { ret = PTR_ERR(adsp->dvfs); dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); - goto out_coeff; + return ret; } ret = regulator_enable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", ret); - goto out_coeff; + return ret; } ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); if (ret != 0) { dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", ret); - goto out_coeff; + return ret; } ret = regulator_disable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", ret); - goto out_coeff; + return ret; } } return 0; - -out_coeff: - kfree(adsp->wm_coeff); - return ret; } EXPORT_SYMBOL_GPL(wm_adsp2_init); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 9f922c82536..d018dea6254 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -39,6 +39,7 @@ struct wm_adsp { int type; struct device *dev; struct regmap *regmap; + struct snd_soc_card *card; int base; int sysclk_reg; @@ -57,7 +58,7 @@ struct wm_adsp { struct regulator *dvfs; - struct wm_coeff *wm_coeff; + struct list_head ctl_list; }; #define WM_ADSP1(wname, num) \ diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 70eb37a5dd1..25c31f1655f 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -421,13 +421,11 @@ static int dw_i2s_probe(struct platform_device *pdev) dw_i2s_dai, 1); if (ret != 0) { dev_err(&pdev->dev, "not able to register dai\n"); - goto err_set_drvdata; + goto err_clk_disable; } return 0; -err_set_drvdata: - dev_set_drvdata(&pdev->dev, NULL); err_clk_disable: clk_disable(dev->clk); err_clk_put: @@ -440,7 +438,6 @@ static int dw_i2s_remove(struct platform_device *pdev) struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - dev_set_drvdata(&pdev->dev, NULL); clk_put(dev->clk); diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index aa438546c91..704e246f5b1 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,6 +1,9 @@ config SND_SOC_FSL_SSI tristate +config SND_SOC_FSL_SPDIF + tristate + config SND_SOC_FSL_UTILS tristate @@ -98,7 +101,7 @@ endif # SND_POWERPC_SOC menuconfig SND_IMX_SOC tristate "SoC Audio for Freescale i.MX CPUs" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST help Say Y or M if you want to add support for codecs attached to the i.MX CPUs. @@ -109,11 +112,11 @@ config SND_SOC_IMX_SSI tristate config SND_SOC_IMX_PCM_FIQ - bool + tristate select FIQ config SND_SOC_IMX_PCM_DMA - bool + tristate select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SOC_IMX_AUDMUX @@ -175,7 +178,6 @@ config SND_SOC_IMX_WM8962 select SND_SOC_IMX_PCM_DMA select SND_SOC_IMX_AUDMUX select SND_SOC_FSL_SSI - select SND_SOC_FSL_UTILS help Say Y if you want to add support for SoC audio on an i.MX board with a wm8962 codec. @@ -187,14 +189,23 @@ config SND_SOC_IMX_SGTL5000 select SND_SOC_IMX_PCM_DMA select SND_SOC_IMX_AUDMUX select SND_SOC_FSL_SSI - select SND_SOC_FSL_UTILS help Say Y if you want to add support for SoC audio on an i.MX board with a sgtl5000 codec. +config SND_SOC_IMX_SPDIF + tristate "SoC Audio support for i.MX boards with S/PDIF" + select SND_SOC_IMX_PCM_DMA + select SND_SOC_FSL_SPDIF + select SND_SOC_SPDIF + help + SoC Audio support for i.MX boards with S/PDIF + Say Y if you want to add support for SoC audio on an i.MX board with + a S/DPDIF. + config SND_SOC_IMX_MC13783 tristate "SoC Audio support for I.MX boards with mc13783" - depends on MFD_MC13783 + depends on MFD_MC13783 && ARM select SND_SOC_IMX_SSI select SND_SOC_IMX_AUDMUX select SND_SOC_MC13783 diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index d4b4aa8b564..e2aaff717f8 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -12,9 +12,11 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale PowerPC SSI/DMA Platform Support snd-soc-fsl-ssi-objs := fsl_ssi.o +snd-soc-fsl-spdif-objs := fsl_spdif.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o +obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o @@ -43,6 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-wm8962-objs := imx-wm8962.o +snd-soc-imx-spdif-objs :=imx-spdif.o snd-soc-imx-mc13783-objs := imx-mc13783.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o @@ -51,4 +54,5 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o +obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c new file mode 100644 index 00000000000..e93dc0dfb0d --- /dev/null +++ b/sound/soc/fsl/fsl_spdif.c @@ -0,0 +1,1225 @@ +/* + * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * Based on stmp3xxx_spdif_dai.c + * Vladimir Barinov <vbarinov@embeddedalley.com> + * Copyright 2008 SigmaTel, Inc + * Copyright 2008 Embedded Alley Solutions, Inc + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/clk-private.h> +#include <linux/bitrev.h> +#include <linux/regmap.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> + +#include <sound/asoundef.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +#include "fsl_spdif.h" +#include "imx-pcm.h" + +#define FSL_SPDIF_TXFIFO_WML 0x8 +#define FSL_SPDIF_RXFIFO_WML 0x8 + +#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC) +#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV|\ + INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR |\ + INT_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED) + +/* Index list for the values that has if (DPLL Locked) condition */ +static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb }; +#define SRPC_NODPLL_START1 0x5 +#define SRPC_NODPLL_START2 0xc + +#define DEFAULT_RXCLK_SRC 1 + +/* + * SPDIF control structure + * Defines channel status, subcode and Q sub + */ +struct spdif_mixer_control { + /* spinlock to access control data */ + spinlock_t ctl_lock; + + /* IEC958 channel tx status bit */ + unsigned char ch_status[4]; + + /* User bits */ + unsigned char subcode[2 * SPDIF_UBITS_SIZE]; + + /* Q subcode part of user bits */ + unsigned char qsub[2 * SPDIF_QSUB_SIZE]; + + /* Buffer offset for U/Q */ + u32 upos; + u32 qpos; + + /* Ready buffer index of the two buffers */ + u32 ready_buf; +}; + +struct fsl_spdif_priv { + struct spdif_mixer_control fsl_spdif_control; + struct snd_soc_dai_driver cpu_dai_drv; + struct platform_device *pdev; + struct regmap *regmap; + bool dpll_locked; + u8 txclk_div[SPDIF_TXRATE_MAX]; + u8 txclk_src[SPDIF_TXRATE_MAX]; + u8 rxclk_src; + struct clk *txclk[SPDIF_TXRATE_MAX]; + struct clk *rxclk; + struct snd_dmaengine_dai_dma_data dma_params_tx; + struct snd_dmaengine_dai_dma_data dma_params_rx; + + /* The name space will be allocated dynamically */ + char name[0]; +}; + + +/* DPLL locked and lock loss interrupt handler */ +static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv) +{ + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u32 locked; + + regmap_read(regmap, REG_SPDIF_SRPC, &locked); + locked &= SRPC_DPLL_LOCKED; + + dev_dbg(&pdev->dev, "isr: Rx dpll %s \n", + locked ? "locked" : "loss lock"); + + spdif_priv->dpll_locked = locked ? true : false; +} + +/* Receiver found illegal symbol interrupt handler */ +static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv) +{ + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + + dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n"); + + if (!spdif_priv->dpll_locked) { + /* DPLL unlocked seems no audio stream */ + regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0); + } +} + +/* U/Q Channel receive register full */ +static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name) +{ + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u32 *pos, size, val, reg; + + switch (name) { + case 'U': + pos = &ctrl->upos; + size = SPDIF_UBITS_SIZE; + reg = REG_SPDIF_SRU; + break; + case 'Q': + pos = &ctrl->qpos; + size = SPDIF_QSUB_SIZE; + reg = REG_SPDIF_SRQ; + break; + default: + dev_err(&pdev->dev, "unsupported channel name\n"); + return; + } + + dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name); + + if (*pos >= size * 2) { + *pos = 0; + } else if (unlikely((*pos % size) + 3 > size)) { + dev_err(&pdev->dev, "User bit receivce buffer overflow\n"); + return; + } + + regmap_read(regmap, reg, &val); + ctrl->subcode[*pos++] = val >> 16; + ctrl->subcode[*pos++] = val >> 8; + ctrl->subcode[*pos++] = val; +} + +/* U/Q Channel sync found */ +static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv) +{ + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct platform_device *pdev = spdif_priv->pdev; + + dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n"); + + /* U/Q buffer reset */ + if (ctrl->qpos == 0) + return; + + /* Set ready to this buffer */ + ctrl->ready_buf = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE + 1; +} + +/* U/Q Channel framing error */ +static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv) +{ + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u32 val; + + dev_dbg(&pdev->dev, "isr: U/Q Channel framing error\n"); + + /* Read U/Q data to clear the irq and do buffer reset */ + regmap_read(regmap, REG_SPDIF_SRU, &val); + regmap_read(regmap, REG_SPDIF_SRQ, &val); + + /* Drop this U/Q buffer */ + ctrl->ready_buf = 0; + ctrl->upos = 0; + ctrl->qpos = 0; +} + +/* Get spdif interrupt status and clear the interrupt */ +static u32 spdif_intr_status_clear(struct fsl_spdif_priv *spdif_priv) +{ + struct regmap *regmap = spdif_priv->regmap; + u32 val, val2; + + regmap_read(regmap, REG_SPDIF_SIS, &val); + regmap_read(regmap, REG_SPDIF_SIE, &val2); + + regmap_write(regmap, REG_SPDIF_SIC, val & val2); + + return val; +} + +static irqreturn_t spdif_isr(int irq, void *devid) +{ + struct fsl_spdif_priv *spdif_priv = (struct fsl_spdif_priv *)devid; + struct platform_device *pdev = spdif_priv->pdev; + u32 sis; + + sis = spdif_intr_status_clear(spdif_priv); + + if (sis & INT_DPLL_LOCKED) + spdif_irq_dpll_lock(spdif_priv); + + if (sis & INT_TXFIFO_UNOV) + dev_dbg(&pdev->dev, "isr: Tx FIFO under/overrun\n"); + + if (sis & INT_TXFIFO_RESYNC) + dev_dbg(&pdev->dev, "isr: Tx FIFO resync\n"); + + if (sis & INT_CNEW) + dev_dbg(&pdev->dev, "isr: cstatus new\n"); + + if (sis & INT_VAL_NOGOOD) + dev_dbg(&pdev->dev, "isr: validity flag no good\n"); + + if (sis & INT_SYM_ERR) + spdif_irq_sym_error(spdif_priv); + + if (sis & INT_BIT_ERR) + dev_dbg(&pdev->dev, "isr: receiver found parity bit error\n"); + + if (sis & INT_URX_FUL) + spdif_irq_uqrx_full(spdif_priv, 'U'); + + if (sis & INT_URX_OV) + dev_dbg(&pdev->dev, "isr: U Channel receive register overrun\n"); + + if (sis & INT_QRX_FUL) + spdif_irq_uqrx_full(spdif_priv, 'Q'); + + if (sis & INT_QRX_OV) + dev_dbg(&pdev->dev, "isr: Q Channel receive register overrun\n"); + + if (sis & INT_UQ_SYNC) + spdif_irq_uq_sync(spdif_priv); + + if (sis & INT_UQ_ERR) + spdif_irq_uq_err(spdif_priv); + + if (sis & INT_RXFIFO_UNOV) + dev_dbg(&pdev->dev, "isr: Rx FIFO under/overrun\n"); + + if (sis & INT_RXFIFO_RESYNC) + dev_dbg(&pdev->dev, "isr: Rx FIFO resync\n"); + + if (sis & INT_LOSS_LOCK) + spdif_irq_dpll_lock(spdif_priv); + + /* FIXME: Write Tx FIFO to clear TxEm */ + if (sis & INT_TX_EM) + dev_dbg(&pdev->dev, "isr: Tx FIFO empty\n"); + + /* FIXME: Read Rx FIFO to clear RxFIFOFul */ + if (sis & INT_RXFIFO_FUL) + dev_dbg(&pdev->dev, "isr: Rx FIFO full\n"); + + return IRQ_HANDLED; +} + +static int spdif_softreset(struct fsl_spdif_priv *spdif_priv) +{ + struct regmap *regmap = spdif_priv->regmap; + u32 val, cycle = 1000; + + regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET); + + /* + * RESET bit would be cleared after finishing its reset procedure, + * which typically lasts 8 cycles. 1000 cycles will keep it safe. + */ + do { + regmap_read(regmap, REG_SPDIF_SCR, &val); + } while ((val & SCR_SOFT_RESET) && cycle--); + + if (cycle) + return 0; + else + return -EBUSY; +} + +static void spdif_set_cstatus(struct spdif_mixer_control *ctrl, + u8 mask, u8 cstatus) +{ + ctrl->ch_status[3] &= ~mask; + ctrl->ch_status[3] |= cstatus & mask; +} + +static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv) +{ + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u32 ch_status; + + ch_status = (bitrev8(ctrl->ch_status[0]) << 16) | + (bitrev8(ctrl->ch_status[1]) << 8) | + bitrev8(ctrl->ch_status[2]); + regmap_write(regmap, REG_SPDIF_STCSCH, ch_status); + + dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status); + + ch_status = bitrev8(ctrl->ch_status[3]) << 16; + regmap_write(regmap, REG_SPDIF_STCSCL, ch_status); + + dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status); +} + +/* Set SPDIF PhaseConfig register for rx clock */ +static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv, + enum spdif_gainsel gainsel, int dpll_locked) +{ + struct regmap *regmap = spdif_priv->regmap; + u8 clksrc = spdif_priv->rxclk_src; + + if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX) + return -EINVAL; + + regmap_update_bits(regmap, REG_SPDIF_SRPC, + SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK, + SRPC_CLKSRC_SEL_SET(clksrc) | SRPC_GAINSEL_SET(gainsel)); + + return 0; +} + +static int spdif_set_sample_rate(struct snd_pcm_substream *substream, + int sample_rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + unsigned long csfs = 0; + u32 stc, mask, rate; + u8 clk, div; + int ret; + + switch (sample_rate) { + case 32000: + rate = SPDIF_TXRATE_32000; + csfs = IEC958_AES3_CON_FS_32000; + break; + case 44100: + rate = SPDIF_TXRATE_44100; + csfs = IEC958_AES3_CON_FS_44100; + break; + case 48000: + rate = SPDIF_TXRATE_48000; + csfs = IEC958_AES3_CON_FS_48000; + break; + default: + dev_err(&pdev->dev, "unsupported sample rate %d\n", sample_rate); + return -EINVAL; + } + + clk = spdif_priv->txclk_src[rate]; + if (clk >= STC_TXCLK_SRC_MAX) { + dev_err(&pdev->dev, "tx clock source is out of range\n"); + return -EINVAL; + } + + div = spdif_priv->txclk_div[rate]; + if (div == 0) { + dev_err(&pdev->dev, "the divisor can't be zero\n"); + return -EINVAL; + } + + /* + * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block + * will divide by (div). So request 64 * fs * (div+1) which will + * get rounded. + */ + ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1)); + if (ret) { + dev_err(&pdev->dev, "failed to set tx clock rate\n"); + return ret; + } + + dev_dbg(&pdev->dev, "expected clock rate = %d\n", + (64 * sample_rate * div)); + dev_dbg(&pdev->dev, "actual clock rate = %ld\n", + clk_get_rate(spdif_priv->txclk[rate])); + + /* set fs field in consumer channel status */ + spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs); + + /* select clock source and divisor */ + stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div); + mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK; + regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc); + + dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate); + + return 0; +} + +int fsl_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct platform_device *pdev = spdif_priv->pdev; + struct regmap *regmap = spdif_priv->regmap; + u32 scr, mask, i; + int ret; + + /* Reset module and interrupts only for first initialization */ + if (!cpu_dai->active) { + ret = spdif_softreset(spdif_priv); + if (ret) { + dev_err(&pdev->dev, "failed to soft reset\n"); + return ret; + } + + /* Disable all the interrupts */ + regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + scr = SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_CTRL_NORMAL | + SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP | + SCR_TXFIFO_FSEL_IF8; + mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | + SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | + SCR_TXFIFO_FSEL_MASK; + for (i = 0; i < SPDIF_TXRATE_MAX; i++) + clk_prepare_enable(spdif_priv->txclk[i]); + } else { + scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC; + mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| + SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; + clk_prepare_enable(spdif_priv->rxclk); + } + regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); + + /* Power up SPDIF module */ + regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0); + + return 0; +} + +static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 scr, mask, i; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + scr = 0; + mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | + SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | + SCR_TXFIFO_FSEL_MASK; + for (i = 0; i < SPDIF_TXRATE_MAX; i++) + clk_disable_unprepare(spdif_priv->txclk[i]); + } else { + scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO; + mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| + SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; + clk_disable_unprepare(spdif_priv->rxclk); + } + regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); + + /* Power down SPDIF module only if tx&rx are both inactive */ + if (!cpu_dai->active) { + spdif_intr_status_clear(spdif_priv); + regmap_update_bits(regmap, REG_SPDIF_SCR, + SCR_LOW_POWER, SCR_LOW_POWER); + } +} + +static int fsl_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct platform_device *pdev = spdif_priv->pdev; + u32 sample_rate = params_rate(params); + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = spdif_set_sample_rate(substream, sample_rate); + if (ret) { + dev_err(&pdev->dev, "%s: set sample rate failed: %d\n", + __func__, sample_rate); + return ret; + } + spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK, + IEC958_AES3_CON_CLOCK_1000PPM); + spdif_write_channel_status(spdif_priv); + } else { + /* Setup rx clock source */ + ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1); + } + + return ret; +} + +static int fsl_spdif_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE; + u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + regmap_update_bits(regmap, REG_SPDIF_SIE, intr, intr); + regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, dmaen); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0); + regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +struct snd_soc_dai_ops fsl_spdif_dai_ops = { + .startup = fsl_spdif_startup, + .hw_params = fsl_spdif_hw_params, + .trigger = fsl_spdif_trigger, + .shutdown = fsl_spdif_shutdown, +}; + + +/* + * FSL SPDIF IEC958 controller(mixer) functions + * + * Channel status get/put control + * User bit value get/put control + * Valid bit value get control + * DPLL lock status get control + * User bit sync mode selection control + */ + +static int fsl_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + + uvalue->value.iec958.status[0] = ctrl->ch_status[0]; + uvalue->value.iec958.status[1] = ctrl->ch_status[1]; + uvalue->value.iec958.status[2] = ctrl->ch_status[2]; + uvalue->value.iec958.status[3] = ctrl->ch_status[3]; + + return 0; +} + +static int fsl_spdif_pb_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + + ctrl->ch_status[0] = uvalue->value.iec958.status[0]; + ctrl->ch_status[1] = uvalue->value.iec958.status[1]; + ctrl->ch_status[2] = uvalue->value.iec958.status[2]; + ctrl->ch_status[3] = uvalue->value.iec958.status[3]; + + spdif_write_channel_status(spdif_priv); + + return 0; +} + +/* Get channel status from SPDIF_RX_CCHAN register */ +static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 cstatus, val; + + regmap_read(regmap, REG_SPDIF_SIS, &val); + if (!(val & INT_CNEW)) { + return -EAGAIN; + } + + regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus); + ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF; + ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF; + ucontrol->value.iec958.status[2] = cstatus & 0xFF; + + regmap_read(regmap, REG_SPDIF_SRCSL, &cstatus); + ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF; + ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF; + ucontrol->value.iec958.status[5] = cstatus & 0xFF; + + /* Clear intr */ + regmap_write(regmap, REG_SPDIF_SIC, INT_CNEW); + + return 0; +} + +/* + * Get User bits (subcode) from chip value which readed out + * in UChannel register. + */ +static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ctrl->ctl_lock, flags); + if (ctrl->ready_buf) { + int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; + memcpy(&ucontrol->value.iec958.subcode[0], + &ctrl->subcode[idx], SPDIF_UBITS_SIZE); + } else { + ret = -EAGAIN; + } + spin_unlock_irqrestore(&ctrl->ctl_lock, flags); + + return ret; +} + +/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */ +static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = SPDIF_QSUB_SIZE; + + return 0; +} + +/* Get Q subcode from chip value which readed out in QChannel register */ +static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ctrl->ctl_lock, flags); + if (ctrl->ready_buf) { + int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; + memcpy(&ucontrol->value.bytes.data[0], + &ctrl->qsub[idx], SPDIF_QSUB_SIZE); + } else { + ret = -EAGAIN; + } + spin_unlock_irqrestore(&ctrl->ctl_lock, flags); + + return ret; +} + +/* Valid bit infomation */ +static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +/* Get valid good bit from interrupt status register */ +static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val; + + val = regmap_read(regmap, REG_SPDIF_SIS, &val); + ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0; + regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD); + + return 0; +} + +/* DPLL lock infomation */ +static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 16000; + uinfo->value.integer.max = 96000; + + return 0; +} + +static u32 gainsel_multi[GAINSEL_MULTI_MAX] = { + 24, 16, 12, 8, 6, 4, 3, +}; + +/* Get RX data clock rate given the SPDIF bus_clk */ +static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv, + enum spdif_gainsel gainsel) +{ + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u64 tmpval64, busclk_freq = 0; + u32 freqmeas, phaseconf; + u8 clksrc; + + regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas); + regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf); + + clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf; + if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) { + /* Get bus clock from system */ + busclk_freq = clk_get_rate(spdif_priv->rxclk); + } + + /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */ + tmpval64 = (u64) busclk_freq * freqmeas; + do_div(tmpval64, gainsel_multi[gainsel] * 1024); + do_div(tmpval64, 128 * 1024); + + dev_dbg(&pdev->dev, "FreqMeas: %d\n", freqmeas); + dev_dbg(&pdev->dev, "BusclkFreq: %lld\n", busclk_freq); + dev_dbg(&pdev->dev, "RxRate: %lld\n", tmpval64); + + return (int)tmpval64; +} + +/* + * Get DPLL lock or not info from stable interrupt status register. + * User application must use this control to get locked, + * then can do next PCM operation + */ +static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL); + + if (spdif_priv->dpll_locked) + ucontrol->value.integer.value[0] = rate; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +/* User bit sync mode info */ +static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +/* + * User bit sync mode: + * 1 CD User channel subcode + * 0 Non-CD data + */ +static int fsl_spdif_usync_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val; + + regmap_read(regmap, REG_SPDIF_SRCD, &val); + ucontrol->value.integer.value[0] = (val & SRCD_CD_USER) != 0; + + return 0; +} + +/* + * User bit sync mode: + * 1 CD User channel subcode + * 0 Non-CD data + */ +static int fsl_spdif_usync_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET; + + regmap_update_bits(regmap, REG_SPDIF_SRCD, SRCD_CD_USER, val); + + return 0; +} + +/* FSL SPDIF IEC958 controller defines */ +static struct snd_kcontrol_new fsl_spdif_ctrls[] = { + /* Status cchanel controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_WRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_info, + .get = fsl_spdif_pb_get, + .put = fsl_spdif_pb_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_info, + .get = fsl_spdif_capture_get, + }, + /* User bits controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Subcode Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_info, + .get = fsl_spdif_subcode_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Q-subcode Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_qinfo, + .get = fsl_spdif_qget, + }, + /* Valid bit error controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 V-Bit Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_vbit_info, + .get = fsl_spdif_vbit_get, + }, + /* DPLL lock info get controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "RX Sample Rate", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_rxrate_info, + .get = fsl_spdif_rxrate_get, + }, + /* User bit sync mode set/get controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 USyncMode CDText", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_WRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_usync_info, + .get = fsl_spdif_usync_get, + .put = fsl_spdif_usync_put, + }, +}; + +static int fsl_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &spdif_private->dma_params_tx; + dai->capture_dma_data = &spdif_private->dma_params_rx; + + snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls)); + + return 0; +} + +struct snd_soc_dai_driver fsl_spdif_dai = { + .probe = &fsl_spdif_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = FSL_SPDIF_RATES_PLAYBACK, + .formats = FSL_SPDIF_FORMATS_PLAYBACK, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = FSL_SPDIF_RATES_CAPTURE, + .formats = FSL_SPDIF_FORMATS_CAPTURE, + }, + .ops = &fsl_spdif_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_spdif_component = { + .name = "fsl-spdif", +}; + +/* FSL SPDIF REGMAP */ + +static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_SPDIF_SCR: + case REG_SPDIF_SRCD: + case REG_SPDIF_SRPC: + case REG_SPDIF_SIE: + case REG_SPDIF_SIS: + case REG_SPDIF_SRL: + case REG_SPDIF_SRR: + case REG_SPDIF_SRCSH: + case REG_SPDIF_SRCSL: + case REG_SPDIF_SRU: + case REG_SPDIF_SRQ: + case REG_SPDIF_STCSCH: + case REG_SPDIF_STCSCL: + case REG_SPDIF_SRFM: + case REG_SPDIF_STC: + return true; + default: + return false; + }; +} + +static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_SPDIF_SCR: + case REG_SPDIF_SRCD: + case REG_SPDIF_SRPC: + case REG_SPDIF_SIE: + case REG_SPDIF_SIC: + case REG_SPDIF_STL: + case REG_SPDIF_STR: + case REG_SPDIF_STCSCH: + case REG_SPDIF_STCSCL: + case REG_SPDIF_STC: + return true; + default: + return false; + }; +} + +static const struct regmap_config fsl_spdif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = REG_SPDIF_STC, + .readable_reg = fsl_spdif_readable_reg, + .writeable_reg = fsl_spdif_writeable_reg, +}; + +static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, + struct clk *clk, u64 savesub, + enum spdif_txrate index) +{ + const u32 rate[] = { 32000, 44100, 48000 }; + u64 rate_ideal, rate_actual, sub; + u32 div, arate; + + for (div = 1; div <= 128; div++) { + rate_ideal = rate[index] * (div + 1) * 64; + rate_actual = clk_round_rate(clk, rate_ideal); + + arate = rate_actual / 64; + arate /= div; + + if (arate == rate[index]) { + /* We are lucky */ + savesub = 0; + spdif_priv->txclk_div[index] = div; + break; + } else if (arate / rate[index] == 1) { + /* A little bigger than expect */ + sub = (arate - rate[index]) * 100000; + do_div(sub, rate[index]); + if (sub < savesub) { + savesub = sub; + spdif_priv->txclk_div[index] = div; + } + } else if (rate[index] / arate == 1) { + /* A little smaller than expect */ + sub = (rate[index] - arate) * 100000; + do_div(sub, rate[index]); + if (sub < savesub) { + savesub = sub; + spdif_priv->txclk_div[index] = div; + } + } + } + + return savesub; +} + +static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, + enum spdif_txrate index) +{ + const u32 rate[] = { 32000, 44100, 48000 }; + struct platform_device *pdev = spdif_priv->pdev; + struct device *dev = &pdev->dev; + u64 savesub = 100000, ret; + struct clk *clk; + char tmp[16]; + int i; + + for (i = 0; i < STC_TXCLK_SRC_MAX; i++) { + sprintf(tmp, "rxtx%d", i); + clk = devm_clk_get(&pdev->dev, tmp); + if (IS_ERR(clk)) { + dev_err(dev, "no rxtx%d clock in devicetree\n", i); + return PTR_ERR(clk); + } + if (!clk_get_rate(clk)) + continue; + + ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index); + if (savesub == ret) + continue; + + savesub = ret; + spdif_priv->txclk[index] = clk; + spdif_priv->txclk_src[index] = i; + + /* To quick catch a divisor, we allow a 0.1% deviation */ + if (savesub < 100) + break; + } + + dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate", + spdif_priv->txclk_src[index], rate[index]); + dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate", + spdif_priv->txclk_div[index], rate[index]); + + return 0; +} + +static int fsl_spdif_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_spdif_priv *spdif_priv; + struct spdif_mixer_control *ctrl; + struct resource *res; + void __iomem *regs; + int irq, ret, i; + + if (!np) + return -ENODEV; + + spdif_priv = devm_kzalloc(&pdev->dev, + sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1, + GFP_KERNEL); + if (!spdif_priv) + return -ENOMEM; + + strcpy(spdif_priv->name, np->name); + + spdif_priv->pdev = pdev; + + /* Initialize this copy of the CPU DAI driver structure */ + memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai)); + spdif_priv->cpu_dai_drv.name = spdif_priv->name; + + /* Get the addresses and IRQ */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (IS_ERR(res)) { + dev_err(&pdev->dev, "could not determine device resources\n"); + return PTR_ERR(res); + } + + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "core", regs, &fsl_spdif_regmap_config); + if (IS_ERR(spdif_priv->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(spdif_priv->regmap); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0, + spdif_priv->name, spdif_priv); + if (ret) { + dev_err(&pdev->dev, "could not claim irq %u\n", irq); + return ret; + } + + /* Select clock source for rx/tx clock */ + spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1"); + if (IS_ERR(spdif_priv->rxclk)) { + dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n"); + return PTR_ERR(spdif_priv->rxclk); + } + spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC; + + for (i = 0; i < SPDIF_TXRATE_MAX; i++) { + ret = fsl_spdif_probe_txclk(spdif_priv, i); + if (ret) + return ret; + } + + /* Initial spinlock for control data */ + ctrl = &spdif_priv->fsl_spdif_control; + spin_lock_init(&ctrl->ctl_lock); + + /* Init tx channel status default value */ + ctrl->ch_status[0] = + IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015; + ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID; + ctrl->ch_status[2] = 0x00; + ctrl->ch_status[3] = + IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM; + + spdif_priv->dpll_locked = false; + + spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML; + spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML; + spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL; + spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL; + + /* Register with ASoC */ + dev_set_drvdata(&pdev->dev, spdif_priv); + + ret = snd_soc_register_component(&pdev->dev, &fsl_spdif_component, + &spdif_priv->cpu_dai_drv, 1); + if (ret) { + dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); + return ret; + } + + ret = imx_pcm_dma_init(pdev); + if (ret) { + dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret); + goto error_component; + } + + return ret; + +error_component: + snd_soc_unregister_component(&pdev->dev); + + return ret; +} + +static int fsl_spdif_remove(struct platform_device *pdev) +{ + imx_pcm_dma_exit(pdev); + snd_soc_unregister_component(&pdev->dev); + + return 0; +} + +static const struct of_device_id fsl_spdif_dt_ids[] = { + { .compatible = "fsl,imx35-spdif", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); + +static struct platform_driver fsl_spdif_driver = { + .driver = { + .name = "fsl-spdif-dai", + .owner = THIS_MODULE, + .of_match_table = fsl_spdif_dt_ids, + }, + .probe = fsl_spdif_probe, + .remove = fsl_spdif_remove, +}; + +module_platform_driver(fsl_spdif_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale S/PDIF CPU DAI Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:fsl-spdif-dai"); diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h new file mode 100644 index 00000000000..b1266790d11 --- /dev/null +++ b/sound/soc/fsl/fsl_spdif.h @@ -0,0 +1,191 @@ +/* + * fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen <b42378@freescale.com> + * + * Based on fsl_ssi.h + * Author: Timur Tabi <timur@freescale.com> + * Copyright 2007-2008 Freescale Semiconductor, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef _FSL_SPDIF_DAI_H +#define _FSL_SPDIF_DAI_H + +/* S/PDIF Register Map */ +#define REG_SPDIF_SCR 0x0 /* SPDIF Configuration Register */ +#define REG_SPDIF_SRCD 0x4 /* CDText Control Register */ +#define REG_SPDIF_SRPC 0x8 /* PhaseConfig Register */ +#define REG_SPDIF_SIE 0xc /* InterruptEn Register */ +#define REG_SPDIF_SIS 0x10 /* InterruptStat Register */ +#define REG_SPDIF_SIC 0x10 /* InterruptClear Register */ +#define REG_SPDIF_SRL 0x14 /* SPDIFRxLeft Register */ +#define REG_SPDIF_SRR 0x18 /* SPDIFRxRight Register */ +#define REG_SPDIF_SRCSH 0x1c /* SPDIFRxCChannel_h Register */ +#define REG_SPDIF_SRCSL 0x20 /* SPDIFRxCChannel_l Register */ +#define REG_SPDIF_SRU 0x24 /* UchannelRx Register */ +#define REG_SPDIF_SRQ 0x28 /* QchannelRx Register */ +#define REG_SPDIF_STL 0x2C /* SPDIFTxLeft Register */ +#define REG_SPDIF_STR 0x30 /* SPDIFTxRight Register */ +#define REG_SPDIF_STCSCH 0x34 /* SPDIFTxCChannelCons_h Register */ +#define REG_SPDIF_STCSCL 0x38 /* SPDIFTxCChannelCons_l Register */ +#define REG_SPDIF_SRFM 0x44 /* FreqMeas Register */ +#define REG_SPDIF_STC 0x50 /* SPDIFTxClk Register */ + + +/* SPDIF Configuration register */ +#define SCR_RXFIFO_CTL_OFFSET 23 +#define SCR_RXFIFO_CTL_MASK (1 << SCR_RXFIFO_CTL_OFFSET) +#define SCR_RXFIFO_CTL_ZERO (1 << SCR_RXFIFO_CTL_OFFSET) +#define SCR_RXFIFO_OFF_OFFSET 22 +#define SCR_RXFIFO_OFF_MASK (1 << SCR_RXFIFO_OFF_OFFSET) +#define SCR_RXFIFO_OFF (1 << SCR_RXFIFO_OFF_OFFSET) +#define SCR_RXFIFO_RST_OFFSET 21 +#define SCR_RXFIFO_RST_MASK (1 << SCR_RXFIFO_RST_OFFSET) +#define SCR_RXFIFO_RST (1 << SCR_RXFIFO_RST_OFFSET) +#define SCR_RXFIFO_FSEL_OFFSET 19 +#define SCR_RXFIFO_FSEL_MASK (0x3 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_FSEL_IF0 (0x0 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_FSEL_IF4 (0x1 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_FSEL_IF8 (0x2 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_FSEL_IF12 (0x3 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_AUTOSYNC_OFFSET 18 +#define SCR_RXFIFO_AUTOSYNC_MASK (1 << SCR_RXFIFO_AUTOSYNC_OFFSET) +#define SCR_RXFIFO_AUTOSYNC (1 << SCR_RXFIFO_AUTOSYNC_OFFSET) +#define SCR_TXFIFO_AUTOSYNC_OFFSET 17 +#define SCR_TXFIFO_AUTOSYNC_MASK (1 << SCR_TXFIFO_AUTOSYNC_OFFSET) +#define SCR_TXFIFO_AUTOSYNC (1 << SCR_TXFIFO_AUTOSYNC_OFFSET) +#define SCR_TXFIFO_FSEL_OFFSET 15 +#define SCR_TXFIFO_FSEL_MASK (0x3 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_TXFIFO_FSEL_IF0 (0x0 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_TXFIFO_FSEL_IF4 (0x1 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_TXFIFO_FSEL_IF8 (0x2 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_TXFIFO_FSEL_IF12 (0x3 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_LOW_POWER (1 << 13) +#define SCR_SOFT_RESET (1 << 12) +#define SCR_TXFIFO_CTRL_OFFSET 10 +#define SCR_TXFIFO_CTRL_MASK (0x3 << SCR_TXFIFO_CTRL_OFFSET) +#define SCR_TXFIFO_CTRL_ZERO (0x0 << SCR_TXFIFO_CTRL_OFFSET) +#define SCR_TXFIFO_CTRL_NORMAL (0x1 << SCR_TXFIFO_CTRL_OFFSET) +#define SCR_TXFIFO_CTRL_ONESAMPLE (0x2 << SCR_TXFIFO_CTRL_OFFSET) +#define SCR_DMA_RX_EN_OFFSET 9 +#define SCR_DMA_RX_EN_MASK (1 << SCR_DMA_RX_EN_OFFSET) +#define SCR_DMA_RX_EN (1 << SCR_DMA_RX_EN_OFFSET) +#define SCR_DMA_TX_EN_OFFSET 8 +#define SCR_DMA_TX_EN_MASK (1 << SCR_DMA_TX_EN_OFFSET) +#define SCR_DMA_TX_EN (1 << SCR_DMA_TX_EN_OFFSET) +#define SCR_VAL_OFFSET 5 +#define SCR_VAL_MASK (1 << SCR_VAL_OFFSET) +#define SCR_VAL_CLEAR (1 << SCR_VAL_OFFSET) +#define SCR_TXSEL_OFFSET 2 +#define SCR_TXSEL_MASK (0x7 << SCR_TXSEL_OFFSET) +#define SCR_TXSEL_OFF (0 << SCR_TXSEL_OFFSET) +#define SCR_TXSEL_RX (1 << SCR_TXSEL_OFFSET) +#define SCR_TXSEL_NORMAL (0x5 << SCR_TXSEL_OFFSET) +#define SCR_USRC_SEL_OFFSET 0x0 +#define SCR_USRC_SEL_MASK (0x3 << SCR_USRC_SEL_OFFSET) +#define SCR_USRC_SEL_NONE (0x0 << SCR_USRC_SEL_OFFSET) +#define SCR_USRC_SEL_RECV (0x1 << SCR_USRC_SEL_OFFSET) +#define SCR_USRC_SEL_CHIP (0x3 << SCR_USRC_SEL_OFFSET) + +/* SPDIF CDText control */ +#define SRCD_CD_USER_OFFSET 1 +#define SRCD_CD_USER (1 << SRCD_CD_USER_OFFSET) + +/* SPDIF Phase Configuration register */ +#define SRPC_DPLL_LOCKED (1 << 6) +#define SRPC_CLKSRC_SEL_OFFSET 7 +#define SRPC_CLKSRC_SEL_MASK (0xf << SRPC_CLKSRC_SEL_OFFSET) +#define SRPC_CLKSRC_SEL_SET(x) ((x << SRPC_CLKSRC_SEL_OFFSET) & SRPC_CLKSRC_SEL_MASK) +#define SRPC_CLKSRC_SEL_LOCKED_OFFSET1 5 +#define SRPC_CLKSRC_SEL_LOCKED_OFFSET2 2 +#define SRPC_GAINSEL_OFFSET 3 +#define SRPC_GAINSEL_MASK (0x7 << SRPC_GAINSEL_OFFSET) +#define SRPC_GAINSEL_SET(x) ((x << SRPC_GAINSEL_OFFSET) & SRPC_GAINSEL_MASK) + +#define SRPC_CLKSRC_MAX 16 + +enum spdif_gainsel { + GAINSEL_MULTI_24 = 0, + GAINSEL_MULTI_16, + GAINSEL_MULTI_12, + GAINSEL_MULTI_8, + GAINSEL_MULTI_6, + GAINSEL_MULTI_4, + GAINSEL_MULTI_3, +}; +#define GAINSEL_MULTI_MAX (GAINSEL_MULTI_3 + 1) +#define SPDIF_DEFAULT_GAINSEL GAINSEL_MULTI_8 + +/* SPDIF interrupt mask define */ +#define INT_DPLL_LOCKED (1 << 20) +#define INT_TXFIFO_UNOV (1 << 19) +#define INT_TXFIFO_RESYNC (1 << 18) +#define INT_CNEW (1 << 17) +#define INT_VAL_NOGOOD (1 << 16) +#define INT_SYM_ERR (1 << 15) +#define INT_BIT_ERR (1 << 14) +#define INT_URX_FUL (1 << 10) +#define INT_URX_OV (1 << 9) +#define INT_QRX_FUL (1 << 8) +#define INT_QRX_OV (1 << 7) +#define INT_UQ_SYNC (1 << 6) +#define INT_UQ_ERR (1 << 5) +#define INT_RXFIFO_UNOV (1 << 4) +#define INT_RXFIFO_RESYNC (1 << 3) +#define INT_LOSS_LOCK (1 << 2) +#define INT_TX_EM (1 << 1) +#define INT_RXFIFO_FUL (1 << 0) + +/* SPDIF Clock register */ +#define STC_SYSCLK_DIV_OFFSET 11 +#define STC_SYSCLK_DIV_MASK (0x1ff << STC_TXCLK_SRC_OFFSET) +#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK) +#define STC_TXCLK_SRC_OFFSET 8 +#define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET) +#define STC_TXCLK_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK) +#define STC_TXCLK_ALL_EN_OFFSET 7 +#define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET) +#define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET) +#define STC_TXCLK_DIV_OFFSET 0 +#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET) +#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK) +#define STC_TXCLK_SRC_MAX 8 + +/* SPDIF tx rate */ +enum spdif_txrate { + SPDIF_TXRATE_32000 = 0, + SPDIF_TXRATE_44100, + SPDIF_TXRATE_48000, +}; +#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_48000 + 1) + + +#define SPDIF_CSTATUS_BYTE 6 +#define SPDIF_UBITS_SIZE 96 +#define SPDIF_QSUB_SIZE (SPDIF_UBITS_SIZE / 8) + + +#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_64000 | \ + SNDRV_PCM_RATE_96000) + +#define FSL_SPDIF_FORMATS_PLAYBACK (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define FSL_SPDIF_FORMATS_CAPTURE (SNDRV_PCM_FMTBIT_S24_LE) + +#endif /* _FSL_SPDIF_DAI_H */ diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 2f2d837df07..c6b743978d5 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -8,6 +8,26 @@ * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. + * + * + * Some notes why imx-pcm-fiq is used instead of DMA on some boards: + * + * The i.MX SSI core has some nasty limitations in AC97 mode. While most + * sane processor vendors have a FIFO per AC97 slot, the i.MX has only + * one FIFO which combines all valid receive slots. We cannot even select + * which slots we want to receive. The WM9712 with which this driver + * was developed with always sends GPIO status data in slot 12 which + * we receive in our (PCM-) data stream. The only chance we have is to + * manually skip this data in the FIQ handler. With sampling rates different + * from 48000Hz not every frame has valid receive data, so the ratio + * between pcm data and GPIO status data changes. Our FIQ handler is not + * able to handle this, hence this driver only works with 48000Hz sampling + * rate. + * Reading and writing AC97 registers is another challenge. The core + * provides us status bits when the read register is updated with *another* + * value. When we read the same register two times (and the register still + * contains the same value) these status bits are not set. We work + * around this by not polling these bits but only wait a fixed delay. */ #include <linux/init.h> @@ -36,7 +56,7 @@ #define read_ssi(addr) in_be32(addr) #define write_ssi(val, addr) out_be32(addr, val) #define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set) -#elif defined ARM +#else #define read_ssi(addr) readl(addr) #define write_ssi(val, addr) writel(val, addr) /* @@ -121,11 +141,14 @@ struct fsl_ssi_private { bool new_binding; bool ssi_on_imx; + bool imx_ac97; + bool use_dma; struct clk *clk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; struct imx_dma_data filter_data_tx; struct imx_dma_data filter_data_rx; + struct imx_pcm_fiq_params fiq_params; struct { unsigned int rfrc; @@ -298,6 +321,102 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) return ret; } +static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) +{ + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + u8 i2s_mode; + u8 wm; + int synchronous = ssi_private->cpu_dai_drv.symmetric_rates; + + if (ssi_private->imx_ac97) + i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET; + else + i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE; + + /* + * Section 16.5 of the MPC8610 reference manual says that the SSI needs + * to be disabled before updating the registers we set here. + */ + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); + + /* + * Program the SSI into I2S Slave Non-Network Synchronous mode. Also + * enable the transmit and receive FIFO. + * + * FIXME: Little-endian samples require a different shift dir + */ + write_ssi_mask(&ssi->scr, + CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, + CCSR_SSI_SCR_TFR_CLK_DIS | + i2s_mode | + (synchronous ? CCSR_SSI_SCR_SYN : 0)); + + write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | + CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | + CCSR_SSI_STCR_TSCKP, &ssi->stcr); + + write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | + CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | + CCSR_SSI_SRCR_RSCKP, &ssi->srcr); + /* + * The DC and PM bits are only used if the SSI is the clock master. + */ + + /* + * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't + * use FIFO 1. We program the transmit water to signal a DMA transfer + * if there are only two (or fewer) elements left in the FIFO. Two + * elements equals one frame (left channel, right channel). This value, + * however, depends on the depth of the transmit buffer. + * + * We set the watermark on the same level as the DMA burstsize. For + * fiq it is probably better to use the biggest possible watermark + * size. + */ + if (ssi_private->use_dma) + wm = ssi_private->fifo_depth - 2; + else + wm = ssi_private->fifo_depth; + + write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) | + CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm), + &ssi->sfcsr); + + /* + * For ac97 interrupts are enabled with the startup of the substream + * because it is also running without an active substream. Normally SSI + * is only enabled when there is a substream. + */ + if (ssi_private->imx_ac97) { + /* + * Setup the clock control register + */ + write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), + &ssi->stccr); + write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), + &ssi->srccr); + + /* + * Enable AC97 mode and startup the SSI + */ + write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV, + &ssi->sacnt); + write_ssi(0xff, &ssi->saccdis); + write_ssi(0x300, &ssi->saccen); + + /* + * Enable SSI, Transmit and Receive + */ + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN | + CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); + + write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); + } + + return 0; +} + + /** * fsl_ssi_startup: create a new substream * @@ -319,70 +438,14 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, * and initialize the SSI registers. */ if (!ssi_private->first_stream) { - struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - ssi_private->first_stream = substream; /* - * Section 16.5 of the MPC8610 reference manual says that the - * SSI needs to be disabled before updating the registers we set - * here. - */ - write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); - - /* - * Program the SSI into I2S Slave Non-Network Synchronous mode. - * Also enable the transmit and receive FIFO. - * - * FIXME: Little-endian samples require a different shift dir - */ - write_ssi_mask(&ssi->scr, - CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, - CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE - | (synchronous ? CCSR_SSI_SCR_SYN : 0)); - - write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | - CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | - CCSR_SSI_STCR_TSCKP, &ssi->stcr); - - write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | - CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | - CCSR_SSI_SRCR_RSCKP, &ssi->srcr); - - /* - * The DC and PM bits are only used if the SSI is the clock - * master. - */ - - /* Enable the interrupts and DMA requests */ - write_ssi(SIER_FLAGS, &ssi->sier); - - /* - * Set the watermark for transmit FIFI 0 and receive FIFO 0. We - * don't use FIFO 1. We program the transmit water to signal a - * DMA transfer if there are only two (or fewer) elements left - * in the FIFO. Two elements equals one frame (left channel, - * right channel). This value, however, depends on the depth of - * the transmit buffer. - * - * We program the receive FIFO to notify us if at least two - * elements (one frame) have been written to the FIFO. We could - * make this value larger (and maybe we should), but this way - * data will be written to memory as soon as it's available. - */ - write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) | - CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2), - &ssi->sfcsr); - - /* - * We keep the SSI disabled because if we enable it, then the - * DMA controller will start. It's not supposed to start until - * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The - * DMA controller will transfer one "BWC" of data (i.e. the - * amount of data that the MR.BWC bits are set to). The reason - * this is bad is because at this point, the PCM driver has not - * finished initializing the DMA controller. + * fsl_ssi_setup was already called by ac97_init earlier if + * the driver is in ac97 mode. */ + if (!ssi_private->imx_ac97) + fsl_ssi_setup(ssi_private); } else { if (synchronous) { struct snd_pcm_runtime *first_runtime = @@ -492,6 +555,27 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + unsigned int sier_bits; + + /* + * Enable only the interrupts and DMA requests + * that are needed for the channel. As the fiq + * is polling for this bits, we have to ensure + * that this are aligned with the preallocated + * buffers + */ + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (ssi_private->use_dma) + sier_bits = SIER_FLAGS; + else + sier_bits = CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN; + } else { + if (ssi_private->use_dma) + sier_bits = SIER_FLAGS; + else + sier_bits = CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_RFF0_EN; + } switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -510,12 +594,18 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0); else write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0); + + if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) & + (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); break; default: return -EINVAL; } + write_ssi(sier_bits, &ssi->sier); + return 0; } @@ -534,22 +624,13 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, ssi_private->first_stream = ssi_private->second_stream; ssi_private->second_stream = NULL; - - /* - * If this is the last active substream, disable the SSI. - */ - if (!ssi_private->first_stream) { - struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - - write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); - } } static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) { struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai); - if (ssi_private->ssi_on_imx) { + if (ssi_private->ssi_on_imx && ssi_private->use_dma) { dai->playback_dma_data = &ssi_private->dma_params_tx; dai->capture_dma_data = &ssi_private->dma_params_rx; } @@ -587,6 +668,133 @@ static const struct snd_soc_component_driver fsl_ssi_component = { .name = "fsl-ssi", }; +/** + * fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit. + * + * This function is called by ALSA to start, stop, pause, and resume the + * transfer of data. + */ +static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata( + rtd->cpu_dai); + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE | + CCSR_SSI_SIER_TFE0_EN); + else + write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE | + CCSR_SSI_SIER_TFE0_EN, 0); + else + write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN, 0); + break; + + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor); + else + write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor); + + return 0; +} + +static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = { + .startup = fsl_ssi_startup, + .shutdown = fsl_ssi_shutdown, + .trigger = fsl_ssi_ac97_trigger, +}; + +static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { + .ac97_control = 1, + .playback = { + .stream_name = "AC97 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "AC97 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &fsl_ssi_ac97_dai_ops, +}; + + +static struct fsl_ssi_private *fsl_ac97_data; + +static void fsl_ssi_ac97_init(void) +{ + fsl_ssi_setup(fsl_ac97_data); +} + +void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + struct ccsr_ssi *ssi = fsl_ac97_data->ssi; + unsigned int lreg; + unsigned int lval; + + if (reg > 0x7f) + return; + + + lreg = reg << 12; + write_ssi(lreg, &ssi->sacadd); + + lval = val << 4; + write_ssi(lval , &ssi->sacdat); + + write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK, + CCSR_SSI_SACNT_WR); + udelay(100); +} + +unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct ccsr_ssi *ssi = fsl_ac97_data->ssi; + + unsigned short val = -1; + unsigned int lreg; + + lreg = (reg & 0x7f) << 12; + write_ssi(lreg, &ssi->sacadd); + write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK, + CCSR_SSI_SACNT_RD); + + udelay(100); + + val = (read_ssi(&ssi->sacdat) >> 4) & 0xffff; + + return val; +} + +static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { + .read = fsl_ssi_ac97_read, + .write = fsl_ssi_ac97_write, +}; + /* Show the statistics of a flag only if its interrupt is enabled. The * compiler will optimze this code to a no-op if the interrupt is not * enabled. @@ -663,6 +871,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) struct resource res; char name[64]; bool shared; + bool ac97 = false; /* SSIs that are not connected on the board should have a * status = "disabled" @@ -673,14 +882,20 @@ static int fsl_ssi_probe(struct platform_device *pdev) /* We only support the SSI in "I2S Slave" mode */ sprop = of_get_property(np, "fsl,mode", NULL); - if (!sprop || strcmp(sprop, "i2s-slave")) { + if (!sprop) { + dev_err(&pdev->dev, "fsl,mode property is necessary\n"); + return -EINVAL; + } + if (!strcmp(sprop, "ac97-slave")) { + ac97 = true; + } else if (strcmp(sprop, "i2s-slave")) { dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop); return -ENODEV; } /* The DAI name is the last part of the full name of the node. */ p = strrchr(np->full_name, '/') + 1; - ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p), + ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p), GFP_KERNEL); if (!ssi_private) { dev_err(&pdev->dev, "could not allocate DAI object\n"); @@ -689,38 +904,41 @@ static int fsl_ssi_probe(struct platform_device *pdev) strcpy(ssi_private->name, p); - /* Initialize this copy of the CPU DAI driver structure */ - memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, - sizeof(fsl_ssi_dai_template)); + ssi_private->use_dma = !of_property_read_bool(np, + "fsl,fiq-stream-filter"); + + if (ac97) { + memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai, + sizeof(fsl_ssi_ac97_dai)); + + fsl_ac97_data = ssi_private; + ssi_private->imx_ac97 = true; + + snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); + } else { + /* Initialize this copy of the CPU DAI driver structure */ + memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, + sizeof(fsl_ssi_dai_template)); + } ssi_private->cpu_dai_drv.name = ssi_private->name; /* Get the addresses and IRQ */ ret = of_address_to_resource(np, 0, &res); if (ret) { dev_err(&pdev->dev, "could not determine device resources\n"); - goto error_kmalloc; + return ret; } ssi_private->ssi = of_iomap(np, 0); if (!ssi_private->ssi) { dev_err(&pdev->dev, "could not map device resources\n"); - ret = -ENOMEM; - goto error_kmalloc; + return -ENOMEM; } ssi_private->ssi_phys = res.start; ssi_private->irq = irq_of_parse_and_map(np, 0); if (ssi_private->irq == NO_IRQ) { dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); - ret = -ENXIO; - goto error_iomap; - } - - /* The 'name' should not have any slashes in it. */ - ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name, - ssi_private); - if (ret < 0) { - dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq); - goto error_irqmap; + return -ENXIO; } /* Are the RX and the TX clocks locked? */ @@ -739,13 +957,18 @@ static int fsl_ssi_probe(struct platform_device *pdev) u32 dma_events[2]; ssi_private->ssi_on_imx = true; - ssi_private->clk = clk_get(&pdev->dev, NULL); + ssi_private->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(ssi_private->clk)) { ret = PTR_ERR(ssi_private->clk); dev_err(&pdev->dev, "could not get clock: %d\n", ret); - goto error_irq; + goto error_irqmap; + } + ret = clk_prepare_enable(ssi_private->clk); + if (ret) { + dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", + ret); + goto error_irqmap; } - clk_prepare_enable(ssi_private->clk); /* * We have burstsize be "fifo_depth - 2" to match the SSI @@ -763,24 +986,38 @@ static int fsl_ssi_probe(struct platform_device *pdev) &ssi_private->filter_data_tx; ssi_private->dma_params_rx.filter_data = &ssi_private->filter_data_rx; - /* - * TODO: This is a temporary solution and should be changed - * to use generic DMA binding later when the helplers get in. - */ - ret = of_property_read_u32_array(pdev->dev.of_node, + if (!of_property_read_bool(pdev->dev.of_node, "dmas") && + ssi_private->use_dma) { + /* + * FIXME: This is a temporary solution until all + * necessary dma drivers support the generic dma + * bindings. + */ + ret = of_property_read_u32_array(pdev->dev.of_node, "fsl,ssi-dma-events", dma_events, 2); - if (ret) { - dev_err(&pdev->dev, "could not get dma events\n"); - goto error_clk; + if (ret && ssi_private->use_dma) { + dev_err(&pdev->dev, "could not get dma events but fsl-ssi is configured to use DMA\n"); + goto error_clk; + } } shared = of_device_is_compatible(of_get_parent(np), "fsl,spba-bus"); imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx, - dma_events[0], shared); + dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx, - dma_events[1], shared); + dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); + } else if (ssi_private->use_dma) { + /* The 'name' should not have any slashes in it. */ + ret = devm_request_irq(&pdev->dev, ssi_private->irq, + fsl_ssi_isr, 0, ssi_private->name, + ssi_private); + if (ret < 0) { + dev_err(&pdev->dev, "could not claim irq %u\n", + ssi_private->irq); + goto error_irqmap; + } } /* Initialize the the device_attribute structure */ @@ -794,7 +1031,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "could not create sysfs %s file\n", ssi_private->dev_attr.attr.name); - goto error_irq; + goto error_clk; } /* Register with ASoC */ @@ -808,9 +1045,30 @@ static int fsl_ssi_probe(struct platform_device *pdev) } if (ssi_private->ssi_on_imx) { - ret = imx_pcm_dma_init(pdev); - if (ret) - goto error_dev; + if (!ssi_private->use_dma) { + + /* + * Some boards use an incompatible codec. To get it + * working, we are using imx-fiq-pcm-audio, that + * can handle those codecs. DMA is not possible in this + * situation. + */ + + ssi_private->fiq_params.irq = ssi_private->irq; + ssi_private->fiq_params.base = ssi_private->ssi; + ssi_private->fiq_params.dma_params_rx = + &ssi_private->dma_params_rx; + ssi_private->fiq_params.dma_params_tx = + &ssi_private->dma_params_tx; + + ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params); + if (ret) + goto error_dev; + } else { + ret = imx_pcm_dma_init(pdev); + if (ret) + goto error_dev; + } } /* @@ -845,6 +1103,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) } done: + if (ssi_private->imx_ac97) + fsl_ssi_ac97_init(); + return 0; error_dai: @@ -853,27 +1114,15 @@ error_dai: snd_soc_unregister_component(&pdev->dev); error_dev: - dev_set_drvdata(&pdev->dev, NULL); device_remove_file(&pdev->dev, dev_attr); error_clk: - if (ssi_private->ssi_on_imx) { + if (ssi_private->ssi_on_imx) clk_disable_unprepare(ssi_private->clk); - clk_put(ssi_private->clk); - } - -error_irq: - free_irq(ssi_private->irq, ssi_private); error_irqmap: irq_dispose_mapping(ssi_private->irq); -error_iomap: - iounmap(ssi_private->ssi); - -error_kmalloc: - kfree(ssi_private); - return ret; } @@ -883,20 +1132,15 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (!ssi_private->new_binding) platform_device_unregister(ssi_private->pdev); - if (ssi_private->ssi_on_imx) { + if (ssi_private->ssi_on_imx) imx_pcm_dma_exit(pdev); - clk_disable_unprepare(ssi_private->clk); - clk_put(ssi_private->clk); - } snd_soc_unregister_component(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); device_remove_file(&pdev->dev, &ssi_private->dev_attr); - - free_irq(ssi_private->irq, ssi_private); + if (ssi_private->ssi_on_imx) + clk_disable_unprepare(ssi_private->clk); irq_dispose_mapping(ssi_private->irq); - kfree(ssi_private); - dev_set_drvdata(&pdev->dev, NULL); - return 0; } @@ -919,6 +1163,7 @@ static struct platform_driver fsl_ssi_driver = { module_platform_driver(fsl_ssi_driver); +MODULE_ALIAS("platform:fsl-ssi-dai"); MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index e260f1f899d..ab17381cc98 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -73,8 +73,11 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; - if (audmux_clk) - clk_prepare_enable(audmux_clk); + if (audmux_clk) { + ret = clk_prepare_enable(audmux_clk); + if (ret) + return ret; + } ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port)); pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port)); @@ -224,14 +227,19 @@ EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port); int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, unsigned int pdcr) { + int ret; + if (audmux_type != IMX31_AUDMUX) return -EINVAL; if (!audmux_base) return -ENOSYS; - if (audmux_clk) - clk_prepare_enable(audmux_clk); + if (audmux_clk) { + ret = clk_prepare_enable(audmux_clk); + if (ret) + return ret; + } writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port)); writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port)); @@ -243,6 +251,66 @@ int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, } EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); +static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, + struct device_node *of_node) +{ + struct device_node *child; + + for_each_available_child_of_node(of_node, child) { + unsigned int port; + unsigned int ptcr = 0; + unsigned int pdcr = 0; + unsigned int pcr = 0; + unsigned int val; + int ret; + int i = 0; + + ret = of_property_read_u32(child, "fsl,audmux-port", &port); + if (ret) { + dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%s\"\n", + child->full_name); + continue; + } + if (!of_property_read_bool(child, "fsl,port-config")) { + dev_warn(&pdev->dev, "child node \"%s\" does not have property fsl,port-config\n", + child->full_name); + continue; + } + + for (i = 0; (ret = of_property_read_u32_index(child, + "fsl,port-config", i, &val)) == 0; + ++i) { + if (audmux_type == IMX31_AUDMUX) { + if (i % 2) + pdcr |= val; + else + ptcr |= val; + } else { + pcr |= val; + } + } + + if (ret != -EOVERFLOW) { + dev_err(&pdev->dev, "Failed to read u32 at index %d of child %s\n", + i, child->full_name); + continue; + } + + if (audmux_type == IMX31_AUDMUX) { + if (i % 2) { + dev_err(&pdev->dev, "One pdcr value is missing in child node %s\n", + child->full_name); + continue; + } + imx_audmux_v2_configure_port(port, ptcr, pdcr); + } else { + imx_audmux_v1_configure_port(port, pcr); + } + } + + return 0; +} + static int imx_audmux_probe(struct platform_device *pdev) { struct resource *res; @@ -267,6 +335,8 @@ static int imx_audmux_probe(struct platform_device *pdev) if (audmux_type == IMX31_AUDMUX) audmux_debugfs_init(); + imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node); + return 0; } diff --git a/sound/soc/fsl/imx-audmux.h b/sound/soc/fsl/imx-audmux.h index b8ff44b9daf..38a4209af7c 100644 --- a/sound/soc/fsl/imx-audmux.h +++ b/sound/soc/fsl/imx-audmux.h @@ -1,57 +1,7 @@ #ifndef __IMX_AUDMUX_H #define __IMX_AUDMUX_H -#define MX27_AUDMUX_HPCR1_SSI0 0 -#define MX27_AUDMUX_HPCR2_SSI1 1 -#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 -#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 -#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 -#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 - -#define MX31_AUDMUX_PORT1_SSI0 0 -#define MX31_AUDMUX_PORT2_SSI1 1 -#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 -#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 -#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 -#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 -#define MX31_AUDMUX_PORT7_SSI_PINS_7 6 - -#define MX51_AUDMUX_PORT1_SSI0 0 -#define MX51_AUDMUX_PORT2_SSI1 1 -#define MX51_AUDMUX_PORT3 2 -#define MX51_AUDMUX_PORT4 3 -#define MX51_AUDMUX_PORT5 4 -#define MX51_AUDMUX_PORT6 5 -#define MX51_AUDMUX_PORT7 6 - -/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) -#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) -#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) -#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) -#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) -#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) -#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) -#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) -#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) -#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) - -/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) -#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) -#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) -#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) -#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) -#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) -#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) -#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) -#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) - -#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) -#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) -#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) +#include <dt-bindings/sound/fsl-imx-audmux.h> int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr); diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c index 9df173c091a..a3d60d4bea4 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -90,6 +90,7 @@ static const struct snd_soc_dapm_route imx_mc13783_routes[] = { static struct snd_soc_card imx_mc13783 = { .name = "imx_mc13783", + .owner = THIS_MODULE, .dai_link = imx_mc13783_dai_mc13783, .num_links = ARRAY_SIZE(imx_mc13783_dai_mc13783), .dapm_widgets = imx_mc13783_widget, diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index fde4d2ea68c..4dc1296688e 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/dmaengine.h> #include <linux/types.h> +#include <linux/module.h> #include <sound/core.h> #include <sound/pcm.h> @@ -64,7 +65,6 @@ int imx_pcm_dma_init(struct platform_device *pdev) { return snd_dmaengine_pcm_register(&pdev->dev, &imx_dmaengine_pcm_config, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | - SND_DMAENGINE_PCM_FLAG_NO_DT | SND_DMAENGINE_PCM_FLAG_COMPAT); } EXPORT_SYMBOL_GPL(imx_pcm_dma_init); @@ -74,3 +74,5 @@ void imx_pcm_dma_exit(struct platform_device *pdev) snd_dmaengine_pcm_unregister(&pdev->dev); } EXPORT_SYMBOL_GPL(imx_pcm_dma_exit); + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 310d9029032..34043c55f2a 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <sound/core.h> +#include <sound/dmaengine_pcm.h> #include <sound/initval.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -32,6 +33,7 @@ #include <linux/platform_data/asoc-imx-ssi.h> #include "imx-ssi.h" +#include "imx-pcm.h" struct imx_pcm_runtime_data { unsigned int period; @@ -366,9 +368,9 @@ static struct snd_soc_platform_driver imx_soc_platform_fiq = { .pcm_free = imx_pcm_fiq_free, }; -int imx_pcm_fiq_init(struct platform_device *pdev) +int imx_pcm_fiq_init(struct platform_device *pdev, + struct imx_pcm_fiq_params *params) { - struct imx_ssi *ssi = platform_get_drvdata(pdev); int ret; ret = claim_fiq(&fh); @@ -377,15 +379,15 @@ int imx_pcm_fiq_init(struct platform_device *pdev) return ret; } - mxc_set_irq_fiq(ssi->irq, 1); - ssi_irq = ssi->irq; + mxc_set_irq_fiq(params->irq, 1); + ssi_irq = params->irq; - imx_pcm_fiq = ssi->irq; + imx_pcm_fiq = params->irq; - imx_ssi_fiq_base = (unsigned long)ssi->base; + imx_ssi_fiq_base = (unsigned long)params->base; - ssi->dma_params_tx.maxburst = 4; - ssi->dma_params_rx.maxburst = 6; + params->dma_params_tx->maxburst = 4; + params->dma_params_rx->maxburst = 6; ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_fiq); if (ret) @@ -406,3 +408,5 @@ void imx_pcm_fiq_exit(struct platform_device *pdev) snd_soc_unregister_platform(&pdev->dev); } EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit); + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 67f656c7c32..5d5b73303e1 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -22,17 +22,23 @@ static inline void imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data, - int dma, bool shared) + int dma, enum sdma_peripheral_type peripheral_type) { dma_data->dma_request = dma; dma_data->priority = DMA_PRIO_HIGH; - if (shared) - dma_data->peripheral_type = IMX_DMATYPE_SSI_SP; - else - dma_data->peripheral_type = IMX_DMATYPE_SSI; + dma_data->peripheral_type = peripheral_type; } -#ifdef CONFIG_SND_SOC_IMX_PCM_DMA +struct imx_pcm_fiq_params { + int irq; + void __iomem *base; + + /* Pointer to original ssi driver to setup tx rx sizes */ + struct snd_dmaengine_dai_dma_data *dma_params_rx; + struct snd_dmaengine_dai_dma_data *dma_params_tx; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA) int imx_pcm_dma_init(struct platform_device *pdev); void imx_pcm_dma_exit(struct platform_device *pdev); #else @@ -46,11 +52,13 @@ static inline void imx_pcm_dma_exit(struct platform_device *pdev) } #endif -#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ -int imx_pcm_fiq_init(struct platform_device *pdev); +#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_FIQ) +int imx_pcm_fiq_init(struct platform_device *pdev, + struct imx_pcm_fiq_params *params); void imx_pcm_fiq_exit(struct platform_device *pdev); #else -static inline int imx_pcm_fiq_init(struct platform_device *pdev) +static inline int imx_pcm_fiq_init(struct platform_device *pdev, + struct imx_pcm_fiq_params *params) { return -ENODEV; } diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 3f726e4f88d..389cbfa6dca 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -129,8 +129,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) } data->codec_clk = devm_clk_get(&codec_dev->dev, NULL); - if (IS_ERR(data->codec_clk)) + if (IS_ERR(data->codec_clk)) { + ret = PTR_ERR(data->codec_clk); goto fail; + } data->clk_frequency = clk_get_rate(data->codec_clk); diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c new file mode 100644 index 00000000000..816013b0ebb --- /dev/null +++ b/sound/soc/fsl/imx-spdif.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <sound/soc.h> + +struct imx_spdif_data { + struct snd_soc_dai_link dai[2]; + struct snd_soc_card card; + struct platform_device *txdev; + struct platform_device *rxdev; +}; + +static int imx_spdif_audio_probe(struct platform_device *pdev) +{ + struct device_node *spdif_np, *np = pdev->dev.of_node; + struct imx_spdif_data *data; + int ret = 0, num_links = 0; + + spdif_np = of_parse_phandle(np, "spdif-controller", 0); + if (!spdif_np) { + dev_err(&pdev->dev, "failed to find spdif-controller\n"); + ret = -EINVAL; + goto end; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + ret = -ENOMEM; + goto end; + } + + if (of_property_read_bool(np, "spdif-out")) { + data->dai[num_links].name = "S/PDIF TX"; + data->dai[num_links].stream_name = "S/PDIF PCM Playback"; + data->dai[num_links].codec_dai_name = "dit-hifi"; + data->dai[num_links].codec_name = "spdif-dit"; + data->dai[num_links].cpu_of_node = spdif_np; + data->dai[num_links].platform_of_node = spdif_np; + num_links++; + + data->txdev = platform_device_register_simple("spdif-dit", -1, NULL, 0); + if (IS_ERR(data->txdev)) { + ret = PTR_ERR(data->txdev); + dev_err(&pdev->dev, "register dit failed: %d\n", ret); + goto end; + } + } + + if (of_property_read_bool(np, "spdif-in")) { + data->dai[num_links].name = "S/PDIF RX"; + data->dai[num_links].stream_name = "S/PDIF PCM Capture"; + data->dai[num_links].codec_dai_name = "dir-hifi"; + data->dai[num_links].codec_name = "spdif-dir"; + data->dai[num_links].cpu_of_node = spdif_np; + data->dai[num_links].platform_of_node = spdif_np; + num_links++; + + data->rxdev = platform_device_register_simple("spdif-dir", -1, NULL, 0); + if (IS_ERR(data->rxdev)) { + ret = PTR_ERR(data->rxdev); + dev_err(&pdev->dev, "register dir failed: %d\n", ret); + goto error_dit; + } + } + + if (!num_links) { + dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n"); + goto error_dir; + } + + data->card.dev = &pdev->dev; + data->card.num_links = num_links; + data->card.dai_link = data->dai; + + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + goto error_dir; + + ret = snd_soc_register_card(&data->card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret); + goto error_dir; + } + + platform_set_drvdata(pdev, data); + + goto end; + +error_dir: + if (data->rxdev) + platform_device_unregister(data->rxdev); +error_dit: + if (data->txdev) + platform_device_unregister(data->txdev); +end: + if (spdif_np) + of_node_put(spdif_np); + + return ret; +} + +static int imx_spdif_audio_remove(struct platform_device *pdev) +{ + struct imx_spdif_data *data = platform_get_drvdata(pdev); + + if (data->rxdev) + platform_device_unregister(data->rxdev); + if (data->txdev) + platform_device_unregister(data->txdev); + + snd_soc_unregister_card(&data->card); + + return 0; +} + +static const struct of_device_id imx_spdif_dt_ids[] = { + { .compatible = "fsl,imx-audio-spdif", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids); + +static struct platform_driver imx_spdif_driver = { + .driver = { + .name = "imx-spdif", + .owner = THIS_MODULE, + .of_match_table = imx_spdif_dt_ids, + }, + .probe = imx_spdif_audio_probe, + .remove = imx_spdif_audio_remove, +}; + +module_platform_driver(imx_spdif_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-spdif"); diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 51be3772cba..f58bcd85c07 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -571,13 +571,13 @@ static int imx_ssi_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); if (res) { imx_pcm_dma_params_init_data(&ssi->filter_data_tx, res->start, - false); + IMX_DMATYPE_SSI); } res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); if (res) { imx_pcm_dma_params_init_data(&ssi->filter_data_rx, res->start, - false); + IMX_DMATYPE_SSI); } platform_set_drvdata(pdev, ssi); @@ -595,7 +595,12 @@ static int imx_ssi_probe(struct platform_device *pdev) goto failed_register; } - ret = imx_pcm_fiq_init(pdev); + ssi->fiq_params.irq = ssi->irq; + ssi->fiq_params.base = ssi->base; + ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx; + ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx; + + ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params); if (ret) goto failed_pcm_fiq; diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h index d5003cefca8..fb1616ba8c5 100644 --- a/sound/soc/fsl/imx-ssi.h +++ b/sound/soc/fsl/imx-ssi.h @@ -209,6 +209,7 @@ struct imx_ssi { struct snd_dmaengine_dai_dma_data dma_params_tx; struct imx_dma_data filter_data_tx; struct imx_dma_data filter_data_rx; + struct imx_pcm_fiq_params fiq_params; int enabled; }; diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 52a36a90f4f..1d70e278e91 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -217,7 +217,8 @@ static int imx_wm8962_probe(struct platform_device *pdev) codec_dev = of_find_i2c_device_by_node(codec_np); if (!codec_dev || !codec_dev->driver) { dev_err(&pdev->dev, "failed to find codec platform device\n"); - return -EINVAL; + ret = -EINVAL; + goto fail; } data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 6cf8355a854..8c49147db84 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -105,6 +105,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev) static struct platform_driver asoc_simple_card = { .driver = { .name = "asoc-simple-card", + .owner = THIS_MODULE, }, .probe = asoc_simple_card_probe, .remove = asoc_simple_card_remove, @@ -112,6 +113,7 @@ static struct platform_driver asoc_simple_card = { module_platform_driver(asoc_simple_card); +MODULE_ALIAS("platform:asoc-simple-card"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ASoC Simple Sound Card"); MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig index c62d715235e..78ed4a42ad2 100644 --- a/sound/soc/kirkwood/Kconfig +++ b/sound/soc/kirkwood/Kconfig @@ -1,19 +1,15 @@ config SND_KIRKWOOD_SOC - tristate "SoC Audio for the Marvell Kirkwood chip" - depends on ARCH_KIRKWOOD + tristate "SoC Audio for the Marvell Kirkwood and Dove chips" + depends on ARCH_KIRKWOOD || ARCH_DOVE || COMPILE_TEST help Say Y or M if you want to add support for codecs attached to the Kirkwood I2S interface. You will also need to select the audio interfaces to support below. -config SND_KIRKWOOD_SOC_I2S - tristate - config SND_KIRKWOOD_SOC_OPENRD tristate "SoC Audio support for Kirkwood Openrd Client" - depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE) + depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE || COMPILE_TEST) depends on I2C - select SND_KIRKWOOD_SOC_I2S select SND_SOC_CS42L51 help Say Y if you want to add support for SoC audio on @@ -21,8 +17,7 @@ config SND_KIRKWOOD_SOC_OPENRD config SND_KIRKWOOD_SOC_T5325 tristate "SoC Audio support for HP t5325" - depends on SND_KIRKWOOD_SOC && MACH_T5325 && I2C - select SND_KIRKWOOD_SOC_I2S + depends on SND_KIRKWOOD_SOC && (MACH_T5325 || COMPILE_TEST) && I2C select SND_SOC_ALC5623 help Say Y if you want to add support for SoC audio on diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile index 3e62ae9e7bb..9e781385cb8 100644 --- a/sound/soc/kirkwood/Makefile +++ b/sound/soc/kirkwood/Makefile @@ -1,8 +1,6 @@ -snd-soc-kirkwood-objs := kirkwood-dma.o -snd-soc-kirkwood-i2s-objs := kirkwood-i2s.o +snd-soc-kirkwood-objs := kirkwood-dma.o kirkwood-i2s.o obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o -obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o snd-soc-openrd-objs := kirkwood-openrd.o snd-soc-t5325-objs := kirkwood-t5325.o diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index a9f14530c3d..b238434f92b 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -33,11 +33,11 @@ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE) -struct kirkwood_dma_priv { - struct snd_pcm_substream *play_stream; - struct snd_pcm_substream *rec_stream; - struct kirkwood_dma_data *data; -}; +static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs) +{ + struct snd_soc_pcm_runtime *soc_runtime = subs->private_data; + return snd_soc_dai_get_drvdata(soc_runtime->cpu_dai); +} static struct snd_pcm_hardware kirkwood_dma_snd_hw = { .info = (SNDRV_PCM_INFO_INTERLEAVED | @@ -51,7 +51,7 @@ static struct snd_pcm_hardware kirkwood_dma_snd_hw = { .rate_max = 384000, .channels_min = 1, .channels_max = 8, - .buffer_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES * KIRKWOOD_SND_MAX_PERIODS, + .buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES, .period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES, .period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES, .periods_min = KIRKWOOD_SND_MIN_PERIODS, @@ -63,8 +63,7 @@ static u64 kirkwood_dma_dmamask = DMA_BIT_MASK(32); static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id) { - struct kirkwood_dma_priv *prdata = dev_id; - struct kirkwood_dma_data *priv = prdata->data; + struct kirkwood_dma_data *priv = dev_id; unsigned long mask, status, cause; mask = readl(priv->io + KIRKWOOD_INT_MASK); @@ -89,10 +88,10 @@ static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id) writel(status, priv->io + KIRKWOOD_INT_CAUSE); if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES) - snd_pcm_period_elapsed(prdata->play_stream); + snd_pcm_period_elapsed(priv->substream_play); if (status & KIRKWOOD_INT_CAUSE_REC_BYTES) - snd_pcm_period_elapsed(prdata->rec_stream); + snd_pcm_period_elapsed(priv->substream_rec); return IRQ_HANDLED; } @@ -126,15 +125,10 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) { int err; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_platform *platform = soc_runtime->platform; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct kirkwood_dma_data *priv; - struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform); + struct kirkwood_dma_data *priv = kirkwood_priv(substream); const struct mbus_dram_target_info *dram; unsigned long addr; - priv = snd_soc_dai_get_dma_data(cpu_dai, substream); snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw); /* Ensure that all constraints linked to dma burst are fulfilled */ @@ -157,21 +151,11 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) if (err < 0) return err; - if (prdata == NULL) { - prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL); - if (prdata == NULL) - return -ENOMEM; - - prdata->data = priv; - + if (!priv->substream_play && !priv->substream_rec) { err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED, - "kirkwood-i2s", prdata); - if (err) { - kfree(prdata); + "kirkwood-i2s", priv); + if (err) return -EBUSY; - } - - snd_soc_platform_set_drvdata(platform, prdata); /* * Enable Error interrupts. We're only ack'ing them but @@ -183,11 +167,11 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) dram = mv_mbus_dram_info(); addr = substream->dma_buffer.addr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - prdata->play_stream = substream; + priv->substream_play = substream; kirkwood_dma_conf_mbus_windows(priv->io, KIRKWOOD_PLAYBACK_WIN, addr, dram); } else { - prdata->rec_stream = substream; + priv->substream_rec = substream; kirkwood_dma_conf_mbus_windows(priv->io, KIRKWOOD_RECORD_WIN, addr, dram); } @@ -197,27 +181,19 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) static int kirkwood_dma_close(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct snd_soc_platform *platform = soc_runtime->platform; - struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform); - struct kirkwood_dma_data *priv; - - priv = snd_soc_dai_get_dma_data(cpu_dai, substream); + struct kirkwood_dma_data *priv = kirkwood_priv(substream); - if (!prdata || !priv) + if (!priv) return 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - prdata->play_stream = NULL; + priv->substream_play = NULL; else - prdata->rec_stream = NULL; + priv->substream_rec = NULL; - if (!prdata->play_stream && !prdata->rec_stream) { + if (!priv->substream_play && !priv->substream_rec) { writel(0, priv->io + KIRKWOOD_ERR_MASK); - free_irq(priv->irq, prdata); - kfree(prdata); - snd_soc_platform_set_drvdata(platform, NULL); + free_irq(priv->irq, priv); } return 0; @@ -243,13 +219,9 @@ static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream) static int kirkwood_dma_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct kirkwood_dma_data *priv; + struct kirkwood_dma_data *priv = kirkwood_priv(substream); unsigned long size, count; - priv = snd_soc_dai_get_dma_data(cpu_dai, substream); - /* compute buffer size in term of "words" as requested in specs */ size = frames_to_bytes(runtime, runtime->buffer_size); size = (size>>2)-1; @@ -272,13 +244,9 @@ static int kirkwood_dma_prepare(struct snd_pcm_substream *substream) static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct kirkwood_dma_data *priv; + struct kirkwood_dma_data *priv = kirkwood_priv(substream); snd_pcm_uframes_t count; - priv = snd_soc_dai_get_dma_data(cpu_dai, substream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) count = bytes_to_frames(substream->runtime, readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT)); @@ -366,36 +334,8 @@ static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm) } } -static struct snd_soc_platform_driver kirkwood_soc_platform = { +struct snd_soc_platform_driver kirkwood_soc_platform = { .ops = &kirkwood_dma_ops, .pcm_new = kirkwood_dma_new, .pcm_free = kirkwood_dma_free_dma_buffers, }; - -static int kirkwood_soc_platform_probe(struct platform_device *pdev) -{ - return snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform); -} - -static int kirkwood_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver kirkwood_pcm_driver = { - .driver = { - .name = "kirkwood-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = kirkwood_soc_platform_probe, - .remove = kirkwood_soc_platform_remove, -}; - -module_platform_driver(kirkwood_pcm_driver); - -MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); -MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:kirkwood-pcm-audio"); diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 4c9dad3263c..7fce340ab3e 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -22,13 +22,12 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <linux/platform_data/asoc-kirkwood.h> +#include <linux/of.h> + #include "kirkwood.h" -#define DRV_NAME "kirkwood-i2s" +#define DRV_NAME "mvebu-audio" -#define KIRKWOOD_I2S_RATES \ - (SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) #define KIRKWOOD_I2S_FORMATS \ (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ @@ -105,14 +104,16 @@ static void kirkwood_set_rate(struct snd_soc_dai *dai, uint32_t clks_ctrl; if (rate == 44100 || rate == 48000 || rate == 96000) { - /* use internal dco for supported rates */ + /* use internal dco for the supported rates + * defined in kirkwood_i2s_dai */ dev_dbg(dai->dev, "%s: dco set rate = %lu\n", __func__, rate); kirkwood_set_dco(priv->io, rate); clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO; - } else if (!IS_ERR(priv->extclk)) { - /* use optional external clk for other rates */ + } else { + /* use the external clock for the other rates + * defined in kirkwood_i2s_dai_extclk */ dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n", __func__, rate, 256 * rate); clk_set_rate(priv->extclk, 256 * rate); @@ -199,8 +200,7 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF; priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK | - KIRKWOOD_PLAYCTL_I2S_EN | - KIRKWOOD_PLAYCTL_SPDIF_EN | + KIRKWOOD_PLAYCTL_ENABLE_MASK | KIRKWOOD_PLAYCTL_SIZE_MASK); priv->ctl_play |= ctl_play; } else { @@ -244,8 +244,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: /* configure */ ctl = priv->ctl_play; - value = ctl & ~(KIRKWOOD_PLAYCTL_I2S_EN | - KIRKWOOD_PLAYCTL_SPDIF_EN); + value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK; writel(value, priv->io + KIRKWOOD_PLAYCTL); /* enable interrupts */ @@ -267,7 +266,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, writel(value, priv->io + KIRKWOOD_INT_MASK); /* disable all playbacks */ - ctl &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN); + ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; writel(ctl, priv->io + KIRKWOOD_PLAYCTL); break; @@ -387,7 +386,7 @@ static int kirkwood_i2s_probe(struct snd_soc_dai *dai) /* disable playback/record */ value = readl(priv->io + KIRKWOOD_PLAYCTL); - value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN); + value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; writel(value, priv->io + KIRKWOOD_PLAYCTL); value = readl(priv->io + KIRKWOOD_RECCTL); @@ -398,11 +397,6 @@ static int kirkwood_i2s_probe(struct snd_soc_dai *dai) } -static int kirkwood_i2s_remove(struct snd_soc_dai *dai) -{ - return 0; -} - static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { .startup = kirkwood_i2s_startup, .trigger = kirkwood_i2s_trigger, @@ -413,17 +407,18 @@ static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { static struct snd_soc_dai_driver kirkwood_i2s_dai = { .probe = kirkwood_i2s_probe, - .remove = kirkwood_i2s_remove, .playback = { .channels_min = 1, .channels_max = 2, - .rates = KIRKWOOD_I2S_RATES, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, .formats = KIRKWOOD_I2S_FORMATS, }, .capture = { .channels_min = 1, .channels_max = 2, - .rates = KIRKWOOD_I2S_RATES, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, .formats = KIRKWOOD_I2S_FORMATS, }, .ops = &kirkwood_i2s_dai_ops, @@ -431,7 +426,6 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai = { static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk = { .probe = kirkwood_i2s_probe, - .remove = kirkwood_i2s_remove, .playback = { .channels_min = 1, .channels_max = 2, @@ -461,6 +455,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) struct snd_soc_dai_driver *soc_dai = &kirkwood_i2s_dai; struct kirkwood_dma_data *priv; struct resource *mem; + struct device_node *np = pdev->dev.of_node; int err; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); @@ -481,14 +476,16 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) return -ENXIO; } - if (!data) { - dev_err(&pdev->dev, "no platform data ?!\n"); + if (np) { + priv->burst = 128; /* might be 32 or 128 */ + } else if (data) { + priv->burst = data->burst; + } else { + dev_err(&pdev->dev, "no DT nor platform data ?!\n"); return -EINVAL; } - priv->burst = data->burst; - - priv->clk = devm_clk_get(&pdev->dev, NULL); + priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "no clock\n"); return PTR_ERR(priv->clk); @@ -498,10 +495,10 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) if (err < 0) return err; - priv->extclk = clk_get(&pdev->dev, "extclk"); + priv->extclk = devm_clk_get(&pdev->dev, "extclk"); if (!IS_ERR(priv->extclk)) { if (priv->extclk == priv->clk) { - clk_put(priv->extclk); + devm_clk_put(&pdev->dev, priv->extclk); priv->extclk = ERR_PTR(-EINVAL); } else { dev_info(&pdev->dev, "found external clock\n"); @@ -515,7 +512,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24; /* Select the burst size */ - if (data->burst == 32) { + if (priv->burst == 32) { priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32; priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32; } else { @@ -525,14 +522,22 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component, soc_dai, 1); - if (!err) - return 0; - dev_err(&pdev->dev, "snd_soc_register_component failed\n"); + if (err) { + dev_err(&pdev->dev, "snd_soc_register_component failed\n"); + goto err_component; + } - if (!IS_ERR(priv->extclk)) { - clk_disable_unprepare(priv->extclk); - clk_put(priv->extclk); + err = snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform); + if (err) { + dev_err(&pdev->dev, "snd_soc_register_platform failed\n"); + goto err_platform; } + return 0; + err_platform: + snd_soc_unregister_component(&pdev->dev); + err_component: + if (!IS_ERR(priv->extclk)) + clk_disable_unprepare(priv->extclk); clk_disable_unprepare(priv->clk); return err; @@ -542,23 +547,31 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev) { struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - if (!IS_ERR(priv->extclk)) { + if (!IS_ERR(priv->extclk)) clk_disable_unprepare(priv->extclk); - clk_put(priv->extclk); - } clk_disable_unprepare(priv->clk); return 0; } +#ifdef CONFIG_OF +static struct of_device_id mvebu_audio_of_match[] = { + { .compatible = "marvell,mvebu-audio" }, + { } +}; +MODULE_DEVICE_TABLE(of, mvebu_audio_of_match); +#endif + static struct platform_driver kirkwood_i2s_driver = { .probe = kirkwood_i2s_dev_probe, .remove = kirkwood_i2s_dev_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mvebu_audio_of_match), }, }; @@ -568,4 +581,4 @@ module_platform_driver(kirkwood_i2s_driver); MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>"); MODULE_DESCRIPTION("Kirkwood I2S SoC Interface"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:kirkwood-i2s"); +MODULE_ALIAS("platform:mvebu-audio"); diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c index b979c715471..025be0e9716 100644 --- a/sound/soc/kirkwood/kirkwood-openrd.c +++ b/sound/soc/kirkwood/kirkwood-openrd.c @@ -16,9 +16,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <sound/soc.h> -#include <mach/kirkwood.h> #include <linux/platform_data/asoc-kirkwood.h> -#include <asm/mach-types.h> #include "../codecs/cs42l51.h" static int openrd_client_hw_params(struct snd_pcm_substream *substream, @@ -54,8 +52,8 @@ static struct snd_soc_dai_link openrd_client_dai[] = { { .name = "CS42L51", .stream_name = "CS42L51 HiFi", - .cpu_dai_name = "kirkwood-i2s", - .platform_name = "kirkwood-pcm-audio", + .cpu_dai_name = "mvebu-audio", + .platform_name = "mvebu-audio", .codec_dai_name = "cs42l51-hifi", .codec_name = "cs42l51-codec.0-004a", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, diff --git a/sound/soc/kirkwood/kirkwood-t5325.c b/sound/soc/kirkwood/kirkwood-t5325.c index 1d0ed6f8add..27545b0c485 100644 --- a/sound/soc/kirkwood/kirkwood-t5325.c +++ b/sound/soc/kirkwood/kirkwood-t5325.c @@ -15,9 +15,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <sound/soc.h> -#include <mach/kirkwood.h> #include <linux/platform_data/asoc-kirkwood.h> -#include <asm/mach-types.h> #include "../codecs/alc5623.h" static int t5325_hw_params(struct snd_pcm_substream *substream, @@ -70,8 +68,8 @@ static struct snd_soc_dai_link t5325_dai[] = { { .name = "ALC5621", .stream_name = "ALC5621 HiFi", - .cpu_dai_name = "kirkwood-i2s", - .platform_name = "kirkwood-pcm-audio", + .cpu_dai_name = "mvebu-audio", + .platform_name = "mvebu-audio", .codec_dai_name = "alc5621-hifi", .codec_name = "alc562x-codec.0-001a", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index 4d92637ddb3..f8e1ccc1c58 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -54,7 +54,7 @@ #define KIRKWOOD_PLAYCTL_MONO_OFF (0<<5) #define KIRKWOOD_PLAYCTL_I2S_MUTE (1<<7) #define KIRKWOOD_PLAYCTL_SPDIF_EN (1<<4) -#define KIRKWOOD_PLAYCTL_I2S_EN (1<<3) +#define KIRKWOOD_PLAYCTL_I2S_EN (1<<3) #define KIRKWOOD_PLAYCTL_SIZE_MASK (7<<0) #define KIRKWOOD_PLAYCTL_SIZE_16 (7<<0) #define KIRKWOOD_PLAYCTL_SIZE_16_C (3<<0) @@ -62,6 +62,9 @@ #define KIRKWOOD_PLAYCTL_SIZE_24 (1<<0) #define KIRKWOOD_PLAYCTL_SIZE_32 (0<<0) +#define KIRKWOOD_PLAYCTL_ENABLE_MASK (KIRKWOOD_PLAYCTL_SPDIF_EN | \ + KIRKWOOD_PLAYCTL_I2S_EN) + #define KIRKWOOD_PLAY_BUF_ADDR 0x1104 #define KIRKWOOD_PLAY_BUF_SIZE 0x1108 #define KIRKWOOD_PLAY_BYTE_COUNT 0x110C @@ -122,6 +125,8 @@ #define KIRKWOOD_SND_MAX_PERIODS 16 #define KIRKWOOD_SND_MIN_PERIOD_BYTES 0x4000 #define KIRKWOOD_SND_MAX_PERIOD_BYTES 0x4000 +#define KIRKWOOD_SND_MAX_BUFFER_BYTES (KIRKWOOD_SND_MAX_PERIOD_BYTES \ + * KIRKWOOD_SND_MAX_PERIODS) struct kirkwood_dma_data { void __iomem *io; @@ -129,8 +134,12 @@ struct kirkwood_dma_data { struct clk *extclk; uint32_t ctl_play; uint32_t ctl_rec; + struct snd_pcm_substream *substream_play; + struct snd_pcm_substream *substream_rec; int irq; int burst; }; +extern struct snd_soc_platform_driver kirkwood_soc_platform; + #endif diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig index 78d321cbe8b..219235c0221 100644 --- a/sound/soc/mxs/Kconfig +++ b/sound/soc/mxs/Kconfig @@ -1,6 +1,7 @@ menuconfig SND_MXS_SOC tristate "SoC Audio for Freescale MXS CPUs" - depends on ARCH_MXS + depends on ARCH_MXS || COMPILE_TEST + depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 54511c5e6a7..b56b8a0e8de 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -31,7 +31,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <asm/mach-types.h> #include "mxs-saif.h" diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 1b134d72f12..ce084eb10c4 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -25,7 +25,6 @@ #include <sound/soc.h> #include <sound/jack.h> #include <sound/soc-dapm.h> -#include <asm/mach-types.h> #include "../codecs/sgtl5000.h" #include "mxs-saif.h" @@ -51,18 +50,27 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, } /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */ - if (mclk < 8000000 || mclk > 27000000) + if (mclk < 8000000 || mclk > 27000000) { + dev_err(codec_dai->dev, "Invalid mclk frequency: %u.%03uMHz\n", + mclk / 1000000, mclk / 1000 % 1000); return -EINVAL; + } /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */ ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0); - if (ret) + if (ret) { + dev_err(codec_dai->dev, "Failed to set sysclk to %u.%03uMHz\n", + mclk / 1000000, mclk / 1000 % 1000); return ret; + } /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */ ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0); - if (ret) + if (ret) { + dev_err(cpu_dai->dev, "Failed to set sysclk to %u.%03uMHz\n", + mclk / 1000000, mclk / 1000 % 1000); return ret; + } /* set codec to slave mode */ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | @@ -70,13 +78,19 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, dai_format); - if (ret) + if (ret) { + dev_err(codec_dai->dev, "Failed to set dai format to %08x\n", + dai_format); return ret; + } /* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); - if (ret) + if (ret) { + dev_err(cpu_dai->dev, "Failed to set dai format to %08x\n", + dai_format); return ret; + } return 0; } @@ -154,8 +168,10 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) * should be >= 8MHz and <= 27M. */ ret = mxs_saif_get_mclk(0, 44100 * 256, 44100); - if (ret) + if (ret) { + dev_err(&pdev->dev, "failed to get mclk\n"); return ret; + } card->dev = &pdev->dev; platform_set_drvdata(pdev, card); diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c index f4c2417a873..8987bf987e5 100644 --- a/sound/soc/nuc900/nuc900-ac97.c +++ b/sound/soc/nuc900/nuc900-ac97.c @@ -333,9 +333,6 @@ static int nuc900_ac97_drvprobe(struct platform_device *pdev) spin_lock_init(&nuc900_audio->lock); nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!nuc900_audio->res) - return ret; - nuc900_audio->mmio = devm_ioremap_resource(&pdev->dev, nuc900_audio->res); if (IS_ERR(nuc900_audio->mmio)) diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 9f5d55e6b17..daa78a0095f 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -1,7 +1,7 @@ config SND_OMAP_SOC tristate "SoC Audio for the Texas Instruments OMAP chips" - depends on ARCH_OMAP && DMA_OMAP - select SND_SOC_DMAENGINE_PCM + depends on (ARCH_OMAP && DMA_OMAP) || (ARCH_ARM && COMPILE_TEST) + select SND_DMAENGINE_PCM config SND_OMAP_SOC_DMIC tristate @@ -26,7 +26,7 @@ config SND_OMAP_SOC_N810 config SND_OMAP_SOC_RX51 tristate "SoC Audio support for Nokia RX-51" - depends on SND_OMAP_SOC && MACH_NOKIA_RX51 + depends on SND_OMAP_SOC && ARCH_ARM && (MACH_NOKIA_RX51 || COMPILE_TEST) select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X select SND_SOC_TPA6130A2 @@ -87,7 +87,7 @@ config SND_OMAP_SOC_OMAP_TWL4030 config SND_OMAP_SOC_OMAP_ABE_TWL6040 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" - depends on TWL6040_CORE && SND_OMAP_SOC && ARCH_OMAP4 + depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || COMPILE_TEST) select SND_OMAP_SOC_DMIC select SND_OMAP_SOC_MCPDM select SND_SOC_TWL6040 diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 361e4c03646..83433fdea32 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -781,7 +781,7 @@ static ssize_t prop##_store(struct device *dev, \ unsigned long val; \ int status; \ \ - status = strict_strtoul(buf, 0, &val); \ + status = kstrtoul(buf, 0, &val); \ if (status) \ return status; \ \ diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index 70cd5c7b2e1..ebb13906b3a 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -23,7 +23,6 @@ #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/mfd/twl6040.h> -#include <linux/platform_data/omap-abe-twl6040.h> #include <linux/module.h> #include <linux/of.h> @@ -166,19 +165,10 @@ static const struct snd_soc_dapm_route audio_map[] = { {"AFMR", NULL, "Line In"}, }; -static inline void twl6040_disconnect_pin(struct snd_soc_dapm_context *dapm, - int connected, char *pin) -{ - if (!connected) - snd_soc_dapm_disable_pin(dapm, pin); -} - static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; - struct snd_soc_dapm_context *dapm = &codec->dapm; - struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev); struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); int hs_trim; int ret = 0; @@ -203,24 +193,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET); } - /* - * NULL pdata means we booted with DT. In this case the routing is - * provided and the card is fully routed, no need to mark pins. - */ - if (!pdata) - return ret; - - /* Disable not connected paths if not used */ - twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); - twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk"); - twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk"); - twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out"); - twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vibrator"); - twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In"); - return ret; } @@ -274,13 +246,18 @@ static struct snd_soc_card omap_abe_card = { static int omap_abe_probe(struct platform_device *pdev) { - struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev); struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &omap_abe_card; + struct device_node *dai_node; struct abe_twl6040 *priv; int num_links = 0; int ret = 0; + if (!node) { + dev_err(&pdev->dev, "of node is missing.\n"); + return -ENODEV; + } + card->dev = &pdev->dev; priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL); @@ -289,78 +266,50 @@ static int omap_abe_probe(struct platform_device *pdev) priv->dmic_codec_dev = ERR_PTR(-EINVAL); - if (node) { - struct device_node *dai_node; - - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } + if (snd_soc_of_parse_card_name(card, "ti,model")) { + dev_err(&pdev->dev, "Card name is not provided\n"); + return -ENODEV; + } - ret = snd_soc_of_parse_audio_routing(card, - "ti,audio-routing"); - if (ret) { - dev_err(&pdev->dev, - "Error while parsing DAPM routing\n"); - return ret; - } + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); + return ret; + } - dai_node = of_parse_phandle(node, "ti,mcpdm", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McPDM node is not provided\n"); - return -EINVAL; - } - abe_twl6040_dai_links[0].cpu_dai_name = NULL; - abe_twl6040_dai_links[0].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,mcpdm", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McPDM node is not provided\n"); + return -EINVAL; + } + abe_twl6040_dai_links[0].cpu_dai_name = NULL; + abe_twl6040_dai_links[0].cpu_of_node = dai_node; - dai_node = of_parse_phandle(node, "ti,dmic", 0); - if (dai_node) { - num_links = 2; - abe_twl6040_dai_links[1].cpu_dai_name = NULL; - abe_twl6040_dai_links[1].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,dmic", 0); + if (dai_node) { + num_links = 2; + abe_twl6040_dai_links[1].cpu_dai_name = NULL; + abe_twl6040_dai_links[1].cpu_of_node = dai_node; - priv->dmic_codec_dev = platform_device_register_simple( + priv->dmic_codec_dev = platform_device_register_simple( "dmic-codec", -1, NULL, 0); - if (IS_ERR(priv->dmic_codec_dev)) { - dev_err(&pdev->dev, - "Can't instantiate dmic-codec\n"); - return PTR_ERR(priv->dmic_codec_dev); - } - } else { - num_links = 1; - } - - priv->jack_detection = of_property_read_bool(node, - "ti,jack-detection"); - of_property_read_u32(node, "ti,mclk-freq", - &priv->mclk_freq); - if (!priv->mclk_freq) { - dev_err(&pdev->dev, "MCLK frequency not provided\n"); - ret = -EINVAL; - goto err_unregister; + if (IS_ERR(priv->dmic_codec_dev)) { + dev_err(&pdev->dev, "Can't instantiate dmic-codec\n"); + return PTR_ERR(priv->dmic_codec_dev); } - - omap_abe_card.fully_routed = 1; - } else if (pdata) { - if (pdata->card_name) { - card->name = pdata->card_name; - } else { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - if (pdata->has_dmic) - num_links = 2; - else - num_links = 1; - - priv->jack_detection = pdata->jack_detection; - priv->mclk_freq = pdata->mclk_freq; } else { - dev_err(&pdev->dev, "Missing pdata\n"); - return -ENODEV; + num_links = 1; + } + + priv->jack_detection = of_property_read_bool(node, "ti,jack-detection"); + of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq); + if (!priv->mclk_freq) { + dev_err(&pdev->dev, "MCLK frequency not provided\n"); + ret = -EINVAL; + goto err_unregister; } + card->fully_routed = 1; if (!priv->mclk_freq) { dev_err(&pdev->dev, "MCLK frequency missing\n"); diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 4db1f8e6e17..12e566be379 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -480,15 +480,12 @@ static int asoc_dmic_probe(struct platform_device *pdev) dmic->dma_data.filter_data = "up_link"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (!res) { - dev_err(dmic->dev, "invalid memory resource\n"); - ret = -ENODEV; + dmic->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dmic->io_base)) { + ret = PTR_ERR(dmic->io_base); goto err_put_clk; } - dmic->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dmic->io_base)) - return PTR_ERR(dmic->io_base); ret = snd_soc_register_component(&pdev->dev, &omap_dmic_component, &omap_dmic_dai, 1); diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 7483efb6dc6..6c19bba2357 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -433,6 +433,11 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, /* Sample rate generator drives the FS */ regs->srgr2 |= FSGM; break; + case SND_SOC_DAIFMT_CBM_CFS: + /* McBSP slave. FS clock as output */ + regs->srgr2 |= FSGM; + regs->pcr0 |= FSXM; + break; case SND_SOC_DAIFMT_CBM_CFM: /* McBSP slave */ break; diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index a49dc52f8ab..90d2a7cd256 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -480,9 +480,6 @@ static int asoc_mcpdm_probe(struct platform_device *pdev) mcpdm->dma_data[1].filter_data = "up_link"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (res == NULL) - return -ENOMEM; - mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(mcpdm->io_base)) return PTR_ERR(mcpdm->io_base); diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index b3580946754..4db74a083db 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -11,7 +11,7 @@ config SND_PXA2XX_SOC config SND_MMP_SOC bool "Soc Audio for Marvell MMP chips" depends on ARCH_MMP - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM select SND_ARM help Say Y if you want to add support for codecs attached to diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 4ad76099dd4..5b7d969f89a 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -129,6 +129,7 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = { /* audio machine driver */ static struct snd_soc_card brownstone = { .name = "brownstone", + .owner = THIS_MODULE, .dai_link = brownstone_wm8994_dai, .num_links = ARRAY_SIZE(brownstone_wm8994_dai), diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 97b711e1282..bbea7780eac 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -56,8 +56,6 @@ #include "pxa2xx-ac97.h" #include "../codecs/wm9713.h" -#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) - #define AC97_GPIO_PULL 0x58 /* Use GPIO8 for rear speaker amplifier */ @@ -133,10 +131,11 @@ static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd) unsigned short reg; /* Add mioa701 specific widgets */ - snd_soc_dapm_new_controls(dapm, ARRAY_AND_SIZE(mioa701_dapm_widgets)); + snd_soc_dapm_new_controls(dapm, mioa701_dapm_widgets, + ARRAY_SIZE(mioa701_dapm_widgets)); /* Set up mioa701 specific audio path audio_mapnects */ - snd_soc_dapm_add_routes(dapm, ARRAY_AND_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* Prepare GPIO8 for rear speaker amplifier */ reg = codec->driver->read(codec, AC97_GPIO_CFG); diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 5d57e071cdf..8235e231d89 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -17,6 +17,7 @@ #include <linux/dmaengine.h> #include <linux/platform_data/dma-mmp_tdma.h> #include <linux/platform_data/mmp_audio.h> + #include <sound/pxa2xx-lib.h> #include <sound/core.h> #include <sound/pcm.h> @@ -67,7 +68,7 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, { struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; struct dma_slave_config slave_config; int ret; @@ -80,10 +81,10 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, return ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.dst_addr = dma_params->dev_addr; + slave_config.dst_addr = dma_params->addr; slave_config.dst_maxburst = 4; } else { - slave_config.src_addr = dma_params->dev_addr; + slave_config.src_addr = dma_params->addr; slave_config.src_maxburst = 4; } diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index 62142ce367c..41752a5fe3b 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -27,12 +27,15 @@ #include <linux/slab.h> #include <linux/pxa2xx_ssp.h> #include <linux/io.h> +#include <linux/dmaengine.h> + #include <sound/core.h> #include <sound/pcm.h> #include <sound/initval.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include "mmp-sspa.h" /* @@ -40,7 +43,7 @@ */ struct sspa_priv { struct ssp_device *sspa; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; struct clk *audio_clk; struct clk *sysclk; int dai_fmt; @@ -266,7 +269,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai); struct ssp_device *sspa = sspa_priv->sspa; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; u32 sspa_ctrl; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -309,7 +312,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, } dma_params = &sspa_priv->dma_params[substream->stream]; - dma_params->dev_addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? (sspa->phys_base + SSPA_TXD) : (sspa->phys_base + SSPA_RXD); snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params); @@ -425,14 +428,12 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev) return -ENOMEM; priv->dma_params = devm_kzalloc(&pdev->dev, - 2 * sizeof(struct pxa2xx_pcm_dma_params), GFP_KERNEL); + 2 * sizeof(struct snd_dmaengine_dai_dma_data), + GFP_KERNEL); if (priv->dma_params == NULL) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) - return -ENOMEM; - priv->sspa->mmio_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->sspa->mmio_base)) return PTR_ERR(priv->sspa->mmio_base); diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 6f4dd7543e8..a3119a00d8f 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -21,6 +21,8 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/pxa2xx_ssp.h> +#include <linux/of.h> +#include <linux/dmaengine.h> #include <asm/irq.h> @@ -30,9 +32,9 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include <mach/hardware.h> -#include <mach/dma.h> #include "../../arm/pxa2xx-pcm.h" #include "pxa-ssp.h" @@ -79,27 +81,13 @@ static void pxa_ssp_disable(struct ssp_device *ssp) __raw_writel(sscr0, ssp->mmio_base + SSCR0); } -struct pxa2xx_pcm_dma_data { - struct pxa2xx_pcm_dma_params params; - char name[20]; -}; - static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4, - int out, struct pxa2xx_pcm_dma_params *dma_data) + int out, struct snd_dmaengine_dai_dma_data *dma) { - struct pxa2xx_pcm_dma_data *dma; - - dma = container_of(dma_data, struct pxa2xx_pcm_dma_data, params); - - snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id, - width4 ? "32-bit" : "16-bit", out ? "out" : "in"); - - dma->params.name = dma->name; - dma->params.drcmr = &DRCMR(out ? ssp->drcmr_tx : ssp->drcmr_rx); - dma->params.dcmd = (out ? (DCMD_INCSRCADDR | DCMD_FLOWTRG) : - (DCMD_INCTRGADDR | DCMD_FLOWSRC)) | - (width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16; - dma->params.dev_addr = ssp->phys_base + SSDR; + dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES : + DMA_SLAVE_BUSWIDTH_2_BYTES; + dma->maxburst = 16; + dma->addr = ssp->phys_base + SSDR; } static int pxa_ssp_startup(struct snd_pcm_substream *substream, @@ -107,7 +95,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, { struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; - struct pxa2xx_pcm_dma_data *dma; + struct snd_dmaengine_dai_dma_data *dma; int ret = 0; if (!cpu_dai->active) { @@ -115,10 +103,14 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, pxa_ssp_disable(ssp); } - dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL); + dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); if (!dma) return -ENOMEM; - snd_soc_dai_set_dma_data(cpu_dai, substream, &dma->params); + + dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &ssp->drcmr_tx : &ssp->drcmr_rx; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma); return ret; } @@ -559,7 +551,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, u32 sspsp; int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); @@ -719,6 +711,7 @@ static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd, static int pxa_ssp_probe(struct snd_soc_dai *dai) { + struct device *dev = dai->dev; struct ssp_priv *priv; int ret; @@ -726,10 +719,26 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai) if (!priv) return -ENOMEM; - priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); - if (priv->ssp == NULL) { - ret = -ENODEV; - goto err_priv; + if (dev->of_node) { + struct device_node *ssp_handle; + + ssp_handle = of_parse_phandle(dev->of_node, "port", 0); + if (!ssp_handle) { + dev_err(dev, "unable to get 'port' phandle\n"); + return -ENODEV; + } + + priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio"); + if (priv->ssp == NULL) { + ret = -ENODEV; + goto err_priv; + } + } else { + priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); + if (priv->ssp == NULL) { + ret = -ENODEV; + goto err_priv; + } } priv->dai_fmt = (unsigned int) -1; @@ -798,6 +807,12 @@ static const struct snd_soc_component_driver pxa_ssp_component = { .name = "pxa-ssp", }; +#ifdef CONFIG_OF +static const struct of_device_id pxa_ssp_of_ids[] = { + { .compatible = "mrvl,pxa-ssp-dai" }, +}; +#endif + static int asoc_ssp_probe(struct platform_device *pdev) { return snd_soc_register_component(&pdev->dev, &pxa_ssp_component, @@ -812,8 +827,9 @@ static int asoc_ssp_remove(struct platform_device *pdev) static struct platform_driver asoc_ssp_driver = { .driver = { - .name = "pxa-ssp-dai", - .owner = THIS_MODULE, + .name = "pxa-ssp-dai", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pxa_ssp_of_ids), }, .probe = asoc_ssp_probe, diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 1475515712e..f1059d999de 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -14,15 +14,16 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/dmaengine.h> #include <sound/core.h> #include <sound/ac97_codec.h> #include <sound/soc.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include <mach/hardware.h> #include <mach/regs-ac97.h> -#include <mach/dma.h> #include <mach/audio.h> #include "pxa2xx-ac97.h" @@ -48,44 +49,44 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { - .name = "AC97 PCM Stereo out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { - .name = "AC97 PCM Stereo in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { - .name = "AC97 Aux PCM (Slot 5) Mono out", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(10), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { - .name = "AC97 Aux PCM (Slot 5) Mono in", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(9), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { - .name = "AC97 Mic PCM (Slot 6) Mono in", - .dev_addr = __PREG(MCDR), - .drcmr = &DRCMR(8), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { + .addr = __PREG(MCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; #ifdef CONFIG_PM @@ -119,7 +120,7 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &pxa2xx_ac97_pcm_stereo_out; @@ -135,7 +136,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &pxa2xx_ac97_pcm_aux_mono_out; diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index f7ca7166411..d5340a08885 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -23,9 +23,9 @@ #include <sound/initval.h> #include <sound/soc.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include <mach/hardware.h> -#include <mach/dma.h> #include <mach/audio.h> #include "pxa2xx-i2s.h" @@ -82,20 +82,20 @@ static struct pxa_i2s_port pxa_i2s; static struct clk *clk_i2s; static int clk_ena = 0; -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { - .name = "I2S PCM Stereo out", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(3), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { - .name = "I2S PCM Stereo in", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(2), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_in_req, }; static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, @@ -163,7 +163,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; BUG_ON(IS_ERR(clk_i2s)); clk_prepare_enable(clk_i2s); diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index ecff116cb7b..806da27b8b6 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -12,10 +12,13 @@ #include <linux/dma-mapping.h> #include <linux/module.h> +#include <linux/dmaengine.h> +#include <linux/of.h> #include <sound/core.h> #include <sound/soc.h> #include <sound/pxa2xx-lib.h> +#include <sound/dmaengine_pcm.h> #include "../../arm/pxa2xx-pcm.h" @@ -25,7 +28,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma; + struct snd_dmaengine_dai_dma_data *dma; int ret; dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); @@ -39,7 +42,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, * with different params */ if (prtd->params == NULL) { prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; @@ -47,7 +50,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, } else if (prtd->params != dma) { pxa_free_dma(prtd->dma_ch); prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; @@ -131,10 +134,18 @@ static int pxa2xx_soc_platform_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id snd_soc_pxa_audio_match[] = { + { .compatible = "mrvl,pxa-pcm-audio" }, + { } +}; +#endif + static struct platform_driver pxa_pcm_driver = { .driver = { - .name = "pxa-pcm-audio", - .owner = THIS_MODULE, + .name = "pxa-pcm-audio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(snd_soc_pxa_audio_match), }, .probe = pxa2xx_soc_platform_probe, diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index f4ea4f6663a..13c9ee0cb83 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -122,6 +122,7 @@ static struct snd_soc_dai_link ttc_pm860x_hifi_dai[] = { /* ttc/td audio machine driver */ static struct snd_soc_card ttc_dkb_card = { .name = "ttc-dkb-hifi", + .owner = THIS_MODULE, .dai_link = ttc_pm860x_hifi_dai, .num_links = ARRAY_SIZE(ttc_pm860x_hifi_dai), diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c index 58cfb1eb7dd..945e8abdc10 100644 --- a/sound/soc/s6000/s6105-ipcam.c +++ b/sound/soc/s6000/s6105-ipcam.c @@ -192,7 +192,7 @@ static struct snd_soc_card snd_soc_card_s6105 = { .num_links = 1, }; -static struct s6000_snd_platform_data __initdata s6105_snd_data = { +static struct s6000_snd_platform_data s6105_snd_data __initdata = { .wide = 0, .channel_in = 0, .channel_out = 1, diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index 2dd623fa388..2acf987844e 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -404,18 +404,13 @@ static int s3c_ac97_probe(struct platform_device *pdev) return -ENXIO; } - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - dev_err(&pdev->dev, "Unable to get register resource\n"); - return -ENXIO; - } - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq_res) { dev_err(&pdev->dev, "AC97 IRQ not provided!\n"); return -ENXIO; } + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res); if (IS_ERR(s3c_ac97.regs)) return PTR_ERR(s3c_ac97.regs); @@ -462,7 +457,7 @@ static int s3c_ac97_probe(struct platform_device *pdev) if (ret) goto err5; - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); goto err6; @@ -485,7 +480,7 @@ static int s3c_ac97_remove(struct platform_device *pdev) { struct resource *irq_res; - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 21b79262010..9338d11e921 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -90,6 +90,13 @@ static void dma_enqueue(struct snd_pcm_substream *substream) dma_info.period = prtd->dma_period; dma_info.len = prtd->dma_period*limit; + if (dma_info.cap == DMA_CYCLIC) { + dma_info.buf = pos; + prtd->params->ops->prepare(prtd->params->ch, &dma_info); + prtd->dma_loaded += limit; + return; + } + while (prtd->dma_loaded < limit) { pr_debug("dma_loaded: %d\n", prtd->dma_loaded); @@ -176,6 +183,10 @@ static int dma_hw_params(struct snd_pcm_substream *substream, prtd->params->ch = prtd->params->ops->request( prtd->params->channel, &req, rtd->cpu_dai->dev, prtd->params->ch_name); + if (!prtd->params->ch) { + pr_err("Failed to allocate DMA channel\n"); + return -ENXIO; + } prtd->params->ops->config(prtd->params->ch, &config); } @@ -433,17 +444,17 @@ static struct snd_soc_platform_driver samsung_asoc_platform = { .pcm_free = dma_free_dma_buffers, }; -int asoc_dma_platform_register(struct device *dev) +int samsung_asoc_dma_platform_register(struct device *dev) { return snd_soc_register_platform(dev, &samsung_asoc_platform); } -EXPORT_SYMBOL_GPL(asoc_dma_platform_register); +EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register); -void asoc_dma_platform_unregister(struct device *dev) +void samsung_asoc_dma_platform_unregister(struct device *dev) { snd_soc_unregister_platform(dev); } -EXPORT_SYMBOL_GPL(asoc_dma_platform_unregister); +EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister); MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); MODULE_DESCRIPTION("Samsung ASoC DMA Driver"); diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index 189a7a6d502..0e86315a3ea 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -22,7 +22,7 @@ struct s3c_dma_params { char *ch_name; }; -int asoc_dma_platform_register(struct device *dev); -void asoc_dma_platform_unregister(struct device *dev); +int samsung_asoc_dma_platform_register(struct device *dev); +void samsung_asoc_dma_platform_unregister(struct device *dev); #endif diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index c0e6d9a19ef..821a5023100 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -31,6 +31,10 @@ #define I2SLVL1ADDR 0x34 #define I2SLVL2ADDR 0x38 #define I2SLVL3ADDR 0x3c +#define I2SSTR1 0x40 +#define I2SVER 0x44 +#define I2SFIC2 0x48 +#define I2STDM 0x4c #define CON_RSTCLR (1 << 31) #define CON_FRXOFSTATUS (1 << 26) @@ -95,24 +99,39 @@ #define MOD_RXONLY (1 << 8) #define MOD_TXRX (2 << 8) #define MOD_MASK (3 << 8) -#define MOD_LR_LLOW (0 << 7) -#define MOD_LR_RLOW (1 << 7) -#define MOD_SDF_IIS (0 << 5) -#define MOD_SDF_MSB (1 << 5) -#define MOD_SDF_LSB (2 << 5) -#define MOD_SDF_MASK (3 << 5) -#define MOD_RCLK_256FS (0 << 3) -#define MOD_RCLK_512FS (1 << 3) -#define MOD_RCLK_384FS (2 << 3) -#define MOD_RCLK_768FS (3 << 3) -#define MOD_RCLK_MASK (3 << 3) -#define MOD_BCLK_32FS (0 << 1) -#define MOD_BCLK_48FS (1 << 1) -#define MOD_BCLK_16FS (2 << 1) -#define MOD_BCLK_24FS (3 << 1) -#define MOD_BCLK_MASK (3 << 1) +#define MOD_LRP_SHIFT 7 +#define MOD_LR_LLOW 0 +#define MOD_LR_RLOW 1 +#define MOD_SDF_SHIFT 5 +#define MOD_SDF_IIS 0 +#define MOD_SDF_MSB 1 +#define MOD_SDF_LSB 2 +#define MOD_SDF_MASK 3 +#define MOD_RCLK_SHIFT 3 +#define MOD_RCLK_256FS 0 +#define MOD_RCLK_512FS 1 +#define MOD_RCLK_384FS 2 +#define MOD_RCLK_768FS 3 +#define MOD_RCLK_MASK 3 +#define MOD_BCLK_SHIFT 1 +#define MOD_BCLK_32FS 0 +#define MOD_BCLK_48FS 1 +#define MOD_BCLK_16FS 2 +#define MOD_BCLK_24FS 3 +#define MOD_BCLK_MASK 3 #define MOD_8BIT (1 << 0) +#define EXYNOS5420_MOD_LRP_SHIFT 15 +#define EXYNOS5420_MOD_SDF_SHIFT 6 +#define EXYNOS5420_MOD_RCLK_SHIFT 4 +#define EXYNOS5420_MOD_BCLK_SHIFT 0 +#define EXYNOS5420_MOD_BCLK_64FS 4 +#define EXYNOS5420_MOD_BCLK_96FS 5 +#define EXYNOS5420_MOD_BCLK_128FS 6 +#define EXYNOS5420_MOD_BCLK_192FS 7 +#define EXYNOS5420_MOD_BCLK_256FS 8 +#define EXYNOS5420_MOD_BCLK_MASK 0xf + #define MOD_CDCLKCON (1 << 12) #define PSR_PSREN (1 << 15) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 959c702235c..b302f3b7a58 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -40,6 +40,7 @@ enum samsung_dai_type { struct samsung_i2s_dai_data { int dai_type; + u32 quirks; }; struct i2s_dai { @@ -198,7 +199,13 @@ static inline bool is_manager(struct i2s_dai *i2s) /* Read RCLK of I2S (in multiples of LRCLK) */ static inline unsigned get_rfs(struct i2s_dai *i2s) { - u32 rfs = (readl(i2s->addr + I2SMOD) >> 3) & 0x3; + u32 rfs; + + if (i2s->quirks & QUIRK_SUPPORTS_TDM) + rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT; + else + rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); + rfs &= MOD_RCLK_MASK; switch (rfs) { case 3: return 768; @@ -212,21 +219,26 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) { u32 mod = readl(i2s->addr + I2SMOD); + int rfs_shift; - mod &= ~MOD_RCLK_MASK; + if (i2s->quirks & QUIRK_SUPPORTS_TDM) + rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT; + else + rfs_shift = MOD_RCLK_SHIFT; + mod &= ~(MOD_RCLK_MASK << rfs_shift); switch (rfs) { case 768: - mod |= MOD_RCLK_768FS; + mod |= (MOD_RCLK_768FS << rfs_shift); break; case 512: - mod |= MOD_RCLK_512FS; + mod |= (MOD_RCLK_512FS << rfs_shift); break; case 384: - mod |= MOD_RCLK_384FS; + mod |= (MOD_RCLK_384FS << rfs_shift); break; default: - mod |= MOD_RCLK_256FS; + mod |= (MOD_RCLK_256FS << rfs_shift); break; } @@ -236,9 +248,22 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) /* Read Bit-Clock of I2S (in multiples of LRCLK) */ static inline unsigned get_bfs(struct i2s_dai *i2s) { - u32 bfs = (readl(i2s->addr + I2SMOD) >> 1) & 0x3; + u32 bfs; + + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT; + bfs &= EXYNOS5420_MOD_BCLK_MASK; + } else { + bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; + bfs &= MOD_BCLK_MASK; + } switch (bfs) { + case 8: return 256; + case 7: return 192; + case 6: return 128; + case 5: return 96; + case 4: return 64; case 3: return 24; case 2: return 16; case 1: return 48; @@ -250,21 +275,50 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) { u32 mod = readl(i2s->addr + I2SMOD); + int bfs_shift; + int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; - mod &= ~MOD_BCLK_MASK; + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT; + mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift); + } else { + bfs_shift = MOD_BCLK_SHIFT; + mod &= ~(MOD_BCLK_MASK << bfs_shift); + } + + /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ + if (!tdm && bfs > 48) { + dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n"); + return; + } switch (bfs) { case 48: - mod |= MOD_BCLK_48FS; + mod |= (MOD_BCLK_48FS << bfs_shift); break; case 32: - mod |= MOD_BCLK_32FS; + mod |= (MOD_BCLK_32FS << bfs_shift); break; case 24: - mod |= MOD_BCLK_24FS; + mod |= (MOD_BCLK_24FS << bfs_shift); break; case 16: - mod |= MOD_BCLK_16FS; + mod |= (MOD_BCLK_16FS << bfs_shift); + break; + case 64: + mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift); + break; + case 96: + mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift); + break; + case 128: + mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift); + break; + case 192: + mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift); + break; + case 256: + mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift); break; default: dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); @@ -491,20 +545,32 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, { struct i2s_dai *i2s = to_info(dai); u32 mod = readl(i2s->addr + I2SMOD); + int lrp_shift, sdf_shift, sdf_mask, lrp_rlow; u32 tmp = 0; + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + lrp_shift = EXYNOS5420_MOD_LRP_SHIFT; + sdf_shift = EXYNOS5420_MOD_SDF_SHIFT; + } else { + lrp_shift = MOD_LRP_SHIFT; + sdf_shift = MOD_SDF_SHIFT; + } + + sdf_mask = MOD_SDF_MASK << sdf_shift; + lrp_rlow = MOD_LR_RLOW << lrp_shift; + /* Format is priority */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: - tmp |= MOD_LR_RLOW; - tmp |= MOD_SDF_MSB; + tmp |= lrp_rlow; + tmp |= (MOD_SDF_MSB << sdf_shift); break; case SND_SOC_DAIFMT_LEFT_J: - tmp |= MOD_LR_RLOW; - tmp |= MOD_SDF_LSB; + tmp |= lrp_rlow; + tmp |= (MOD_SDF_LSB << sdf_shift); break; case SND_SOC_DAIFMT_I2S: - tmp |= MOD_SDF_IIS; + tmp |= (MOD_SDF_IIS << sdf_shift); break; default: dev_err(&i2s->pdev->dev, "Format not supported\n"); @@ -519,10 +585,10 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_NB_IF: - if (tmp & MOD_LR_RLOW) - tmp &= ~MOD_LR_RLOW; + if (tmp & lrp_rlow) + tmp &= ~lrp_rlow; else - tmp |= MOD_LR_RLOW; + tmp |= lrp_rlow; break; default: dev_err(&i2s->pdev->dev, "Polarity not supported\n"); @@ -544,15 +610,18 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; } + /* + * Don't change the I2S mode if any controller is active on this + * channel. + */ if (any_active(i2s) && - ((mod & (MOD_SDF_MASK | MOD_LR_RLOW - | MOD_SLAVE)) != tmp)) { + ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; } - mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE); + mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE); mod |= tmp; writel(mod, i2s->addr + I2SMOD); @@ -1007,6 +1076,8 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) if (IS_ERR(i2s->pdev)) return NULL; + i2s->pdev->dev.parent = &pdev->dev; + platform_set_drvdata(i2s->pdev, i2s); ret = platform_device_add(i2s->pdev); if (ret < 0) @@ -1018,18 +1089,18 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) static const struct of_device_id exynos_i2s_match[]; -static inline int samsung_i2s_get_driver_data(struct platform_device *pdev) +static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data( + struct platform_device *pdev) { #ifdef CONFIG_OF - struct samsung_i2s_dai_data *data; if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(exynos_i2s_match, pdev->dev.of_node); - data = (struct samsung_i2s_dai_data *) match->data; - return data->dai_type; + return match->data; } else #endif - return platform_get_device_id(pdev)->driver_data; + return (struct samsung_i2s_dai_data *) + platform_get_device_id(pdev)->driver_data; } #ifdef CONFIG_PM_RUNTIME @@ -1060,13 +1131,13 @@ static int samsung_i2s_probe(struct platform_device *pdev) struct resource *res; u32 regs_base, quirks = 0, idma_addr = 0; struct device_node *np = pdev->dev.of_node; - enum samsung_dai_type samsung_dai_type; + const struct samsung_i2s_dai_data *i2s_dai_data; int ret = 0; /* Call during Seconday interface registration */ - samsung_dai_type = samsung_i2s_get_driver_data(pdev); + i2s_dai_data = samsung_i2s_get_driver_data(pdev); - if (samsung_dai_type == TYPE_SEC) { + if (i2s_dai_data->dai_type == TYPE_SEC) { sec_dai = dev_get_drvdata(&pdev->dev); if (!sec_dai) { dev_err(&pdev->dev, "Unable to get drvdata\n"); @@ -1075,7 +1146,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) snd_soc_register_component(&sec_dai->pdev->dev, &samsung_i2s_component, &sec_dai->i2s_dai_drv, 1); - asoc_dma_platform_register(&pdev->dev); + samsung_asoc_dma_platform_register(&pdev->dev); return 0; } @@ -1115,15 +1186,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) idma_addr = i2s_cfg->idma_addr; } } else { - if (of_find_property(np, "samsung,supports-6ch", NULL)) - quirks |= QUIRK_PRI_6CHAN; - - if (of_find_property(np, "samsung,supports-secdai", NULL)) - quirks |= QUIRK_SEC_DAI; - - if (of_find_property(np, "samsung,supports-rstclr", NULL)) - quirks |= QUIRK_NEED_RSTCLR; - + quirks = i2s_dai_data->quirks; if (of_property_read_u32(np, "samsung,idma-addr", &idma_addr)) { if (quirks & QUIRK_SEC_DAI) { @@ -1200,7 +1263,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - asoc_dma_platform_register(&pdev->dev); + samsung_asoc_dma_platform_register(&pdev->dev); return 0; err: @@ -1230,33 +1293,59 @@ static int samsung_i2s_remove(struct platform_device *pdev) i2s->pri_dai = NULL; i2s->sec_dai = NULL; - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); return 0; } +static const struct samsung_i2s_dai_data i2sv3_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_NO_MUXPSR, +}; + +static const struct samsung_i2s_dai_data i2sv5_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR, +}; + +static const struct samsung_i2s_dai_data i2sv6_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | + QUIRK_SUPPORTS_TDM, +}; + +static const struct samsung_i2s_dai_data samsung_dai_type_pri = { + .dai_type = TYPE_PRI, +}; + +static const struct samsung_i2s_dai_data samsung_dai_type_sec = { + .dai_type = TYPE_SEC, +}; + static struct platform_device_id samsung_i2s_driver_ids[] = { { .name = "samsung-i2s", - .driver_data = TYPE_PRI, + .driver_data = (kernel_ulong_t)&samsung_dai_type_pri, }, { .name = "samsung-i2s-sec", - .driver_data = TYPE_SEC, + .driver_data = (kernel_ulong_t)&samsung_dai_type_sec, }, {}, }; MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); #ifdef CONFIG_OF -static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = { - [TYPE_PRI] = { TYPE_PRI }, - [TYPE_SEC] = { TYPE_SEC }, -}; - static const struct of_device_id exynos_i2s_match[] = { - { .compatible = "samsung,i2s-v5", - .data = &samsung_i2s_dai_data_array[TYPE_PRI], + { + .compatible = "samsung,s3c6410-i2s", + .data = &i2sv3_dai_type, + }, { + .compatible = "samsung,s5pv210-i2s", + .data = &i2sv5_dai_type, + }, { + .compatible = "samsung,exynos5420-i2s", + .data = &i2sv6_dai_type, }, {}, }; diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index 1566afe9ef5..e54256fc4b2 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -594,7 +594,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev) goto err5; } - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); goto err6; @@ -623,7 +623,7 @@ static int s3c_pcm_dev_remove(struct platform_device *pdev) struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id]; struct resource *mem_res; - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index 47e23864ea7..ea885cb9f76 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -176,7 +176,7 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev) return ret; } - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { pr_err("failed to register the DMA: %d\n", ret); goto err; @@ -190,7 +190,7 @@ err: static int s3c2412_iis_dev_remove(struct platform_device *pdev) { - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); return 0; } diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 8b3414551a6..9c8ebd872fa 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -480,7 +480,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev) return ret; } - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { pr_err("failed to register the dma: %d\n", ret); goto err; @@ -494,7 +494,7 @@ err: static int s3c24xx_iis_dev_remove(struct platform_device *pdev) { - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); return 0; } diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index 581ea4a06fc..5fd7a05a9b9 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -11,6 +11,7 @@ #include <sound/pcm_params.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> /* * Default CFG switch settings to use this driver: @@ -37,11 +38,19 @@ /* SMDK has a 16.934MHZ crystal attached to WM8994 */ #define SMDK_WM8994_FREQ 16934000 +struct smdk_wm8994_data { + int mclk1_rate; +}; + +/* Default SMDKs */ +static struct smdk_wm8994_data smdk_board_data = { + .mclk1_rate = SMDK_WM8994_FREQ, +}; + static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; unsigned int pll_out; int ret; @@ -54,18 +63,6 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, else pll_out = params_rate(params) * 256; - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, SMDK_WM8994_FREQ, pll_out); if (ret < 0) @@ -131,6 +128,8 @@ static struct snd_soc_dai_link smdk_dai[] = { .platform_name = "samsung-i2s.0", .codec_name = "wm8994-codec", .init = smdk_wm8994_init_paiftx, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &smdk_ops, }, { /* Sec_Fifo Playback i/f */ .name = "Sec_FIFO TX", @@ -139,6 +138,8 @@ static struct snd_soc_dai_link smdk_dai[] = { .codec_dai_name = "wm8994-aif1", .platform_name = "samsung-i2s-sec", .codec_name = "wm8994-codec", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &smdk_ops, }, }; @@ -150,15 +151,28 @@ static struct snd_soc_card smdk = { .num_links = ARRAY_SIZE(smdk_dai), }; +#ifdef CONFIG_OF +static const struct of_device_id samsung_wm8994_of_match[] = { + { .compatible = "samsung,smdk-wm8994", .data = &smdk_board_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); +#endif /* CONFIG_OF */ static int smdk_audio_probe(struct platform_device *pdev) { int ret; struct device_node *np = pdev->dev.of_node; struct snd_soc_card *card = &smdk; + struct smdk_wm8994_data *board; + const struct of_device_id *id; card->dev = &pdev->dev; + board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL); + if (!board) + return -ENOMEM; + if (np) { smdk_dai[0].cpu_dai_name = NULL; smdk_dai[0].cpu_of_node = of_parse_phandle(np, @@ -173,6 +187,12 @@ static int smdk_audio_probe(struct platform_device *pdev) smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node; } + id = of_match_device(samsung_wm8994_of_match, &pdev->dev); + if (id) + *board = *((struct smdk_wm8994_data *)id->data); + + platform_set_drvdata(pdev, board); + ret = snd_soc_register_card(card); if (ret) @@ -190,17 +210,9 @@ static int smdk_audio_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_OF -static const struct of_device_id samsung_wm8994_of_match[] = { - { .compatible = "samsung,smdk-wm8994", }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); -#endif /* CONFIG_OF */ - static struct platform_driver smdk_audio_driver = { .driver = { - .name = "smdk-audio", + .name = "smdk-audio-wm8894", .owner = THIS_MODULE, .of_match_table = of_match_ptr(samsung_wm8994_of_match), }, @@ -212,4 +224,4 @@ module_platform_driver(smdk_audio_driver); MODULE_DESCRIPTION("ALSA SoC SMDK WM8994"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:smdk-audio"); +MODULE_ALIAS("platform:smdk-audio-wm8994"); diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 2e5ebb2f198..28487dcc453 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -395,7 +395,7 @@ static int spdif_probe(struct platform_device *pdev) spin_lock_init(&spdif->lock); - spdif->pclk = clk_get(&pdev->dev, "spdif"); + spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); if (IS_ERR(spdif->pclk)) { dev_err(&pdev->dev, "failed to get peri-clock\n"); ret = -ENOENT; @@ -403,7 +403,7 @@ static int spdif_probe(struct platform_device *pdev) } clk_prepare_enable(spdif->pclk); - spdif->sclk = clk_get(&pdev->dev, "sclk_spdif"); + spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); if (IS_ERR(spdif->sclk)) { dev_err(&pdev->dev, "failed to get internal source clock\n"); ret = -ENOENT; @@ -442,7 +442,7 @@ static int spdif_probe(struct platform_device *pdev) spdif->dma_playback = &spdif_stereo_out; - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "failed to register DMA: %d\n", ret); goto err5; @@ -457,10 +457,8 @@ err3: release_mem_region(mem_res->start, resource_size(mem_res)); err2: clk_disable_unprepare(spdif->sclk); - clk_put(spdif->sclk); err1: clk_disable_unprepare(spdif->pclk); - clk_put(spdif->pclk); err0: return ret; } @@ -470,7 +468,7 @@ static int spdif_remove(struct platform_device *pdev) struct samsung_spdif_info *spdif = &spdif_info; struct resource *mem_res; - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); iounmap(spdif->regs); @@ -480,9 +478,7 @@ static int spdif_remove(struct platform_device *pdev) release_mem_region(mem_res->start, resource_size(mem_res)); clk_disable_unprepare(spdif->sclk); - clk_put(spdif->sclk); clk_disable_unprepare(spdif->pclk); - clk_put(spdif->pclk); return 0; } diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 6bcb1164d59..56d8ff6a402 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -34,6 +34,13 @@ config SND_SOC_SH4_SIU select SH_DMAE select FW_LOADER +config SND_SOC_RCAR + tristate "R-Car series SRU/SCU/SSIU/SSI support" + select SND_SIMPLE_CARD + select RCAR_CLK_ADG + help + This option enables R-Car SUR/SCU/SSIU/SSI sound support + ## ## Boards ## diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile index 849b387d17d..aaf3dcd1ee2 100644 --- a/sound/soc/sh/Makefile +++ b/sound/soc/sh/Makefile @@ -12,6 +12,9 @@ obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o +## audio units for R-Car +obj-$(CONFIG_SND_SOC_RCAR) += rcar/ + ## boards snd-soc-sh7760-ac97-objs := sh7760-ac97.o snd-soc-migor-objs := migor.o diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 30390260bb6..b33ca7cd085 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -235,6 +235,8 @@ struct fsi_stream { struct sh_dmae_slave slave; /* see fsi_handler_init() */ struct work_struct work; dma_addr_t dma; + int loop_cnt; + int additional_pos; }; struct fsi_clk { @@ -1289,6 +1291,8 @@ static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io) io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | BUSOP_SET(16, PACKAGE_16BITBUS_STREAM); + io->loop_cnt = 2; /* push 1st, 2nd period first, then 3rd, 4th... */ + io->additional_pos = 0; io->dma = dma_map_single(dai->dev, runtime->dma_area, snd_pcm_lib_buffer_bytes(io->substream), dir); return 0; @@ -1305,11 +1309,15 @@ static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io) return 0; } -static dma_addr_t fsi_dma_get_area(struct fsi_stream *io) +static dma_addr_t fsi_dma_get_area(struct fsi_stream *io, int additional) { struct snd_pcm_runtime *runtime = io->substream->runtime; + int period = io->period_pos + additional; - return io->dma + samples_to_bytes(runtime, io->buff_sample_pos); + if (period >= runtime->periods) + period = 0; + + return io->dma + samples_to_bytes(runtime, period * io->period_samples); } static void fsi_dma_complete(void *data) @@ -1321,7 +1329,7 @@ static void fsi_dma_complete(void *data) enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io), + dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io, 0), samples_to_bytes(runtime, io->period_samples), dir); io->buff_sample_pos += io->period_samples; @@ -1347,7 +1355,7 @@ static void fsi_dma_do_work(struct work_struct *work) struct snd_pcm_runtime *runtime; enum dma_data_direction dir; int is_play = fsi_stream_is_play(fsi, io); - int len; + int len, i; dma_addr_t buf; if (!fsi_stream_is_working(fsi, io)) @@ -1357,26 +1365,33 @@ static void fsi_dma_do_work(struct work_struct *work) runtime = io->substream->runtime; dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; len = samples_to_bytes(runtime, io->period_samples); - buf = fsi_dma_get_area(io); - dma_sync_single_for_device(dai->dev, buf, len, dir); + for (i = 0; i < io->loop_cnt; i++) { + buf = fsi_dma_get_area(io, io->additional_pos); - desc = dmaengine_prep_slave_single(io->chan, buf, len, dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n"); - return; - } + dma_sync_single_for_device(dai->dev, buf, len, dir); - desc->callback = fsi_dma_complete; - desc->callback_param = io; + desc = dmaengine_prep_slave_single(io->chan, buf, len, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n"); + return; + } - if (dmaengine_submit(desc) < 0) { - dev_err(dai->dev, "tx_submit() fail\n"); - return; + desc->callback = fsi_dma_complete; + desc->callback_param = io; + + if (dmaengine_submit(desc) < 0) { + dev_err(dai->dev, "tx_submit() fail\n"); + return; + } + + dma_async_issue_pending(io->chan); + + io->additional_pos = 1; } - dma_async_issue_pending(io->chan); + io->loop_cnt = 1; /* * FIXME diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile new file mode 100644 index 00000000000..0ff492df792 --- /dev/null +++ b/sound/soc/sh/rcar/Makefile @@ -0,0 +1,2 @@ +snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
\ No newline at end of file diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c new file mode 100644 index 00000000000..d80deb7ccf1 --- /dev/null +++ b/sound/soc/sh/rcar/adg.c @@ -0,0 +1,234 @@ +/* + * Helper routines for R-Car sound ADG. + * + * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/sh_clk.h> +#include <mach/clock.h> +#include "rsnd.h" + +#define CLKA 0 +#define CLKB 1 +#define CLKC 2 +#define CLKI 3 +#define CLKMAX 4 + +struct rsnd_adg { + struct clk *clk[CLKMAX]; + + int rate_of_441khz_div_6; + int rate_of_48khz_div_6; +}; + +#define for_each_rsnd_clk(pos, adg, i) \ + for (i = 0, (pos) = adg->clk[i]; \ + i < CLKMAX; \ + i++, (pos) = adg->clk[i]) +#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) + +static enum rsnd_reg rsnd_adg_ssi_reg_get(int id) +{ + enum rsnd_reg reg; + + /* + * SSI 8 is not connected to ADG. + * it works with SSI 7 + */ + if (id == 8) + return RSND_REG_MAX; + + if (0 <= id && id <= 3) + reg = RSND_REG_AUDIO_CLK_SEL0; + else if (4 <= id && id <= 7) + reg = RSND_REG_AUDIO_CLK_SEL1; + else + reg = RSND_REG_AUDIO_CLK_SEL2; + + return reg; +} + +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + enum rsnd_reg reg; + int id; + + /* + * "mod" = "ssi" here. + * we can get "ssi id" from mod + */ + id = rsnd_mod_id(mod); + reg = rsnd_adg_ssi_reg_get(id); + + rsnd_write(priv, mod, reg, 0); + + return 0; +} + +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + enum rsnd_reg reg; + int id, shift, i; + u32 data; + int sel_table[] = { + [CLKA] = 0x1, + [CLKB] = 0x2, + [CLKC] = 0x3, + [CLKI] = 0x0, + }; + + dev_dbg(dev, "request clock = %d\n", rate); + + /* + * find suitable clock from + * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. + */ + data = 0; + for_each_rsnd_clk(clk, adg, i) { + if (rate == clk_get_rate(clk)) { + data = sel_table[i]; + goto found_clock; + } + } + + /* + * find 1/6 clock from BRGA/BRGB + */ + if (rate == adg->rate_of_441khz_div_6) { + data = 0x10; + goto found_clock; + } + + if (rate == adg->rate_of_48khz_div_6) { + data = 0x20; + goto found_clock; + } + + return -EIO; + +found_clock: + + /* + * This "mod" = "ssi" here. + * we can get "ssi id" from mod + */ + id = rsnd_mod_id(mod); + reg = rsnd_adg_ssi_reg_get(id); + + dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate); + + /* + * Enable SSIx clock + */ + shift = (id % 4) * 8; + + rsnd_bset(priv, mod, reg, + 0xFF << shift, + data << shift); + + return 0; +} + +static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) +{ + struct clk *clk; + unsigned long rate; + u32 ckr; + int i; + int brg_table[] = { + [CLKA] = 0x0, + [CLKB] = 0x1, + [CLKC] = 0x4, + [CLKI] = 0x2, + }; + + /* + * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC + * have 44.1kHz or 48kHz base clocks for now. + * + * SSI itself can divide parent clock by 1/1 - 1/16 + * So, BRGA outputs 44.1kHz base parent clock 1/32, + * and, BRGB outputs 48.0kHz base parent clock 1/32 here. + * see + * rsnd_adg_ssi_clk_try_start() + */ + ckr = 0; + adg->rate_of_441khz_div_6 = 0; + adg->rate_of_48khz_div_6 = 0; + for_each_rsnd_clk(clk, adg, i) { + rate = clk_get_rate(clk); + + if (0 == rate) /* not used */ + continue; + + /* RBGA */ + if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) { + adg->rate_of_441khz_div_6 = rate / 6; + ckr |= brg_table[i] << 20; + } + + /* RBGB */ + if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) { + adg->rate_of_48khz_div_6 = rate / 6; + ckr |= brg_table[i] << 16; + } + } + + rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr); + rsnd_priv_write(priv, BRRA, 0x00000002); /* 1/6 */ + rsnd_priv_write(priv, BRRB, 0x00000002); /* 1/6 */ +} + +int rsnd_adg_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct rsnd_adg *adg; + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + int i; + + adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); + if (!adg) { + dev_err(dev, "ADG allocate failed\n"); + return -ENOMEM; + } + + adg->clk[CLKA] = clk_get(NULL, "audio_clk_a"); + adg->clk[CLKB] = clk_get(NULL, "audio_clk_b"); + adg->clk[CLKC] = clk_get(NULL, "audio_clk_c"); + adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal"); + for_each_rsnd_clk(clk, adg, i) { + if (IS_ERR(clk)) { + dev_err(dev, "Audio clock failed\n"); + return -EIO; + } + } + + rsnd_adg_ssi_clk_init(priv, adg); + + priv->adg = adg; + + dev_dbg(dev, "adg probed\n"); + + return 0; +} + +void rsnd_adg_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = priv->adg; + struct clk *clk; + int i; + + for_each_rsnd_clk(clk, adg, i) + clk_put(clk); +} diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c new file mode 100644 index 00000000000..a3570602851 --- /dev/null +++ b/sound/soc/sh/rcar/core.c @@ -0,0 +1,861 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * Based on fsi.c + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * 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. + */ + +/* + * Renesas R-Car sound device structure + * + * Gen1 + * + * SRU : Sound Routing Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * - SSI : Serial Sound Interface + * + * Gen2 + * + * SCU : Sampling Rate Converter Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * SSIU : Serial Sound Interface Unit + * - SSI : Serial Sound Interface + */ + +/* + * driver data Image + * + * rsnd_priv + * | + * | ** this depends on Gen1/Gen2 + * | + * +- gen + * | + * | ** these depend on data path + * | ** gen and platform data control it + * | + * +- rdai[0] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * | + * +- rdai[1] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * ... + * | + * | ** these control ssi + * | + * +- ssi + * | | + * | +- ssi[0] + * | +- ssi[1] + * | +- ssi[2] + * | ... + * | + * | ** these control scu + * | + * +- scu + * | + * +- scu[0] + * +- scu[1] + * +- scu[2] + * ... + * + * + * for_each_rsnd_dai(xx, priv, xx) + * rdai[0] => rdai[1] => rdai[2] => ... + * + * for_each_rsnd_mod(xx, rdai, xx) + * [mod] => [mod] => [mod] => ... + * + * rsnd_dai_call(xxx, fn ) + * [mod]->fn() -> [mod]->fn() -> [mod]->fn()... + * + */ +#include <linux/pm_runtime.h> +#include "rsnd.h" + +#define RSND_RATES SNDRV_PCM_RATE_8000_96000 +#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) + +/* + * rsnd_platform functions + */ +#define rsnd_platform_call(priv, dai, func, param...) \ + (!(priv->info->func) ? -ENODEV : \ + priv->info->func(param)) + + +/* + * basic function + */ +u32 rsnd_read(struct rsnd_priv *priv, + struct rsnd_mod *mod, enum rsnd_reg reg) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + + BUG_ON(!base); + + return ioread32(base); +} + +void rsnd_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + + BUG_ON(!base); + + dev_dbg(dev, "w %p : %08x\n", base, data); + + iowrite32(data, base); +} + +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + u32 val; + + BUG_ON(!base); + + val = ioread32(base); + val &= ~mask; + val |= data & mask; + iowrite32(val, base); + + dev_dbg(dev, "s %p : %08x\n", base, val); +} + +/* + * rsnd_mod functions + */ +char *rsnd_mod_name(struct rsnd_mod *mod) +{ + if (!mod || !mod->ops) + return "unknown"; + + return mod->ops->name; +} + +void rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_mod_ops *ops, + int id) +{ + mod->priv = priv; + mod->id = id; + mod->ops = ops; + INIT_LIST_HEAD(&mod->list); +} + +/* + * rsnd_dma functions + */ +static void rsnd_dma_continue(struct rsnd_dma *dma) +{ + /* push next A or B plane */ + dma->submit_loop = 1; + schedule_work(&dma->work); +} + +void rsnd_dma_start(struct rsnd_dma *dma) +{ + /* push both A and B plane*/ + dma->submit_loop = 2; + schedule_work(&dma->work); +} + +void rsnd_dma_stop(struct rsnd_dma *dma) +{ + dma->submit_loop = 0; + cancel_work_sync(&dma->work); + dmaengine_terminate_all(dma->chan); +} + +static void rsnd_dma_complete(void *data) +{ + struct rsnd_dma *dma = (struct rsnd_dma *)data; + struct rsnd_priv *priv = dma->priv; + unsigned long flags; + + rsnd_lock(priv, flags); + + dma->complete(dma); + + if (dma->submit_loop) + rsnd_dma_continue(dma); + + rsnd_unlock(priv, flags); +} + +static void rsnd_dma_do_work(struct work_struct *work) +{ + struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); + struct rsnd_priv *priv = dma->priv; + struct device *dev = rsnd_priv_to_dev(priv); + struct dma_async_tx_descriptor *desc; + dma_addr_t buf; + size_t len; + int i; + + for (i = 0; i < dma->submit_loop; i++) { + + if (dma->inquiry(dma, &buf, &len) < 0) + return; + + desc = dmaengine_prep_slave_single( + dma->chan, buf, len, dma->dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); + return; + } + + desc->callback = rsnd_dma_complete; + desc->callback_param = dma; + + if (dmaengine_submit(desc) < 0) { + dev_err(dev, "dmaengine_submit() fail\n"); + return; + } + + } + + dma_async_issue_pending(dma->chan); +} + +int rsnd_dma_available(struct rsnd_dma *dma) +{ + return !!dma->chan; +} + +static bool rsnd_dma_filter(struct dma_chan *chan, void *param) +{ + chan->private = param; + + return true; +} + +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, + int is_play, int id, + int (*inquiry)(struct rsnd_dma *dma, + dma_addr_t *buf, int *len), + int (*complete)(struct rsnd_dma *dma)) +{ + struct device *dev = rsnd_priv_to_dev(priv); + dma_cap_mask_t mask; + + if (dma->chan) { + dev_err(dev, "it already has dma channel\n"); + return -EIO; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dma->slave.shdma_slave.slave_id = id; + + dma->chan = dma_request_channel(mask, rsnd_dma_filter, + &dma->slave.shdma_slave); + if (!dma->chan) { + dev_err(dev, "can't get dma channel\n"); + return -EIO; + } + + dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + dma->priv = priv; + dma->inquiry = inquiry; + dma->complete = complete; + INIT_WORK(&dma->work, rsnd_dma_do_work); + + return 0; +} + +void rsnd_dma_quit(struct rsnd_priv *priv, + struct rsnd_dma *dma) +{ + if (dma->chan) + dma_release_channel(dma->chan); + + dma->chan = NULL; +} + +/* + * rsnd_dai functions + */ +#define rsnd_dai_call(rdai, io, fn) \ +({ \ + struct rsnd_mod *mod, *n; \ + int ret = 0; \ + for_each_rsnd_mod(mod, n, io) { \ + ret = rsnd_mod_call(mod, fn, rdai, io); \ + if (ret < 0) \ + break; \ + } \ + ret; \ +}) + +int rsnd_dai_connect(struct rsnd_dai *rdai, + struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + if (!mod) { + dev_err(dev, "NULL mod\n"); + return -EIO; + } + + if (!list_empty(&mod->list)) { + dev_err(dev, "%s%d is not empty\n", + rsnd_mod_name(mod), + rsnd_mod_id(mod)); + return -EIO; + } + + list_add_tail(&mod->list, &io->head); + + return 0; +} + +int rsnd_dai_disconnect(struct rsnd_mod *mod) +{ + list_del_init(&mod->list); + + return 0; +} + +int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) +{ + int id = rdai - priv->rdai; + + if ((id < 0) || (id >= rsnd_dai_nr(priv))) + return -EINVAL; + + return id; +} + +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) +{ + return priv->rdai + id; +} + +static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + + return rsnd_dai_get(priv, dai->id); +} + +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io) +{ + return &rdai->playback == io; +} + +/* + * rsnd_soc_dai functions + */ +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) +{ + struct snd_pcm_substream *substream = io->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + int pos = io->byte_pos + additional; + + pos %= (runtime->periods * io->byte_per_period); + + return pos; +} + +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) +{ + io->byte_pos += byte; + + if (io->byte_pos >= io->next_period_byte) { + struct snd_pcm_substream *substream = io->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + + io->period_pos++; + io->next_period_byte += io->byte_per_period; + + if (io->period_pos >= runtime->periods) { + io->byte_pos = 0; + io->period_pos = 0; + io->next_period_byte = io->byte_per_period; + } + + snd_pcm_period_elapsed(substream); + } +} + +static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + if (!list_empty(&io->head)) + return -EIO; + + INIT_LIST_HEAD(&io->head); + io->substream = substream; + io->byte_pos = 0; + io->period_pos = 0; + io->byte_per_period = runtime->period_size * + runtime->channels * + samples_to_bytes(runtime, 1); + io->next_period_byte = io->byte_per_period; + + return 0; +} + +static +struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + return rtd->cpu_dai; +} + +static +struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, + struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return &rdai->playback; + else + return &rdai->capture; +} + +static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + int ssi_id = rsnd_mod_id(mod); + int ret; + unsigned long flags; + + rsnd_lock(priv, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = rsnd_dai_stream_init(io, substream); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_platform_call(priv, dai, start, ssi_id); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_gen_path_init(priv, rdai, io); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_dai_call(rdai, io, init); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_dai_call(rdai, io, start); + if (ret < 0) + goto dai_trigger_end; + break; + case SNDRV_PCM_TRIGGER_STOP: + ret = rsnd_dai_call(rdai, io, stop); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_dai_call(rdai, io, quit); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_gen_path_exit(priv, rdai, io); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_platform_call(priv, dai, stop, ssi_id); + if (ret < 0) + goto dai_trigger_end; + break; + default: + ret = -EINVAL; + } + +dai_trigger_end: + rsnd_unlock(priv, flags); + + return ret; +} + +static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rdai->clk_master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rdai->clk_master = 0; + break; + default: + return -EINVAL; + } + + /* set clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + rdai->bit_clk_inv = 0; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + rdai->bit_clk_inv = 1; + rdai->frm_clk_inv = 0; + break; + case SND_SOC_DAIFMT_IB_IF: + rdai->bit_clk_inv = 1; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + default: + rdai->bit_clk_inv = 0; + rdai->frm_clk_inv = 0; + break; + } + + /* set format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + rdai->sys_delay = 0; + rdai->data_alignment = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + rdai->sys_delay = 1; + rdai->data_alignment = 0; + break; + case SND_SOC_DAIFMT_RIGHT_J: + rdai->sys_delay = 1; + rdai->data_alignment = 1; + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { + .trigger = rsnd_soc_dai_trigger, + .set_fmt = rsnd_soc_dai_set_fmt, +}; + +static int rsnd_dai_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct snd_soc_dai_driver *drv; + struct rsnd_dai *rdai; + struct rsnd_mod *pmod, *cmod; + struct device *dev = rsnd_priv_to_dev(priv); + int dai_nr; + int i; + + /* get max dai nr */ + for (dai_nr = 0; dai_nr < 32; dai_nr++) { + pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); + + if (!pmod && !cmod) + break; + } + + if (!dai_nr) { + dev_err(dev, "no dai\n"); + return -EIO; + } + + drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); + rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); + if (!drv || !rdai) { + dev_err(dev, "dai allocate failed\n"); + return -ENOMEM; + } + + for (i = 0; i < dai_nr; i++) { + + pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0); + + /* + * init rsnd_dai + */ + INIT_LIST_HEAD(&rdai[i].playback.head); + INIT_LIST_HEAD(&rdai[i].capture.head); + + snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); + + /* + * init snd_soc_dai_driver + */ + drv[i].name = rdai[i].name; + drv[i].ops = &rsnd_soc_dai_ops; + if (pmod) { + drv[i].playback.rates = RSND_RATES; + drv[i].playback.formats = RSND_FMTS; + drv[i].playback.channels_min = 2; + drv[i].playback.channels_max = 2; + } + if (cmod) { + drv[i].capture.rates = RSND_RATES; + drv[i].capture.formats = RSND_FMTS; + drv[i].capture.channels_min = 2; + drv[i].capture.channels_max = 2; + } + + dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name, + pmod ? "play" : " -- ", + cmod ? "capture" : " -- "); + } + + priv->dai_nr = dai_nr; + priv->daidrv = drv; + priv->rdai = rdai; + + return 0; +} + +static void rsnd_dai_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} + +/* + * pcm ops + */ +static struct snd_pcm_hardware rsnd_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE, + .formats = RSND_FMTS, + .rates = RSND_RATES, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 32, + .fifo_size = 256, +}; + +static int rsnd_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + + return ret; +} + +static int rsnd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + + return bytes_to_frames(runtime, io->byte_pos); +} + +static struct snd_pcm_ops rsnd_pcm_ops = { + .open = rsnd_pcm_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = rsnd_hw_params, + .hw_free = snd_pcm_lib_free_pages, + .pointer = rsnd_pointer, +}; + +/* + * snd_soc_platform + */ + +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (32 * 1024) + +static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + return snd_pcm_lib_preallocate_pages_for_all( + rtd->pcm, + SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); +} + +static void rsnd_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static struct snd_soc_platform_driver rsnd_soc_platform = { + .ops = &rsnd_pcm_ops, + .pcm_new = rsnd_pcm_new, + .pcm_free = rsnd_pcm_free, +}; + +static const struct snd_soc_component_driver rsnd_soc_component = { + .name = "rsnd", +}; + +/* + * rsnd probe + */ +static int rsnd_probe(struct platform_device *pdev) +{ + struct rcar_snd_info *info; + struct rsnd_priv *priv; + struct device *dev = &pdev->dev; + int ret; + + info = pdev->dev.platform_data; + if (!info) { + dev_err(dev, "driver needs R-Car sound information\n"); + return -ENODEV; + } + + /* + * init priv data + */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "priv allocate failed\n"); + return -ENODEV; + } + + priv->dev = dev; + priv->info = info; + spin_lock_init(&priv->lock); + + /* + * init each module + */ + ret = rsnd_gen_probe(pdev, info, priv); + if (ret < 0) + return ret; + + ret = rsnd_scu_probe(pdev, info, priv); + if (ret < 0) + return ret; + + ret = rsnd_adg_probe(pdev, info, priv); + if (ret < 0) + return ret; + + ret = rsnd_ssi_probe(pdev, info, priv); + if (ret < 0) + return ret; + + ret = rsnd_dai_probe(pdev, info, priv); + if (ret < 0) + return ret; + + /* + * asoc register + */ + ret = snd_soc_register_platform(dev, &rsnd_soc_platform); + if (ret < 0) { + dev_err(dev, "cannot snd soc register\n"); + return ret; + } + + ret = snd_soc_register_component(dev, &rsnd_soc_component, + priv->daidrv, rsnd_dai_nr(priv)); + if (ret < 0) { + dev_err(dev, "cannot snd dai register\n"); + goto exit_snd_soc; + } + + dev_set_drvdata(dev, priv); + + pm_runtime_enable(dev); + + dev_info(dev, "probed\n"); + return ret; + +exit_snd_soc: + snd_soc_unregister_platform(dev); + + return ret; +} + +static int rsnd_remove(struct platform_device *pdev) +{ + struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + /* + * remove each module + */ + rsnd_ssi_remove(pdev, priv); + rsnd_adg_remove(pdev, priv); + rsnd_scu_remove(pdev, priv); + rsnd_dai_remove(pdev, priv); + rsnd_gen_remove(pdev, priv); + + return 0; +} + +static struct platform_driver rsnd_driver = { + .driver = { + .name = "rcar_sound", + }, + .probe = rsnd_probe, + .remove = rsnd_remove, +}; +module_platform_driver(rsnd_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas R-Car audio driver"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); +MODULE_ALIAS("platform:rcar-pcm-audio"); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c new file mode 100644 index 00000000000..babb203b43b --- /dev/null +++ b/sound/soc/sh/rcar/gen.c @@ -0,0 +1,280 @@ +/* + * Renesas R-Car Gen1 SRU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "rsnd.h" + +struct rsnd_gen_ops { + int (*path_init)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*path_exit)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +}; + +struct rsnd_gen_reg_map { + int index; /* -1 : not supported */ + u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ + u32 offset_adr; /* offset of SSICR, SSISR, ... */ +}; + +struct rsnd_gen { + void __iomem *base[RSND_BASE_MAX]; + + struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; + struct rsnd_gen_ops *ops; +}; + +#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) + +/* + * Gen2 + * will be filled in the future + */ + +/* + * Gen1 + */ +static int rsnd_gen1_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod; + int ret; + int id; + + /* + * Gen1 is created by SRU/SSI, and this SRU is base module of + * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) + * + * Easy image is.. + * Gen1 SRU = Gen2 SCU + SSIU + etc + * + * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is + * using fixed path. + * + * Then, SSI id = SCU id here + */ + + /* get SSI's ID */ + mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + id = rsnd_mod_id(mod); + + /* SSI */ + mod = rsnd_ssi_mod_get(priv, id); + ret = rsnd_dai_connect(rdai, mod, io); + if (ret < 0) + return ret; + + /* SCU */ + mod = rsnd_scu_mod_get(priv, id); + ret = rsnd_dai_connect(rdai, mod, io); + + return ret; +} + +static int rsnd_gen1_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod, *n; + int ret = 0; + + /* + * remove all mod from rdai + */ + for_each_rsnd_mod(mod, n, io) + ret |= rsnd_dai_disconnect(mod); + + return ret; +} + +static struct rsnd_gen_ops rsnd_gen1_ops = { + .path_init = rsnd_gen1_path_init, + .path_exit = rsnd_gen1_path_exit, +}; + +#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ + do { \ + (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ + (g)->reg_map[RSND_REG_##i].offset_id = oi; \ + (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ + } while (0) + +static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) +{ + RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); + RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214); + + RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); + RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); + RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); + + RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00); + RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04); + RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08); + RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c); + RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20); +} + +static int rsnd_gen1_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct resource *sru_res; + struct resource *adg_res; + struct resource *ssi_res; + + /* + * map address + */ + sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); + adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); + ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI); + + gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); + gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); + gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res); + if (IS_ERR(gen->base[RSND_GEN1_SRU]) || + IS_ERR(gen->base[RSND_GEN1_ADG]) || + IS_ERR(gen->base[RSND_GEN1_SSI])) + return -ENODEV; + + gen->ops = &rsnd_gen1_ops; + rsnd_gen1_reg_map_init(gen); + + dev_dbg(dev, "Gen1 device probed\n"); + dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, + gen->base[RSND_GEN1_SRU]); + dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, + gen->base[RSND_GEN1_ADG]); + dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start, + gen->base[RSND_GEN1_SSI]); + + return 0; + +} + +static void rsnd_gen1_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} + +/* + * Gen + */ +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_init(priv, rdai, io); +} + +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_exit(priv, rdai, io); +} + +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int index; + u32 offset_id, offset_adr; + + if (reg >= RSND_REG_MAX) { + dev_err(dev, "rsnd_reg reg error\n"); + return NULL; + } + + index = gen->reg_map[reg].index; + offset_id = gen->reg_map[reg].offset_id; + offset_adr = gen->reg_map[reg].offset_adr; + + if (index < 0) { + dev_err(dev, "unsupported reg access %d\n", reg); + return NULL; + } + + if (offset_id && mod) + offset_id *= rsnd_mod_id(mod); + + /* + * index/offset were set on gen1/gen2 + */ + + return gen->base[index] + offset_id + offset_adr; +} + +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen; + int i; + + gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); + if (!gen) { + dev_err(dev, "GEN allocate failed\n"); + return -ENOMEM; + } + + priv->gen = gen; + + /* + * see + * rsnd_reg_get() + * rsnd_gen_probe() + */ + for (i = 0; i < RSND_REG_MAX; i++) + gen->reg_map[i].index = -1; + + /* + * init each module + */ + if (rsnd_is_gen1(priv)) + return rsnd_gen1_probe(pdev, info, priv); + + dev_err(dev, "unknown generation R-Car sound device\n"); + + return -ENODEV; +} + +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + if (rsnd_is_gen1(priv)) + rsnd_gen1_remove(pdev, priv); +} diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h new file mode 100644 index 00000000000..9cc6986a8cf --- /dev/null +++ b/sound/soc/sh/rcar/rsnd.h @@ -0,0 +1,302 @@ +/* + * Renesas R-Car + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef RSND_H +#define RSND_H + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/sh_dma.h> +#include <linux/workqueue.h> +#include <sound/rcar_snd.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +/* + * pseudo register + * + * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different. + * This driver uses pseudo register in order to hide it. + * see gen1/gen2 for detail + */ +enum rsnd_reg { + /* SRU/SCU */ + RSND_REG_SRC_ROUTE_SEL, + RSND_REG_SRC_TMG_SEL0, + RSND_REG_SRC_TMG_SEL1, + RSND_REG_SRC_TMG_SEL2, + RSND_REG_SRC_CTRL, + RSND_REG_SSI_MODE0, + RSND_REG_SSI_MODE1, + RSND_REG_BUSIF_MODE, + RSND_REG_BUSIF_ADINR, + + /* ADG */ + RSND_REG_BRRA, + RSND_REG_BRRB, + RSND_REG_SSICKR, + RSND_REG_AUDIO_CLK_SEL0, + RSND_REG_AUDIO_CLK_SEL1, + RSND_REG_AUDIO_CLK_SEL2, + RSND_REG_AUDIO_CLK_SEL3, + RSND_REG_AUDIO_CLK_SEL4, + RSND_REG_AUDIO_CLK_SEL5, + + /* SSI */ + RSND_REG_SSICR, + RSND_REG_SSISR, + RSND_REG_SSITDR, + RSND_REG_SSIRDR, + RSND_REG_SSIWSR, + + RSND_REG_MAX, +}; + +struct rsnd_priv; +struct rsnd_mod; +struct rsnd_dai; +struct rsnd_dai_stream; + +/* + * R-Car basic functions + */ +#define rsnd_mod_read(m, r) \ + rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) +#define rsnd_mod_write(m, r, d) \ + rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) +#define rsnd_mod_bset(m, r, s, d) \ + rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) + +#define rsnd_priv_read(p, r) rsnd_read(p, NULL, RSND_REG_##r) +#define rsnd_priv_write(p, r, d) rsnd_write(p, NULL, RSND_REG_##r, d) +#define rsnd_priv_bset(p, r, s, d) rsnd_bset(p, NULL, RSND_REG_##r, s, d) + +u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data); +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, + u32 mask, u32 data); + +/* + * R-Car DMA + */ +struct rsnd_dma { + struct rsnd_priv *priv; + struct sh_dmae_slave slave; + struct work_struct work; + struct dma_chan *chan; + enum dma_data_direction dir; + int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len); + int (*complete)(struct rsnd_dma *dma); + + int submit_loop; +}; + +void rsnd_dma_start(struct rsnd_dma *dma); +void rsnd_dma_stop(struct rsnd_dma *dma); +int rsnd_dma_available(struct rsnd_dma *dma); +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, + int is_play, int id, + int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len), + int (*complete)(struct rsnd_dma *dma)); +void rsnd_dma_quit(struct rsnd_priv *priv, + struct rsnd_dma *dma); + + +/* + * R-Car sound mod + */ + +struct rsnd_mod_ops { + char *name; + int (*init)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*quit)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*start)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*stop)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +}; + +struct rsnd_mod { + int id; + struct rsnd_priv *priv; + struct rsnd_mod_ops *ops; + struct list_head list; /* connect to rsnd_dai playback/capture */ + struct rsnd_dma dma; +}; + +#define rsnd_mod_to_priv(mod) ((mod)->priv) +#define rsnd_mod_to_dma(mod) (&(mod)->dma) +#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) +#define rsnd_mod_id(mod) ((mod)->id) +#define for_each_rsnd_mod(pos, n, io) \ + list_for_each_entry_safe(pos, n, &(io)->head, list) +#define rsnd_mod_call(mod, func, rdai, io) \ + (!(mod) ? -ENODEV : \ + !((mod)->ops->func) ? 0 : \ + (mod)->ops->func(mod, rdai, io)) + +void rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_mod_ops *ops, + int id); +char *rsnd_mod_name(struct rsnd_mod *mod); + +/* + * R-Car sound DAI + */ +#define RSND_DAI_NAME_SIZE 16 +struct rsnd_dai_stream { + struct list_head head; /* head of rsnd_mod list */ + struct snd_pcm_substream *substream; + int byte_pos; + int period_pos; + int byte_per_period; + int next_period_byte; +}; + +struct rsnd_dai { + char name[RSND_DAI_NAME_SIZE]; + struct rsnd_dai_platform_info *info; /* rcar_snd.h */ + struct rsnd_dai_stream playback; + struct rsnd_dai_stream capture; + + int clk_master:1; + int bit_clk_inv:1; + int frm_clk_inv:1; + int sys_delay:1; + int data_alignment:1; +}; + +#define rsnd_dai_nr(priv) ((priv)->dai_nr) +#define for_each_rsnd_dai(rdai, priv, i) \ + for (i = 0, (rdai) = rsnd_dai_get(priv, i); \ + i < rsnd_dai_nr(priv); \ + i++, (rdai) = rsnd_dai_get(priv, i)) + +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); +int rsnd_dai_disconnect(struct rsnd_mod *mod); +int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, + struct rsnd_dai_stream *io); +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); +int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai); +#define rsnd_dai_get_platform_info(rdai) ((rdai)->info) +#define rsnd_io_to_runtime(io) ((io)->substream->runtime) + +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); + +/* + * R-Car Gen1/Gen2 + */ +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg); +#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) +#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) + +/* + * R-Car ADG + */ +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); +int rsnd_adg_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_adg_remove(struct platform_device *pdev, + struct rsnd_priv *priv); + +/* + * R-Car sound priv + */ +struct rsnd_priv { + + struct device *dev; + struct rcar_snd_info *info; + spinlock_t lock; + + /* + * below value will be filled on rsnd_gen_probe() + */ + void *gen; + + /* + * below value will be filled on rsnd_scu_probe() + */ + void *scu; + int scu_nr; + + /* + * below value will be filled on rsnd_adg_probe() + */ + void *adg; + + /* + * below value will be filled on rsnd_ssi_probe() + */ + void *ssiu; + + /* + * below value will be filled on rsnd_dai_probe() + */ + struct snd_soc_dai_driver *daidrv; + struct rsnd_dai *rdai; + int dai_nr; +}; + +#define rsnd_priv_to_dev(priv) ((priv)->dev) +#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) +#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) + +/* + * R-Car SCU + */ +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_scu_nr(priv) ((priv)->scu_nr) + +/* + * R-Car SSI + */ +int rsnd_ssi_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play); + +#endif diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c new file mode 100644 index 00000000000..184d9008cec --- /dev/null +++ b/sound/soc/sh/rcar/scu.c @@ -0,0 +1,236 @@ +/* + * Renesas R-Car SCU support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "rsnd.h" + +struct rsnd_scu { + struct rsnd_scu_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; +}; + +#define rsnd_scu_mode_flags(p) ((p)->info->flags) + +/* + * ADINR + */ +#define OTBL_24 (0 << 16) +#define OTBL_22 (2 << 16) +#define OTBL_20 (4 << 16) +#define OTBL_18 (6 << 16) +#define OTBL_16 (8 << 16) + + +#define rsnd_mod_to_scu(_mod) \ + container_of((_mod), struct rsnd_scu, mod) + +#define for_each_rsnd_scu(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_scu_nr(priv)) && \ + ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ + i++) + +static int rsnd_scu_set_route(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct scu_route_config { + u32 mask; + int shift; + } routes[] = { + { 0xF, 0, }, /* 0 */ + { 0xF, 4, }, /* 1 */ + { 0xF, 8, }, /* 2 */ + { 0x7, 12, }, /* 3 */ + { 0x7, 16, }, /* 4 */ + { 0x7, 20, }, /* 5 */ + { 0x7, 24, }, /* 6 */ + { 0x3, 28, }, /* 7 */ + { 0x3, 30, }, /* 8 */ + }; + + u32 mask; + u32 val; + int shift; + int id; + + /* + * Gen1 only + */ + if (!rsnd_is_gen1(priv)) + return 0; + + id = rsnd_mod_id(mod); + if (id < 0 || id > ARRAY_SIZE(routes)) + return -EIO; + + /* + * SRC_ROUTE_SELECT + */ + val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; + val = val << routes[id].shift; + mask = routes[id].mask << routes[id].shift; + + rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); + + /* + * SRC_TIMING_SELECT + */ + shift = (id % 4) * 8; + mask = 0x1F << shift; + if (8 == id) /* SRU8 is very special */ + val = id << shift; + else + val = (id + 1) << shift; + + switch (id / 4) { + case 0: + rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); + break; + case 1: + rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); + break; + case 2: + rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); + break; + } + + return 0; +} + +static int rsnd_scu_set_mode(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + u32 val; + + if (rsnd_is_gen1(priv)) { + val = (1 << id); + rsnd_mod_bset(mod, SRC_CTRL, val, val); + } + + return 0; +} + +static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 adinr = runtime->channels; + + switch (runtime->sample_bits) { + case 16: + adinr |= OTBL_16; + break; + case 32: + adinr |= OTBL_24; + break; + default: + return -EIO; + } + + rsnd_mod_write(mod, BUSIF_MODE, 1); + rsnd_mod_write(mod, BUSIF_ADINR, adinr); + + return 0; +} + +static int rsnd_scu_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 flags = rsnd_scu_mode_flags(scu); + int ret; + + /* + * SCU will be used if it has RSND_SCU_USB_HPBIF flags + */ + if (!(flags & RSND_SCU_USB_HPBIF)) { + /* it use PIO transter */ + dev_dbg(dev, "%s%d is not used\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; + } + + /* it use DMA transter */ + ret = rsnd_scu_set_route(priv, mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_scu_set_mode(priv, mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); + if (ret < 0) + return ret; + + dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static struct rsnd_mod_ops rsnd_scu_ops = { + .name = "scu", + .start = rsnd_scu_start, +}; + +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) +{ + BUG_ON(id < 0 || id >= rsnd_scu_nr(priv)); + + return &((struct rsnd_scu *)(priv->scu) + id)->mod; +} + +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_scu *scu; + int i, nr; + + /* + * init SCU + */ + nr = info->scu_info_nr; + scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); + if (!scu) { + dev_err(dev, "SCU allocate failed\n"); + return -ENOMEM; + } + + priv->scu_nr = nr; + priv->scu = scu; + + for_each_rsnd_scu(scu, priv, i) { + rsnd_mod_init(priv, &scu->mod, + &rsnd_scu_ops, i); + scu->info = &info->scu_info[i]; + + dev_dbg(dev, "SCU%d probed\n", i); + } + dev_dbg(dev, "scu probed\n"); + + return 0; +} + +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c new file mode 100644 index 00000000000..fae26d3f79d --- /dev/null +++ b/sound/soc/sh/rcar/ssi.c @@ -0,0 +1,728 @@ +/* + * Renesas R-Car SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * Based on fsi.c + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/delay.h> +#include "rsnd.h" +#define RSND_SSI_NAME_SIZE 16 + +/* + * SSICR + */ +#define FORCE (1 << 31) /* Fixed */ +#define DMEN (1 << 28) /* DMA Enable */ +#define UIEN (1 << 27) /* Underflow Interrupt Enable */ +#define OIEN (1 << 26) /* Overflow Interrupt Enable */ +#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ +#define DIEN (1 << 24) /* Data Interrupt Enable */ + +#define DWL_8 (0 << 19) /* Data Word Length */ +#define DWL_16 (1 << 19) /* Data Word Length */ +#define DWL_18 (2 << 19) /* Data Word Length */ +#define DWL_20 (3 << 19) /* Data Word Length */ +#define DWL_22 (4 << 19) /* Data Word Length */ +#define DWL_24 (5 << 19) /* Data Word Length */ +#define DWL_32 (6 << 19) /* Data Word Length */ + +#define SWL_32 (3 << 16) /* R/W System Word Length */ +#define SCKD (1 << 15) /* Serial Bit Clock Direction */ +#define SWSD (1 << 14) /* Serial WS Direction */ +#define SCKP (1 << 13) /* Serial Bit Clock Polarity */ +#define SWSP (1 << 12) /* Serial WS Polarity */ +#define SDTA (1 << 10) /* Serial Data Alignment */ +#define DEL (1 << 8) /* Serial Data Delay */ +#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ +#define TRMD (1 << 1) /* Transmit/Receive Mode Select */ +#define EN (1 << 0) /* SSI Module Enable */ + +/* + * SSISR + */ +#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */ +#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */ +#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ +#define DIRQ (1 << 24) /* Data Interrupt Status Flag */ + +/* + * SSIWSR + */ +#define CONT (1 << 8) /* WS Continue Function */ + +struct rsnd_ssi { + struct clk *clk; + struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ + struct rsnd_ssi *parent; + struct rsnd_mod mod; + + struct rsnd_dai *rdai; + struct rsnd_dai_stream *io; + u32 cr_own; + u32 cr_clk; + u32 cr_etc; + int err; + int dma_offset; + unsigned int usrcnt; + unsigned int rate; +}; + +struct rsnd_ssiu { + u32 ssi_mode0; + u32 ssi_mode1; + + int ssi_nr; + struct rsnd_ssi *ssi; +}; + +#define for_each_rsnd_ssi(pos, priv, i) \ + for (i = 0; \ + (i < rsnd_ssi_nr(priv)) && \ + ((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \ + i++) + +#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) +#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) +#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) +#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_ssi_dma_available(ssi) \ + rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) +#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) +#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) +#define rsnd_ssi_mode_flags(p) ((p)->info->flags) +#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) +#define rsnd_ssi_to_ssiu(ssi)\ + (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) + +static void rsnd_ssi_mode_init(struct rsnd_priv *priv, + struct rsnd_ssiu *ssiu) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ssi *ssi; + u32 flags; + u32 val; + int i; + + /* + * SSI_MODE0 + */ + ssiu->ssi_mode0 = 0; + for_each_rsnd_ssi(ssi, priv, i) { + flags = rsnd_ssi_mode_flags(ssi); + + /* see also BUSIF_MODE */ + if (!(flags & RSND_SSI_DEPENDENT)) { + ssiu->ssi_mode0 |= (1 << i); + dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); + } else { + dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); + } + } + + /* + * SSI_MODE1 + */ +#define ssi_parent_set(p, sync, adg, ext) \ + do { \ + ssi->parent = ssiu->ssi + p; \ + if (flags & RSND_SSI_CLK_FROM_ADG) \ + val = adg; \ + else \ + val = ext; \ + if (flags & RSND_SSI_SYNC) \ + val |= sync; \ + } while (0) + + ssiu->ssi_mode1 = 0; + for_each_rsnd_ssi(ssi, priv, i) { + flags = rsnd_ssi_mode_flags(ssi); + + if (!(flags & RSND_SSI_CLK_PIN_SHARE)) + continue; + + val = 0; + switch (i) { + case 1: + ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0)); + break; + case 2: + ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2)); + break; + case 4: + ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16)); + break; + case 8: + ssi_parent_set(7, 0, 0, 0); + break; + } + + ssiu->ssi_mode1 |= val; + } +} + +static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi) +{ + struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); + + rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0); + rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1); +} + +static void rsnd_ssi_status_check(struct rsnd_mod *mod, + u32 bit) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 status; + int i; + + for (i = 0; i < 1024; i++) { + status = rsnd_mod_read(mod, SSISR); + if (status & bit) + return; + + udelay(50); + } + + dev_warn(dev, "status check failed\n"); +} + +static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, + unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + int i, j, ret; + int adg_clk_div_table[] = { + 1, 6, /* see adg.c */ + }; + int ssi_clk_mul_table[] = { + 1, 2, 4, 8, 16, 6, 12, + }; + unsigned int main_rate; + + /* + * Find best clock, and try to start ADG + */ + for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) { + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * this driver is assuming that + * system word is 64fs (= 2 x 32bit) + * see rsnd_ssi_start() + */ + main_rate = rate / adg_clk_div_table[i] + * 32 * 2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate); + if (0 == ret) { + ssi->rate = rate; + ssi->cr_clk = FORCE | SWL_32 | + SCKD | SWSD | CKDV(j); + + dev_dbg(dev, "ssi%d outputs %u Hz\n", + rsnd_mod_id(&ssi->mod), rate); + + return 0; + } + } + } + + dev_err(dev, "unsupported clock rate\n"); + return -EIO; +} + +static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) +{ + ssi->rate = 0; + ssi->cr_clk = 0; + rsnd_adg_ssi_clk_stop(&ssi->mod); +} + +static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 cr; + + if (0 == ssi->usrcnt) { + clk_enable(ssi->clk); + + if (rsnd_rdai_is_clk_master(rdai)) { + struct snd_pcm_runtime *runtime; + + runtime = rsnd_io_to_runtime(io); + + if (rsnd_ssi_clk_from_parent(ssi)) + rsnd_ssi_hw_start(ssi->parent, rdai, io); + else + rsnd_ssi_master_clk_start(ssi, runtime->rate); + } + } + + cr = ssi->cr_own | + ssi->cr_clk | + ssi->cr_etc | + EN; + + rsnd_mod_write(&ssi->mod, SSICR, cr); + + ssi->usrcnt++; + + dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod)); +} + +static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi, + struct rsnd_dai *rdai) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 cr; + + if (0 == ssi->usrcnt) /* stop might be called without start */ + return; + + ssi->usrcnt--; + + if (0 == ssi->usrcnt) { + /* + * disable all IRQ, + * and, wait all data was sent + */ + cr = ssi->cr_own | + ssi->cr_clk; + + rsnd_mod_write(&ssi->mod, SSICR, cr | EN); + rsnd_ssi_status_check(&ssi->mod, DIRQ); + + /* + * disable SSI, + * and, wait idle state + */ + rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(&ssi->mod, IIRQ); + + if (rsnd_rdai_is_clk_master(rdai)) { + if (rsnd_ssi_clk_from_parent(ssi)) + rsnd_ssi_hw_stop(ssi->parent, rdai); + else + rsnd_ssi_master_clk_stop(ssi); + } + + clk_disable(ssi->clk); + } + + dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod)); +} + +/* + * SSI mod common functions + */ +static int rsnd_ssi_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 cr; + + cr = FORCE; + + /* + * always use 32bit system word for easy clock calculation. + * see also rsnd_ssi_master_clk_enable() + */ + cr |= SWL_32; + + /* + * init clock settings for SSICR + */ + switch (runtime->sample_bits) { + case 16: + cr |= DWL_16; + break; + case 32: + cr |= DWL_24; + break; + default: + return -EIO; + } + + if (rdai->bit_clk_inv) + cr |= SCKP; + if (rdai->frm_clk_inv) + cr |= SWSP; + if (rdai->data_alignment) + cr |= SDTA; + if (rdai->sys_delay) + cr |= DEL; + if (rsnd_dai_is_play(rdai, io)) + cr |= TRMD; + + /* + * set ssi parameter + */ + ssi->rdai = rdai; + ssi->io = io; + ssi->cr_own = cr; + ssi->err = -1; /* ignore 1st error */ + + rsnd_ssi_mode_set(ssi); + + dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_ssi_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + if (ssi->err > 0) + dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err); + + ssi->rdai = NULL; + ssi->io = NULL; + ssi->cr_own = 0; + ssi->err = 0; + + return 0; +} + +static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) +{ + /* under/over flow error */ + if (status & (UIRQ | OIRQ)) { + ssi->err++; + + /* clear error status */ + rsnd_mod_write(&ssi->mod, SSISR, 0); + } +} + +/* + * SSI PIO + */ +static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) +{ + struct rsnd_ssi *ssi = data; + struct rsnd_dai_stream *io = ssi->io; + u32 status = rsnd_mod_read(&ssi->mod, SSISR); + irqreturn_t ret = IRQ_NONE; + + if (io && (status & DIRQ)) { + struct rsnd_dai *rdai = ssi->rdai; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 *buf = (u32 *)(runtime->dma_area + + rsnd_dai_pointer_offset(io, 0)); + + rsnd_ssi_record_error(ssi, status); + + /* + * 8/16/32 data can be assesse to TDR/RDR register + * directly as 32bit data + * see rsnd_ssi_init() + */ + if (rsnd_dai_is_play(rdai, io)) + rsnd_mod_write(&ssi->mod, SSITDR, *buf); + else + *buf = rsnd_mod_read(&ssi->mod, SSIRDR); + + rsnd_dai_pointer_update(io, sizeof(*buf)); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static int rsnd_ssi_pio_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + /* enable PIO IRQ */ + ssi->cr_etc = UIEN | OIEN | DIEN; + + rsnd_ssi_hw_start(ssi, rdai, io); + + dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + ssi->cr_etc = 0; + + rsnd_ssi_hw_stop(ssi, rdai); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_pio_ops = { + .name = "ssi (pio)", + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_pio_start, + .stop = rsnd_ssi_pio_stop, +}; + +static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + *len = io->byte_per_period; + *buf = runtime->dma_addr + + rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); + ssi->dma_offset = *len; /* it cares A/B plane */ + + return 0; +} + +static int rsnd_ssi_dma_complete(struct rsnd_dma *dma) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + u32 status = rsnd_mod_read(&ssi->mod, SSISR); + + rsnd_ssi_record_error(ssi, status); + + rsnd_dai_pointer_update(ssi->io, io->byte_per_period); + + return 0; +} + +static int rsnd_ssi_dma_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + /* enable DMA transfer */ + ssi->cr_etc = DMEN; + ssi->dma_offset = 0; + + rsnd_dma_start(dma); + + rsnd_ssi_hw_start(ssi, ssi->rdai, io); + + /* enable WS continue */ + if (rsnd_rdai_is_clk_master(rdai)) + rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + + return 0; +} + +static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + ssi->cr_etc = 0; + + rsnd_ssi_hw_stop(ssi, rdai); + + rsnd_dma_stop(dma); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_dma_ops = { + .name = "ssi (dma)", + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_dma_start, + .stop = rsnd_ssi_dma_stop, +}; + +/* + * Non SSI + */ +static int rsnd_ssi_non(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_non_ops = { + .name = "ssi (non)", + .init = rsnd_ssi_non, + .quit = rsnd_ssi_non, + .start = rsnd_ssi_non, + .stop = rsnd_ssi_non, +}; + +/* + * ssi mod function + */ +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play) +{ + struct rsnd_ssi *ssi; + int i, has_play; + + is_play = !!is_play; + + for_each_rsnd_ssi(ssi, priv, i) { + if (rsnd_ssi_dai_id(ssi) != dai_id) + continue; + + has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); + + if (is_play == has_play) + return &ssi->mod; + } + + return NULL; +} + +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) +{ + BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); + + return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod; +} + +int rsnd_ssi_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct rsnd_ssi_platform_info *pinfo; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod_ops *ops; + struct clk *clk; + struct rsnd_ssiu *ssiu; + struct rsnd_ssi *ssi; + char name[RSND_SSI_NAME_SIZE]; + int i, nr, ret; + + /* + * init SSI + */ + nr = info->ssi_info_nr; + ssiu = devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr), + GFP_KERNEL); + if (!ssiu) { + dev_err(dev, "SSI allocate failed\n"); + return -ENOMEM; + } + + priv->ssiu = ssiu; + ssiu->ssi = (struct rsnd_ssi *)(ssiu + 1); + ssiu->ssi_nr = nr; + + for_each_rsnd_ssi(ssi, priv, i) { + pinfo = &info->ssi_info[i]; + + snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i); + + clk = clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ssi->info = pinfo; + ssi->clk = clk; + + ops = &rsnd_ssi_non_ops; + + /* + * SSI DMA case + */ + if (pinfo->dma_id > 0) { + ret = rsnd_dma_init( + priv, rsnd_mod_to_dma(&ssi->mod), + (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY), + pinfo->dma_id, + rsnd_ssi_dma_inquiry, + rsnd_ssi_dma_complete); + if (ret < 0) + dev_info(dev, "SSI DMA failed. try PIO transter\n"); + else + ops = &rsnd_ssi_dma_ops; + + dev_dbg(dev, "SSI%d use DMA transfer\n", i); + } + + /* + * SSI PIO case + */ + if (!rsnd_ssi_dma_available(ssi) && + rsnd_ssi_pio_available(ssi)) { + ret = devm_request_irq(dev, pinfo->pio_irq, + &rsnd_ssi_pio_interrupt, + IRQF_SHARED, + dev_name(dev), ssi); + if (ret) { + dev_err(dev, "SSI request interrupt failed\n"); + return ret; + } + + ops = &rsnd_ssi_pio_ops; + + dev_dbg(dev, "SSI%d use PIO transfer\n", i); + } + + rsnd_mod_init(priv, &ssi->mod, ops, i); + } + + rsnd_ssi_mode_init(priv, ssiu); + + dev_dbg(dev, "ssi probed\n"); + + return 0; +} + +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) { + clk_put(ssi->clk); + if (rsnd_ssi_dma_available(ssi)) + rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod)); + } + +} diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 06a8000aa07..53c9ecdd119 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -149,8 +149,9 @@ static int soc_compr_free(struct snd_compr_stream *cstream) SND_SOC_DAPM_STREAM_STOP); } else { rtd->pop_wait = 1; - schedule_delayed_work(&rtd->delayed_work, - msecs_to_jiffies(rtd->pmdown_time)); + queue_delayed_work(system_power_efficient_wq, + &rtd->delayed_work, + msecs_to_jiffies(rtd->pmdown_time)); } } else { /* capture streams can be powered down now */ @@ -334,7 +335,7 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, return ret; } -static int sst_compr_set_metadata(struct snd_compr_stream *cstream, +static int soc_compr_set_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -347,7 +348,7 @@ static int sst_compr_set_metadata(struct snd_compr_stream *cstream, return ret; } -static int sst_compr_get_metadata(struct snd_compr_stream *cstream, +static int soc_compr_get_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -364,8 +365,8 @@ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, .free = soc_compr_free, .set_params = soc_compr_set_params, - .set_metadata = sst_compr_set_metadata, - .get_metadata = sst_compr_get_metadata, + .set_metadata = soc_compr_set_metadata, + .get_metadata = soc_compr_get_metadata, .get_params = soc_compr_get_params, .trigger = soc_compr_trigger, .pointer = soc_compr_pointer, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d82ee386eab..4d0561312f3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -30,9 +30,12 @@ #include <linux/bitops.h> #include <linux/debugfs.h> #include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h> #include <linux/ctype.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> #include <sound/ac97_codec.h> #include <sound/core.h> #include <sound/jack.h> @@ -47,8 +50,6 @@ #define NAME_SIZE 32 -static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); - #ifdef CONFIG_DEBUG_FS struct dentry *snd_soc_debugfs_root; EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); @@ -69,6 +70,16 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); +struct snd_ac97_reset_cfg { + struct pinctrl *pctl; + struct pinctrl_state *pstate_reset; + struct pinctrl_state *pstate_warm_reset; + struct pinctrl_state *pstate_run; + int gpio_sdata; + int gpio_sync; + int gpio_reset; +}; + /* returns the minimum number of bytes needed to represent * a particular given value */ static int min_bytes_needed(unsigned long val) @@ -192,7 +203,7 @@ static ssize_t pmdown_time_set(struct device *dev, struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); int ret; - ret = strict_strtol(buf, 10, &rtd->pmdown_time); + ret = kstrtol(buf, 10, &rtd->pmdown_time); if (ret) return ret; @@ -237,6 +248,7 @@ static ssize_t codec_reg_write_file(struct file *file, char *start = buf; unsigned long reg, value; struct snd_soc_codec *codec = file->private_data; + int ret; buf_size = min(count, (sizeof(buf)-1)); if (copy_from_user(buf, user_buf, buf_size)) @@ -248,8 +260,9 @@ static ssize_t codec_reg_write_file(struct file *file, reg = simple_strtoul(start, &start, 16); while (*start == ' ') start++; - if (strict_strtoul(start, 16, &value)) - return -EINVAL; + ret = kstrtoul(start, 16, &value); + if (ret) + return ret; /* Userspace has been fiddling around behind the kernel's back */ add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); @@ -530,6 +543,15 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) } #endif +static void codec2codec_close_delayed_work(struct work_struct *work) +{ + /* Currently nothing to do for c2c links + * Since c2c links are internal nodes in the DAPM graph and + * don't interface with the outside world or application layer + * we don't have to do any special handling on close. + */ +} + #ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ int snd_soc_suspend(struct device *dev) @@ -1223,9 +1245,6 @@ static int soc_post_component_init(struct snd_soc_card *card, } rtd->card = card; - /* Make sure all DAPM widgets are instantiated */ - snd_soc_dapm_new_widgets(&codec->dapm); - /* machine controls, routes and widgets are not prefixed */ temp = codec->name_prefix; codec->name_prefix = NULL; @@ -1428,6 +1447,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) return ret; } } else { + INIT_DELAYED_WORK(&rtd->delayed_work, + codec2codec_close_delayed_work); + /* link the DAI widgets */ play_w = codec_dai->playback_widget; capture_w = cpu_dai->capture_widget; @@ -1718,8 +1740,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); - snd_soc_dapm_new_widgets(&card->dapm); - for (i = 0; i < card->num_links; i++) { dai_link = &card->dai_link[i]; dai_fmt = dai_link->dai_fmt; @@ -1798,12 +1818,12 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) } } - snd_soc_dapm_new_widgets(&card->dapm); - if (card->fully_routed) list_for_each_entry(codec, &card->codec_dev_list, card_list) snd_soc_dapm_auto_nc_codec_pins(codec); + snd_soc_dapm_new_widgets(card); + ret = snd_card_register(card->snd_card); if (ret < 0) { dev_err(card->dev, "ASoC: failed to register soundcard %d\n", @@ -2080,6 +2100,117 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); +static struct snd_ac97_reset_cfg snd_ac97_rst_cfg; + +static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static void snd_soc_ac97_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static int snd_soc_ac97_parse_pinctl(struct device *dev, + struct snd_ac97_reset_cfg *cfg) +{ + struct pinctrl *p; + struct pinctrl_state *state; + int gpio; + int ret; + + p = devm_pinctrl_get(dev); + if (IS_ERR(p)) { + dev_err(dev, "Failed to get pinctrl\n"); + return PTR_RET(p); + } + cfg->pctl = p; + + state = pinctrl_lookup_state(p, "ac97-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-reset\n"); + return PTR_RET(state); + } + cfg->pstate_reset = state; + + state = pinctrl_lookup_state(p, "ac97-warm-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n"); + return PTR_RET(state); + } + cfg->pstate_warm_reset = state; + + state = pinctrl_lookup_state(p, "ac97-running"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-running\n"); + return PTR_RET(state); + } + cfg->pstate_run = state; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sync gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sync"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sync gpio\n"); + return ret; + } + cfg->gpio_sync = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sdata"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sdata gpio\n"); + return ret; + } + cfg->gpio_sdata = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-reset gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link reset"); + if (ret) { + dev_err(dev, "Failed requesting ac97-reset gpio\n"); + return ret; + } + cfg->gpio_reset = gpio; + + return 0; +} + struct snd_ac97_bus_ops *soc_ac97_ops; EXPORT_SYMBOL_GPL(soc_ac97_ops); @@ -2098,6 +2229,35 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops); /** + * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions + * + * This function sets the reset and warm_reset properties of ops and parses + * the device node of pdev to get pinctrl states and gpio numbers to use. + */ +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_ac97_reset_cfg cfg; + int ret; + + ret = snd_soc_ac97_parse_pinctl(dev, &cfg); + if (ret) + return ret; + + ret = snd_soc_set_ac97_ops(ops); + if (ret) + return ret; + + ops->warm_reset = snd_soc_ac97_warm_reset; + ops->reset = snd_soc_ac97_reset; + + snd_ac97_rst_cfg = cfg; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset); + +/** * snd_soc_free_ac97_codec - free AC97 codec device * @codec: audio codec * @@ -2299,6 +2459,22 @@ static int snd_soc_add_controls(struct snd_card *card, struct device *dev, return 0; } +struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, + const char *name) +{ + struct snd_card *card = soc_card->snd_card; + struct snd_kcontrol *kctl; + + if (unlikely(!name)) + return NULL; + + list_for_each_entry(kctl, &card->controls, list) + if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) + return kctl; + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); + /** * snd_soc_add_codec_controls - add an array of controls to a codec. * Convenience function to add a list of controls. Many codecs were @@ -2541,59 +2717,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); /** - * snd_soc_info_enum_ext - external enumerated single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about an external enumerated - * single mixer. - * - * Returns 0 for success. - */ -int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = e->max; - - if (uinfo->value.enumerated.item > e->max - 1) - uinfo->value.enumerated.item = e->max - 1; - strcpy(uinfo->value.enumerated.name, - e->texts[uinfo->value.enumerated.item]); - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); - -/** - * snd_soc_info_volsw_ext - external single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a single external mixer control. - * - * Returns 0 for success. - */ -int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int max = kcontrol->private_value; - - if (max == 1 && !strstr(kcontrol->id.name, " Volume")) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = max; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); - -/** * snd_soc_info_volsw - single mixer info callback * @kcontrol: mixer control * @uinfo: control element information diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 13fcb61a922..c17c14c394d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2714,9 +2714,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); * * Returns 0 for success. */ -int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) +int snd_soc_dapm_new_widgets(struct snd_soc_card *card) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; unsigned int val; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 7aa26b5178a..71358e3b54d 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -183,8 +183,6 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, list_add(&(pins[i].list), &jack->pins); } - snd_soc_dapm_new_widgets(&jack->codec->card->dapm); - /* Update to reflect the last reported status; canned jack * implementations are likely to set their state before the * card has an opportunity to associate pins. diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig index 3567d73b218..0a53053495f 100644 --- a/sound/soc/spear/Kconfig +++ b/sound/soc/spear/Kconfig @@ -1,6 +1,6 @@ config SND_SPEAR_SOC tristate - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM config SND_SPEAR_SPDIF_OUT tristate diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 995b120c2cd..8fc653ca3ab 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -1,8 +1,8 @@ config SND_SOC_TEGRA tristate "SoC Audio for the Tegra System-on-Chip" - depends on ARCH_TEGRA && TEGRA20_APB_DMA + depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST select REGMAP_MMIO - select SND_SOC_GENERIC_DMAENGINE_PCM if TEGRA20_APB_DMA + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M here if you want support for SoC audio on Tegra. @@ -61,7 +61,7 @@ config SND_SOC_TEGRA30_I2S config SND_SOC_TEGRA_RT5640 tristate "SoC Audio support for Tegra boards using an RT5640 codec" - depends on SND_SOC_TEGRA && I2C + depends on SND_SOC_TEGRA && I2C && GPIOLIB select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC select SND_SOC_RT5640 @@ -71,7 +71,7 @@ config SND_SOC_TEGRA_RT5640 config SND_SOC_TEGRA_WM8753 tristate "SoC Audio support for Tegra boards using a WM8753 codec" - depends on SND_SOC_TEGRA && I2C + depends on SND_SOC_TEGRA && I2C && GPIOLIB select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC select SND_SOC_WM8753 @@ -81,7 +81,7 @@ config SND_SOC_TEGRA_WM8753 config SND_SOC_TEGRA_WM8903 tristate "SoC Audio support for Tegra boards using a WM8903 codec" - depends on SND_SOC_TEGRA && I2C + depends on SND_SOC_TEGRA && I2C && GPIOLIB select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC select SND_SOC_WM8903 @@ -92,7 +92,7 @@ config SND_SOC_TEGRA_WM8903 config SND_SOC_TEGRA_WM9712 tristate "SoC Audio support for Tegra boards using a WM9712 codec" - depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC && GPIOLIB select SND_SOC_TEGRA20_AC97 select SND_SOC_WM9712 help @@ -110,7 +110,7 @@ config SND_SOC_TEGRA_TRIMSLICE config SND_SOC_TEGRA_ALC5632 tristate "SoC Audio support for Tegra boards using an ALC5632 codec" - depends on SND_SOC_TEGRA && I2C + depends on SND_SOC_TEGRA && I2C && GPIOLIB select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_ALC5632 help diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 6c486625321..ae27bcd586d 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -334,12 +334,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "No memory resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - regs = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(regs)) { ret = PTR_ERR(regs); @@ -432,8 +426,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) return 0; -err_unregister_pcm: - tegra_pcm_platform_unregister(&pdev->dev); err_unregister_component: snd_soc_unregister_component(&pdev->dev); err_asoc_utils_fini: diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index d04146cad61..47565fd0450 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -228,7 +228,7 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream, reg = TEGRA30_I2S_CIF_RX_CTRL; } else { val |= TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX; - reg = TEGRA30_I2S_CIF_RX_CTRL; + reg = TEGRA30_I2S_CIF_TX_CTRL; } regmap_write(i2s->regmap, reg, val); diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 48d05d9e100..c61ea3a1030 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -13,8 +13,6 @@ * published by the Free Software Foundation. */ -#include <asm/mach-types.h> - #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index 08794f915a9..4511c5a875e 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -99,6 +99,7 @@ static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = { static const struct snd_soc_dapm_widget tegra_rt5640_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), }; static const struct snd_kcontrol_new tegra_rt5640_controls[] = { diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index f87fc53e9b8..8e774d1a243 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c @@ -28,8 +28,6 @@ * */ -#include <asm/mach-types.h> - #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 05c68aab5cf..734bfcd2114 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -24,8 +24,6 @@ * */ -#include <asm/mach-types.h> - #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index 4bcce8a3cde..e0305a14856 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -184,9 +184,6 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev) if (irq < 0) return irq; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - return -EBUSY; - drvdata->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(drvdata->base)) return PTR_ERR(drvdata->base); diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index 8f5cd00a6e4..178d1bad625 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -52,6 +52,7 @@ static struct snd_soc_dai_link mop500_dai_links[] = { static struct snd_soc_card mop500_card = { .name = "MOP500-card", + .owner = THIS_MODULE, .probe = NULL, .dai_link = mop500_dai_links, .num_links = ARRAY_SIZE(mop500_dai_links), diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index b9defcdeb7e..780bf3f62d2 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -346,10 +346,10 @@ static int usb6fire_fw_check(u8 *version) if (!memcmp(version, known_fw_versions + i, 2)) return 0; - snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %*ph. " + snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %4ph. " "please reconnect to power. if this failure " "still happens, check your firmware installation.", - 4, version); + version); return -EINVAL; } diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c index 26722423330..f3dd7266c39 100644 --- a/sound/usb/6fire/midi.c +++ b/sound/usb/6fire/midi.c @@ -19,6 +19,10 @@ #include "chip.h" #include "comm.h" +enum { + MIDI_BUFSIZE = 64 +}; + static void usb6fire_midi_out_handler(struct urb *urb) { struct midi_runtime *rt = urb->context; @@ -156,6 +160,12 @@ int usb6fire_midi_init(struct sfire_chip *chip) if (!rt) return -ENOMEM; + rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL); + if (!rt->out_buffer) { + kfree(rt); + return -ENOMEM; + } + rt->chip = chip; rt->in_received = usb6fire_midi_in_received; rt->out_buffer[0] = 0x80; /* 'send midi' command */ @@ -169,6 +179,7 @@ int usb6fire_midi_init(struct sfire_chip *chip) ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance); if (ret < 0) { + kfree(rt->out_buffer); kfree(rt); snd_printk(KERN_ERR PREFIX "unable to create midi.\n"); return ret; @@ -197,6 +208,9 @@ void usb6fire_midi_abort(struct sfire_chip *chip) void usb6fire_midi_destroy(struct sfire_chip *chip) { - kfree(chip->midi); + struct midi_runtime *rt = chip->midi; + + kfree(rt->out_buffer); + kfree(rt); chip->midi = NULL; } diff --git a/sound/usb/6fire/midi.h b/sound/usb/6fire/midi.h index c321006e543..84851b9f555 100644 --- a/sound/usb/6fire/midi.h +++ b/sound/usb/6fire/midi.h @@ -16,10 +16,6 @@ #include "common.h" -enum { - MIDI_BUFSIZE = 64 -}; - struct midi_runtime { struct sfire_chip *chip; struct snd_rawmidi *instance; @@ -32,7 +28,7 @@ struct midi_runtime { struct snd_rawmidi_substream *out; struct urb out_urb; u8 out_serial; /* serial number of out packet */ - u8 out_buffer[MIDI_BUFSIZE]; + u8 *out_buffer; int buffer_offset; void (*in_received)(struct midi_runtime *rt, u8 *data, int length); diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index 3d2551cc10f..b5eb97fdc84 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -582,6 +582,33 @@ static void usb6fire_pcm_init_urb(struct pcm_urb *urb, urb->instance.number_of_packets = PCM_N_PACKETS_PER_URB; } +static int usb6fire_pcm_buffers_init(struct pcm_runtime *rt) +{ + int i; + + for (i = 0; i < PCM_N_URBS; i++) { + rt->out_urbs[i].buffer = kzalloc(PCM_N_PACKETS_PER_URB + * PCM_MAX_PACKET_SIZE, GFP_KERNEL); + if (!rt->out_urbs[i].buffer) + return -ENOMEM; + rt->in_urbs[i].buffer = kzalloc(PCM_N_PACKETS_PER_URB + * PCM_MAX_PACKET_SIZE, GFP_KERNEL); + if (!rt->in_urbs[i].buffer) + return -ENOMEM; + } + return 0; +} + +static void usb6fire_pcm_buffers_destroy(struct pcm_runtime *rt) +{ + int i; + + for (i = 0; i < PCM_N_URBS; i++) { + kfree(rt->out_urbs[i].buffer); + kfree(rt->in_urbs[i].buffer); + } +} + int usb6fire_pcm_init(struct sfire_chip *chip) { int i; @@ -593,6 +620,13 @@ int usb6fire_pcm_init(struct sfire_chip *chip) if (!rt) return -ENOMEM; + ret = usb6fire_pcm_buffers_init(rt); + if (ret) { + usb6fire_pcm_buffers_destroy(rt); + kfree(rt); + return ret; + } + rt->chip = chip; rt->stream_state = STREAM_DISABLED; rt->rate = ARRAY_SIZE(rates); @@ -614,6 +648,7 @@ int usb6fire_pcm_init(struct sfire_chip *chip) ret = snd_pcm_new(chip->card, "DMX6FireUSB", 0, 1, 1, &pcm); if (ret < 0) { + usb6fire_pcm_buffers_destroy(rt); kfree(rt); snd_printk(KERN_ERR PREFIX "cannot create pcm instance.\n"); return ret; @@ -625,6 +660,7 @@ int usb6fire_pcm_init(struct sfire_chip *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); if (ret) { + usb6fire_pcm_buffers_destroy(rt); kfree(rt); snd_printk(KERN_ERR PREFIX "error preallocating pcm buffers.\n"); @@ -669,6 +705,9 @@ void usb6fire_pcm_abort(struct sfire_chip *chip) void usb6fire_pcm_destroy(struct sfire_chip *chip) { - kfree(chip->pcm); + struct pcm_runtime *rt = chip->pcm; + + usb6fire_pcm_buffers_destroy(rt); + kfree(rt); chip->pcm = NULL; } diff --git a/sound/usb/6fire/pcm.h b/sound/usb/6fire/pcm.h index 9b01133ee3f..f5779d6182c 100644 --- a/sound/usb/6fire/pcm.h +++ b/sound/usb/6fire/pcm.h @@ -32,7 +32,7 @@ struct pcm_urb { struct urb instance; struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB]; /* END DO NOT SEPARATE */ - u8 buffer[PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE]; + u8 *buffer; struct pcm_urb *peer; }; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 659950e5b94..93e970f2b3c 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -418,6 +418,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, struct snd_usb_endpoint *ep; int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK; + if (WARN_ON(!alts)) + return NULL; + mutex_lock(&chip->mutex); list_for_each_entry(ep, &chip->ep_list, list) { diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index d5438083fd6..95558ef4a7a 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -888,6 +888,7 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, case USB_ID(0x046d, 0x081b): /* HD Webcam c310 */ case USB_ID(0x046d, 0x081d): /* HD Webcam c510 */ case USB_ID(0x046d, 0x0825): /* HD Webcam c270 */ + case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */ case USB_ID(0x046d, 0x0991): /* Most audio usb devices lie about volume resolution. * Most Logitech webcams have res = 384. diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 15b151ed489..b375d58871e 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -327,6 +327,137 @@ static int search_roland_implicit_fb(struct usb_device *dev, int ifnum, return 0; } +static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, + struct usb_device *dev, + struct usb_interface_descriptor *altsd, + unsigned int attr) +{ + struct usb_host_interface *alts; + struct usb_interface *iface; + unsigned int ep; + + /* Implicit feedback sync EPs consumers are always playback EPs */ + if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + switch (subs->stream->chip->usb_id) { + case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ + case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ + ep = 0x81; + iface = usb_ifnum_to_if(dev, 3); + + if (!iface || iface->num_altsetting == 0) + return -EINVAL; + + alts = &iface->altsetting[1]; + goto add_sync_ep; + break; + case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ + case USB_ID(0x0763, 0x2081): + ep = 0x81; + iface = usb_ifnum_to_if(dev, 2); + + if (!iface || iface->num_altsetting == 0) + return -EINVAL; + + alts = &iface->altsetting[1]; + goto add_sync_ep; + } + if (attr == USB_ENDPOINT_SYNC_ASYNC && + altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && + altsd->bInterfaceProtocol == 2 && + altsd->bNumEndpoints == 1 && + USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ && + search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1, + altsd->bAlternateSetting, + &alts, &ep) >= 0) { + goto add_sync_ep; + } + + /* No quirk */ + return 0; + +add_sync_ep: + subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, + alts, ep, !subs->direction, + SND_USB_ENDPOINT_TYPE_DATA); + if (!subs->sync_endpoint) + return -EINVAL; + + subs->data_endpoint->sync_master = subs->sync_endpoint; + + return 0; +} + +static int set_sync_endpoint(struct snd_usb_substream *subs, + struct audioformat *fmt, + struct usb_device *dev, + struct usb_host_interface *alts, + struct usb_interface_descriptor *altsd) +{ + int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int ep, attr; + bool implicit_fb; + int err; + + /* we need a sync pipe in async OUT or adaptive IN mode */ + /* check the number of EP, since some devices have broken + * descriptors which fool us. if it has only one EP, + * assume it as adaptive-out or sync-in. + */ + attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; + + err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr); + if (err < 0) + return err; + + if (altsd->bNumEndpoints < 2) + return 0; + + if ((is_playback && attr != USB_ENDPOINT_SYNC_ASYNC) || + (!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE)) + return 0; + + /* check sync-pipe endpoint */ + /* ... and check descriptor size before accessing bSynchAddress + because there is a version of the SB Audigy 2 NX firmware lacking + the audio fields in the endpoint descriptors */ + if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC || + (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + get_endpoint(alts, 1)->bSynchAddress != 0)) { + snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n", + dev->devnum, fmt->iface, fmt->altsetting, + get_endpoint(alts, 1)->bmAttributes, + get_endpoint(alts, 1)->bLength, + get_endpoint(alts, 1)->bSynchAddress); + return -EINVAL; + } + ep = get_endpoint(alts, 1)->bEndpointAddress; + if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || + (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { + snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n", + dev->devnum, fmt->iface, fmt->altsetting, + is_playback, ep, get_endpoint(alts, 0)->bSynchAddress); + return -EINVAL; + } + + implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK) + == USB_ENDPOINT_USAGE_IMPLICIT_FB; + + subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, + alts, ep, !subs->direction, + implicit_fb ? + SND_USB_ENDPOINT_TYPE_DATA : + SND_USB_ENDPOINT_TYPE_SYNC); + if (!subs->sync_endpoint) + return -EINVAL; + + subs->data_endpoint->sync_master = subs->sync_endpoint; + + return 0; +} + /* * find a matching format and set up the interface */ @@ -336,9 +467,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; struct usb_interface *iface; - unsigned int ep, attr; - int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - int err, implicit_fb = 0; + int err; iface = usb_ifnum_to_if(dev, fmt->iface); if (WARN_ON(!iface)) @@ -383,118 +512,22 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip, alts, fmt->endpoint, subs->direction, SND_USB_ENDPOINT_TYPE_DATA); + if (!subs->data_endpoint) return -EINVAL; - /* we need a sync pipe in async OUT or adaptive IN mode */ - /* check the number of EP, since some devices have broken - * descriptors which fool us. if it has only one EP, - * assume it as adaptive-out or sync-in. - */ - attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; - - switch (subs->stream->chip->usb_id) { - case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ - case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ - if (is_playback) { - implicit_fb = 1; - ep = 0x81; - iface = usb_ifnum_to_if(dev, 3); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - } - break; - case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ - case USB_ID(0x0763, 0x2081): - if (is_playback) { - implicit_fb = 1; - ep = 0x81; - iface = usb_ifnum_to_if(dev, 2); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - } - } - if (is_playback && - attr == USB_ENDPOINT_SYNC_ASYNC && - altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && - altsd->bInterfaceProtocol == 2 && - altsd->bNumEndpoints == 1 && - USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ && - search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1, - altsd->bAlternateSetting, - &alts, &ep) >= 0) { - implicit_fb = 1; - goto add_sync_ep; - } - - if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || - (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) && - altsd->bNumEndpoints >= 2) { - /* check sync-pipe endpoint */ - /* ... and check descriptor size before accessing bSynchAddress - because there is a version of the SB Audigy 2 NX firmware lacking - the audio fields in the endpoint descriptors */ - if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC || - (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && - get_endpoint(alts, 1)->bSynchAddress != 0 && - !implicit_fb)) { - snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n", - dev->devnum, fmt->iface, fmt->altsetting, - get_endpoint(alts, 1)->bmAttributes, - get_endpoint(alts, 1)->bLength, - get_endpoint(alts, 1)->bSynchAddress); - return -EINVAL; - } - ep = get_endpoint(alts, 1)->bEndpointAddress; - if (!implicit_fb && - get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && - (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || - (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { - snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n", - dev->devnum, fmt->iface, fmt->altsetting, - is_playback, ep, get_endpoint(alts, 0)->bSynchAddress); - return -EINVAL; - } - - implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK) - == USB_ENDPOINT_USAGE_IMPLICIT_FB; - -add_sync_ep: - subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, - alts, ep, !subs->direction, - implicit_fb ? - SND_USB_ENDPOINT_TYPE_DATA : - SND_USB_ENDPOINT_TYPE_SYNC); - if (!subs->sync_endpoint) - return -EINVAL; - - subs->data_endpoint->sync_master = subs->sync_endpoint; - } + err = set_sync_endpoint(subs, fmt, dev, alts, altsd); + if (err < 0) + return err; - if ((err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt)) < 0) + err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt); + if (err < 0) return err; subs->cur_audiofmt = fmt; snd_usb_set_format_quirk(subs, fmt); -#if 0 - printk(KERN_DEBUG - "setting done: format = %d, rate = %d..%d, channels = %d\n", - fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels); - printk(KERN_DEBUG - " datapipe = 0x%0x, syncpipe = 0x%0x\n", - subs->datapipe, subs->syncpipe); -#endif - return 0; } diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 1bc45e71f1f..0df9ede99df 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -319,19 +319,19 @@ static int create_auto_midi_quirk(struct snd_usb_audio *chip, if (altsd->bNumEndpoints < 1) return -ENODEV; epd = get_endpoint(alts, 0); - if (!usb_endpoint_xfer_bulk(epd) || + if (!usb_endpoint_xfer_bulk(epd) && !usb_endpoint_xfer_int(epd)) return -ENODEV; switch (USB_ID_VENDOR(chip->usb_id)) { case 0x0499: /* Yamaha */ err = create_yamaha_midi_quirk(chip, iface, driver, alts); - if (err < 0 && err != -ENODEV) + if (err != -ENODEV) return err; break; case 0x0582: /* Roland */ err = create_roland_midi_quirk(chip, iface, driver, alts); - if (err < 0 && err != -ENODEV) + if (err != -ENODEV) return err; break; } diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 1f9bbd55553..5a51b18c50f 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -305,11 +305,9 @@ static void usX2Y_unlinkSeq(struct snd_usX2Y_AsyncSeq *S) { int i; for (i = 0; i < URBS_AsyncSeq; ++i) { - if (S[i].urb) { - usb_kill_urb(S->urb[i]); - usb_free_urb(S->urb[i]); - S->urb[i] = NULL; - } + usb_kill_urb(S->urb[i]); + usb_free_urb(S->urb[i]); + S->urb[i] = NULL; } kfree(S->buffer); } |