diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-06 19:56:51 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-06 19:56:51 +0200 |
commit | 920f2ecdf6c3b3526f60fbd38c68597953cad3ee (patch) | |
tree | 18188922ba38a5c53ee8d17032eb5c46dffc7fa2 | |
parent | Merge branch 'for-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/c... (diff) | |
parent | ALSA: hda - Fix unbalance of i915 module refcount (diff) | |
download | linux-920f2ecdf6c3b3526f60fbd38c68597953cad3ee.tar.xz linux-920f2ecdf6c3b3526f60fbd38c68597953cad3ee.zip |
Merge tag 'sound-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"This development cycle resulted in a fair amount of changes in both
core and driver sides. The most significant change in ALSA core is
about PCM. Also the support of of-graph card and the new DAPM widget
for DSP are noteworthy changes in ASoC core. And there're lots of
small changes splat over the tree, as you can see in diffstat.
Below are a few highlights:
ALSA core:
- Removal of set_fs() hackery from PCM core stuff, and the code
reorganization / optimization thereafter
- Improved support of PCM ack ops, and a new ABI for improved
control/status mmap handling
- Lots of constifications in various codes
ASoC core:
- The support of of-graph card, which may work as a better generic
device for a replacement of simple-card
- New widget types intended mainly for use with DSPs
ASoC drivers:
- New drivers for Allwinner V3s SoCs
- Ensonic ES8316 codec support
- More Intel SKL and KBL works
- More device support for Intel SST Atom (mostly for cheap tablets
and 2-in-1 devices)
- Support for Rockchip PDM controllers
- Support for STM32 I2S and S/PDIF controllers
- Support for ZTE AUD96P22 codecs
HD-audio:
- Support of new Realtek codecs (ALC215/ALC285/ALC289), more quirks
for HP and Dell machines
- A few more fixes for i915 component binding"
* tag 'sound-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (418 commits)
ALSA: hda - Fix unbalance of i915 module refcount
ASoC: Intel: Skylake: Remove driver debugfs exit
ASoC: Intel: Skylake: explicitly add the headers sst-dsp.h
ALSA: hda/realtek - Remove GPIO_MASK
ALSA: hda/realtek - Fix typo of pincfg for Dell quirk
ALSA: pcm: add a documentation for tracepoints
ALSA: atmel: ac97c: fix error return code in atmel_ac97c_probe()
ALSA: x86: fix error return code in hdmi_lpe_audio_probe()
ASoC: Intel: Skylake: Add support to read firmware registers
ASoC: Intel: Skylake: Add sram address to sst_addr structure
ASoC: Intel: Skylake: Debugfs facility to dump module config
ASoC: Intel: Skylake: Add debugfs support
ASoC: fix semicolon.cocci warnings
ASoC: rt5645: Add quirk override by module option
ASoC: rsnd: make arrays path and cmd_case static const
ASoC: audio-graph-card: add widgets and routing for external amplifier support
ASoC: audio-graph-card: update bindings for amplifier support
ASoC: rt5665: calibration should be done before jack detection
ASoC: rsnd: constify dev_pm_ops structures.
ASoC: nau8825: change crosstalk-bypass property to bool type
...
349 files changed, 13826 insertions, 5620 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt b/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt index 00ea670b8c4d..06668bca7ffc 100644 --- a/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt +++ b/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt @@ -78,6 +78,7 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt. remote endpoint phandle should be a reference to a valid mipi_dsi_host device node. - Video port 1 for the HDMI output +- Audio port 2 for the HDMI audio input Example @@ -112,5 +113,12 @@ Example remote-endpoint = <&hdmi_connector_in>; }; }; + + port@2 { + reg = <2>; + codec_endpoint: endpoint { + remote-endpoint = <&i2s0_cpu_endpoint>; + }; + }; }; }; diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt b/Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt index f6b3f36d422b..81b68580e199 100644 --- a/Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt +++ b/Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt @@ -25,7 +25,8 @@ Required properties: - clock-names: Shall contain "iahb" and "isfr" as defined in dw_hdmi.txt. - ports: See dw_hdmi.txt. The DWC HDMI shall have one port numbered 0 corresponding to the video input of the controller and one port numbered 1 - corresponding to its HDMI output. Each port shall have a single endpoint. + corresponding to its HDMI output, and one port numbered 2 corresponding to + sound input of the controller. Each port shall have a single endpoint. Optional properties: @@ -59,6 +60,12 @@ Example: remote-endpoint = <&hdmi0_con>; }; }; + port@2 { + reg = <2>; + rcar_dw_hdmi0_sound_in: endpoint { + remote-endpoint = <&hdmi_sound_out>; + }; + }; }; }; diff --git a/Documentation/devicetree/bindings/sound/audio-graph-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-card.txt new file mode 100644 index 000000000000..6e6720aa33f1 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/audio-graph-card.txt @@ -0,0 +1,129 @@ +Audio Graph Card: + +Audio Graph Card specifies audio DAI connections of SoC <-> codec. +It is based on common bindings for device graphs. +see ${LINUX}/Documentation/devicetree/bindings/graph.txt + +Basically, Audio Graph Card property is same as Simple Card. +see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt + +Below are same as Simple-Card. + +- label +- widgets +- routing +- dai-format +- frame-master +- bitclock-master +- bitclock-inversion +- frame-inversion +- dai-tdm-slot-num +- dai-tdm-slot-width +- clocks / system-clock-frequency + +Required properties: + +- compatible : "audio-graph-card"; +- dais : list of CPU DAI port{s} + +Optional properties: +- pa-gpios: GPIO used to control external amplifier. + +Example: Single DAI case + + sound_card { + compatible = "audio-graph-card"; + + dais = <&cpu_port>; + }; + + dai-controller { + ... + cpu_port: port { + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + + dai-format = "left_j"; + ... + }; + }; + }; + + audio-codec { + ... + port { + codec_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint>; + }; + }; + }; + +Example: Multi DAI case + + sound-card { + compatible = "audio-graph-card"; + + label = "sound-card"; + + dais = <&cpu_port0 + &cpu_port1 + &cpu_port2>; + }; + + audio-codec@0 { + ... + port { + codec0_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint0>; + }; + }; + }; + + audio-codec@1 { + ... + port { + codec1_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint1>; + }; + }; + }; + + audio-codec@2 { + ... + port { + codec2_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint2>; + }; + }; + }; + + dai-controller { + ... + ports { + cpu_port0: port@0 { + cpu_endpoint0: endpoint { + remote-endpoint = <&codec0_endpoint>; + + dai-format = "left_j"; + ... + }; + }; + cpu_port1: port@1 { + cpu_endpoint1: endpoint { + remote-endpoint = <&codec1_endpoint>; + + dai-format = "i2s"; + ... + }; + }; + cpu_port2: port@2 { + cpu_endpoint2: endpoint { + remote-endpoint = <&codec2_endpoint>; + + dai-format = "i2s"; + ... + }; + }; + }; + }; + diff --git a/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt new file mode 100644 index 000000000000..8b8afe9fcb31 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt @@ -0,0 +1,122 @@ +Audio-Graph-SCU-Card: + +Audio-Graph-SCU-Card is "Audio-Graph-Card" + "ALSA DPCM". + +It is based on common bindings for device graphs. +see ${LINUX}/Documentation/devicetree/bindings/graph.txt + +Basically, Audio-Graph-SCU-Card property is same as +Simple-Card / Simple-SCU-Card / Audio-Graph-Card. +see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt + ${LINUX}/Documentation/devicetree/bindings/sound/simple-scu-card.txt + ${LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt + +Below are same as Simple-Card / Audio-Graph-Card. + +- label +- dai-format +- frame-master +- bitclock-master +- bitclock-inversion +- frame-inversion +- dai-tdm-slot-num +- dai-tdm-slot-width +- clocks / system-clock-frequency + +Below are same as Simple-SCU-Card. + +- convert-rate +- convert-channels +- prefix +- routing + +Required properties: + +- compatible : "audio-graph-scu-card"; +- dais : list of CPU DAI port{s} + +Example 1. Sampling Rate Conversion + + sound_card { + compatible = "audio-graph-scu-card"; + + label = "sound-card"; + prefix = "codec"; + routing = "codec Playback", "DAI0 Playback", + "codec Playback", "DAI1 Playback"; + convert-rate = <48000>; + + dais = <&cpu_port>; + }; + + audio-codec { + ... + + port { + codec_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint>; + }; + }; + }; + + dai-controller { + ... + cpu_port: port { + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + + dai-format = "left_j"; + ... + }; + }; + }; + +Example 2. 2 CPU 1 Codec (Mixing) + + sound_card { + compatible = "audio-graph-scu-card"; + + label = "sound-card"; + prefix = "codec"; + routing = "codec Playback", "DAI0 Playback", + "codec Playback", "DAI1 Playback"; + convert-rate = <48000>; + + dais = <&cpu_port0 + &cpu_port1>; + }; + + audio-codec { + ... + + port { + codec_endpoint0: endpoint { + remote-endpoint = <&cpu_endpoint0>; + }; + codec_endpoint1: endpoint { + remote-endpoint = <&cpu_endpoint1>; + }; + }; + }; + + dai-controller { + ... + ports { + cpu_port0: port { + cpu_endpoint0: endpoint { + remote-endpoint = <&codec_endpoint0>; + + dai-format = "left_j"; + ... + }; + }; + cpu_port1: port { + cpu_endpoint1: endpoint { + remote-endpoint = <&codec_endpoint1>; + + dai-format = "left_j"; + ... + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt index 016b768bc722..77ee75c39233 100644 --- a/Documentation/devicetree/bindings/sound/cs35l35.txt +++ b/Documentation/devicetree/bindings/sound/cs35l35.txt @@ -16,6 +16,9 @@ Required properties: (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for further information relating to interrupt properties) + - cirrus,boost-ind-nanohenry: Inductor value for boost converter. The value is + in nH and they can be values of 1000nH, 1200nH, 1500nH, and 2200nH. + Optional properties: - reset-gpios : gpio used to reset the amplifier diff --git a/Documentation/devicetree/bindings/sound/nau8825.txt b/Documentation/devicetree/bindings/sound/nau8825.txt index d3374231c871..2f5e973285a6 100644 --- a/Documentation/devicetree/bindings/sound/nau8825.txt +++ b/Documentation/devicetree/bindings/sound/nau8825.txt @@ -69,6 +69,8 @@ Optional properties: - nuvoton,jack-insert-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms - nuvoton,jack-eject-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms + - nuvoton,crosstalk-bypass: make crosstalk function bypass if set. + - clocks: list of phandle and clock specifier pairs according to common clock bindings for the clocks described in clock-names - clock-names: should include "mclk" for the MCLK master clock @@ -96,6 +98,7 @@ Example: nuvoton,short-key-debounce = <2>; nuvoton,jack-insert-debounce = <7>; nuvoton,jack-eject-debounce = <7>; + nuvoton,crosstalk-bypass; clock-names = "mclk"; clocks = <&tegra_car TEGRA210_CLK_CLK_OUT_2>; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 15a7316e4c91..7246bb268bf9 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -83,11 +83,11 @@ SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes ** Asynchronous mode ------------------ -You need to use "renesas,rsrc-card" sound card for it. +You need to use "simple-scu-audio-card" sound card for it. example) sound { - compatible = "renesas,rsrc-card"; + compatible = "simple-scu-audio-card"; ... /* * SRC Asynchronous mode setting @@ -97,12 +97,12 @@ example) * Inputed 48kHz data will be converted to * system specified Hz */ - convert-rate = <48000>; + simple-audio-card,convert-rate = <48000>; ... - cpu { + simple-audio-card,cpu { sound-dai = <&rcar_sound>; }; - codec { + simple-audio-card,codec { ... }; }; @@ -141,23 +141,23 @@ For more detail information, see below ${LINUX}/sound/soc/sh/rcar/ctu.c - comment of header -You need to use "renesas,rsrc-card" sound card for it. +You need to use "simple-scu-audio-card" sound card for it. example) sound { - compatible = "renesas,rsrc-card"; + compatible = "simple-scu-audio-card"; ... /* * CTU setting * All input data will be converted to 2ch * as output data */ - convert-channels = <2>; + simple-audio-card,convert-channels = <2>; ... - cpu { + simple-audio-card,cpu { sound-dai = <&rcar_sound>; }; - codec { + simple-audio-card,codec { ... }; }; @@ -190,22 +190,22 @@ and these sounds will be merged by MIX. aplay -D plughw:0,0 xxxx.wav & aplay -D plughw:0,1 yyyy.wav -You need to use "renesas,rsrc-card" sound card for it. +You need to use "simple-scu-audio-card" sound card for it. Ex) [MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0] | [MEM] -> [SRC2] -> [CTU03] -+ sound { - compatible = "renesas,rsrc-card"; + compatible = "simple-scu-audio-card"; ... - cpu@0 { + simple-audio-card,cpu@0 { sound-dai = <&rcar_sound 0>; }; - cpu@1 { + simple-audio-card,cpu@1 { sound-dai = <&rcar_sound 1>; }; - codec { + simple-audio-card,codec { ... }; }; @@ -368,6 +368,10 @@ Required properties: see below for detail. - #sound-dai-cells : it must be 0 if your system is using single DAI it must be 1 if your system is using multi DAI +- clocks : References to SSI/SRC/MIX/CTU/DVC/AUDIO_CLK clocks. +- clock-names : List of necessary clock names. + "ssi-all", "ssi.X", "src.X", "mix.X", "ctu.X", + "dvc.X", "clk_a", "clk_b", "clk_c", "clk_i" Optional properties: - #clock-cells : it must be 0 if your system has audio_clkout @@ -375,6 +379,9 @@ Optional properties: - clock-frequency : for all audio_clkout0/1/2/3 - clkout-lr-asynchronous : boolean property. it indicates that audio_clkoutn is asynchronizes with lr-clock. +- resets : References to SSI resets. +- reset-names : List of valid reset names. + "ssi-all", "ssi.X" SSI subnode properties: - interrupts : Should contain SSI interrupt for PIO transfer diff --git a/Documentation/devicetree/bindings/sound/rockchip,pdm.txt b/Documentation/devicetree/bindings/sound/rockchip,pdm.txt new file mode 100644 index 000000000000..921729de7346 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,pdm.txt @@ -0,0 +1,39 @@ +* Rockchip PDM controller + +Required properties: + +- compatible: "rockchip,pdm" +- reg: physical base address of the controller and length of memory mapped + region. +- dmas: DMA specifiers for rx dma. See the DMA client binding, + Documentation/devicetree/bindings/dma/dma.txt +- dma-names: should include "rx". +- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names. +- clock-names: should contain following: + - "pdm_hclk": clock for PDM BUS + - "pdm_clk" : clock for PDM controller +- pinctrl-names: Must contain a "default" entry. +- pinctrl-N: One property must exist for each entry in + pinctrl-names. See ../pinctrl/pinctrl-bindings.txt + for details of the property values. + +Example for rk3328 PDM controller: + +pdm: pdm@ff040000 { + compatible = "rockchip,pdm"; + reg = <0x0 0xff040000 0x0 0x1000>; + clocks = <&clk_pdm>, <&clk_gates28 0>; + clock-names = "pdm_clk", "pdm_hclk"; + dmas = <&pdma 16>; + #dma-cells = <1>; + dma-names = "rx"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pdmm0_clk + &pdmm0_fsync + &pdmm0_sdi0 + &pdmm0_sdi1 + &pdmm0_sdi2 + &pdmm0_sdi3>; + pinctrl-1 = <&pdmm0_sleep>; + status = "disabled"; +}; diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt index 11046429a118..4706b96d450b 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt +++ b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt @@ -9,7 +9,9 @@ Required properties: - compatible: should be one of the following: - "rockchip,rk3066-spdif" - "rockchip,rk3188-spdif" + - "rockchip,rk3228-spdif" - "rockchip,rk3288-spdif" + - "rockchip,rk3328-spdif" - "rockchip,rk3366-spdif" - "rockchip,rk3368-spdif" - "rockchip,rk3399-spdif" diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.txt b/Documentation/devicetree/bindings/sound/samsung,odroid.txt index c1ac70cb0afb..c30934dd975b 100644 --- a/Documentation/devicetree/bindings/sound/samsung,odroid.txt +++ b/Documentation/devicetree/bindings/sound/samsung,odroid.txt @@ -5,11 +5,6 @@ Required properties: - compatible - "samsung,odroidxu3-audio" - for Odroid XU3 board, "samsung,odroidxu4-audio" - for Odroid XU4 board - model - the user-visible name of this sound complex - - 'cpu' subnode with a 'sound-dai' property containing the phandle of the I2S - controller - - 'codec' subnode with a 'sound-dai' property containing list of phandles - to the CODEC nodes, first entry must be corresponding to the MAX98090 - CODEC and the second entry must be the phandle of the HDMI IP block node - clocks - should contain entries matching clock names in the clock-names property - clock-names - should contain following entries: @@ -32,12 +27,18 @@ Required properties: For Odroid XU4: no entries +Required sub-nodes: + + - 'cpu' subnode with a 'sound-dai' property containing the phandle of the I2S + controller + - 'codec' subnode with a 'sound-dai' property containing list of phandles + to the CODEC nodes, first entry must be corresponding to the MAX98090 + CODEC and the second entry must be the phandle of the HDMI IP block node + Example: sound { compatible = "samsung,odroidxu3-audio"; - samsung,cpu-dai = <&i2s0>; - samsung,codec-dai = <&max98090>; model = "Odroid-XU3"; samsung,audio-routing = "Headphone Jack", "HPL", diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt index d6fe47ed09af..327d229a51b2 100644 --- a/Documentation/devicetree/bindings/sound/simple-scu-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-scu-card.txt @@ -1,35 +1,29 @@ -ASoC simple SCU Sound Card +ASoC Simple SCU Sound Card -Simple-Card specifies audio DAI connections of SoC <-> codec. +Simple SCU Sound Card is "Simple Sound Card" + "ALSA DPCM". +For example, you can use this driver if you want to exchange sampling rate convert, +Mixing, etc... Required properties: - compatible : "simple-scu-audio-card" "renesas,rsrc-card" - Optional properties: -- simple-audio-card,name : User specified audio sound card name, one string - property. -- simple-audio-card,cpu : CPU sub-node -- simple-audio-card,codec : CODEC sub-node +- simple-audio-card,name : see simple-audio-card.txt +- simple-audio-card,cpu : see simple-audio-card.txt +- simple-audio-card,codec : see simple-audio-card.txt Optional subnode properties: -- simple-audio-card,format : CPU/CODEC common audio format. - "i2s", "right_j", "left_j" , "dsp_a" - "dsp_b", "ac97", "pdm", "msb", "lsb" -- simple-audio-card,frame-master : Indicates dai-link frame master. - phandle to a cpu or codec subnode. -- simple-audio-card,bitclock-master : Indicates dai-link bit clock master. - phandle to a cpu or codec subnode. -- simple-audio-card,bitclock-inversion : bool property. Add this if the - dai-link uses bit clock inversion. -- simple-audio-card,frame-inversion : bool property. Add this if the - dai-link uses frame clock inversion. +- simple-audio-card,format : see simple-audio-card.txt +- simple-audio-card,frame-master : see simple-audio-card.txt +- simple-audio-card,bitclock-master : see simple-audio-card.txt +- simple-audio-card,bitclock-inversion : see simple-audio-card.txt +- simple-audio-card,frame-inversion : see simple-audio-card.txt - simple-audio-card,convert-rate : platform specified sampling rate convert - simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch) -- simple-audio-card,prefix : see audio-routing +- simple-audio-card,prefix : see routing - simple-audio-card,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. @@ -38,32 +32,23 @@ Optional subnode properties: Required CPU/CODEC subnodes properties: -- sound-dai : phandle and port of CPU/CODEC +- sound-dai : see simple-audio-card.txt Optional CPU/CODEC subnodes properties: -- clocks / system-clock-frequency : specify subnode's clock if needed. - it can be specified via "clocks" if system has - clock node (= common clock), or "system-clock-frequency" - (if system doens't support common clock) - If a clock is specified, it is - enabled with clk_prepare_enable() - in dai startup() and disabled with - clk_disable_unprepare() in dai - shutdown(). +- clocks / system-clock-frequency : see simple-audio-card.txt -Example 1. Sampling Rate Covert +Example 1. Sampling Rate Conversion sound { compatible = "simple-scu-audio-card"; simple-audio-card,name = "rsnd-ak4643"; simple-audio-card,format = "left_j"; - simple-audio-card,format = "left_j"; simple-audio-card,bitclock-master = <&sndcodec>; simple-audio-card,frame-master = <&sndcodec>; - simple-audio-card,convert-rate = <48000>; /* see audio_clk_a */ + simple-audio-card,convert-rate = <48000>; simple-audio-card,prefix = "ak4642"; simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback", @@ -79,20 +64,18 @@ sound { }; }; -Example 2. 2 CPU 1 Codec +Example 2. 2 CPU 1 Codec (Mixing) sound { - compatible = "renesas,rsrc-card"; - - card-name = "rsnd-ak4643"; - format = "left_j"; - bitclock-master = <&dpcmcpu>; - frame-master = <&dpcmcpu>; + compatible = "simple-scu-audio-card"; - convert-rate = <48000>; /* see audio_clk_a */ + simple-audio-card,name = "rsnd-ak4643"; + simple-audio-card,format = "left_j"; + simple-audio-card,bitclock-master = <&dpcmcpu>; + simple-audio-card,frame-master = <&dpcmcpu>; - audio-prefix = "ak4642"; - audio-routing = "ak4642 Playback", "DAI0 Playback", + simple-audio-card,prefix = "ak4642"; + simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback", "ak4642 Playback", "DAI1 Playback"; dpcmcpu: cpu@0 { diff --git a/Documentation/devicetree/bindings/sound/st,stm32-i2s.txt b/Documentation/devicetree/bindings/sound/st,stm32-i2s.txt new file mode 100644 index 000000000000..4bda52042402 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,stm32-i2s.txt @@ -0,0 +1,62 @@ +STMicroelectronics STM32 SPI/I2S Controller + +The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode. +Only some SPI instances support I2S. + +Required properties: + - compatible: Must be "st,stm32h7-i2s" + - reg: Offset and length of the device's register set. + - interrupts: Must contain the interrupt line id. + - clocks: Must contain phandle and clock specifier pairs for each entry + in clock-names. + - clock-names: Must contain "i2sclk", "pclk", "x8k" and "x11k". + "i2sclk": clock which feeds the internal clock generator + "pclk": clock which feeds the peripheral bus interface + "x8k": I2S parent clock for sampling rates multiple of 8kHz. + "x11k": I2S parent clock for sampling rates multiple of 11.025kHz. + - dmas: DMA specifiers for tx and rx dma. + See Documentation/devicetree/bindings/dma/stm32-dma.txt. + - dma-names: Identifier for each DMA request line. Must be "tx" and "rx". + - pinctrl-names: should contain only value "default" + - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt + +Optional properties: + - resets: Reference to a reset controller asserting the reset controller + +The device node should contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in Documentation/devicetree/bindings/ +graph.txt. + +Example: +sound_card { + compatible = "audio-graph-card"; + dais = <&i2s2_port>; +}; + +i2s2: audio-controller@40003800 { + compatible = "st,stm32h7-i2s"; + reg = <0x40003800 0x400>; + interrupts = <36>; + clocks = <&rcc PCLK1>, <&rcc SPI2_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>; + clock-names = "pclk", "i2sclk", "x8k", "x11k"; + dmas = <&dmamux2 2 39 0x400 0x1>, + <&dmamux2 3 40 0x400 0x1>; + dma-names = "rx", "tx"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2s2>; + + i2s2_port: port@0 { + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + format = "i2s"; + }; + }; +}; + +audio-codec { + codec_port: port@0 { + codec_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint>; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt index c59a3d779e06..f1c5ae59e7c9 100644 --- a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt +++ b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt @@ -6,7 +6,7 @@ The SAI contains two independent audio sub-blocks. Each sub-block has its own clock generator and I/O lines controller. Required properties: - - compatible: Should be "st,stm32f4-sai" + - compatible: Should be "st,stm32f4-sai" or "st,stm32h7-sai" - reg: Base address and size of SAI common register set. - clocks: Must contain phandle and clock specifier pairs for each entry in clock-names. @@ -36,6 +36,10 @@ SAI subnodes required properties: - pinctrl-names: should contain only value "default" - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt +The device node should contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in Documentation/devicetree/bindings/ +graph.txt. + Example: sound_card { compatible = "audio-graph-card"; @@ -43,38 +47,29 @@ sound_card { }; sai1: sai1@40015800 { - compatible = "st,stm32f4-sai"; + compatible = "st,stm32h7-sai"; #address-cells = <1>; #size-cells = <1>; - ranges; + ranges = <0 0x40015800 0x400>; reg = <0x40015800 0x4>; - clocks = <&rcc 1 CLK_SAIQ_PDIV>, <&rcc 1 CLK_I2SQ_PDIV>; + clocks = <&rcc PLL1_Q>, <&rcc PLL2_P>; clock-names = "x8k", "x11k"; interrupts = <87>; - sai1b: audio-controller@40015824 { - #sound-dai-cells = <0>; - compatible = "st,stm32-sai-sub-b"; - reg = <0x40015824 0x1C>; - clocks = <&rcc 1 CLK_SAI2>; + sai1a: audio-controller@40015804 { + compatible = "st,stm32-sai-sub-a"; + reg = <0x4 0x1C>; + clocks = <&rcc SAI1_CK>; clock-names = "sai_ck"; - dmas = <&dma2 5 0 0x400 0x0>; + dmas = <&dmamux1 1 87 0x400 0x0>; dma-names = "tx"; pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_sai1b>; - - ports { - #address-cells = <1>; - #size-cells = <0>; + pinctrl-0 = <&pinctrl_sai1a>; - sai1b_port: port@0 { - reg = <0>; - cpu_endpoint: endpoint { - remote-endpoint = <&codec_endpoint>; - audio-graph-card,format = "i2s"; - audio-graph-card,bitclock-master = <&codec_endpoint>; - audio-graph-card,frame-master = <&codec_endpoint>; - }; + sai1b_port: port { + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + format = "i2s"; }; }; }; diff --git a/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt b/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt new file mode 100644 index 000000000000..33826f2459fa --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,stm32-spdifrx.txt @@ -0,0 +1,56 @@ +STMicroelectronics STM32 S/PDIF receiver (SPDIFRX). + +The SPDIFRX peripheral, is designed to receive an S/PDIF flow compliant with +IEC-60958 and IEC-61937. + +Required properties: + - compatible: should be "st,stm32h7-spdifrx" + - reg: cpu DAI IP base address and size + - clocks: must contain an entry for kclk (used as S/PDIF signal reference) + - clock-names: must contain "kclk" + - interrupts: cpu DAI interrupt line + - dmas: DMA specifiers for audio data DMA and iec control flow DMA + See STM32 DMA bindings, Documentation/devicetree/bindings/dma/stm32-dma.txt + - dma-names: two dmas have to be defined, "rx" and "rx-ctrl" + +Optional properties: + - resets: Reference to a reset controller asserting the SPDIFRX + +The device node should contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in Documentation/devicetree/bindings/ +graph.txt. + +Example: +spdifrx: spdifrx@40004000 { + compatible = "st,stm32h7-spdifrx"; + reg = <0x40004000 0x400>; + clocks = <&rcc SPDIFRX_CK>; + clock-names = "kclk"; + interrupts = <97>; + dmas = <&dmamux1 2 93 0x400 0x0>, + <&dmamux1 3 94 0x400 0x0>; + dma-names = "rx", "rx-ctrl"; + pinctrl-0 = <&spdifrx_pins>; + pinctrl-names = "default"; + + spdifrx_port: port { + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + }; + }; +}; + +spdif_in: spdif-in { + compatible = "linux,spdif-dir"; + + codec_port: port { + codec_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint>; + }; + }; +}; + +soundcard { + compatible = "audio-graph-card"; + dais = <&spdifrx_port>; +}; diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt index 3863531d1e6d..2d4e10deb6f4 100644 --- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt +++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt @@ -7,6 +7,7 @@ Required properties: - "allwinner,sun7i-a20-codec" - "allwinner,sun8i-a23-codec" - "allwinner,sun8i-h3-codec" + - "allwinner,sun8i-v3s-codec" - reg: must contain the registers location and length - interrupts: must contain the codec interrupt - dmas: DMA channels for tx and rx dma. See the DMA client binding, @@ -25,6 +26,7 @@ Required properties for the following compatibles: - "allwinner,sun6i-a31-codec" - "allwinner,sun8i-a23-codec" - "allwinner,sun8i-h3-codec" + - "allwinner,sun8i-v3s-codec" - resets: phandle to the reset control for this device - allwinner,audio-routing: A list of the connections between audio components. Each entry is a pair of strings, the first being the @@ -34,15 +36,15 @@ Required properties for the following compatibles: Audio pins on the SoC: "HP" "HPCOM" - "LINEIN" - "LINEOUT" (not on sun8i-a23) + "LINEIN" (not on sun8i-v3s) + "LINEOUT" (not on sun8i-a23 or sun8i-v3s) "MIC1" - "MIC2" + "MIC2" (not on sun8i-v3s) "MIC3" (sun6i-a31 only) Microphone biases from the SoC: "HBIAS" - "MBIAS" + "MBIAS" (not on sun8i-v3s) Board connectors: "Headphone" @@ -55,6 +57,7 @@ Required properties for the following compatibles: Required properties for the following compatibles: - "allwinner,sun8i-a23-codec" - "allwinner,sun8i-h3-codec" + - "allwinner,sun8i-v3s-codec" - allwinner,codec-analog-controls: A phandle to the codec analog controls block in the PRCM. diff --git a/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt b/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt index 779b735781ba..1b6e7c4e50ab 100644 --- a/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt +++ b/Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt @@ -4,6 +4,7 @@ Required properties: - compatible: must be one of the following compatibles: - "allwinner,sun8i-a23-codec-analog" - "allwinner,sun8i-h3-codec-analog" + - "allwinner,sun8i-v3s-codec-analog" Required properties if not a sub-node of the PRCM node: - reg: must contain the registers location and length diff --git a/Documentation/devicetree/bindings/sound/zte,zx-aud96p22.txt b/Documentation/devicetree/bindings/sound/zte,zx-aud96p22.txt new file mode 100644 index 000000000000..41bb1040eb71 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/zte,zx-aud96p22.txt @@ -0,0 +1,24 @@ +ZTE ZX AUD96P22 Audio Codec + +Required properties: + - compatible: Must be "zte,zx-aud96p22" + - #sound-dai-cells: Should be 0 + - reg: I2C bus slave address of AUD96P22 + +Example: + + i2c0: i2c@1486000 { + compatible = "zte,zx296718-i2c"; + reg = <0x01486000 0x1000>; + interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&audiocrm AUDIO_I2C0_WCLK>; + clock-frequency = <1600000>; + + aud96p22: codec@22 { + compatible = "zte,zx-aud96p22"; + #sound-dai-cells = <0>; + reg = <0x22>; + }; + }; diff --git a/Documentation/sound/designs/index.rst b/Documentation/sound/designs/index.rst index 04dcdae3e4f2..f0749943ccb2 100644 --- a/Documentation/sound/designs/index.rst +++ b/Documentation/sound/designs/index.rst @@ -9,6 +9,7 @@ Designs and Implementations compress-offload timestamping jack-controls + tracepoints procfile powersave oss-emulation diff --git a/Documentation/sound/designs/tracepoints.rst b/Documentation/sound/designs/tracepoints.rst new file mode 100644 index 000000000000..78bc5572f829 --- /dev/null +++ b/Documentation/sound/designs/tracepoints.rst @@ -0,0 +1,172 @@ +=================== +Tracepoints in ALSA +=================== + +2017/07/02 +Takasahi Sakamoto + +Tracepoints in ALSA PCM core +============================ + +ALSA PCM core registers ``snd_pcm`` subsystem to kernel tracepoint system. +This subsystem includes two categories of tracepoints; for state of PCM buffer +and for processing of PCM hardware parameters. These tracepoints are available +when corresponding kernel configurations are enabled. When ``CONFIG_SND_DEBUG`` +is enabled, the latter tracepoints are available. When additional +``SND_PCM_XRUN_DEBUG`` is enabled too, the former trace points are enabled. + +Tracepoints for state of PCM buffer +------------------------------------ + +This category includes four tracepoints; ``hwptr``, ``applptr``, ``xrun`` and +``hw_ptr_error``. + +Tracepoints for processing of PCM hardware parameters +----------------------------------------------------- + +This category includes two tracepoints; ``hw_mask_param`` and +``hw_interval_param``. + +In a design of ALSA PCM core, data transmission is abstracted as PCM substream. +Applications manage PCM substream to maintain data transmission for PCM frames. +Before starting the data transmission, applications need to configure PCM +substream. In this procedure, PCM hardware parameters are decided by +interaction between applications and ALSA PCM core. Once decided, runtime of +the PCM substream keeps the parameters. + +The parameters are described in :c:type:`struct snd_pcm_hw_params`. This +structure includes several types of parameters. Applications set preferable +value to these parameters, then execute ioctl(2) with SNDRV_PCM_IOCTL_HW_REFINE +or SNDRV_PCM_IOCTL_HW_PARAMS. The former is used just for refining available +set of parameters. The latter is used for an actual decision of the parameters. + +The :c:type:`struct snd_pcm_hw_params` structure has below members: + +``flags`` + Configurable. ALSA PCM core and some drivers handle this flag to select + convenient parameters or change their behaviour. +``masks`` + Configurable. This type of parameter is described in + :c:type:`struct snd_mask` and represent mask values. As of PCM protocol + v2.0.13, three types are defined. + + - SNDRV_PCM_HW_PARAM_ACCESS + - SNDRV_PCM_HW_PARAM_FORMAT + - SNDRV_PCM_HW_PARAM_SUBFORMAT +``intervals`` + Configurable. This type of parameter is described in + :c:type:`struct snd_interval` and represent values with a range. As of + PCM protocol v2.0.13, twelve types are defined. + + - SNDRV_PCM_HW_PARAM_SAMPLE_BITS + - SNDRV_PCM_HW_PARAM_FRAME_BITS + - SNDRV_PCM_HW_PARAM_CHANNELS + - SNDRV_PCM_HW_PARAM_RATE + - SNDRV_PCM_HW_PARAM_PERIOD_TIME + - SNDRV_PCM_HW_PARAM_PERIOD_SIZE + - SNDRV_PCM_HW_PARAM_PERIOD_BYTES + - SNDRV_PCM_HW_PARAM_PERIODS + - SNDRV_PCM_HW_PARAM_BUFFER_TIME + - SNDRV_PCM_HW_PARAM_BUFFER_SIZE + - SNDRV_PCM_HW_PARAM_BUFFER_BYTES + - SNDRV_PCM_HW_PARAM_TICK_TIME +``rmask`` + Configurable. This is evaluated at ioctl(2) with + SNDRV_PCM_IOCTL_HW_REFINE only. Applications can select which + mask/interval parameter can be changed by ALSA PCM core. For + SNDRV_PCM_IOCTL_HW_PARAMS, this mask is ignored and all of parameters + are going to be changed. +``cmask`` + Read-only. After returning from ioctl(2), buffer in user space for + :c:type:`struct snd_pcm_hw_params` includes result of each operation. + This mask represents which mask/interval parameter is actually changed. +``info`` + Read-only. This represents hardware/driver capabilities as bit flags + with SNDRV_PCM_INFO_XXX. Typically, applications execute ioctl(2) with + SNDRV_PCM_IOCTL_HW_REFINE to retrieve this flag, then decide candidates + of parameters and execute ioctl(2) with SNDRV_PCM_IOCTL_HW_PARAMS to + configure PCM substream. +``msbits`` + Read-only. This value represents available bit width in MSB side of + a PCM sample. When a parameter of SNDRV_PCM_HW_PARAM_SAMPLE_BITS was + decided as a fixed number, this value is also calculated according to + it. Else, zero. But this behaviour depends on implementations in driver + side. +``rate_num`` + Read-only. This value represents numerator of sampling rate in fraction + notation. Basically, when a parameter of SNDRV_PCM_HW_PARAM_RATE was + decided as a single value, this value is also calculated according to + it. Else, zero. But this behaviour depends on implementations in driver + side. +``rate_den`` + Read-only. This value represents denominator of sampling rate in + fraction notation. Basically, when a parameter of + SNDRV_PCM_HW_PARAM_RATE was decided as a single value, this value is + also calculated according to it. Else, zero. But this behaviour depends + on implementations in driver side. +``fifo_size`` + Read-only. This value represents the size of FIFO in serial sound + interface of hardware. Basically, each driver can assigns a proper + value to this parameter but some drivers intentionally set zero with + a care of hardware design or data transmission protocol. + +ALSA PCM core handles buffer of :c:type:`struct snd_pcm_hw_params` when +applications execute ioctl(2) with SNDRV_PCM_HW_REFINE or SNDRV_PCM_HW_PARAMS. +Parameters in the buffer are changed according to +:c:type:`struct snd_pcm_hardware` and rules of constraints in the runtime. The +structure describes capabilities of handled hardware. The rules describes +dependencies on which a parameter is decided according to several parameters. +A rule has a callback function, and drivers can register arbitrary functions +to compute the target parameter. ALSA PCM core registers some rules to the +runtime as a default. + +Each driver can join in the interaction as long as it prepared for two stuffs +in a callback of :c:type:`struct snd_pcm_ops.open`. + +1. In the callback, drivers are expected to change a member of + :c:type:`struct snd_pcm_hardware` type in the runtime, according to + capacities of corresponding hardware. +2. In the same callback, drivers are also expected to register additional rules + of constraints into the runtime when several parameters have dependencies + due to hardware design. + +The driver can refers to result of the interaction in a callback of +:c:type:`struct snd_pcm_ops.hw_params`, however it should not change the +content. + +Tracepoints in this category are designed to trace changes of the +mask/interval parameters. When ALSA PCM core changes them, ``hw_mask_param`` or +``hw_interval_param`` event is probed according to type of the changed parameter. + +ALSA PCM core also has a pretty print format for each of the tracepoints. Below +is an example for ``hw_mask_param``. + +:: + + hw_mask_param: pcmC0D0p 001/023 FORMAT 00000000000000000000001000000044 00000000000000000000001000000044 + + +Below is an example for ``hw_interval_param``. + +:: + + hw_interval_param: pcmC0D0p 000/023 BUFFER_SIZE 0 0 [0 4294967295] 0 1 [0 4294967295] + +The first three fields are common. They represent name of ALSA PCM character +device, rules of constraint and name of the changed parameter, in order. The +field for rules of constraint consists of two sub-fields; index of applied rule +and total number of rules added to the runtime. As an exception, the index 000 +means that the parameter is changed by ALSA PCM core, regardless of the rules. + +The rest of field represent state of the parameter before/after changing. These +fields are different according to type of the parameter. For parameters of mask +type, the fields represent hexadecimal dump of content of the parameter. For +parameters of interval type, the fields represent values of each member of +``empty``, ``integer``, ``openmin``, ``min``, ``max``, ``openmax`` in +:c:type:`struct snd_interval` in this order. + +Tracepoints in drivers +====================== + +Some drivers have tracepoints for developers' convenience. For them, please +refer to each documentation or implementation. diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst index 95c5443eff38..58ffa3f5bda7 100644 --- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst +++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -2080,8 +2080,8 @@ sleeping poll threads, etc. This callback is also atomic as default. -copy and silence callbacks -~~~~~~~~~~~~~~~~~~~~~~~~~~ +copy_user, copy_kernel and fill_silence ops +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These callbacks are not mandatory, and can be omitted in most cases. These callbacks are used when the hardware buffer cannot be in the @@ -3532,8 +3532,9 @@ external hardware buffer in interrupts (or in tasklets, preferably). The first case works fine if the external hardware buffer is large enough. This method doesn't need any extra buffers and thus is more -effective. You need to define the ``copy`` and ``silence`` callbacks -for the data transfer. However, there is a drawback: it cannot be +effective. You need to define the ``copy_user`` and ``copy_kernel`` +callbacks for the data transfer, in addition to ``fill_silence`` +callback for playback. However, there is a drawback: it cannot be mmapped. The examples are GUS's GF1 PCM or emu8000's wavetable PCM. The second case allows for mmap on the buffer, although you have to @@ -3545,30 +3546,34 @@ Another case is when the chip uses a PCI memory-map region for the buffer instead of the host memory. In this case, mmap is available only on certain architectures like the Intel one. In non-mmap mode, the data cannot be transferred as in the normal way. Thus you need to define the -``copy`` and ``silence`` callbacks as well, as in the cases above. The -examples are found in ``rme32.c`` and ``rme96.c``. +``copy_user``, ``copy_kernel`` and ``fill_silence`` callbacks as well, +as in the cases above. The examples are found in ``rme32.c`` and +``rme96.c``. -The implementation of the ``copy`` and ``silence`` callbacks depends -upon whether the hardware supports interleaved or non-interleaved -samples. The ``copy`` callback is defined like below, a bit -differently depending whether the direction is playback or capture: +The implementation of the ``copy_user``, ``copy_kernel`` and +``silence`` callbacks depends upon whether the hardware supports +interleaved or non-interleaved samples. The ``copy_user`` callback is +defined like below, a bit differently depending whether the direction +is playback or capture: :: - static int playback_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count); - static int capture_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count); + static int playback_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count); + static int capture_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *dst, unsigned long count); In the case of interleaved samples, the second argument (``channel``) is not used. The third argument (``pos``) points the current position -offset in frames. +offset in bytes. The meaning of the fourth argument is different between playback and capture. For playback, it holds the source data pointer, and for capture, it's the destination data pointer. -The last argument is the number of frames to be copied. +The last argument is the number of bytes to be copied. What you have to do in this callback is again different between playback and capture directions. In the playback case, you copy the given amount @@ -3578,8 +3583,7 @@ way, the copy would be like: :: - my_memcpy(my_buffer + frames_to_bytes(runtime, pos), src, - frames_to_bytes(runtime, count)); + my_memcpy_from_user(my_buffer + pos, src, count); For the capture direction, you copy the given amount of data (``count``) at the specified offset (``pos``) on the hardware buffer to the @@ -3587,31 +3591,68 @@ specified pointer (``dst``). :: - my_memcpy(dst, my_buffer + frames_to_bytes(runtime, pos), - frames_to_bytes(runtime, count)); + my_memcpy_to_user(dst, my_buffer + pos, count); + +Here the functions are named as ``from_user`` and ``to_user`` because +it's the user-space buffer that is passed to these callbacks. That +is, the callback is supposed to copy from/to the user-space data +directly to/from the hardware buffer. -Note that both the position and the amount of data are given in frames. +Careful readers might notice that these callbacks receive the +arguments in bytes, not in frames like other callbacks. It's because +it would make coding easier like the examples above, and also it makes +easier to unify both the interleaved and non-interleaved cases, as +explained in the following. In the case of non-interleaved samples, the implementation will be a bit -more complicated. +more complicated. The callback is called for each channel, passed by +the second argument, so totally it's called for N-channels times per +transfer. + +The meaning of other arguments are almost same as the interleaved +case. The callback is supposed to copy the data from/to the given +user-space buffer, but only for the given channel. For the detailed +implementations, please check ``isa/gus/gus_pcm.c`` or +"pci/rme9652/rme9652.c" as examples. + +The above callbacks are the copy from/to the user-space buffer. There +are some cases where we want copy from/to the kernel-space buffer +instead. In such a case, ``copy_kernel`` callback is called. It'd +look like: + +:: + + static int playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count); + static int capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count); + +As found easily, the only difference is that the buffer pointer is +without ``__user`` prefix; that is, a kernel-buffer pointer is passed +in the fourth argument. Correspondingly, the implementation would be +a version without the user-copy, such as: -You need to check the channel argument, and if it's -1, copy the whole -channels. Otherwise, you have to copy only the specified channel. Please -check ``isa/gus/gus_pcm.c`` as an example. +:: + + my_memcpy(my_buffer + pos, src, count); -The ``silence`` callback is also implemented in a similar way +Usually for the playback, another callback ``fill_silence`` is +defined. It's implemented in a similar way as the copy callbacks +above: :: static int silence(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count); + unsigned long pos, unsigned long count); -The meanings of arguments are the same as in the ``copy`` callback, -although there is no ``src/dst`` argument. In the case of interleaved -samples, the channel argument has no meaning, as well as on ``copy`` -callback. +The meanings of arguments are the same as in the ``copy_user`` and +``copy_kernel`` callbacks, although there is no buffer pointer +argument. In the case of interleaved samples, the channel argument has +no meaning, as well as on ``copy_*`` callbacks. -The role of ``silence`` callback is to set the given amount +The role of ``fill_silence`` callback is to set the given amount (``count``) of silence data at the specified offset (``pos``) on the hardware buffer. Suppose that the data format is signed (that is, the silent-data is 0), and the implementation using a memset-like function @@ -3619,11 +3660,11 @@ would be like: :: - my_memcpy(my_buffer + frames_to_bytes(runtime, pos), 0, - frames_to_bytes(runtime, count)); + my_memset(my_buffer + pos, 0, count); In the case of non-interleaved samples, again, the implementation -becomes a bit more complicated. See, for example, ``isa/gus/gus_pcm.c``. +becomes a bit more complicated, as it's called N-times per transfer +for each channel. See, for example, ``isa/gus/gus_pcm.c``. Non-Contiguous Buffers ---------------------- diff --git a/Documentation/sound/soc/dapm.rst b/Documentation/sound/soc/dapm.rst index a27f42befa4d..8e44107933ab 100644 --- a/Documentation/sound/soc/dapm.rst +++ b/Documentation/sound/soc/dapm.rst @@ -105,6 +105,24 @@ Pre Special PRE widget (exec before all others) Post Special POST widget (exec after all others) +Buffer + Inter widget audio data buffer within a DSP. +Scheduler + DSP internal scheduler that schedules component/pipeline processing + work. +Effect + Widget that performs an audio processing effect. +SRC + Sample Rate Converter within DSP or CODEC +ASRC + Asynchronous Sample Rate Converter within DSP or CODEC +Encoder + Widget that encodes audio data from one format (usually PCM) to another + usually more compressed format. +Decoder + Widget that decodes audio data from a compressed format to an + uncompressed format like PCM. + (Widgets are defined in include/sound/soc-dapm.h) diff --git a/drivers/dma/dw/Kconfig b/drivers/dma/dw/Kconfig index 5a37b9fcf40d..04b9728c1d26 100644 --- a/drivers/dma/dw/Kconfig +++ b/drivers/dma/dw/Kconfig @@ -6,17 +6,12 @@ config DW_DMAC_CORE tristate select DMA_ENGINE -config DW_DMAC_BIG_ENDIAN_IO - bool - config DW_DMAC tristate "Synopsys DesignWare AHB DMA platform driver" select DW_DMAC_CORE - select DW_DMAC_BIG_ENDIAN_IO if AVR32 - default y if CPU_AT32AP7000 help Support the Synopsys DesignWare AHB DMA controller. This - can be integrated in chips such as the Atmel AT32ap7000. + can be integrated in chips such as the Intel Cherrytrail. config DW_DMAC_PCI tristate "Synopsys DesignWare AHB DMA PCI driver" diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index e500950dad82..f43e6dafe446 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -561,92 +561,14 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc_descriptor_complete(dwc, bad_desc, true); } -/* --------------------- Cyclic DMA API extensions -------------------- */ - -dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - return channel_readl(dwc, SAR); -} -EXPORT_SYMBOL(dw_dma_get_src_addr); - -dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - return channel_readl(dwc, DAR); -} -EXPORT_SYMBOL(dw_dma_get_dst_addr); - -/* Called with dwc->lock held and all DMAC interrupts disabled */ -static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, - u32 status_block, u32 status_err, u32 status_xfer) -{ - unsigned long flags; - - if (status_block & dwc->mask) { - void (*callback)(void *param); - void *callback_param; - - dev_vdbg(chan2dev(&dwc->chan), "new cyclic period llp 0x%08x\n", - channel_readl(dwc, LLP)); - dma_writel(dw, CLEAR.BLOCK, dwc->mask); - - callback = dwc->cdesc->period_callback; - callback_param = dwc->cdesc->period_callback_param; - - if (callback) - callback(callback_param); - } - - /* - * Error and transfer complete are highly unlikely, and will most - * likely be due to a configuration error by the user. - */ - if (unlikely(status_err & dwc->mask) || - unlikely(status_xfer & dwc->mask)) { - unsigned int i; - - dev_err(chan2dev(&dwc->chan), - "cyclic DMA unexpected %s interrupt, stopping DMA transfer\n", - status_xfer ? "xfer" : "error"); - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_dump_chan_regs(dwc); - - dwc_chan_disable(dw, dwc); - - /* Make sure DMA does not restart by loading a new list */ - channel_writel(dwc, LLP, 0); - channel_writel(dwc, CTL_LO, 0); - channel_writel(dwc, CTL_HI, 0); - - dma_writel(dw, CLEAR.BLOCK, dwc->mask); - dma_writel(dw, CLEAR.ERROR, dwc->mask); - dma_writel(dw, CLEAR.XFER, dwc->mask); - - for (i = 0; i < dwc->cdesc->periods; i++) - dwc_dump_lli(dwc, dwc->cdesc->desc[i]); - - spin_unlock_irqrestore(&dwc->lock, flags); - } - - /* Re-enable interrupts */ - channel_set_bit(dw, MASK.BLOCK, dwc->mask); -} - -/* ------------------------------------------------------------------------- */ - static void dw_dma_tasklet(unsigned long data) { struct dw_dma *dw = (struct dw_dma *)data; struct dw_dma_chan *dwc; - u32 status_block; u32 status_xfer; u32 status_err; unsigned int i; - status_block = dma_readl(dw, RAW.BLOCK); status_xfer = dma_readl(dw, RAW.XFER); status_err = dma_readl(dw, RAW.ERROR); @@ -655,8 +577,7 @@ static void dw_dma_tasklet(unsigned long data) for (i = 0; i < dw->dma.chancnt; i++) { dwc = &dw->chan[i]; if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) - dwc_handle_cyclic(dw, dwc, status_block, status_err, - status_xfer); + dev_vdbg(dw->dma.dev, "Cyclic xfer is not implemented\n"); else if (status_err & (1 << i)) dwc_handle_error(dw, dwc); else if (status_xfer & (1 << i)) @@ -1264,255 +1185,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan) dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } -/* --------------------- Cyclic DMA API extensions -------------------- */ - -/** - * dw_dma_cyclic_start - start the cyclic DMA transfer - * @chan: the DMA channel to start - * - * Must be called with soft interrupts disabled. Returns zero on success or - * -errno on failure. - */ -int dw_dma_cyclic_start(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - unsigned long flags; - - if (!test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) { - dev_err(chan2dev(&dwc->chan), "missing prep for cyclic DMA\n"); - return -ENODEV; - } - - spin_lock_irqsave(&dwc->lock, flags); - - /* Enable interrupts to perform cyclic transfer */ - channel_set_bit(dw, MASK.BLOCK, dwc->mask); - - dwc_dostart(dwc, dwc->cdesc->desc[0]); - - spin_unlock_irqrestore(&dwc->lock, flags); - - return 0; -} -EXPORT_SYMBOL(dw_dma_cyclic_start); - -/** - * dw_dma_cyclic_stop - stop the cyclic DMA transfer - * @chan: the DMA channel to stop - * - * Must be called with soft interrupts disabled. - */ -void dw_dma_cyclic_stop(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_disable(dw, dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); -} -EXPORT_SYMBOL(dw_dma_cyclic_stop); - -/** - * dw_dma_cyclic_prep - prepare the cyclic DMA transfer - * @chan: the DMA channel to prepare - * @buf_addr: physical DMA address where the buffer starts - * @buf_len: total number of bytes for the entire buffer - * @period_len: number of bytes for each period - * @direction: transfer direction, to or from device - * - * Must be called before trying to start the transfer. Returns a valid struct - * dw_cyclic_desc if successful or an ERR_PTR(-errno) if not successful. - */ -struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, - dma_addr_t buf_addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction direction) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dma_slave_config *sconfig = &dwc->dma_sconfig; - struct dw_cyclic_desc *cdesc; - struct dw_cyclic_desc *retval = NULL; - struct dw_desc *desc; - struct dw_desc *last = NULL; - u8 lms = DWC_LLP_LMS(dwc->dws.m_master); - unsigned long was_cyclic; - unsigned int reg_width; - unsigned int periods; - unsigned int i; - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - if (dwc->nollp) { - spin_unlock_irqrestore(&dwc->lock, flags); - dev_dbg(chan2dev(&dwc->chan), - "channel doesn't support LLP transfers\n"); - return ERR_PTR(-EINVAL); - } - - if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) { - spin_unlock_irqrestore(&dwc->lock, flags); - dev_dbg(chan2dev(&dwc->chan), - "queue and/or active list are not empty\n"); - return ERR_PTR(-EBUSY); - } - - was_cyclic = test_and_set_bit(DW_DMA_IS_CYCLIC, &dwc->flags); - spin_unlock_irqrestore(&dwc->lock, flags); - if (was_cyclic) { - dev_dbg(chan2dev(&dwc->chan), - "channel already prepared for cyclic DMA\n"); - return ERR_PTR(-EBUSY); - } - - retval = ERR_PTR(-EINVAL); - - if (unlikely(!is_slave_direction(direction))) - goto out_err; - - dwc->direction = direction; - - if (direction == DMA_MEM_TO_DEV) - reg_width = __ffs(sconfig->dst_addr_width); - else - reg_width = __ffs(sconfig->src_addr_width); - - periods = buf_len / period_len; - - /* Check for too big/unaligned periods and unaligned DMA buffer. */ - if (period_len > (dwc->block_size << reg_width)) - goto out_err; - if (unlikely(period_len & ((1 << reg_width) - 1))) - goto out_err; - if (unlikely(buf_addr & ((1 << reg_width) - 1))) - goto out_err; - - retval = ERR_PTR(-ENOMEM); - - cdesc = kzalloc(sizeof(struct dw_cyclic_desc), GFP_KERNEL); - if (!cdesc) - goto out_err; - - cdesc->desc = kzalloc(sizeof(struct dw_desc *) * periods, GFP_KERNEL); - if (!cdesc->desc) - goto out_err_alloc; - - for (i = 0; i < periods; i++) { - desc = dwc_desc_get(dwc); - if (!desc) - goto out_err_desc_get; - - switch (direction) { - case DMA_MEM_TO_DEV: - lli_write(desc, dar, sconfig->dst_addr); - lli_write(desc, sar, buf_addr + period_len * i); - lli_write(desc, ctllo, (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_DST_WIDTH(reg_width) - | DWC_CTLL_SRC_WIDTH(reg_width) - | DWC_CTLL_DST_FIX - | DWC_CTLL_SRC_INC - | DWC_CTLL_INT_EN)); - - lli_set(desc, ctllo, sconfig->device_fc ? - DWC_CTLL_FC(DW_DMA_FC_P_M2P) : - DWC_CTLL_FC(DW_DMA_FC_D_M2P)); - - break; - case DMA_DEV_TO_MEM: - lli_write(desc, dar, buf_addr + period_len * i); - lli_write(desc, sar, sconfig->src_addr); - lli_write(desc, ctllo, (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_SRC_WIDTH(reg_width) - | DWC_CTLL_DST_WIDTH(reg_width) - | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_FIX - | DWC_CTLL_INT_EN)); - - lli_set(desc, ctllo, sconfig->device_fc ? - DWC_CTLL_FC(DW_DMA_FC_P_P2M) : - DWC_CTLL_FC(DW_DMA_FC_D_P2M)); - - break; - default: - break; - } - - lli_write(desc, ctlhi, period_len >> reg_width); - cdesc->desc[i] = desc; - - if (last) - lli_write(last, llp, desc->txd.phys | lms); - - last = desc; - } - - /* Let's make a cyclic list */ - lli_write(last, llp, cdesc->desc[0]->txd.phys | lms); - - dev_dbg(chan2dev(&dwc->chan), - "cyclic prepared buf %pad len %zu period %zu periods %d\n", - &buf_addr, buf_len, period_len, periods); - - cdesc->periods = periods; - dwc->cdesc = cdesc; - - return cdesc; - -out_err_desc_get: - while (i--) - dwc_desc_put(dwc, cdesc->desc[i]); -out_err_alloc: - kfree(cdesc); -out_err: - clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); - return (struct dw_cyclic_desc *)retval; -} -EXPORT_SYMBOL(dw_dma_cyclic_prep); - -/** - * dw_dma_cyclic_free - free a prepared cyclic DMA transfer - * @chan: the DMA channel to free - */ -void dw_dma_cyclic_free(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - struct dw_cyclic_desc *cdesc = dwc->cdesc; - unsigned int i; - unsigned long flags; - - dev_dbg(chan2dev(&dwc->chan), "%s\n", __func__); - - if (!cdesc) - return; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_disable(dw, dwc); - - dma_writel(dw, CLEAR.BLOCK, dwc->mask); - dma_writel(dw, CLEAR.ERROR, dwc->mask); - dma_writel(dw, CLEAR.XFER, dwc->mask); - - spin_unlock_irqrestore(&dwc->lock, flags); - - for (i = 0; i < cdesc->periods; i++) - dwc_desc_put(dwc, cdesc->desc[i]); - - kfree(cdesc->desc); - kfree(cdesc); - - dwc->cdesc = NULL; - - clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); -} -EXPORT_SYMBOL(dw_dma_cyclic_free); - -/*----------------------------------------------------------------------*/ - int dw_dma_probe(struct dw_dma_chip *chip) { struct dw_dma_platform_data *pdata; @@ -1642,7 +1314,7 @@ int dw_dma_probe(struct dw_dma_chip *chip) if (autocfg) { unsigned int r = DW_DMA_MAX_NR_CHANNELS - i - 1; void __iomem *addr = &__dw_regs(dw)->DWC_PARAMS[r]; - unsigned int dwc_params = dma_readl_native(addr); + unsigned int dwc_params = readl(addr); dev_dbg(chip->dev, "DWC_PARAMS[%d]: 0x%08x\n", i, dwc_params); diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 32a328721c88..09e7dfdbb790 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -116,20 +116,6 @@ struct dw_dma_regs { DW_REG(GLOBAL_CFG); }; -/* - * Big endian I/O access when reading and writing to the DMA controller - * registers. This is needed on some platforms, like the Atmel AVR32 - * architecture. - */ - -#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO -#define dma_readl_native ioread32be -#define dma_writel_native iowrite32be -#else -#define dma_readl_native readl -#define dma_writel_native writel -#endif - /* Bitfields in DW_PARAMS */ #define DW_PARAMS_NR_CHAN 8 /* number of channels */ #define DW_PARAMS_NR_MASTER 11 /* number of AHB masters */ @@ -280,7 +266,6 @@ struct dw_dma_chan { unsigned long flags; struct list_head active_list; struct list_head queue; - struct dw_cyclic_desc *cdesc; unsigned int descs_allocated; @@ -302,9 +287,9 @@ __dwc_regs(struct dw_dma_chan *dwc) } #define channel_readl(dwc, name) \ - dma_readl_native(&(__dwc_regs(dwc)->name)) + readl(&(__dwc_regs(dwc)->name)) #define channel_writel(dwc, name, val) \ - dma_writel_native((val), &(__dwc_regs(dwc)->name)) + writel((val), &(__dwc_regs(dwc)->name)) static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) { @@ -333,9 +318,9 @@ static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) } #define dma_readl(dw, name) \ - dma_readl_native(&(__dw_regs(dw)->name)) + readl(&(__dw_regs(dw)->name)) #define dma_writel(dw, name, val) \ - dma_writel_native((val), &(__dw_regs(dw)->name)) + writel((val), &(__dw_regs(dw)->name)) #define idma32_readq(dw, name) \ hi_lo_readq(&(__dw_regs(dw)->name)) @@ -352,43 +337,30 @@ static inline struct dw_dma *to_dw_dma(struct dma_device *ddev) return container_of(ddev, struct dw_dma, dma); } -#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO -typedef __be32 __dw32; -#else -typedef __le32 __dw32; -#endif - /* LLI == Linked List Item; a.k.a. DMA block descriptor */ struct dw_lli { /* values that are not changed by hardware */ - __dw32 sar; - __dw32 dar; - __dw32 llp; /* chain to next lli */ - __dw32 ctllo; + __le32 sar; + __le32 dar; + __le32 llp; /* chain to next lli */ + __le32 ctllo; /* values that may get written back: */ - __dw32 ctlhi; + __le32 ctlhi; /* sstat and dstat can snapshot peripheral register state. * silicon config may discard either or both... */ - __dw32 sstat; - __dw32 dstat; + __le32 sstat; + __le32 dstat; }; struct dw_desc { /* FIRST values the hardware uses */ struct dw_lli lli; -#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO -#define lli_set(d, reg, v) ((d)->lli.reg |= cpu_to_be32(v)) -#define lli_clear(d, reg, v) ((d)->lli.reg &= ~cpu_to_be32(v)) -#define lli_read(d, reg) be32_to_cpu((d)->lli.reg) -#define lli_write(d, reg, v) ((d)->lli.reg = cpu_to_be32(v)) -#else #define lli_set(d, reg, v) ((d)->lli.reg |= cpu_to_le32(v)) #define lli_clear(d, reg, v) ((d)->lli.reg &= ~cpu_to_le32(v)) #define lli_read(d, reg) le32_to_cpu((d)->lli.reg) #define lli_write(d, reg, v) ((d)->lli.reg = cpu_to_le32(v)) -#endif /* THEN values for driver housekeeping */ struct list_head desc_node; diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c index cf92ebfe6ab7..67469c26bae8 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c @@ -11,6 +11,7 @@ #include <sound/hdmi-codec.h> #include <sound/pcm.h> #include <sound/soc.h> +#include <linux/of_graph.h> #include "adv7511.h" @@ -182,10 +183,31 @@ static void audio_shutdown(struct device *dev, void *data) { } +static int adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + struct of_endpoint of_ep; + int ret; + + ret = of_graph_parse_endpoint(endpoint, &of_ep); + if (ret < 0) + return ret; + + /* + * HDMI sound should be located as reg = <2> + * Then, it is sound port 0 + */ + if (of_ep.port == 2) + return 0; + + return -EINVAL; +} + static const struct hdmi_codec_ops adv7511_codec_ops = { .hw_params = adv7511_hdmi_hw_params, .audio_shutdown = audio_shutdown, .audio_startup = audio_startup, + .get_dai_id = adv7511_hdmi_i2s_get_dai_id, }; static struct hdmi_codec_pdata codec_data = { diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index aaf287d2e91d..b2cf59f54c88 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -82,9 +82,30 @@ static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data) hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0); } +static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + struct of_endpoint of_ep; + int ret; + + ret = of_graph_parse_endpoint(endpoint, &of_ep); + if (ret < 0) + return ret; + + /* + * HDMI sound should be located as reg = <2> + * Then, it is sound port 0 + */ + if (of_ep.port == 2) + return 0; + + return -EINVAL; +} + static struct hdmi_codec_ops dw_hdmi_i2s_ops = { .hw_params = dw_hdmi_i2s_hw_params, .audio_shutdown = dw_hdmi_i2s_audio_shutdown, + .get_dai_id = dw_hdmi_i2s_get_dai_id, }; static int snd_dw_hdmi_probe(struct platform_device *pdev) diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index 36e93540bb49..3ca947092775 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -223,9 +223,9 @@ static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) return idx * G723_FRAMES_PER_PAGE; } -static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, - snd_pcm_uframes_t pos, void __user *dst, - snd_pcm_uframes_t count) +static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss, + unsigned long pos, void *dst, + unsigned long count, bool in_kernel) { struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); struct solo_dev *solo_dev = solo_pcm->solo_dev; @@ -242,16 +242,31 @@ static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, if (err) return err; - err = copy_to_user(dst + (i * G723_PERIOD_BYTES), - solo_pcm->g723_buf, G723_PERIOD_BYTES); - - if (err) + if (in_kernel) + memcpy(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES); + else if (copy_to_user((void __user *)dst, + solo_pcm->g723_buf, G723_PERIOD_BYTES)) return -EFAULT; + dst += G723_PERIOD_BYTES; } return 0; } +static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel, + unsigned long pos, void __user *dst, + unsigned long count) +{ + return __snd_solo_pcm_copy(ss, pos, (void *)dst, count, false); +} + +static int snd_solo_pcm_copy_kernel(struct snd_pcm_substream *ss, int channel, + unsigned long pos, void *dst, + unsigned long count) +{ + return __snd_solo_pcm_copy(ss, pos, dst, count, true); +} + static const struct snd_pcm_ops snd_solo_pcm_ops = { .open = snd_solo_pcm_open, .close = snd_solo_pcm_close, @@ -261,7 +276,8 @@ static const struct snd_pcm_ops snd_solo_pcm_ops = { .prepare = snd_solo_pcm_prepare, .trigger = snd_solo_pcm_trigger, .pointer = snd_solo_pcm_pointer, - .copy = snd_solo_pcm_copy, + .copy_user = snd_solo_pcm_copy_user, + .copy_kernel = snd_solo_pcm_copy_kernel, }; static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, diff --git a/drivers/of/base.c b/drivers/of/base.c index 28d5f53bc631..cb1c49ae3b88 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1601,6 +1601,7 @@ int of_phandle_iterator_init(struct of_phandle_iterator *it, return 0; } +EXPORT_SYMBOL_GPL(of_phandle_iterator_init); int of_phandle_iterator_next(struct of_phandle_iterator *it) { @@ -1670,6 +1671,7 @@ err: return -EINVAL; } +EXPORT_SYMBOL_GPL(of_phandle_iterator_next); int of_phandle_iterator_args(struct of_phandle_iterator *it, uint32_t *args, @@ -2485,6 +2487,41 @@ struct device_node *of_graph_get_endpoint_by_regs( EXPORT_SYMBOL(of_graph_get_endpoint_by_regs); /** + * of_graph_get_remote_endpoint() - get remote endpoint node + * @node: pointer to a local endpoint device_node + * + * Return: Remote endpoint node associated with remote endpoint node linked + * to @node. Use of_node_put() on it when done. + */ +struct device_node *of_graph_get_remote_endpoint(const struct device_node *node) +{ + /* Get remote endpoint node. */ + return of_parse_phandle(node, "remote-endpoint", 0); +} +EXPORT_SYMBOL(of_graph_get_remote_endpoint); + +/** + * of_graph_get_port_parent() - get port's parent node + * @node: pointer to a local endpoint device_node + * + * Return: device node associated with endpoint node linked + * to @node. Use of_node_put() on it when done. + */ +struct device_node *of_graph_get_port_parent(struct device_node *node) +{ + unsigned int depth; + + /* Walk 3 levels up only if there is 'ports' node. */ + for (depth = 3; depth && node; depth--) { + node = of_get_next_parent(node); + if (depth == 2 && of_node_cmp(node->name, "ports")) + break; + } + return node; +} +EXPORT_SYMBOL(of_graph_get_port_parent); + +/** * of_graph_get_remote_port_parent() - get remote port's parent node * @node: pointer to a local endpoint device_node * @@ -2495,18 +2532,11 @@ struct device_node *of_graph_get_remote_port_parent( const struct device_node *node) { struct device_node *np; - unsigned int depth; /* Get remote endpoint node. */ - np = of_parse_phandle(node, "remote-endpoint", 0); + np = of_graph_get_remote_endpoint(node); - /* Walk 3 levels up only if there is 'ports' node. */ - for (depth = 3; depth && np; depth--) { - np = of_get_next_parent(np); - if (depth == 2 && of_node_cmp(np->name, "ports")) - break; - } - return np; + return of_graph_get_port_parent(np); } EXPORT_SYMBOL(of_graph_get_remote_port_parent); @@ -2522,13 +2552,25 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node) struct device_node *np; /* Get remote endpoint node. */ - np = of_parse_phandle(node, "remote-endpoint", 0); + np = of_graph_get_remote_endpoint(node); if (!np) return NULL; return of_get_next_parent(np); } EXPORT_SYMBOL(of_graph_get_remote_port); +int of_graph_get_endpoint_count(const struct device_node *np) +{ + struct device_node *endpoint; + int num = 0; + + for_each_endpoint_of_node(np, endpoint) + num++; + + return num; +} +EXPORT_SYMBOL(of_graph_get_endpoint_count); + /** * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint * @node: pointer to parent device_node containing graph port/endpoint diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c index e8cf0b97bf02..3637ddf909a4 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c @@ -353,9 +353,8 @@ static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream) struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect; pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max; - snd_pcm_indirect_playback_transfer(substream, pcm_indirect, - snd_bcm2835_pcm_transfer); - return 0; + return snd_pcm_indirect_playback_transfer(substream, pcm_indirect, + snd_bcm2835_pcm_transfer); } /* trigger callback */ diff --git a/drivers/usb/gadget/function/u_uac1_legacy.c b/drivers/usb/gadget/function/u_uac1_legacy.c index 8aa76b4dc117..fa4684a1c54c 100644 --- a/drivers/usb/gadget/function/u_uac1_legacy.c +++ b/drivers/usb/gadget/function/u_uac1_legacy.c @@ -157,7 +157,6 @@ size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) struct gaudio_snd_dev *snd = &card->playback; struct snd_pcm_substream *substream = snd->substream; struct snd_pcm_runtime *runtime = substream->runtime; - mm_segment_t old_fs; ssize_t result; snd_pcm_sframes_t frames; @@ -174,15 +173,11 @@ try_again: } frames = bytes_to_frames(runtime, count); - old_fs = get_fs(); - set_fs(KERNEL_DS); - result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames); + result = snd_pcm_kernel_write(snd->substream, buf, frames); if (result != frames) { ERROR(card, "Playback error: %d\n", (int)result); - set_fs(old_fs); goto try_again; } - set_fs(old_fs); return 0; } diff --git a/include/linux/dma/dw.h b/include/linux/dma/dw.h index b63b25814d77..e166cac8e870 100644 --- a/include/linux/dma/dw.h +++ b/include/linux/dma/dw.h @@ -50,25 +50,4 @@ static inline int dw_dma_probe(struct dw_dma_chip *chip) { return -ENODEV; } static inline int dw_dma_remove(struct dw_dma_chip *chip) { return 0; } #endif /* CONFIG_DW_DMAC_CORE */ -/* DMA API extensions */ -struct dw_desc; - -struct dw_cyclic_desc { - struct dw_desc **desc; - unsigned long periods; - void (*period_callback)(void *param); - void *period_callback_param; -}; - -struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, - dma_addr_t buf_addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction direction); -void dw_dma_cyclic_free(struct dma_chan *chan); -int dw_dma_cyclic_start(struct dma_chan *chan); -void dw_dma_cyclic_stop(struct dma_chan *chan); - -dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan); - -dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan); - #endif /* _DMA_DW_H */ diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h index abdb02eaef06..3e058f05ab04 100644 --- a/include/linux/of_graph.h +++ b/include/linux/of_graph.h @@ -43,11 +43,15 @@ struct of_endpoint { #ifdef CONFIG_OF int of_graph_parse_endpoint(const struct device_node *node, struct of_endpoint *endpoint); +int of_graph_get_endpoint_count(const struct device_node *np); struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id); struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, struct device_node *previous); struct device_node *of_graph_get_endpoint_by_regs( const struct device_node *parent, int port_reg, int reg); +struct device_node *of_graph_get_remote_endpoint( + const struct device_node *node); +struct device_node *of_graph_get_port_parent(struct device_node *node); struct device_node *of_graph_get_remote_port_parent( const struct device_node *node); struct device_node *of_graph_get_remote_port(const struct device_node *node); @@ -61,6 +65,11 @@ static inline int of_graph_parse_endpoint(const struct device_node *node, return -ENOSYS; } +static inline int of_graph_get_endpoint_count(const struct device_node *np) +{ + return 0; +} + static inline struct device_node *of_graph_get_port_by_id( struct device_node *node, u32 id) { @@ -80,6 +89,18 @@ static inline struct device_node *of_graph_get_endpoint_by_regs( return NULL; } +static inline struct device_node *of_graph_get_remote_endpoint( + const struct device_node *node) +{ + return NULL; +} + +static inline struct device_node *of_graph_get_port_parent( + struct device_node *node) +{ + return NULL; +} + static inline struct device_node *of_graph_get_remote_port_parent( const struct device_node *node) { diff --git a/include/sound/ak4113.h b/include/sound/ak4113.h index 58c145620c3c..b2d09fd09559 100644 --- a/include/sound/ak4113.h +++ b/include/sound/ak4113.h @@ -281,6 +281,14 @@ typedef void (ak4113_write_t)(void *private_data, unsigned char addr, unsigned char data); typedef unsigned char (ak4113_read_t)(void *private_data, unsigned char addr); +enum { + AK4113_PARITY_ERRORS, + AK4113_V_BIT_ERRORS, + AK4113_QCRC_ERRORS, + AK4113_CCRC_ERRORS, + AK4113_NUM_ERRORS +}; + struct ak4113 { struct snd_card *card; ak4113_write_t *write; @@ -292,10 +300,7 @@ struct ak4113 { unsigned char regmap[AK4113_WRITABLE_REGS]; struct snd_kcontrol *kctls[AK4113_CONTROLS]; struct snd_pcm_substream *substream; - unsigned long parity_errors; - unsigned long v_bit_errors; - unsigned long qcrc_errors; - unsigned long ccrc_errors; + unsigned long errors[AK4113_NUM_ERRORS]; unsigned char rcs0; unsigned char rcs1; unsigned char rcs2; diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h index b6feb7e225f2..39df064c82fc 100644 --- a/include/sound/ak4114.h +++ b/include/sound/ak4114.h @@ -163,6 +163,14 @@ typedef void (ak4114_write_t)(void *private_data, unsigned char addr, unsigned char data); typedef unsigned char (ak4114_read_t)(void *private_data, unsigned char addr); +enum { + AK4114_PARITY_ERRORS, + AK4114_V_BIT_ERRORS, + AK4114_QCRC_ERRORS, + AK4114_CCRC_ERRORS, + AK4114_NUM_ERRORS +}; + struct ak4114 { struct snd_card *card; ak4114_write_t * write; @@ -176,10 +184,7 @@ struct ak4114 { struct snd_kcontrol *kctls[AK4114_CONTROLS]; struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; - unsigned long parity_errors; - unsigned long v_bit_errors; - unsigned long qcrc_errors; - unsigned long ccrc_errors; + unsigned long errors[AK4114_NUM_ERRORS]; unsigned char rcs0; unsigned char rcs1; struct delayed_work work; diff --git a/include/sound/ak4117.h b/include/sound/ak4117.h index 1e8178171baf..5fab517cfe46 100644 --- a/include/sound/ak4117.h +++ b/include/sound/ak4117.h @@ -155,6 +155,14 @@ typedef void (ak4117_write_t)(void *private_data, unsigned char addr, unsigned char data); typedef unsigned char (ak4117_read_t)(void *private_data, unsigned char addr); +enum { + AK4117_PARITY_ERRORS, + AK4117_V_BIT_ERRORS, + AK4117_QCRC_ERRORS, + AK4117_CCRC_ERRORS, + AK4117_NUM_ERRORS +}; + struct ak4117 { struct snd_card *card; ak4117_write_t * write; @@ -165,10 +173,7 @@ struct ak4117 { unsigned char regmap[5]; struct snd_kcontrol *kctls[AK4117_CONTROLS]; struct snd_pcm_substream *substream; - unsigned long parity_errors; - unsigned long v_bit_errors; - unsigned long qcrc_errors; - unsigned long ccrc_errors; + unsigned long errors[AK4117_NUM_ERRORS]; unsigned char rcs0; unsigned char rcs1; unsigned char rcs2; diff --git a/include/sound/core.h b/include/sound/core.h index f7d8c10c4c45..55385588eefa 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -142,7 +142,7 @@ struct snd_card { wait_queue_head_t power_sleep; #endif -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS) struct snd_mixer_oss *mixer_oss; int mixer_oss_change_count; #endif @@ -243,7 +243,7 @@ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size extern struct snd_card *snd_cards[SNDRV_CARDS]; int snd_card_locked(int card); -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS) #define SND_MIXER_OSS_NOTIFY_REGISTER 0 #define SND_MIXER_OSS_NOTIFY_DISCONNECT 1 #define SND_MIXER_OSS_NOTIFY_FREE 2 @@ -394,7 +394,7 @@ static inline void snd_printdd(const char *format, ...) {} #define SNDRV_OSS_VERSION ((3<<16)|(8<<8)|(1<<4)|(0)) /* 3.8.1a */ /* for easier backward-porting */ -#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) +#if IS_ENABLED(CONFIG_GAMEPORT) #define gameport_set_dev_parent(gp,xdev) ((gp)->dev.parent = (xdev)) #define gameport_set_port_data(gp,r) ((gp)->port_data = (r)) #define gameport_get_port_data(gp) (gp)->port_data diff --git a/include/sound/cs35l35.h b/include/sound/cs35l35.h index 29da899e17e4..d69cd7847afd 100644 --- a/include/sound/cs35l35.h +++ b/include/sound/cs35l35.h @@ -99,6 +99,8 @@ struct cs35l35_platform_data { bool shared_bst; /* Specifies this amp is using an external boost supply */ bool ext_bst; + /* Inductor Value */ + int boost_ind; /* ClassH Algorithm */ struct classh_cfg classh_algo; /* Monitor Config */ diff --git a/include/sound/designware_i2s.h b/include/sound/designware_i2s.h index 5681855396c4..830f5caa915c 100644 --- a/include/sound/designware_i2s.h +++ b/include/sound/designware_i2s.h @@ -47,6 +47,7 @@ struct i2s_platform_data { #define DW_I2S_QUIRK_COMP_REG_OFFSET (1 << 0) #define DW_I2S_QUIRK_COMP_PARAM1 (1 << 1) + #define DW_I2S_QUIRK_16BIT_IDX_OVERRIDE (1 << 2) unsigned int quirks; unsigned int i2s_reg_comp1; unsigned int i2s_reg_comp2; diff --git a/include/sound/emux_synth.h b/include/sound/emux_synth.h index a0a40b74bf13..19a0cb561ffc 100644 --- a/include/sound/emux_synth.h +++ b/include/sound/emux_synth.h @@ -25,9 +25,7 @@ #include <sound/seq_device.h> #include <sound/soundfont.h> #include <sound/seq_midi_emul.h> -#ifdef CONFIG_SND_SEQUENCER_OSS #include <sound/seq_oss.h> -#endif #include <sound/emux_legacy.h> #include <sound/seq_virmidi.h> @@ -66,7 +64,7 @@ struct snd_emux_operators { const void __user *data, long count); void (*sysex)(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_channel_set *chset); -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) int (*oss_ioctl)(struct snd_emux *emu, int cmd, int p1, int p2); #endif }; @@ -129,7 +127,7 @@ struct snd_emux { struct snd_info_entry *proc; #endif -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) struct snd_seq_device *oss_synth; #endif }; @@ -150,7 +148,7 @@ struct snd_emux_port { #ifdef SNDRV_EMUX_USE_RAW_EFFECT struct snd_emux_effect_table *effect; #endif -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) struct snd_seq_oss_arg *oss_arg; #endif }; diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h index 915c4357945c..9483c55f871b 100644 --- a/include/sound/hdmi-codec.h +++ b/include/sound/hdmi-codec.h @@ -18,9 +18,11 @@ #ifndef __HDMI_CODEC_H__ #define __HDMI_CODEC_H__ +#include <linux/of_graph.h> #include <linux/hdmi.h> #include <drm/drm_edid.h> #include <sound/asoundef.h> +#include <sound/soc.h> #include <uapi/sound/asound.h> /* @@ -87,6 +89,13 @@ struct hdmi_codec_ops { */ int (*get_eld)(struct device *dev, void *data, uint8_t *buf, size_t len); + + /* + * Getting DAI ID + * Optional + */ + int (*get_dai_id)(struct snd_soc_component *comment, + struct device_node *endpoint); }; /* HDMI codec initalization data */ diff --git a/include/sound/mixer_oss.h b/include/sound/mixer_oss.h index 13cb0b430a1b..930da10fb65b 100644 --- a/include/sound/mixer_oss.h +++ b/include/sound/mixer_oss.h @@ -22,7 +22,7 @@ * */ -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_MIXER_OSS) #define SNDRV_OSS_MAX_MIXERS 32 diff --git a/include/sound/opl3.h b/include/sound/opl3.h index 6ba670707831..a4a593590cff 100644 --- a/include/sound/opl3.h +++ b/include/sound/opl3.h @@ -55,10 +55,8 @@ #include <sound/hwdep.h> #include <sound/timer.h> #include <sound/seq_midi_emul.h> -#ifdef CONFIG_SND_SEQUENCER_OSS #include <sound/seq_oss.h> #include <sound/seq_oss_legacy.h> -#endif #include <sound/seq_device.h> #include <sound/asound_fm.h> @@ -321,7 +319,7 @@ struct snd_opl3 { unsigned char fm_mode; /* OPL mode, see SNDRV_DM_FM_MODE_XXX */ unsigned char rhythm; /* percussion mode flag */ unsigned char max_voices; /* max number of voices */ -#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) #define SNDRV_OPL3_MODE_SYNTH 0 /* OSS - voices allocated by application */ #define SNDRV_OPL3_MODE_SEQ 1 /* ALSA - driver handles voice allocation */ int synth_mode; /* synth mode */ @@ -330,7 +328,7 @@ struct snd_opl3 { struct snd_seq_device *seq_dev; /* sequencer device */ struct snd_midi_channel_set * chset; -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) struct snd_seq_device *oss_seq_dev; /* OSS sequencer device */ struct snd_midi_channel_set * oss_chset; #endif @@ -374,7 +372,7 @@ int snd_opl3_release(struct snd_hwdep * hw, struct file *file); void snd_opl3_reset(struct snd_opl3 * opl3); -#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count, loff_t *offset); int snd_opl3_load_patch(struct snd_opl3 *opl3, diff --git a/include/sound/pcm-indirect.h b/include/sound/pcm-indirect.h index 1df7acaaa535..7ade285328cf 100644 --- a/include/sound/pcm-indirect.h +++ b/include/sound/pcm-indirect.h @@ -43,7 +43,7 @@ typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream, /* * helper function for playback ack callback */ -static inline void +static inline int snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream, struct snd_pcm_indirect *rec, snd_pcm_indirect_copy_t copy) @@ -56,6 +56,8 @@ snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream, if (diff) { if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) diff += runtime->boundary; + if (diff < 0) + return -EINVAL; rec->sw_ready += (int)frames_to_bytes(runtime, diff); rec->appl_ptr = appl_ptr; } @@ -82,6 +84,7 @@ snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream, rec->hw_ready += bytes; rec->sw_ready -= bytes; } + return 0; } /* @@ -109,7 +112,7 @@ snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream, /* * helper function for capture ack callback */ -static inline void +static inline int snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream, struct snd_pcm_indirect *rec, snd_pcm_indirect_copy_t copy) @@ -121,6 +124,8 @@ snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream, if (diff) { if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) diff += runtime->boundary; + if (diff < 0) + return -EINVAL; rec->sw_ready -= frames_to_bytes(runtime, diff); rec->appl_ptr = appl_ptr; } @@ -147,6 +152,7 @@ snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream, rec->hw_ready -= bytes; rec->sw_ready += bytes; } + return 0; } /* diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 361749e60799..24febf9e177c 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -34,7 +34,7 @@ #define snd_pcm_substream_chip(substream) ((substream)->private_data) #define snd_pcm_chip(pcm) ((pcm)->private_data) -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) #include <sound/pcm_oss.h> #endif @@ -78,11 +78,13 @@ struct snd_pcm_ops { struct timespec *system_ts, struct timespec *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report); - int (*copy)(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, - void __user *buf, snd_pcm_uframes_t count); - int (*silence)(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count); + int (*fill_silence)(struct snd_pcm_substream *substream, int channel, + unsigned long pos, unsigned long bytes); + int (*copy_user)(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void __user *buf, + unsigned long bytes); + int (*copy_kernel)(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void *buf, unsigned long bytes); struct page *(*page)(struct snd_pcm_substream *substream, unsigned long offset); int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma); @@ -100,9 +102,9 @@ struct snd_pcm_ops { #endif #define SNDRV_PCM_IOCTL1_RESET 0 -#define SNDRV_PCM_IOCTL1_INFO 1 +/* 1 is absent slot. */ #define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2 -#define SNDRV_PCM_IOCTL1_GSTATE 3 +/* 3 is absent slot. */ #define SNDRV_PCM_IOCTL1_FIFO_SIZE 4 #define SNDRV_PCM_TRIGGER_STOP 0 @@ -216,6 +218,7 @@ struct snd_pcm_ops { struct snd_pcm_file { struct snd_pcm_substream *substream; int no_compat_mmap; + unsigned int user_pversion; /* supported protocol version */ }; struct snd_pcm_hw_rule; @@ -418,7 +421,7 @@ struct snd_pcm_runtime { struct snd_pcm_audio_tstamp_report audio_tstamp_report; struct timespec driver_tstamp; -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) /* -- OSS things -- */ struct snd_pcm_oss_runtime oss; #endif @@ -464,7 +467,7 @@ struct snd_pcm_substream { unsigned int f_flags; void (*pcm_release)(struct snd_pcm_substream *); struct pid *pid; -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) /* -- OSS things -- */ struct snd_pcm_oss_substream oss; #endif @@ -494,7 +497,7 @@ struct snd_pcm_str { unsigned int substream_count; unsigned int substream_opened; struct snd_pcm_substream *substream; -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) /* -- OSS things -- */ struct snd_pcm_oss_stream oss; #endif @@ -526,18 +529,11 @@ struct snd_pcm { void (*private_free) (struct snd_pcm *pcm); bool internal; /* pcm is for internal use only */ bool nonatomic; /* whole PCM operations are in non-atomic context */ -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) struct snd_pcm_oss oss; #endif }; -struct snd_pcm_notify { - int (*n_register) (struct snd_pcm * pcm); - int (*n_disconnect) (struct snd_pcm * pcm); - int (*n_unregister) (struct snd_pcm * pcm); - struct list_head list; -}; - /* * Registering */ @@ -552,7 +548,15 @@ int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, struct snd_pcm **rpcm); int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count); +#if IS_ENABLED(CONFIG_SND_PCM_OSS) +struct snd_pcm_notify { + int (*n_register) (struct snd_pcm * pcm); + int (*n_disconnect) (struct snd_pcm * pcm); + int (*n_unregister) (struct snd_pcm * pcm); + struct list_head list; +}; int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree); +#endif /* * Native I/O @@ -968,12 +972,6 @@ static inline unsigned int params_buffer_bytes(const struct snd_pcm_hw_params *p } int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v); -void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); -void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); -void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, - unsigned int k, struct snd_interval *c); -void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, - const struct snd_interval *b, struct snd_interval *c); int snd_interval_list(struct snd_interval *i, unsigned int count, const unsigned int *list, unsigned int mask); int snd_interval_ranges(struct snd_interval *i, unsigned int count, @@ -984,15 +982,9 @@ int snd_interval_ratnum(struct snd_interval *i, void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params); void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var); -int snd_pcm_hw_params_choose(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); -int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); -int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); - -int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, - u_int32_t mask); int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int64_t mask); int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, @@ -1054,7 +1046,7 @@ int snd_pcm_format_unsigned(snd_pcm_format_t format); int snd_pcm_format_linear(snd_pcm_format_t format); int snd_pcm_format_little_endian(snd_pcm_format_t format); int snd_pcm_format_big_endian(snd_pcm_format_t format); -#if 0 /* just for DocBook */ +#if 0 /* just for kernel-doc */ /** * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian * @format: the format to check @@ -1080,22 +1072,66 @@ void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, void snd_pcm_set_sync(struct snd_pcm_substream *substream); int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg); -int snd_pcm_update_state(struct snd_pcm_substream *substream, - struct snd_pcm_runtime *runtime); -int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); -void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr); void snd_pcm_period_elapsed(struct snd_pcm_substream *substream); -snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, - const void __user *buf, - snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, - void __user *buf, snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, - void __user **bufs, snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, - void __user **bufs, snd_pcm_uframes_t frames); - -extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; +snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, + void *buf, bool interleaved, + snd_pcm_uframes_t frames, bool in_kernel); + +static inline snd_pcm_sframes_t +snd_pcm_lib_write(struct snd_pcm_substream *substream, + const void __user *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames, false); +} + +static inline snd_pcm_sframes_t +snd_pcm_lib_read(struct snd_pcm_substream *substream, + void __user *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames, false); +} + +static inline snd_pcm_sframes_t +snd_pcm_lib_writev(struct snd_pcm_substream *substream, + void __user **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames, false); +} + +static inline snd_pcm_sframes_t +snd_pcm_lib_readv(struct snd_pcm_substream *substream, + void __user **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames, false); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_write(struct snd_pcm_substream *substream, + const void *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames, true); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_read(struct snd_pcm_substream *substream, + void *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, buf, true, frames, true); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_writev(struct snd_pcm_substream *substream, + void **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, bufs, false, frames, true); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_readv(struct snd_pcm_substream *substream, + void **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, bufs, false, frames, true); +} int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime); unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); @@ -1130,20 +1166,6 @@ static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substrea } } -/* - * Timer interface - */ - -#ifdef CONFIG_SND_PCM_TIMER -void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); -void snd_pcm_timer_init(struct snd_pcm_substream *substream); -void snd_pcm_timer_done(struct snd_pcm_substream *substream); -#else -static inline void -snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {} -static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {} -static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {} -#endif /** * snd_pcm_gettime - Fill the timespec depending on the timestamp mode * @runtime: PCM runtime instance diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h index 492a3ca7f17b..6665cb29e1a2 100644 --- a/include/sound/rawmidi.h +++ b/include/sound/rawmidi.h @@ -30,7 +30,7 @@ #include <linux/workqueue.h> #include <linux/device.h> -#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) #include <sound/seq_device.h> #endif @@ -144,7 +144,7 @@ struct snd_rawmidi { struct snd_info_entry *proc_entry; -#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) struct snd_seq_device *seq_dev; #endif }; diff --git a/include/sound/rt5645.h b/include/sound/rt5645.h index a5cf6152e778..d0c33a9972b9 100644 --- a/include/sound/rt5645.h +++ b/include/sound/rt5645.h @@ -21,8 +21,10 @@ struct rt5645_platform_data { /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */ unsigned int jd_mode; - /* Invert JD when jack insert */ - bool jd_invert; + /* Use level triggered irq */ + bool level_trigger_irq; + /* Invert JD1_1 status polarity */ + bool inv_jd1_1; }; #endif diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index af58d2362975..42c6a6ac3ce6 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -22,6 +22,11 @@ struct asoc_simple_dai { struct clk *clk; }; +struct asoc_simple_card_data { + u32 convert_rate; + u32 convert_channels; +}; + int asoc_simple_card_parse_daifmt(struct device *dev, struct device_node *node, struct device_node *codec, @@ -35,13 +40,18 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card, char *prefix); #define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \ - asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai) + asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \ + dai_link->cpu_dai_name) #define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai) \ - asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai) + asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\ + dai_link->codec_dai_name) int asoc_simple_card_parse_clk(struct device *dev, struct device_node *node, struct device_node *dai_of_node, - struct asoc_simple_dai *simple_dai); + struct asoc_simple_dai *simple_dai, + const char *name); +int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai); +void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai); #define asoc_simple_card_parse_cpu(node, dai_link, \ list_name, cells_name, is_single_link) \ @@ -60,6 +70,22 @@ int asoc_simple_card_parse_dai(struct device_node *node, const char *cells_name, int *is_single_links); +#define asoc_simple_card_parse_graph_cpu(ep, dai_link) \ + asoc_simple_card_parse_graph_dai(ep, &dai_link->cpu_of_node, \ + &dai_link->cpu_dai_name) +#define asoc_simple_card_parse_graph_codec(ep, dai_link) \ + asoc_simple_card_parse_graph_dai(ep, &dai_link->codec_of_node, \ + &dai_link->codec_dai_name) +int asoc_simple_card_parse_graph_dai(struct device_node *ep, + struct device_node **endpoint_np, + const char **dai_name); + +#define asoc_simple_card_of_parse_tdm(np, dai) \ + snd_soc_of_parse_tdm_slot(np, &(dai)->tx_slot_mask, \ + &(dai)->rx_slot_mask, \ + &(dai)->slots, \ + &(dai)->slot_width); + int asoc_simple_card_init_dai(struct snd_soc_dai *dai, struct asoc_simple_dai *simple_dai); @@ -69,4 +95,15 @@ void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link, int asoc_simple_card_clean_reference(struct snd_soc_card *card); +void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, + struct snd_pcm_hw_params *params); +void asoc_simple_card_parse_convert(struct device *dev, char *prefix, + struct asoc_simple_card_data *data); + +int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, + char *prefix, + int optional); +int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card, + char *prefix); + #endif /* __SIMPLE_CARD_UTILS_H */ diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index a466f4bdc835..344b96c206a3 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -510,6 +510,13 @@ enum snd_soc_dapm_type { snd_soc_dapm_dai_out, snd_soc_dapm_dai_link, /* link between two DAI structures */ snd_soc_dapm_kcontrol, /* Auto-disabled kcontrol */ + snd_soc_dapm_buffer, /* DSP/CODEC internal buffer */ + snd_soc_dapm_scheduler, /* DSP/CODEC internal scheduler */ + snd_soc_dapm_effect, /* DSP/CODEC effect component */ + snd_soc_dapm_src, /* DSP/CODEC SRC component */ + snd_soc_dapm_asrc, /* DSP/CODEC ASRC component */ + snd_soc_dapm_encoder, /* FW/SW audio encoder component */ + snd_soc_dapm_decoder, /* FW/SW audio decoder component */ }; enum snd_soc_dapm_subclass { diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index f9cc7b9271ac..f552c3f56368 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -28,6 +28,8 @@ struct snd_soc_component; struct snd_soc_tplg_pcm_fe; struct snd_soc_dapm_context; struct snd_soc_card; +struct snd_kcontrol_new; +struct snd_soc_dai_link; /* object scan be loaded and unloaded in groups with identfying indexes */ #define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */ @@ -116,6 +118,9 @@ struct snd_soc_tplg_ops { int (*widget_load)(struct snd_soc_component *, struct snd_soc_dapm_widget *, struct snd_soc_tplg_dapm_widget *); + int (*widget_ready)(struct snd_soc_component *, + struct snd_soc_dapm_widget *, + struct snd_soc_tplg_dapm_widget *); int (*widget_unload)(struct snd_soc_component *, struct snd_soc_dobj *); diff --git a/include/sound/soc.h b/include/sound/soc.h index 5170fd81e1fd..9c94b97c17f8 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -803,6 +803,8 @@ struct snd_soc_component_driver { int (*of_xlate_dai_name)(struct snd_soc_component *component, struct of_phandle_args *args, const char **dai_name); + int (*of_xlate_dai_id)(struct snd_soc_component *comment, + struct device_node *endpoint); void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, int subseq); int (*stream_event)(struct snd_soc_component *, int event); @@ -1676,6 +1678,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, const char *prefix, struct device_node **bitclkmaster, struct device_node **framemaster); +int snd_soc_get_dai_id(struct device_node *ep); int snd_soc_get_dai_name(struct of_phandle_args *args, const char **dai_name); int snd_soc_of_get_dai_name(struct device_node *of_node, diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 6702533c8bd8..78014ec56357 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -73,7 +73,15 @@ #define SND_SOC_TPLG_DAPM_DAI_IN 13 #define SND_SOC_TPLG_DAPM_DAI_OUT 14 #define SND_SOC_TPLG_DAPM_DAI_LINK 15 -#define SND_SOC_TPLG_DAPM_LAST SND_SOC_TPLG_DAPM_DAI_LINK +#define SND_SOC_TPLG_DAPM_BUFFER 16 +#define SND_SOC_TPLG_DAPM_SCHEDULER 17 +#define SND_SOC_TPLG_DAPM_EFFECT 18 +#define SND_SOC_TPLG_DAPM_SIGGEN 19 +#define SND_SOC_TPLG_DAPM_SRC 20 +#define SND_SOC_TPLG_DAPM_ASRC 21 +#define SND_SOC_TPLG_DAPM_ENCODER 22 +#define SND_SOC_TPLG_DAPM_DECODER 23 +#define SND_SOC_TPLG_DAPM_LAST SND_SOC_TPLG_DAPM_DECODER /* Header magic number and string sizes */ #define SND_SOC_TPLG_MAGIC 0x41536F43 /* ASoC */ diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index fd41697cb4d3..1949923a40bf 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -152,7 +152,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 13) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 14) typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; @@ -268,6 +268,7 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ #define SNDRV_PCM_INFO_DOUBLE 0x00000004 /* Double buffering needed for PCM start/stop */ #define SNDRV_PCM_INFO_BATCH 0x00000010 /* double buffering */ +#define SNDRV_PCM_INFO_SYNC_APPLPTR 0x00000020 /* need the explicit sync of appl_ptr update */ #define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 /* channels are interleaved */ #define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 /* channels are not interleaved */ #define SNDRV_PCM_INFO_COMPLEX 0x00000400 /* complex frame organization (mmap only) */ @@ -563,6 +564,7 @@ enum { #define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info) #define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int) #define SNDRV_PCM_IOCTL_TTSTAMP _IOW('A', 0x03, int) +#define SNDRV_PCM_IOCTL_USER_PVERSION _IOW('A', 0x04, int) #define SNDRV_PCM_IOCTL_HW_REFINE _IOWR('A', 0x10, struct snd_pcm_hw_params) #define SNDRV_PCM_IOCTL_HW_PARAMS _IOWR('A', 0x11, struct snd_pcm_hw_params) #define SNDRV_PCM_IOCTL_HW_FREE _IO('A', 0x12) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 93392bedcc58..dedb2056160d 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -161,6 +161,8 @@ * * %SKL_TKL_U32_D0I3_CAPS: Specifies the D0i3 capability for module * + * %SKL_TKN_U32_DMA_BUF_SIZE: DMA buffer size in millisec + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest */ @@ -213,8 +215,10 @@ enum SKL_TKNS { SKL_TKN_U32_LIB_COUNT, SKL_TKN_STR_LIB_NAME, SKL_TKN_U32_PMODE, - SKL_TKL_U32_D0I3_CAPS, - SKL_TKN_MAX = SKL_TKL_U32_D0I3_CAPS, + SKL_TKL_U32_D0I3_CAPS, /* Typo added at v4.10 */ + SKL_TKN_U32_D0I3_CAPS = SKL_TKL_U32_D0I3_CAPS, + SKL_TKN_U32_DMA_BUF_SIZE, + SKL_TKN_MAX = SKL_TKN_U32_DMA_BUF_SIZE, }; #endif diff --git a/sound/Kconfig b/sound/Kconfig index 6a215a8c0490..d7d2aac9542e 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -56,7 +56,7 @@ config SOUND_OSS_CORE_PRECLAIM source "sound/oss/dmasound/Kconfig" -if !M68K && !UML +if !UML menuconfig SND tristate "Advanced Linux Sound Architecture" @@ -110,6 +110,8 @@ source "sound/soc/Kconfig" source "sound/x86/Kconfig" +source "sound/synth/Kconfig" + endif # SND menuconfig SOUND_PRIME @@ -125,7 +127,7 @@ source "sound/oss/Kconfig" endif # SOUND_PRIME -endif # !M68K +endif # !UML endif # SOUND diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c index 78ed1ffbf786..733b6365dad6 100644 --- a/sound/aoa/codecs/tas.c +++ b/sound/aoa/codecs/tas.c @@ -271,7 +271,7 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new volume_control = { +static const struct snd_kcontrol_new volume_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -314,7 +314,7 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new mute_control = { +static const struct snd_kcontrol_new mute_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -426,7 +426,7 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new drc_range_control = { +static const struct snd_kcontrol_new drc_range_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "DRC Range", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -466,7 +466,7 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new drc_switch_control = { +static const struct snd_kcontrol_new drc_switch_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "DRC Range Switch", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -524,7 +524,7 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new capture_source_control = { +static const struct snd_kcontrol_new capture_source_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* If we name this 'Input Source', it properly shows up in * alsamixer as a selection, * but it's shown under the @@ -586,7 +586,7 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new treble_control = { +static const struct snd_kcontrol_new treble_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Treble", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -637,7 +637,7 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new bass_control = { +static const struct snd_kcontrol_new bass_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Bass", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c index a0c4a5de809c..1eddf8fa188f 100644 --- a/sound/aoa/fabrics/layout.c +++ b/sound/aoa/fabrics/layout.c @@ -707,7 +707,7 @@ static int detect_choice_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new headphone_detect_choice = { +static const struct snd_kcontrol_new headphone_detect_choice = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Detect Autoswitch", .info = control_info, @@ -717,7 +717,7 @@ static struct snd_kcontrol_new headphone_detect_choice = { .private_value = 0, }; -static struct snd_kcontrol_new lineout_detect_choice = { +static const struct snd_kcontrol_new lineout_detect_choice = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line-Out Detect Autoswitch", .info = control_info, @@ -749,7 +749,7 @@ static int detected_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new headphone_detected = { +static const struct snd_kcontrol_new headphone_detected = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Detected", .info = control_info, @@ -758,7 +758,7 @@ static struct snd_kcontrol_new headphone_detected = { .private_value = 0, }; -static struct snd_kcontrol_new lineout_detected = { +static const struct snd_kcontrol_new lineout_detected = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line-Out Detected", .info = control_info, diff --git a/sound/atmel/Kconfig b/sound/atmel/Kconfig index 94de43a096f1..d789cbcb9106 100644 --- a/sound/atmel/Kconfig +++ b/sound/atmel/Kconfig @@ -1,18 +1,11 @@ -menu "Atmel devices (AVR32 and AT91)" - depends on AVR32 || ARCH_AT91 - -config SND_ATMEL_ABDAC - tristate "Atmel Audio Bitstream DAC (ABDAC) driver" - select SND_PCM - depends on DW_DMAC && AVR32 - help - ALSA sound driver for the Atmel Audio Bitstream DAC (ABDAC). +menu "Atmel devices (AT91)" + depends on ARCH_AT91 config SND_ATMEL_AC97C tristate "Atmel AC97 Controller (AC97C) driver" select SND_PCM select SND_AC97_CODEC - depends on (DW_DMAC && AVR32) || ARCH_AT91 + depends on ARCH_AT91 help ALSA sound driver for the Atmel AC97 controller. diff --git a/sound/atmel/Makefile b/sound/atmel/Makefile index 219dcfac6086..d4009d1430ed 100644 --- a/sound/atmel/Makefile +++ b/sound/atmel/Makefile @@ -1,5 +1,3 @@ -snd-atmel-abdac-objs := abdac.o snd-atmel-ac97c-objs := ac97c.o -obj-$(CONFIG_SND_ATMEL_ABDAC) += snd-atmel-abdac.o obj-$(CONFIG_SND_ATMEL_AC97C) += snd-atmel-ac97c.o diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c deleted file mode 100644 index 558618802000..000000000000 --- a/sound/atmel/abdac.c +++ /dev/null @@ -1,610 +0,0 @@ -/* - * Driver for the Atmel on-chip Audio Bitstream DAC (ABDAC) - * - * Copyright (C) 2006-2009 Atmel Corporation - * - * 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/clk.h> -#include <linux/bitmap.h> -#include <linux/dmaengine.h> -#include <linux/dma-mapping.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/types.h> -#include <linux/io.h> - -#include <sound/core.h> -#include <sound/initval.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/atmel-abdac.h> - -#include <linux/platform_data/dma-dw.h> -#include <linux/dma/dw.h> - -/* DAC register offsets */ -#define DAC_DATA 0x0000 -#define DAC_CTRL 0x0008 -#define DAC_INT_MASK 0x000c -#define DAC_INT_EN 0x0010 -#define DAC_INT_DIS 0x0014 -#define DAC_INT_CLR 0x0018 -#define DAC_INT_STATUS 0x001c - -/* Bitfields in CTRL */ -#define DAC_SWAP_OFFSET 30 -#define DAC_SWAP_SIZE 1 -#define DAC_EN_OFFSET 31 -#define DAC_EN_SIZE 1 - -/* Bitfields in INT_MASK/INT_EN/INT_DIS/INT_STATUS/INT_CLR */ -#define DAC_UNDERRUN_OFFSET 28 -#define DAC_UNDERRUN_SIZE 1 -#define DAC_TX_READY_OFFSET 29 -#define DAC_TX_READY_SIZE 1 - -/* Bit manipulation macros */ -#define DAC_BIT(name) \ - (1 << DAC_##name##_OFFSET) -#define DAC_BF(name, value) \ - (((value) & ((1 << DAC_##name##_SIZE) - 1)) \ - << DAC_##name##_OFFSET) -#define DAC_BFEXT(name, value) \ - (((value) >> DAC_##name##_OFFSET) \ - & ((1 << DAC_##name##_SIZE) - 1)) -#define DAC_BFINS(name, value, old) \ - (((old) & ~(((1 << DAC_##name##_SIZE) - 1) \ - << DAC_##name##_OFFSET)) \ - | DAC_BF(name, value)) - -/* Register access macros */ -#define dac_readl(port, reg) \ - __raw_readl((port)->regs + DAC_##reg) -#define dac_writel(port, reg, value) \ - __raw_writel((value), (port)->regs + DAC_##reg) - -/* - * ABDAC supports a maximum of 6 different rates from a generic clock. The - * generic clock has a power of two divider, which gives 6 steps from 192 kHz - * to 5112 Hz. - */ -#define MAX_NUM_RATES 6 -/* ALSA seems to use rates between 192000 Hz and 5112 Hz. */ -#define RATE_MAX 192000 -#define RATE_MIN 5112 - -enum { - DMA_READY = 0, -}; - -struct atmel_abdac_dma { - struct dma_chan *chan; - struct dw_cyclic_desc *cdesc; -}; - -struct atmel_abdac { - struct clk *pclk; - struct clk *sample_clk; - struct platform_device *pdev; - struct atmel_abdac_dma dma; - - struct snd_pcm_hw_constraint_list constraints_rates; - struct snd_pcm_substream *substream; - struct snd_card *card; - struct snd_pcm *pcm; - - void __iomem *regs; - unsigned long flags; - unsigned int rates[MAX_NUM_RATES]; - unsigned int rates_num; - int irq; -}; - -#define get_dac(card) ((struct atmel_abdac *)(card)->private_data) - -/* This function is called by the DMA driver. */ -static void atmel_abdac_dma_period_done(void *arg) -{ - struct atmel_abdac *dac = arg; - snd_pcm_period_elapsed(dac->substream); -} - -static int atmel_abdac_prepare_dma(struct atmel_abdac *dac, - struct snd_pcm_substream *substream, - enum dma_data_direction direction) -{ - struct dma_chan *chan = dac->dma.chan; - struct dw_cyclic_desc *cdesc; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long buffer_len, period_len; - - /* - * We don't do DMA on "complex" transfers, i.e. with - * non-halfword-aligned buffers or lengths. - */ - if (runtime->dma_addr & 1 || runtime->buffer_size & 1) { - dev_dbg(&dac->pdev->dev, "too complex transfer\n"); - return -EINVAL; - } - - buffer_len = frames_to_bytes(runtime, runtime->buffer_size); - period_len = frames_to_bytes(runtime, runtime->period_size); - - cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len, - period_len, DMA_MEM_TO_DEV); - if (IS_ERR(cdesc)) { - dev_dbg(&dac->pdev->dev, "could not prepare cyclic DMA\n"); - return PTR_ERR(cdesc); - } - - cdesc->period_callback = atmel_abdac_dma_period_done; - cdesc->period_callback_param = dac; - - dac->dma.cdesc = cdesc; - - set_bit(DMA_READY, &dac->flags); - - return 0; -} - -static struct snd_pcm_hardware atmel_abdac_hw = { - .info = (SNDRV_PCM_INFO_MMAP - | SNDRV_PCM_INFO_MMAP_VALID - | SNDRV_PCM_INFO_INTERLEAVED - | SNDRV_PCM_INFO_BLOCK_TRANSFER - | SNDRV_PCM_INFO_RESUME - | SNDRV_PCM_INFO_PAUSE), - .formats = (SNDRV_PCM_FMTBIT_S16_BE), - .rates = (SNDRV_PCM_RATE_KNOT), - .rate_min = RATE_MIN, - .rate_max = RATE_MAX, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 64 * 4096, - .period_bytes_min = 4096, - .period_bytes_max = 4096, - .periods_min = 6, - .periods_max = 64, -}; - -static int atmel_abdac_open(struct snd_pcm_substream *substream) -{ - struct atmel_abdac *dac = snd_pcm_substream_chip(substream); - - dac->substream = substream; - atmel_abdac_hw.rate_max = dac->rates[dac->rates_num - 1]; - atmel_abdac_hw.rate_min = dac->rates[0]; - substream->runtime->hw = atmel_abdac_hw; - - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &dac->constraints_rates); -} - -static int atmel_abdac_close(struct snd_pcm_substream *substream) -{ - struct atmel_abdac *dac = snd_pcm_substream_chip(substream); - dac->substream = NULL; - return 0; -} - -static int atmel_abdac_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct atmel_abdac *dac = snd_pcm_substream_chip(substream); - int retval; - - retval = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (retval < 0) - return retval; - /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */ - if (retval == 1) - if (test_and_clear_bit(DMA_READY, &dac->flags)) - dw_dma_cyclic_free(dac->dma.chan); - - return retval; -} - -static int atmel_abdac_hw_free(struct snd_pcm_substream *substream) -{ - struct atmel_abdac *dac = snd_pcm_substream_chip(substream); - if (test_and_clear_bit(DMA_READY, &dac->flags)) - dw_dma_cyclic_free(dac->dma.chan); - return snd_pcm_lib_free_pages(substream); -} - -static int atmel_abdac_prepare(struct snd_pcm_substream *substream) -{ - struct atmel_abdac *dac = snd_pcm_substream_chip(substream); - int retval; - - retval = clk_set_rate(dac->sample_clk, 256 * substream->runtime->rate); - if (retval) - return retval; - - if (!test_bit(DMA_READY, &dac->flags)) - retval = atmel_abdac_prepare_dma(dac, substream, DMA_TO_DEVICE); - - return retval; -} - -static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct atmel_abdac *dac = snd_pcm_substream_chip(substream); - int retval = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ - case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ - case SNDRV_PCM_TRIGGER_START: - clk_prepare_enable(dac->sample_clk); - retval = dw_dma_cyclic_start(dac->dma.chan); - if (retval) - goto out; - dac_writel(dac, CTRL, DAC_BIT(EN)); - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ - case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ - case SNDRV_PCM_TRIGGER_STOP: - dw_dma_cyclic_stop(dac->dma.chan); - dac_writel(dac, DATA, 0); - dac_writel(dac, CTRL, 0); - clk_disable_unprepare(dac->sample_clk); - break; - default: - retval = -EINVAL; - break; - } -out: - return retval; -} - -static snd_pcm_uframes_t -atmel_abdac_pointer(struct snd_pcm_substream *substream) -{ - struct atmel_abdac *dac = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t frames; - unsigned long bytes; - - bytes = dw_dma_get_src_addr(dac->dma.chan); - bytes -= runtime->dma_addr; - - frames = bytes_to_frames(runtime, bytes); - if (frames >= runtime->buffer_size) - frames -= runtime->buffer_size; - - return frames; -} - -static irqreturn_t abdac_interrupt(int irq, void *dev_id) -{ - struct atmel_abdac *dac = dev_id; - u32 status; - - status = dac_readl(dac, INT_STATUS); - if (status & DAC_BIT(UNDERRUN)) { - dev_err(&dac->pdev->dev, "underrun detected\n"); - dac_writel(dac, INT_CLR, DAC_BIT(UNDERRUN)); - } else { - dev_err(&dac->pdev->dev, "spurious interrupt (status=0x%x)\n", - status); - dac_writel(dac, INT_CLR, status); - } - - return IRQ_HANDLED; -} - -static struct snd_pcm_ops atmel_abdac_ops = { - .open = atmel_abdac_open, - .close = atmel_abdac_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = atmel_abdac_hw_params, - .hw_free = atmel_abdac_hw_free, - .prepare = atmel_abdac_prepare, - .trigger = atmel_abdac_trigger, - .pointer = atmel_abdac_pointer, -}; - -static int atmel_abdac_pcm_new(struct atmel_abdac *dac) -{ - struct snd_pcm_hardware hw = atmel_abdac_hw; - struct snd_pcm *pcm; - int retval; - - retval = snd_pcm_new(dac->card, dac->card->shortname, - dac->pdev->id, 1, 0, &pcm); - if (retval) - return retval; - - strcpy(pcm->name, dac->card->shortname); - pcm->private_data = dac; - pcm->info_flags = 0; - dac->pcm = pcm; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_abdac_ops); - - retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - &dac->pdev->dev, hw.periods_min * hw.period_bytes_min, - hw.buffer_bytes_max); - - return retval; -} - -static bool filter(struct dma_chan *chan, void *slave) -{ - struct dw_dma_slave *dws = slave; - - if (dws->dma_dev == chan->device->dev) { - chan->private = dws; - return true; - } else - return false; -} - -static int set_sample_rates(struct atmel_abdac *dac) -{ - long new_rate = RATE_MAX; - int retval = -EINVAL; - int index = 0; - - /* we start at 192 kHz and work our way down to 5112 Hz */ - while (new_rate >= RATE_MIN && index < (MAX_NUM_RATES + 1)) { - new_rate = clk_round_rate(dac->sample_clk, 256 * new_rate); - if (new_rate <= 0) - break; - /* make sure we are below the ABDAC clock */ - if (index < MAX_NUM_RATES && - new_rate <= clk_get_rate(dac->pclk)) { - dac->rates[index] = new_rate / 256; - index++; - } - /* divide by 256 and then by two to get next rate */ - new_rate /= 256 * 2; - } - - if (index) { - int i; - - /* reverse array, smallest go first */ - for (i = 0; i < (index / 2); i++) { - unsigned int tmp = dac->rates[index - 1 - i]; - dac->rates[index - 1 - i] = dac->rates[i]; - dac->rates[i] = tmp; - } - - dac->constraints_rates.count = index; - dac->constraints_rates.list = dac->rates; - dac->constraints_rates.mask = 0; - dac->rates_num = index; - - retval = 0; - } - - return retval; -} - -static int atmel_abdac_probe(struct platform_device *pdev) -{ - struct snd_card *card; - struct atmel_abdac *dac; - struct resource *regs; - struct atmel_abdac_pdata *pdata; - struct clk *pclk; - struct clk *sample_clk; - int retval; - int irq; - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_dbg(&pdev->dev, "no memory resource\n"); - return -ENXIO; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_dbg(&pdev->dev, "could not get IRQ number\n"); - return irq; - } - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_dbg(&pdev->dev, "no platform data\n"); - return -ENXIO; - } - - pclk = clk_get(&pdev->dev, "pclk"); - if (IS_ERR(pclk)) { - dev_dbg(&pdev->dev, "no peripheral clock\n"); - return PTR_ERR(pclk); - } - sample_clk = clk_get(&pdev->dev, "sample_clk"); - if (IS_ERR(sample_clk)) { - dev_dbg(&pdev->dev, "no sample clock\n"); - retval = PTR_ERR(sample_clk); - goto out_put_pclk; - } - clk_prepare_enable(pclk); - - retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, - SNDRV_DEFAULT_STR1, THIS_MODULE, - sizeof(struct atmel_abdac), &card); - if (retval) { - dev_dbg(&pdev->dev, "could not create sound card device\n"); - goto out_put_sample_clk; - } - - dac = get_dac(card); - - dac->irq = irq; - dac->card = card; - dac->pclk = pclk; - dac->sample_clk = sample_clk; - dac->pdev = pdev; - - retval = set_sample_rates(dac); - if (retval < 0) { - dev_dbg(&pdev->dev, "could not set supported rates\n"); - goto out_free_card; - } - - dac->regs = ioremap(regs->start, resource_size(regs)); - if (!dac->regs) { - dev_dbg(&pdev->dev, "could not remap register memory\n"); - retval = -ENOMEM; - goto out_free_card; - } - - /* make sure the DAC is silent and disabled */ - dac_writel(dac, DATA, 0); - dac_writel(dac, CTRL, 0); - - retval = request_irq(irq, abdac_interrupt, 0, "abdac", dac); - if (retval) { - dev_dbg(&pdev->dev, "could not request irq\n"); - goto out_unmap_regs; - } - - if (pdata->dws.dma_dev) { - dma_cap_mask_t mask; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - dac->dma.chan = dma_request_channel(mask, filter, &pdata->dws); - if (dac->dma.chan) { - struct dma_slave_config dma_conf = { - .dst_addr = regs->start + DAC_DATA, - .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, - .src_maxburst = 1, - .dst_maxburst = 1, - .direction = DMA_MEM_TO_DEV, - .device_fc = false, - }; - - dmaengine_slave_config(dac->dma.chan, &dma_conf); - } - } - if (!pdata->dws.dma_dev || !dac->dma.chan) { - dev_dbg(&pdev->dev, "DMA not available\n"); - retval = -ENODEV; - goto out_unmap_regs; - } - - strcpy(card->driver, "Atmel ABDAC"); - strcpy(card->shortname, "Atmel ABDAC"); - sprintf(card->longname, "Atmel Audio Bitstream DAC"); - - retval = atmel_abdac_pcm_new(dac); - if (retval) { - dev_dbg(&pdev->dev, "could not register ABDAC pcm device\n"); - goto out_release_dma; - } - - retval = snd_card_register(card); - if (retval) { - dev_dbg(&pdev->dev, "could not register sound card\n"); - goto out_release_dma; - } - - platform_set_drvdata(pdev, card); - - dev_info(&pdev->dev, "Atmel ABDAC at 0x%p using %s\n", - dac->regs, dev_name(&dac->dma.chan->dev->device)); - - return retval; - -out_release_dma: - dma_release_channel(dac->dma.chan); - dac->dma.chan = NULL; -out_unmap_regs: - iounmap(dac->regs); -out_free_card: - snd_card_free(card); -out_put_sample_clk: - clk_put(sample_clk); - clk_disable_unprepare(pclk); -out_put_pclk: - clk_put(pclk); - return retval; -} - -#ifdef CONFIG_PM_SLEEP -static int atmel_abdac_suspend(struct device *pdev) -{ - struct snd_card *card = dev_get_drvdata(pdev); - struct atmel_abdac *dac = card->private_data; - - dw_dma_cyclic_stop(dac->dma.chan); - clk_disable_unprepare(dac->sample_clk); - clk_disable_unprepare(dac->pclk); - - return 0; -} - -static int atmel_abdac_resume(struct device *pdev) -{ - struct snd_card *card = dev_get_drvdata(pdev); - struct atmel_abdac *dac = card->private_data; - - clk_prepare_enable(dac->pclk); - clk_prepare_enable(dac->sample_clk); - if (test_bit(DMA_READY, &dac->flags)) - dw_dma_cyclic_start(dac->dma.chan); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(atmel_abdac_pm, atmel_abdac_suspend, atmel_abdac_resume); -#define ATMEL_ABDAC_PM_OPS &atmel_abdac_pm -#else -#define ATMEL_ABDAC_PM_OPS NULL -#endif - -static int atmel_abdac_remove(struct platform_device *pdev) -{ - struct snd_card *card = platform_get_drvdata(pdev); - struct atmel_abdac *dac = get_dac(card); - - clk_put(dac->sample_clk); - clk_disable_unprepare(dac->pclk); - clk_put(dac->pclk); - - dma_release_channel(dac->dma.chan); - dac->dma.chan = NULL; - iounmap(dac->regs); - free_irq(dac->irq, dac); - snd_card_free(card); - - return 0; -} - -static struct platform_driver atmel_abdac_driver = { - .remove = atmel_abdac_remove, - .driver = { - .name = "atmel_abdac", - .pm = ATMEL_ABDAC_PM_OPS, - }, -}; - -static int __init atmel_abdac_init(void) -{ - return platform_driver_probe(&atmel_abdac_driver, - atmel_abdac_probe); -} -module_init(atmel_abdac_init); - -static void __exit atmel_abdac_exit(void) -{ - platform_driver_unregister(&atmel_abdac_driver); -} -module_exit(atmel_abdac_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Driver for Atmel Audio Bitstream DAC (ABDAC)"); -MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index 6dad042630d8..9d2c9d9af688 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -11,8 +11,6 @@ #include <linux/delay.h> #include <linux/bitmap.h> #include <linux/device.h> -#include <linux/dmaengine.h> -#include <linux/dma-mapping.h> #include <linux/atmel_pdc.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -34,36 +32,14 @@ #include <sound/atmel-ac97c.h> #include <sound/memalloc.h> -#include <linux/platform_data/dma-dw.h> -#include <linux/dma/dw.h> - -#ifdef CONFIG_AVR32 -#include <mach/cpu.h> -#else -#define cpu_is_at32ap7000() 0 -#endif - #include "ac97c.h" -enum { - DMA_TX_READY = 0, - DMA_RX_READY, - DMA_TX_CHAN_PRESENT, - DMA_RX_CHAN_PRESENT, -}; - /* Serialize access to opened variable */ static DEFINE_MUTEX(opened_mutex); -struct atmel_ac97c_dma { - struct dma_chan *rx_chan; - struct dma_chan *tx_chan; -}; - struct atmel_ac97c { struct clk *pclk; struct platform_device *pdev; - struct atmel_ac97c_dma dma; struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; @@ -74,7 +50,6 @@ struct atmel_ac97c { u64 cur_format; unsigned int cur_rate; - unsigned long flags; int playback_period, capture_period; /* Serialize access to opened variable */ spinlock_t lock; @@ -91,65 +66,6 @@ struct atmel_ac97c { #define ac97c_readl(chip, reg) \ __raw_readl((chip)->regs + AC97C_##reg) -/* This function is called by the DMA driver. */ -static void atmel_ac97c_dma_playback_period_done(void *arg) -{ - struct atmel_ac97c *chip = arg; - snd_pcm_period_elapsed(chip->playback_substream); -} - -static void atmel_ac97c_dma_capture_period_done(void *arg) -{ - struct atmel_ac97c *chip = arg; - snd_pcm_period_elapsed(chip->capture_substream); -} - -static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip, - struct snd_pcm_substream *substream, - enum dma_transfer_direction direction) -{ - struct dma_chan *chan; - struct dw_cyclic_desc *cdesc; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long buffer_len, period_len; - - /* - * We don't do DMA on "complex" transfers, i.e. with - * non-halfword-aligned buffers or lengths. - */ - if (runtime->dma_addr & 1 || runtime->buffer_size & 1) { - dev_dbg(&chip->pdev->dev, "too complex transfer\n"); - return -EINVAL; - } - - if (direction == DMA_MEM_TO_DEV) - chan = chip->dma.tx_chan; - else - chan = chip->dma.rx_chan; - - buffer_len = frames_to_bytes(runtime, runtime->buffer_size); - period_len = frames_to_bytes(runtime, runtime->period_size); - - cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len, - period_len, direction); - if (IS_ERR(cdesc)) { - dev_dbg(&chip->pdev->dev, "could not prepare cyclic DMA\n"); - return PTR_ERR(cdesc); - } - - if (direction == DMA_MEM_TO_DEV) { - cdesc->period_callback = atmel_ac97c_dma_playback_period_done; - set_bit(DMA_TX_READY, &chip->flags); - } else { - cdesc->period_callback = atmel_ac97c_dma_capture_period_done; - set_bit(DMA_RX_READY, &chip->flags); - } - - cdesc->period_callback_param = chip; - - return 0; -} - static struct snd_pcm_hardware atmel_ac97c_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID @@ -254,13 +170,7 @@ static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); if (retval < 0) return retval; - /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */ - if (cpu_is_at32ap7000()) { - /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */ - if (retval == 1) - if (test_and_clear_bit(DMA_TX_READY, &chip->flags)) - dw_dma_cyclic_free(chip->dma.tx_chan); - } + /* Set restrictions to params. */ mutex_lock(&opened_mutex); chip->cur_rate = params_rate(hw_params); @@ -280,10 +190,6 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); if (retval < 0) return retval; - /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */ - if (cpu_is_at32ap7000() && retval == 1) - if (test_and_clear_bit(DMA_RX_READY, &chip->flags)) - dw_dma_cyclic_free(chip->dma.rx_chan); /* Set restrictions to params. */ mutex_lock(&opened_mutex); @@ -294,26 +200,6 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream, return retval; } -static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream) -{ - struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); - if (cpu_is_at32ap7000()) { - if (test_and_clear_bit(DMA_TX_READY, &chip->flags)) - dw_dma_cyclic_free(chip->dma.tx_chan); - } - return snd_pcm_lib_free_pages(substream); -} - -static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream) -{ - struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); - if (cpu_is_at32ap7000()) { - if (test_and_clear_bit(DMA_RX_READY, &chip->flags)) - dw_dma_cyclic_free(chip->dma.rx_chan); - } - return snd_pcm_lib_free_pages(substream); -} - static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) { struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); @@ -349,8 +235,6 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) switch (runtime->format) { case SNDRV_PCM_FORMAT_S16_LE: - if (cpu_is_at32ap7000()) - word |= AC97C_CMR_CEM_LITTLE; break; case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ word &= ~(AC97C_CMR_CEM_LITTLE); @@ -389,18 +273,11 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", runtime->rate); - if (cpu_is_at32ap7000()) { - if (!test_bit(DMA_TX_READY, &chip->flags)) - retval = atmel_ac97c_prepare_dma(chip, substream, - DMA_MEM_TO_DEV); - } else { - /* Initialize and start the PDC */ - writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR); - writel(block_size / 2, chip->regs + ATMEL_PDC_TCR); - writel(runtime->dma_addr + block_size, - chip->regs + ATMEL_PDC_TNPR); - writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR); - } + /* Initialize and start the PDC */ + writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR); + writel(block_size / 2, chip->regs + ATMEL_PDC_TCR); + writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_TNPR); + writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR); return retval; } @@ -440,8 +317,6 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) switch (runtime->format) { case SNDRV_PCM_FORMAT_S16_LE: - if (cpu_is_at32ap7000()) - word |= AC97C_CMR_CEM_LITTLE; break; case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ word &= ~(AC97C_CMR_CEM_LITTLE); @@ -480,18 +355,11 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", runtime->rate); - if (cpu_is_at32ap7000()) { - if (!test_bit(DMA_RX_READY, &chip->flags)) - retval = atmel_ac97c_prepare_dma(chip, substream, - DMA_DEV_TO_MEM); - } else { - /* Initialize and start the PDC */ - writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR); - writel(block_size / 2, chip->regs + ATMEL_PDC_RCR); - writel(runtime->dma_addr + block_size, - chip->regs + ATMEL_PDC_RNPR); - writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR); - } + /* Initialize and start the PDC */ + writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR); + writel(block_size / 2, chip->regs + ATMEL_PDC_RCR); + writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_RNPR); + writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR); return retval; } @@ -501,7 +369,6 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); unsigned long camr, ptcr = 0; - int retval = 0; camr = ac97c_readl(chip, CAMR); @@ -509,35 +376,23 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ case SNDRV_PCM_TRIGGER_START: - if (cpu_is_at32ap7000()) { - retval = dw_dma_cyclic_start(chip->dma.tx_chan); - if (retval) - goto out; - } else { - ptcr = ATMEL_PDC_TXTEN; - } + ptcr = ATMEL_PDC_TXTEN; camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ case SNDRV_PCM_TRIGGER_STOP: - if (cpu_is_at32ap7000()) - dw_dma_cyclic_stop(chip->dma.tx_chan); - else - ptcr |= ATMEL_PDC_TXTDIS; + ptcr |= ATMEL_PDC_TXTDIS; if (chip->opened <= 1) camr &= ~AC97C_CMR_CENA; break; default: - retval = -EINVAL; - goto out; + return -EINVAL; } ac97c_writel(chip, CAMR, camr); - if (!cpu_is_at32ap7000()) - writel(ptcr, chip->regs + ATMEL_PDC_PTCR); -out: - return retval; + writel(ptcr, chip->regs + ATMEL_PDC_PTCR); + return 0; } static int @@ -545,7 +400,6 @@ atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); unsigned long camr, ptcr = 0; - int retval = 0; camr = ac97c_readl(chip, CAMR); ptcr = readl(chip->regs + ATMEL_PDC_PTSR); @@ -554,35 +408,23 @@ atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ case SNDRV_PCM_TRIGGER_START: - if (cpu_is_at32ap7000()) { - retval = dw_dma_cyclic_start(chip->dma.rx_chan); - if (retval) - goto out; - } else { - ptcr = ATMEL_PDC_RXTEN; - } + ptcr = ATMEL_PDC_RXTEN; camr |= AC97C_CMR_CENA | AC97C_CSR_ENDRX; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ case SNDRV_PCM_TRIGGER_STOP: - if (cpu_is_at32ap7000()) - dw_dma_cyclic_stop(chip->dma.rx_chan); - else - ptcr |= (ATMEL_PDC_RXTDIS); + ptcr |= ATMEL_PDC_RXTDIS; if (chip->opened <= 1) camr &= ~AC97C_CMR_CENA; break; default: - retval = -EINVAL; - break; + return -EINVAL; } ac97c_writel(chip, CAMR, camr); - if (!cpu_is_at32ap7000()) - writel(ptcr, chip->regs + ATMEL_PDC_PTCR); -out: - return retval; + writel(ptcr, chip->regs + ATMEL_PDC_PTCR); + return 0; } static snd_pcm_uframes_t @@ -593,10 +435,7 @@ atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream) snd_pcm_uframes_t frames; unsigned long bytes; - if (cpu_is_at32ap7000()) - bytes = dw_dma_get_src_addr(chip->dma.tx_chan); - else - bytes = readl(chip->regs + ATMEL_PDC_TPR); + bytes = readl(chip->regs + ATMEL_PDC_TPR); bytes -= runtime->dma_addr; frames = bytes_to_frames(runtime, bytes); @@ -613,10 +452,7 @@ atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream) snd_pcm_uframes_t frames; unsigned long bytes; - if (cpu_is_at32ap7000()) - bytes = dw_dma_get_dst_addr(chip->dma.rx_chan); - else - bytes = readl(chip->regs + ATMEL_PDC_RPR); + bytes = readl(chip->regs + ATMEL_PDC_RPR); bytes -= runtime->dma_addr; frames = bytes_to_frames(runtime, bytes); @@ -630,7 +466,7 @@ static struct snd_pcm_ops atmel_ac97_playback_ops = { .close = atmel_ac97c_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = atmel_ac97c_playback_hw_params, - .hw_free = atmel_ac97c_playback_hw_free, + .hw_free = snd_pcm_lib_free_pages, .prepare = atmel_ac97c_playback_prepare, .trigger = atmel_ac97c_playback_trigger, .pointer = atmel_ac97c_playback_pointer, @@ -641,7 +477,7 @@ static struct snd_pcm_ops atmel_ac97_capture_ops = { .close = atmel_ac97c_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = atmel_ac97c_capture_hw_params, - .hw_free = atmel_ac97c_capture_hw_free, + .hw_free = snd_pcm_lib_free_pages, .prepare = atmel_ac97c_capture_prepare, .trigger = atmel_ac97c_capture_trigger, .pointer = atmel_ac97c_capture_pointer, @@ -666,49 +502,40 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev) casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", casr & AC97C_CSR_TXRDY ? " TXRDY" : "", !casr ? " NONE" : ""); - if (!cpu_is_at32ap7000()) { - if ((casr & camr) & AC97C_CSR_ENDTX) { - runtime = chip->playback_substream->runtime; - block_size = frames_to_bytes(runtime, - runtime->period_size); - chip->playback_period++; - - if (chip->playback_period == runtime->periods) - chip->playback_period = 0; - next_period = chip->playback_period + 1; - if (next_period == runtime->periods) - next_period = 0; - - offset = block_size * next_period; - - writel(runtime->dma_addr + offset, - chip->regs + ATMEL_PDC_TNPR); - writel(block_size / 2, - chip->regs + ATMEL_PDC_TNCR); - - snd_pcm_period_elapsed( - chip->playback_substream); - } - if ((casr & camr) & AC97C_CSR_ENDRX) { - runtime = chip->capture_substream->runtime; - block_size = frames_to_bytes(runtime, - runtime->period_size); - chip->capture_period++; - - if (chip->capture_period == runtime->periods) - chip->capture_period = 0; - next_period = chip->capture_period + 1; - if (next_period == runtime->periods) - next_period = 0; - - offset = block_size * next_period; - - writel(runtime->dma_addr + offset, - chip->regs + ATMEL_PDC_RNPR); - writel(block_size / 2, - chip->regs + ATMEL_PDC_RNCR); - snd_pcm_period_elapsed(chip->capture_substream); - } + if ((casr & camr) & AC97C_CSR_ENDTX) { + runtime = chip->playback_substream->runtime; + block_size = frames_to_bytes(runtime, runtime->period_size); + chip->playback_period++; + + if (chip->playback_period == runtime->periods) + chip->playback_period = 0; + next_period = chip->playback_period + 1; + if (next_period == runtime->periods) + next_period = 0; + + offset = block_size * next_period; + + writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_TNPR); + writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR); + + snd_pcm_period_elapsed(chip->playback_substream); + } + if ((casr & camr) & AC97C_CSR_ENDRX) { + runtime = chip->capture_substream->runtime; + block_size = frames_to_bytes(runtime, runtime->period_size); + chip->capture_period++; + + if (chip->capture_period == runtime->periods) + chip->capture_period = 0; + next_period = chip->capture_period + 1; + if (next_period == runtime->periods) + next_period = 0; + + offset = block_size * next_period; + + writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_RNPR); + writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR); + snd_pcm_period_elapsed(chip->capture_substream); } retval = IRQ_HANDLED; } @@ -763,29 +590,20 @@ static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip) { struct snd_pcm *pcm; struct snd_pcm_hardware hw = atmel_ac97c_hw; - int capture, playback, retval, err; + int retval; - capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags); - playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags); + retval = snd_ac97_pcm_assign(chip->ac97_bus, + ARRAY_SIZE(at91_ac97_pcm_defs), + at91_ac97_pcm_defs); + if (retval) + return retval; - if (!cpu_is_at32ap7000()) { - err = snd_ac97_pcm_assign(chip->ac97_bus, - ARRAY_SIZE(at91_ac97_pcm_defs), - at91_ac97_pcm_defs); - if (err) - return err; - } - retval = snd_pcm_new(chip->card, chip->card->shortname, - 0, playback, capture, &pcm); + retval = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm); if (retval) return retval; - if (capture) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &atmel_ac97_capture_ops); - if (playback) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &atmel_ac97_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &atmel_ac97_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_ac97_playback_ops); retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pdev->dev, hw.periods_min * hw.period_bytes_min, @@ -875,17 +693,6 @@ timed_out: return 0xffff; } -static bool filter(struct dma_chan *chan, void *slave) -{ - struct dw_dma_slave *dws = slave; - - if (dws->dma_dev == chan->device->dev) { - chan->private = dws; - return true; - } else - return false; -} - static void atmel_ac97c_reset(struct atmel_ac97c *chip) { ac97c_writel(chip, MR, 0); @@ -967,16 +774,11 @@ static int atmel_ac97c_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_dbg(&pdev->dev, "could not get irq\n"); - return -ENXIO; - } - - if (cpu_is_at32ap7000()) { - pclk = clk_get(&pdev->dev, "pclk"); - } else { - pclk = clk_get(&pdev->dev, "ac97_clk"); + dev_dbg(&pdev->dev, "could not get irq: %d\n", irq); + return irq; } + pclk = clk_get(&pdev->dev, "ac97_clk"); if (IS_ERR(pclk)) { dev_dbg(&pdev->dev, "no peripheral clock\n"); return PTR_ERR(pclk); @@ -1047,88 +849,16 @@ static int atmel_ac97c_probe(struct platform_device *pdev) goto err_ac97_bus; } - if (cpu_is_at32ap7000()) { - if (pdata->rx_dws.dma_dev) { - dma_cap_mask_t mask; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - chip->dma.rx_chan = dma_request_channel(mask, filter, - &pdata->rx_dws); - if (chip->dma.rx_chan) { - struct dma_slave_config dma_conf = { - .src_addr = regs->start + AC97C_CARHR + - 2, - .src_addr_width = - DMA_SLAVE_BUSWIDTH_2_BYTES, - .src_maxburst = 1, - .dst_maxburst = 1, - .direction = DMA_DEV_TO_MEM, - .device_fc = false, - }; - - dmaengine_slave_config(chip->dma.rx_chan, - &dma_conf); - } - - dev_info(&chip->pdev->dev, "using %s for DMA RX\n", - dev_name(&chip->dma.rx_chan->dev->device)); - set_bit(DMA_RX_CHAN_PRESENT, &chip->flags); - } - - if (pdata->tx_dws.dma_dev) { - dma_cap_mask_t mask; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - chip->dma.tx_chan = dma_request_channel(mask, filter, - &pdata->tx_dws); - if (chip->dma.tx_chan) { - struct dma_slave_config dma_conf = { - .dst_addr = regs->start + AC97C_CATHR + - 2, - .dst_addr_width = - DMA_SLAVE_BUSWIDTH_2_BYTES, - .src_maxburst = 1, - .dst_maxburst = 1, - .direction = DMA_MEM_TO_DEV, - .device_fc = false, - }; - - dmaengine_slave_config(chip->dma.tx_chan, - &dma_conf); - } - - dev_info(&chip->pdev->dev, "using %s for DMA TX\n", - dev_name(&chip->dma.tx_chan->dev->device)); - set_bit(DMA_TX_CHAN_PRESENT, &chip->flags); - } - - if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) && - !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) { - dev_dbg(&pdev->dev, "DMA not available\n"); - retval = -ENODEV; - goto err_dma; - } - } else { - /* Just pretend that we have DMA channel(for at91 i is actually - * the PDC) */ - set_bit(DMA_RX_CHAN_PRESENT, &chip->flags); - set_bit(DMA_TX_CHAN_PRESENT, &chip->flags); - } - retval = atmel_ac97c_pcm_new(chip); if (retval) { dev_dbg(&pdev->dev, "could not register ac97 pcm device\n"); - goto err_dma; + goto err_ac97_bus; } retval = snd_card_register(card); if (retval) { dev_dbg(&pdev->dev, "could not register sound card\n"); - goto err_dma; + goto err_ac97_bus; } platform_set_drvdata(pdev, card); @@ -1138,17 +868,6 @@ static int atmel_ac97c_probe(struct platform_device *pdev) return 0; -err_dma: - if (cpu_is_at32ap7000()) { - if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags)) - dma_release_channel(chip->dma.rx_chan); - if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) - dma_release_channel(chip->dma.tx_chan); - clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags); - clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags); - chip->dma.rx_chan = NULL; - chip->dma.tx_chan = NULL; - } err_ac97_bus: if (gpio_is_valid(chip->reset_pin)) gpio_free(chip->reset_pin); @@ -1170,14 +889,7 @@ static int atmel_ac97c_suspend(struct device *pdev) struct snd_card *card = dev_get_drvdata(pdev); struct atmel_ac97c *chip = card->private_data; - if (cpu_is_at32ap7000()) { - if (test_bit(DMA_RX_READY, &chip->flags)) - dw_dma_cyclic_stop(chip->dma.rx_chan); - if (test_bit(DMA_TX_READY, &chip->flags)) - dw_dma_cyclic_stop(chip->dma.tx_chan); - } clk_disable_unprepare(chip->pclk); - return 0; } @@ -1187,12 +899,6 @@ static int atmel_ac97c_resume(struct device *pdev) struct atmel_ac97c *chip = card->private_data; clk_prepare_enable(chip->pclk); - if (cpu_is_at32ap7000()) { - if (test_bit(DMA_RX_READY, &chip->flags)) - dw_dma_cyclic_start(chip->dma.rx_chan); - if (test_bit(DMA_TX_READY, &chip->flags)) - dw_dma_cyclic_start(chip->dma.tx_chan); - } return 0; } @@ -1219,17 +925,6 @@ static int atmel_ac97c_remove(struct platform_device *pdev) iounmap(chip->regs); free_irq(chip->irq, chip); - if (cpu_is_at32ap7000()) { - if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags)) - dma_release_channel(chip->dma.rx_chan); - if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) - dma_release_channel(chip->dma.tx_chan); - clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags); - clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags); - chip->dma.rx_chan = NULL; - chip->dma.tx_chan = NULL; - } - snd_card_free(card); return 0; diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 9749f9e8b45c..6e937a8146a1 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -18,8 +18,12 @@ config SND_DMAENGINE_PCM config SND_HWDEP tristate +config SND_SEQ_DEVICE + tristate + config SND_RAWMIDI tristate + select SND_SEQ_DEVICE if SND_SEQUENCER != n config SND_COMPRESS_OFFLOAD tristate @@ -33,38 +37,15 @@ config SND_JACK_INPUT_DEV depends on SND_JACK default y if INPUT=y || INPUT=SND -config SND_SEQUENCER - tristate "Sequencer support" - select SND_TIMER - help - Say Y or M to enable MIDI sequencer and router support. This - feature allows routing and enqueueing of MIDI events. Events - can be processed at a given time. - - Many programs require this feature, so you should enable it - unless you know what you're doing. - -config SND_SEQ_DUMMY - tristate "Sequencer dummy client" - depends on SND_SEQUENCER - help - Say Y here to enable the dummy sequencer client. This client - is a simple MIDI-through client: all normal input events are - redirected to the output port immediately. - - You don't need this unless you want to connect many MIDI - devices or applications together. - - To compile this driver as a module, choose M here: the module - will be called snd-seq-dummy. - config SND_OSSEMUL + bool "Enable OSS Emulation" select SOUND_OSS_CORE - bool + help + This option enables the build of OSS emulation layer. config SND_MIXER_OSS tristate "OSS Mixer API" - select SND_OSSEMUL + depends on SND_OSSEMUL help To enable OSS mixer API emulation (/dev/mixer*), say Y here and read <file:Documentation/sound/alsa/OSS-Emulation.txt>. @@ -76,7 +57,7 @@ config SND_MIXER_OSS config SND_PCM_OSS tristate "OSS PCM (digital audio) API" - select SND_OSSEMUL + depends on SND_OSSEMUL select SND_PCM help To enable OSS digital audio (PCM) emulation (/dev/dsp*), say Y @@ -107,20 +88,6 @@ config SND_PCM_TIMER For some embedded devices, we may disable it to reduce memory footprint, about 20KB on x86_64 platform. -config SND_SEQUENCER_OSS - bool "OSS Sequencer API" - depends on SND_SEQUENCER - select SND_OSSEMUL - help - Say Y here to enable OSS sequencer emulation (both - /dev/sequencer and /dev/music interfaces). - - Many programs still use the OSS API, so say Y. - - If you choose M in "Sequencer support" (SND_SEQUENCER), - this will be compiled as a module. The module will be called - snd-seq-oss. - config SND_HRTIMER tristate "HR-timer backend support" depends on HIGH_RES_TIMERS @@ -133,14 +100,6 @@ config SND_HRTIMER To compile this driver as a module, choose M here: the module will be called snd-hrtimer. -config SND_SEQ_HRTIMER_DEFAULT - bool "Use HR-timer as default sequencer timer" - depends on SND_HRTIMER && SND_SEQUENCER - default y - help - Say Y here to use the HR-timer backend as the default sequencer - timer. - config SND_DYNAMIC_MINORS bool "Dynamic device file minor numbers" help diff --git a/sound/core/Makefile b/sound/core/Makefile index e85d9dd12c2d..e2066e2ef9f8 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -22,6 +22,7 @@ snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o # for trace-points CFLAGS_pcm_lib.o := -I$(src) +CFLAGS_pcm_native.o := -I$(src) snd-pcm-dmaengine-objs := pcm_dmaengine.o @@ -30,6 +31,7 @@ snd-timer-objs := timer.o snd-hrtimer-objs := hrtimer.o snd-rtctimer-objs := rtctimer.o snd-hwdep-objs := hwdep.o +snd-seq-device-objs := seq_device.o snd-compress-objs := compress_offload.o @@ -39,6 +41,7 @@ obj-$(CONFIG_SND_TIMER) += snd-timer.o obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o obj-$(CONFIG_SND_PCM) += snd-pcm.o obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o +obj-$(CONFIG_SND_SEQ_DEVICE) += snd-seq-device.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o obj-$(CONFIG_SND_OSSEMUL) += oss/ diff --git a/sound/core/control.c b/sound/core/control.c index 6362da17ac3f..3c6be1452e35 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -747,65 +747,45 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, static int snd_ctl_elem_list(struct snd_card *card, struct snd_ctl_elem_list __user *_list) { - struct list_head *plist; struct snd_ctl_elem_list list; struct snd_kcontrol *kctl; - struct snd_ctl_elem_id *dst, *id; + struct snd_ctl_elem_id id; unsigned int offset, space, jidx; + int err = 0; if (copy_from_user(&list, _list, sizeof(list))) return -EFAULT; offset = list.offset; space = list.space; - /* try limit maximum space */ - if (space > 16384) - return -ENOMEM; + + down_read(&card->controls_rwsem); + list.count = card->controls_count; + list.used = 0; if (space > 0) { - /* allocate temporary buffer for atomic operation */ - dst = vmalloc(space * sizeof(struct snd_ctl_elem_id)); - if (dst == NULL) - return -ENOMEM; - down_read(&card->controls_rwsem); - list.count = card->controls_count; - plist = card->controls.next; - while (plist != &card->controls) { - if (offset == 0) - break; - kctl = snd_kcontrol(plist); - if (offset < kctl->count) - break; - offset -= kctl->count; - plist = plist->next; - } - list.used = 0; - id = dst; - while (space > 0 && plist != &card->controls) { - kctl = snd_kcontrol(plist); - for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) { - snd_ctl_build_ioff(id, kctl, jidx); - id++; - space--; + list_for_each_entry(kctl, &card->controls, list) { + if (offset >= kctl->count) { + offset -= kctl->count; + continue; + } + for (jidx = offset; jidx < kctl->count; jidx++) { + snd_ctl_build_ioff(&id, kctl, jidx); + if (copy_to_user(list.pids + list.used, &id, + sizeof(id))) { + err = -EFAULT; + goto out; + } list.used++; + if (!--space) + goto out; } - plist = plist->next; offset = 0; } - up_read(&card->controls_rwsem); - if (list.used > 0 && - copy_to_user(list.pids, dst, - list.used * sizeof(struct snd_ctl_elem_id))) { - vfree(dst); - return -EFAULT; - } - vfree(dst); - } else { - down_read(&card->controls_rwsem); - list.count = card->controls_count; - up_read(&card->controls_rwsem); } - if (copy_to_user(_list, &list, sizeof(list))) - return -EFAULT; - return 0; + out: + up_read(&card->controls_rwsem); + if (!err && copy_to_user(_list, &list, sizeof(list))) + err = -EFAULT; + return err; } static bool validate_element_member_dimension(struct snd_ctl_elem_info *info) diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c index 84a3cd683068..0249d5e6ac23 100644 --- a/sound/core/ctljack.c +++ b/sound/core/ctljack.c @@ -23,7 +23,7 @@ static int jack_detect_kctl_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new jack_detect_kctl = { +static const struct snd_kcontrol_new jack_detect_kctl = { /* name is filled later */ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .access = SNDRV_CTL_ELEM_ACCESS_READ, diff --git a/sound/core/info.c b/sound/core/info.c index 8ab72e0f5932..bcf6a48cc70d 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -344,12 +344,12 @@ static ssize_t snd_info_text_entry_write(struct file *file, } } if (next > buf->len) { - char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next), - GFP_KERNEL | __GFP_ZERO); + char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL); if (!nbuf) { err = -ENOMEM; goto error; } + kvfree(buf->buffer); buf->buffer = nbuf; buf->len = PAGE_ALIGN(next); } @@ -427,7 +427,7 @@ static int snd_info_text_entry_release(struct inode *inode, struct file *file) single_release(inode, file); kfree(data->rbuffer); if (data->wbuffer) { - kfree(data->wbuffer->buffer); + kvfree(data->wbuffer->buffer); kfree(data->wbuffer); } @@ -652,7 +652,6 @@ int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) *line = '\0'; return 0; } - EXPORT_SYMBOL(snd_info_get_line); /** @@ -690,7 +689,6 @@ const char *snd_info_get_str(char *dest, const char *src, int len) src++; return src; } - EXPORT_SYMBOL(snd_info_get_str); /* @@ -748,7 +746,6 @@ struct snd_info_entry *snd_info_create_module_entry(struct module * module, entry->module = module; return entry; } - EXPORT_SYMBOL(snd_info_create_module_entry); /** @@ -772,7 +769,6 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, } return entry; } - EXPORT_SYMBOL(snd_info_create_card_entry); static void snd_info_disconnect(struct snd_info_entry *entry) @@ -815,7 +811,6 @@ void snd_info_free_entry(struct snd_info_entry * entry) entry->private_free(entry); kfree(entry); } - EXPORT_SYMBOL(snd_info_free_entry); /** @@ -858,7 +853,6 @@ int snd_info_register(struct snd_info_entry * entry) mutex_unlock(&info_mutex); return 0; } - EXPORT_SYMBOL(snd_info_register); /* diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index 1478c8dfd473..f479374b6bd8 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -61,7 +61,6 @@ int snd_oss_info_register(int dev, int num, char *string) mutex_unlock(&strings); return 0; } - EXPORT_SYMBOL(snd_oss_info_register); static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev) diff --git a/sound/core/init.c b/sound/core/init.c index d61d2b3cd521..b4365bcf28a7 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -452,7 +452,6 @@ int snd_card_disconnect(struct snd_card *card) #endif return 0; } - EXPORT_SYMBOL(snd_card_disconnect); static int snd_card_do_free(struct snd_card *card) @@ -718,7 +717,7 @@ int snd_card_add_dev_attr(struct snd_card *card, dev_err(card->dev, "Too many groups assigned\n"); return -ENOSPC; -}; +} EXPORT_SYMBOL_GPL(snd_card_add_dev_attr); /** @@ -775,7 +774,6 @@ int snd_card_register(struct snd_card *card) #endif return 0; } - EXPORT_SYMBOL(snd_card_register); #ifdef CONFIG_SND_PROC_FS @@ -895,7 +893,6 @@ int snd_component_add(struct snd_card *card, const char *component) strcat(card->components, component); return 0; } - EXPORT_SYMBOL(snd_component_add); /** @@ -930,7 +927,6 @@ int snd_card_file_add(struct snd_card *card, struct file *file) spin_unlock(&card->files_lock); return 0; } - EXPORT_SYMBOL(snd_card_file_add); /** @@ -972,7 +968,6 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) put_device(&card->card_dev); return 0; } - EXPORT_SYMBOL(snd_card_file_remove); #ifdef CONFIG_PM @@ -1012,6 +1007,5 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state) remove_wait_queue(&card->power_sleep, &wait); return result; } - EXPORT_SYMBOL(snd_power_wait); #endif /* CONFIG_PM */ diff --git a/sound/core/isadma.c b/sound/core/isadma.c index 31e8544d7f2d..7a8515abb5f9 100644 --- a/sound/core/isadma.c +++ b/sound/core/isadma.c @@ -55,7 +55,6 @@ void snd_dma_program(unsigned long dma, enable_dma(dma); release_dma_lock(flags); } - EXPORT_SYMBOL(snd_dma_program); /** @@ -73,7 +72,6 @@ void snd_dma_disable(unsigned long dma) disable_dma(dma); release_dma_lock(flags); } - EXPORT_SYMBOL(snd_dma_disable); /** @@ -113,5 +111,4 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) else return size - result; } - EXPORT_SYMBOL(snd_dma_pointer); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index f05cb6a8cbe0..7f89d3c79a4b 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -54,6 +54,7 @@ void *snd_malloc_pages(size_t size, gfp_t gfp_flags) pg = get_order(size); return (void *) __get_free_pages(gfp_flags, pg); } +EXPORT_SYMBOL(snd_malloc_pages); /** * snd_free_pages - release the pages @@ -71,6 +72,7 @@ void snd_free_pages(void *ptr, size_t size) pg = get_order(size); free_pages((unsigned long) ptr, pg); } +EXPORT_SYMBOL(snd_free_pages); /* * @@ -217,6 +219,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->bytes = size; return 0; } +EXPORT_SYMBOL(snd_dma_alloc_pages); /** * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback @@ -254,6 +257,7 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size, return -ENOMEM; return 0; } +EXPORT_SYMBOL(snd_dma_alloc_pages_fallback); /** @@ -287,13 +291,4 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) pr_err("snd-malloc: invalid device type %d\n", dmab->dev.type); } } - -/* - * exports - */ -EXPORT_SYMBOL(snd_dma_alloc_pages); -EXPORT_SYMBOL(snd_dma_alloc_pages_fallback); EXPORT_SYMBOL(snd_dma_free_pages); - -EXPORT_SYMBOL(snd_malloc_pages); -EXPORT_SYMBOL(snd_free_pages); diff --git a/sound/core/memory.c b/sound/core/memory.c index 4cd664efad77..19c9ea90d9bf 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -55,7 +55,6 @@ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size return 0; #endif } - EXPORT_SYMBOL(copy_to_user_fromio); /** @@ -88,5 +87,4 @@ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size return 0; #endif } - EXPORT_SYMBOL(copy_from_user_toio); diff --git a/sound/core/misc.c b/sound/core/misc.c index 21b228046e88..0f818d593c9e 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -48,7 +48,6 @@ void release_and_free_resource(struct resource *res) kfree(res); } } - EXPORT_SYMBOL(release_and_free_resource); #ifdef CONFIG_SND_VERBOSE_PRINTK diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c index 6faa1d719206..d870b2d93135 100644 --- a/sound/core/oss/io.c +++ b/sound/core/oss/io.c @@ -26,9 +26,9 @@ #include "pcm_plugin.h" #define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1) -#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1) +#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count) #define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1) -#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1) +#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count) /* * Basic io plugin diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 2ff9c12d664a..379bf486ccc7 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -395,6 +395,7 @@ int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned l fmixer.mixer = card->mixer_oss; return snd_mixer_oss_ioctl1(&fmixer, cmd, arg); } +EXPORT_SYMBOL(snd_mixer_oss_ioctl_card); #ifdef CONFIG_COMPAT /* all compatible */ @@ -1425,5 +1426,3 @@ static void __exit alsa_mixer_oss_exit(void) module_init(alsa_mixer_oss_init) module_exit(alsa_mixer_oss_exit) - -EXPORT_SYMBOL(snd_mixer_oss_ioctl_card); diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index cd8b7bef8d06..e49f448ee04f 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -67,18 +67,6 @@ static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file); -static inline mm_segment_t snd_enter_user(void) -{ - mm_segment_t fs = get_fs(); - set_fs(get_ds()); - return fs; -} - -static inline void snd_leave_user(mm_segment_t fs) -{ - set_fs(fs); -} - /* * helper functions to process hw_params */ @@ -799,7 +787,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, static int choose_rate(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, unsigned int best_rate) { - struct snd_interval *it; + const struct snd_interval *it; struct snd_pcm_hw_params *save; unsigned int rate, prev; @@ -807,7 +795,7 @@ static int choose_rate(struct snd_pcm_substream *substream, if (save == NULL) return -ENOMEM; *save = *params; - it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE); + it = hw_param_interval_c(save, SNDRV_PCM_HW_PARAM_RATE); /* try multiples of the best rate */ rate = best_rate; @@ -848,7 +836,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, int direct; snd_pcm_format_t format, sformat; int n; - struct snd_mask sformat_mask; + const struct snd_mask *sformat_mask; struct snd_mask mask; if (trylock) { @@ -891,18 +879,18 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, format = snd_pcm_oss_format_from(runtime->oss.format); - sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT); + sformat_mask = hw_param_mask_c(sparams, SNDRV_PCM_HW_PARAM_FORMAT); if (direct) sformat = format; else - sformat = snd_pcm_plug_slave_format(format, &sformat_mask); + sformat = snd_pcm_plug_slave_format(format, sformat_mask); if ((__force int)sformat < 0 || - !snd_mask_test(&sformat_mask, (__force int)sformat)) { + !snd_mask_test(sformat_mask, (__force int)sformat)) { for (sformat = (__force snd_pcm_format_t)0; (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) { - if (snd_mask_test(&sformat_mask, (__force int)sformat) && + if (snd_mask_test(sformat_mask, (__force int)sformat) && snd_pcm_oss_format_to(sformat) >= 0) break; } @@ -1191,14 +1179,8 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const if (ret < 0) break; } - if (in_kernel) { - mm_segment_t fs; - fs = snd_enter_user(); - ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); - snd_leave_user(fs); - } else { - ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); - } + ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true, + frames, in_kernel); if (ret != -EPIPE && ret != -ESTRPIPE) break; /* test, if we can't store new data, because the stream */ @@ -1234,14 +1216,8 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p ret = snd_pcm_oss_capture_position_fixup(substream, &delay); if (ret < 0) break; - if (in_kernel) { - mm_segment_t fs; - fs = snd_enter_user(); - ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); - snd_leave_user(fs); - } else { - ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); - } + ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true, + frames, in_kernel); if (ret == -EPIPE) { if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); @@ -1256,7 +1232,8 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p return ret; } -snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +#ifdef CONFIG_SND_PCM_OSS_PLUGINS +snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; int ret; @@ -1273,14 +1250,7 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void if (ret < 0) break; } - if (in_kernel) { - mm_segment_t fs; - fs = snd_enter_user(); - ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames); - snd_leave_user(fs); - } else { - ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames); - } + ret = snd_pcm_kernel_writev(substream, bufs, frames); if (ret != -EPIPE && ret != -ESTRPIPE) break; @@ -1292,7 +1262,7 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void return ret; } -snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; int ret; @@ -1313,19 +1283,13 @@ snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void * if (ret < 0) break; } - if (in_kernel) { - mm_segment_t fs; - fs = snd_enter_user(); - ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames); - snd_leave_user(fs); - } else { - ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames); - } + ret = snd_pcm_kernel_readv(substream, bufs, frames); if (ret != -EPIPE && ret != -ESTRPIPE) break; } return ret; } +#endif /* CONFIG_SND_PCM_OSS_PLUGINS */ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const char *buf, size_t bytes, int in_kernel) { @@ -1650,27 +1614,10 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) size = runtime->control->appl_ptr % runtime->period_size; if (size > 0) { size = runtime->period_size - size; - if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { - size = (runtime->frame_bits * size) / 8; - while (size > 0) { - mm_segment_t fs; - size_t size1 = size < runtime->oss.period_bytes ? size : runtime->oss.period_bytes; - size -= size1; - size1 *= 8; - size1 /= runtime->sample_bits; - snd_pcm_format_set_silence(runtime->format, - runtime->oss.buffer, - size1); - size1 /= runtime->channels; /* frames */ - fs = snd_enter_user(); - snd_pcm_lib_write(substream, (void __force __user *)runtime->oss.buffer, size1); - snd_leave_user(fs); - } - } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { - void __user *buffers[runtime->channels]; - memset(buffers, 0, runtime->channels * sizeof(void *)); - snd_pcm_lib_writev(substream, buffers, size); - } + if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) + snd_pcm_lib_write(substream, NULL, size); + else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + snd_pcm_lib_writev(substream, NULL, size); } mutex_unlock(&runtime->oss.params_lock); /* @@ -1780,7 +1727,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) int direct; struct snd_pcm_hw_params *params; unsigned int formats = 0; - struct snd_mask format_mask; + const struct snd_mask *format_mask; int fmt; if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) @@ -1802,12 +1749,12 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) return -ENOMEM; _snd_pcm_hw_params_any(params); err = snd_pcm_hw_refine(substream, params); - format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); kfree(params); if (err < 0) return err; for (fmt = 0; fmt < 32; ++fmt) { - if (snd_mask_test(&format_mask, fmt)) { + if (snd_mask_test(format_mask, fmt)) { int f = snd_pcm_oss_format_to(fmt); if (f >= 0) formats |= f; diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 727ac44d39f4..cadc93792868 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -266,7 +266,8 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc return frames; } -static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) +static int snd_pcm_plug_formats(const struct snd_mask *mask, + snd_pcm_format_t format) { struct snd_mask formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | @@ -309,7 +310,7 @@ static snd_pcm_format_t preferred_formats[] = { }; snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, - struct snd_mask *format_mask) + const struct snd_mask *format_mask) { int i; diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index a5035c2369a6..c9cd29d86efd 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -126,7 +126,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *slave_params); snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, - struct snd_mask *format_mask); + const struct snd_mask *format_mask); int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin); @@ -162,17 +162,15 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel); snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, - void **bufs, snd_pcm_uframes_t frames, - int in_kernel); + void **bufs, snd_pcm_uframes_t frames); snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, - void **bufs, snd_pcm_uframes_t frames, - int in_kernel); + void **bufs, snd_pcm_uframes_t frames); #else static inline snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size) { return drv_size; } static inline snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size) { return clt_size; } -static inline int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) { return format; } +static inline int snd_pcm_plug_slave_format(int format, const struct snd_mask *format_mask) { return format; } #endif diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 8e980aa678d0..89c7485519cb 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -31,13 +31,17 @@ #include <sound/control.h> #include <sound/info.h> +#include "pcm_local.h" + MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>"); MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); MODULE_LICENSE("GPL"); static LIST_HEAD(snd_pcm_devices); -static LIST_HEAD(snd_pcm_notify_list); static DEFINE_MUTEX(register_mutex); +#if IS_ENABLED(CONFIG_SND_PCM_OSS) +static LIST_HEAD(snd_pcm_notify_list); +#endif static int snd_pcm_free(struct snd_pcm *pcm); static int snd_pcm_dev_free(struct snd_device *device); @@ -884,16 +888,23 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr) put_device(&pstr->dev); } +#if IS_ENABLED(CONFIG_SND_PCM_OSS) +#define pcm_call_notify(pcm, call) \ + do { \ + struct snd_pcm_notify *_notify; \ + list_for_each_entry(_notify, &snd_pcm_notify_list, list) \ + _notify->call(pcm); \ + } while (0) +#else +#define pcm_call_notify(pcm, call) do {} while (0) +#endif + static int snd_pcm_free(struct snd_pcm *pcm) { - struct snd_pcm_notify *notify; - if (!pcm) return 0; - if (!pcm->internal) { - list_for_each_entry(notify, &snd_pcm_notify_list, list) - notify->n_unregister(pcm); - } + if (!pcm->internal) + pcm_call_notify(pcm, n_unregister); if (pcm->private_free) pcm->private_free(pcm); snd_pcm_lib_preallocate_free_for_all(pcm); @@ -1056,7 +1067,7 @@ static struct attribute *pcm_dev_attrs[] = { NULL }; -static struct attribute_group pcm_dev_attr_group = { +static const struct attribute_group pcm_dev_attr_group = { .attrs = pcm_dev_attrs, }; @@ -1069,7 +1080,6 @@ static int snd_pcm_dev_register(struct snd_device *device) { int cidx, err; struct snd_pcm_substream *substream; - struct snd_pcm_notify *notify; struct snd_pcm *pcm; if (snd_BUG_ON(!device || !device->device_data)) @@ -1107,8 +1117,7 @@ static int snd_pcm_dev_register(struct snd_device *device) snd_pcm_timer_init(substream); } - list_for_each_entry(notify, &snd_pcm_notify_list, list) - notify->n_register(pcm); + pcm_call_notify(pcm, n_register); unlock: mutex_unlock(®ister_mutex); @@ -1118,7 +1127,6 @@ static int snd_pcm_dev_register(struct snd_device *device) static int snd_pcm_dev_disconnect(struct snd_device *device) { struct snd_pcm *pcm = device->device_data; - struct snd_pcm_notify *notify; struct snd_pcm_substream *substream; int cidx; @@ -1138,8 +1146,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) } } if (!pcm->internal) { - list_for_each_entry(notify, &snd_pcm_notify_list, list) - notify->n_disconnect(pcm); + pcm_call_notify(pcm, n_disconnect); } for (cidx = 0; cidx < 2; cidx++) { if (!pcm->internal) @@ -1151,6 +1158,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) return 0; } +#if IS_ENABLED(CONFIG_SND_PCM_OSS) /** * snd_pcm_notify - Add/remove the notify list * @notify: PCM notify list @@ -1183,6 +1191,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) return 0; } EXPORT_SYMBOL(snd_pcm_notify); +#endif /* CONFIG_SND_PCM_OSS */ #ifdef CONFIG_SND_PROC_FS /* diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 1f64ab0c2a95..10f537f4d735 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -27,17 +27,13 @@ static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream, s32 __user *src) { snd_pcm_sframes_t delay; - mm_segment_t fs; - int err; - fs = snd_enter_user(); - err = snd_pcm_delay(substream, &delay); - snd_leave_user(fs); - if (err < 0) - return err; + delay = snd_pcm_delay(substream); + if (delay < 0) + return delay; if (put_user(delay, src)) return -EFAULT; - return err; + return 0; } static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream, @@ -680,6 +676,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l case SNDRV_PCM_IOCTL_INFO: case SNDRV_PCM_IOCTL_TSTAMP: case SNDRV_PCM_IOCTL_TTSTAMP: + case SNDRV_PCM_IOCTL_USER_PVERSION: case SNDRV_PCM_IOCTL_HWSYNC: case SNDRV_PCM_IOCTL_PREPARE: case SNDRV_PCM_IOCTL_RESET: diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c index e70379fb63d0..9881d087756f 100644 --- a/sound/core/pcm_drm_eld.c +++ b/sound/core/pcm_drm_eld.c @@ -29,13 +29,13 @@ static int eld_limit_rates(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *r = hw_param_interval(params, rule->var); - struct snd_interval *c; + const struct snd_interval *c; unsigned int rate_mask = 7, i; const u8 *sad, *eld = rule->private; sad = drm_eld_sad(eld); if (sad) { - c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) { unsigned max_channels = sad_max_channels(sad); @@ -57,7 +57,7 @@ static int eld_limit_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *c = hw_param_interval(params, rule->var); - struct snd_interval *r; + const struct snd_interval *r; struct snd_interval t = { .min = 1, .max = 2, .integer = 1, }; unsigned int i; const u8 *sad, *eld = rule->private; @@ -67,7 +67,7 @@ static int eld_limit_channels(struct snd_pcm_hw_params *params, unsigned int rate_mask = 0; /* Convert the rate interval to a mask */ - r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); for (i = 0; i < ARRAY_SIZE(eld_rates); i++) if (r->min <= eld_rates[i] && r->max >= eld_rates[i]) rate_mask |= BIT(i); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 877176067072..a93a4235a332 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -33,6 +33,8 @@ #include <sound/pcm_params.h> #include <sound/timer.h> +#include "pcm_local.h" + #ifdef CONFIG_SND_PCM_XRUN_DEBUG #define CREATE_TRACE_POINTS #include "pcm_trace.h" @@ -40,8 +42,12 @@ #define trace_hwptr(substream, pos, in_interrupt) #define trace_xrun(substream) #define trace_hw_ptr_error(substream, reason) +#define trace_applptr(substream, prev, curr) #endif +static int fill_silence_frames(struct snd_pcm_substream *substream, + snd_pcm_uframes_t off, snd_pcm_uframes_t frames); + /* * fill ring buffer with silence * runtime->silence_start: starting pointer to silence area @@ -55,18 +61,20 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t frames, ofs, transfer; + int err; if (runtime->silence_size < runtime->boundary) { snd_pcm_sframes_t noise_dist, n; - if (runtime->silence_start != runtime->control->appl_ptr) { - n = runtime->control->appl_ptr - runtime->silence_start; + snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr); + if (runtime->silence_start != appl_ptr) { + n = appl_ptr - runtime->silence_start; if (n < 0) n += runtime->boundary; if ((snd_pcm_uframes_t)n < runtime->silence_filled) runtime->silence_filled -= n; else runtime->silence_filled = 0; - runtime->silence_start = runtime->control->appl_ptr; + runtime->silence_start = appl_ptr; } if (runtime->silence_filled >= runtime->buffer_size) return; @@ -107,33 +115,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram ofs = runtime->silence_start % runtime->buffer_size; while (frames > 0) { transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; - if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || - runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { - if (substream->ops->silence) { - int err; - err = substream->ops->silence(substream, -1, ofs, transfer); - snd_BUG_ON(err < 0); - } else { - char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); - snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); - } - } else { - unsigned int c; - unsigned int channels = runtime->channels; - if (substream->ops->silence) { - for (c = 0; c < channels; ++c) { - int err; - err = substream->ops->silence(substream, c, ofs, transfer); - snd_BUG_ON(err < 0); - } - } else { - size_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c) { - char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); - snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); - } - } - } + err = fill_silence_frames(substream, ofs, transfer); + snd_BUG_ON(err < 0); runtime->silence_filled += transfer; frames -= transfer; ofs = 0; @@ -508,7 +491,6 @@ void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, for (substream = stream->substream; substream != NULL; substream = substream->next) substream->ops = ops; } - EXPORT_SYMBOL(snd_pcm_set_ops); /** @@ -526,7 +508,6 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream) runtime->sync.id32[2] = -1; runtime->sync.id32[3] = -1; } - EXPORT_SYMBOL(snd_pcm_set_sync); /* @@ -643,7 +624,6 @@ int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) } return changed; } - EXPORT_SYMBOL(snd_interval_refine); static int snd_interval_refine_first(struct snd_interval *i) @@ -906,7 +886,6 @@ int snd_interval_ratnum(struct snd_interval *i, } return err; } - EXPORT_SYMBOL(snd_interval_ratnum); /** @@ -1044,7 +1023,6 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, } return snd_interval_refine(i, &list_range); } - EXPORT_SYMBOL(snd_interval_list); /** @@ -1183,7 +1161,6 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, va_end(args); return 0; } - EXPORT_SYMBOL(snd_pcm_hw_rule_add); /** @@ -1247,7 +1224,6 @@ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_pa struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; return snd_interval_setinteger(constrs_interval(constrs, var)); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); /** @@ -1273,7 +1249,6 @@ int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_par t.integer = 0; return snd_interval_refine(constrs_interval(constrs, var), &t); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, @@ -1304,7 +1279,6 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_list, (void *)l, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_list); static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, @@ -1371,7 +1345,6 @@ int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_ratnums, (void *)r, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, @@ -1406,7 +1379,6 @@ int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_ratdens, (void *)r, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, @@ -1415,7 +1387,8 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, unsigned int l = (unsigned long) rule->private; int width = l & 0xffff; unsigned int msbits = l >> 16; - struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + const struct snd_interval *i = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); if (!snd_interval_single(i)) return 0; @@ -1452,7 +1425,6 @@ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, (void*) l, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, @@ -1480,7 +1452,6 @@ int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_step, (void *) step, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_step); static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) @@ -1511,7 +1482,6 @@ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_pow2, NULL, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, @@ -1570,7 +1540,6 @@ void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) _snd_pcm_hw_param_any(params, k); params->info = ~0U; } - EXPORT_SYMBOL(_snd_pcm_hw_params_any); /** @@ -1603,7 +1572,6 @@ int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, } return -EINVAL; } - EXPORT_SYMBOL(snd_pcm_hw_param_value); void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, @@ -1621,7 +1589,6 @@ void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_BUG(); } } - EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, @@ -1668,7 +1635,6 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, } return snd_pcm_hw_param_value(params, var, dir); } - EXPORT_SYMBOL(snd_pcm_hw_param_first); static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, @@ -1715,48 +1681,8 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, } return snd_pcm_hw_param_value(params, var, dir); } - EXPORT_SYMBOL(snd_pcm_hw_param_last); -/** - * snd_pcm_hw_param_choose - choose a configuration defined by @params - * @pcm: PCM instance - * @params: the hw_params instance - * - * Choose one configuration from configuration space defined by @params. - * The configuration chosen is that obtained fixing in this order: - * first access, first format, first subformat, min channels, - * min rate, min period time, max buffer size, min tick time - * - * Return: Zero if successful, or a negative error code on failure. - */ -int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, - struct snd_pcm_hw_params *params) -{ - static int vars[] = { - SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_SUBFORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_PERIOD_TIME, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - SNDRV_PCM_HW_PARAM_TICK_TIME, - -1 - }; - int err, *v; - - for (v = vars; *v != -1; v++) { - if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) - err = snd_pcm_hw_param_first(pcm, params, *v, NULL); - else - err = snd_pcm_hw_param_last(pcm, params, *v, NULL); - if (snd_BUG_ON(err < 0)) - return err; - } - return 0; -} - static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, void *arg) { @@ -1843,8 +1769,6 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { switch (cmd) { - case SNDRV_PCM_IOCTL1_INFO: - return 0; case SNDRV_PCM_IOCTL1_RESET: return snd_pcm_lib_ioctl_reset(substream, arg); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: @@ -1854,7 +1778,6 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, } return -ENXIO; } - EXPORT_SYMBOL(snd_pcm_lib_ioctl); /** @@ -1890,7 +1813,6 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) kill_fasync(&runtime->fasync, SIGIO, POLL_IN); snd_pcm_stream_unlock_irqrestore(substream, flags); } - EXPORT_SYMBOL(snd_pcm_period_elapsed); /* @@ -1985,129 +1907,147 @@ static int wait_for_avail(struct snd_pcm_substream *substream, return err; } -static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t frames) +typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes); + +typedef int (*pcm_copy_f)(struct snd_pcm_substream *, snd_pcm_uframes_t, void *, + snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f); + +/* calculate the target DMA-buffer position to be written/read */ +static void *get_dma_ptr(struct snd_pcm_runtime *runtime, + int channel, unsigned long hwoff) { - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); - if (substream->ops->copy) { - if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) - return err; - } else { - char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); - if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) - return -EFAULT; - } + return runtime->dma_area + hwoff + + channel * (runtime->dma_bytes / runtime->channels); +} + +/* default copy_user ops for write; used for both interleaved and non- modes */ +static int default_write_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff), + (void __user *)buf, bytes)) + return -EFAULT; return 0; } - -typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t size); -static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, - unsigned long data, - snd_pcm_uframes_t size, - int nonblock, - transfer_f transfer) +/* default copy_kernel ops for write */ +static int default_write_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + memcpy(get_dma_ptr(substream->runtime, channel, hwoff), buf, bytes); + return 0; +} + +/* fill silence instead of copy data; called as a transfer helper + * from __snd_pcm_lib_write() or directly from noninterleaved_copy() when + * a NULL buffer is passed + */ +static int fill_silence(struct snd_pcm_substream *substream, int channel, + unsigned long hwoff, void *buf, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t xfer = 0; - snd_pcm_uframes_t offset = 0; - snd_pcm_uframes_t avail; - int err = 0; - if (size == 0) + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return 0; + if (substream->ops->fill_silence) + return substream->ops->fill_silence(substream, channel, + hwoff, bytes); - snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_RUNNING: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - default: - err = -EBADFD; - goto _end_unlock; - } + snd_pcm_format_set_silence(runtime->format, + get_dma_ptr(runtime, channel, hwoff), + bytes_to_samples(runtime, bytes)); + return 0; +} - runtime->twake = runtime->control->avail_min ? : 1; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) - snd_pcm_update_hw_ptr(substream); - avail = snd_pcm_playback_avail(runtime); - while (size > 0) { - snd_pcm_uframes_t frames, appl_ptr, appl_ofs; - snd_pcm_uframes_t cont; - if (!avail) { - if (nonblock) { - err = -EAGAIN; - goto _end_unlock; - } - runtime->twake = min_t(snd_pcm_uframes_t, size, - runtime->control->avail_min ? : 1); - err = wait_for_avail(substream, &avail); - if (err < 0) - goto _end_unlock; - } - frames = size > avail ? avail : size; - cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; - if (frames > cont) - frames = cont; - if (snd_BUG_ON(!frames)) { - runtime->twake = 0; - snd_pcm_stream_unlock_irq(substream); - return -EINVAL; - } - appl_ptr = runtime->control->appl_ptr; - appl_ofs = appl_ptr % runtime->buffer_size; - snd_pcm_stream_unlock_irq(substream); - err = transfer(substream, appl_ofs, data, offset, frames); - snd_pcm_stream_lock_irq(substream); - if (err < 0) - goto _end_unlock; - switch (runtime->status->state) { - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - default: - break; - } - appl_ptr += frames; - if (appl_ptr >= runtime->boundary) - appl_ptr -= runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - if (substream->ops->ack) - substream->ops->ack(substream); +/* default copy_user ops for read; used for both interleaved and non- modes */ +static int default_read_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + if (copy_to_user((void __user *)buf, + get_dma_ptr(substream->runtime, channel, hwoff), + bytes)) + return -EFAULT; + return 0; +} - offset += frames; - size -= frames; - xfer += frames; - avail -= frames; - if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && - snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { - err = snd_pcm_start(substream); - if (err < 0) - goto _end_unlock; - } +/* default copy_kernel ops for read */ +static int default_read_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + memcpy(buf, get_dma_ptr(substream->runtime, channel, hwoff), bytes); + return 0; +} + +/* call transfer function with the converted pointers and sizes; + * for interleaved mode, it's one shot for all samples + */ +static int interleaved_copy(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hwoff, void *data, + snd_pcm_uframes_t off, + snd_pcm_uframes_t frames, + pcm_transfer_f transfer) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* convert to bytes */ + hwoff = frames_to_bytes(runtime, hwoff); + off = frames_to_bytes(runtime, off); + frames = frames_to_bytes(runtime, frames); + return transfer(substream, 0, hwoff, data + off, frames); +} + +/* call transfer function with the converted pointers and sizes for each + * non-interleaved channel; when buffer is NULL, silencing instead of copying + */ +static int noninterleaved_copy(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hwoff, void *data, + snd_pcm_uframes_t off, + snd_pcm_uframes_t frames, + pcm_transfer_f transfer) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int channels = runtime->channels; + void **bufs = data; + int c, err; + + /* convert to bytes; note that it's not frames_to_bytes() here. + * in non-interleaved mode, we copy for each channel, thus + * each copy is n_samples bytes x channels = whole frames. + */ + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + hwoff = samples_to_bytes(runtime, hwoff); + for (c = 0; c < channels; ++c, ++bufs) { + if (!data || !*bufs) + err = fill_silence(substream, c, hwoff, NULL, frames); + else + err = transfer(substream, c, hwoff, *bufs + off, + frames); + if (err < 0) + return err; } - _end_unlock: - runtime->twake = 0; - if (xfer > 0 && err >= 0) - snd_pcm_update_state(substream, runtime); - snd_pcm_stream_unlock_irq(substream); - return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; + return 0; +} + +/* fill silence on the given buffer position; + * called from snd_pcm_playback_silence() + */ +static int fill_silence_frames(struct snd_pcm_substream *substream, + snd_pcm_uframes_t off, snd_pcm_uframes_t frames) +{ + if (substream->runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + substream->runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) + return interleaved_copy(substream, off, NULL, 0, frames, + fill_silence); + else + return noninterleaved_copy(substream, off, NULL, 0, frames, + fill_silence); } /* sanity-check for read/write methods */ @@ -2117,164 +2057,137 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) + if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area)) return -EINVAL; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; return 0; } -snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) +static int pcm_accessible_state(struct snd_pcm_runtime *runtime) { - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - nonblock = !!(substream->f_flags & O_NONBLOCK); - - if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && - runtime->channels > 1) - return -EINVAL; - return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, - snd_pcm_lib_write_transfer); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PAUSED: + return 0; + case SNDRV_PCM_STATE_XRUN: + return -EPIPE; + case SNDRV_PCM_STATE_SUSPENDED: + return -ESTRPIPE; + default: + return -EBADFD; + } } -EXPORT_SYMBOL(snd_pcm_lib_write); - -static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t frames) +/* update to the given appl_ptr and call ack callback if needed; + * when an error is returned, take back to the original value + */ +int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t appl_ptr) { struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; - int channels = runtime->channels; - int c; - if (substream->ops->copy) { - if (snd_BUG_ON(!substream->ops->silence)) - return -EINVAL; - for (c = 0; c < channels; ++c, ++bufs) { - if (*bufs == NULL) { - if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) - return err; - } else { - char __user *buf = *bufs + samples_to_bytes(runtime, off); - if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) - return err; - } - } - } else { - /* default transfer behaviour */ - size_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c, ++bufs) { - char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); - if (*bufs == NULL) { - snd_pcm_format_set_silence(runtime->format, hwbuf, frames); - } else { - char __user *buf = *bufs + samples_to_bytes(runtime, off); - if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) - return -EFAULT; - } + snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr; + int ret; + + if (old_appl_ptr == appl_ptr) + return 0; + + runtime->control->appl_ptr = appl_ptr; + if (substream->ops->ack) { + ret = substream->ops->ack(substream); + if (ret < 0) { + runtime->control->appl_ptr = old_appl_ptr; + return ret; } } + + trace_applptr(substream, old_appl_ptr, appl_ptr); + return 0; } - -snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, - void __user **bufs, - snd_pcm_uframes_t frames) + +/* the common loop for read/write data */ +snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, + void *data, bool interleaved, + snd_pcm_uframes_t size, bool in_kernel) { - struct snd_pcm_runtime *runtime; - int nonblock; + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t xfer = 0; + snd_pcm_uframes_t offset = 0; + snd_pcm_uframes_t avail; + pcm_copy_f writer; + pcm_transfer_f transfer; + bool nonblock; + bool is_playback; int err; err = pcm_sanity_check(substream); if (err < 0) return err; - runtime = substream->runtime; - nonblock = !!(substream->f_flags & O_NONBLOCK); - - if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) - return -EINVAL; - return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, - nonblock, snd_pcm_lib_writev_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_writev); -static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); - if (substream->ops->copy) { - if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) - return err; + is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + if (interleaved) { + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && + runtime->channels > 1) + return -EINVAL; + writer = interleaved_copy; } else { - char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); - if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) - return -EFAULT; + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + writer = noninterleaved_copy; } - return 0; -} -static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, - unsigned long data, - snd_pcm_uframes_t size, - int nonblock, - transfer_f transfer) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t xfer = 0; - snd_pcm_uframes_t offset = 0; - snd_pcm_uframes_t avail; - int err = 0; + if (!data) { + if (is_playback) + transfer = fill_silence; + else + return -EINVAL; + } else if (in_kernel) { + if (substream->ops->copy_kernel) + transfer = substream->ops->copy_kernel; + else + transfer = is_playback ? + default_write_copy_kernel : default_read_copy_kernel; + } else { + if (substream->ops->copy_user) + transfer = (pcm_transfer_f)substream->ops->copy_user; + else + transfer = is_playback ? + default_write_copy : default_read_copy; + } if (size == 0) return 0; + nonblock = !!(substream->f_flags & O_NONBLOCK); + snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - if (size >= runtime->start_threshold) { - err = snd_pcm_start(substream); - if (err < 0) - goto _end_unlock; - } - break; - case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_RUNNING: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - default: - err = -EBADFD; + err = pcm_accessible_state(runtime); + if (err < 0) goto _end_unlock; + + if (!is_playback && + runtime->status->state == SNDRV_PCM_STATE_PREPARED && + size >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; } runtime->twake = runtime->control->avail_min ? : 1; if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); - avail = snd_pcm_capture_avail(runtime); + if (is_playback) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t cont; if (!avail) { - if (runtime->status->state == - SNDRV_PCM_STATE_DRAINING) { + if (!is_playback && + runtime->status->state == SNDRV_PCM_STATE_DRAINING) { snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); goto _end_unlock; } @@ -2291,7 +2204,9 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, continue; /* draining */ } frames = size > avail ? avail : size; - cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; + appl_ptr = READ_ONCE(runtime->control->appl_ptr); + appl_ofs = appl_ptr % runtime->buffer_size; + cont = runtime->buffer_size - appl_ofs; if (frames > cont) frames = cont; if (snd_BUG_ON(!frames)) { @@ -2299,34 +2214,33 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, snd_pcm_stream_unlock_irq(substream); return -EINVAL; } - appl_ptr = runtime->control->appl_ptr; - appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); - err = transfer(substream, appl_ofs, data, offset, frames); + err = writer(substream, appl_ofs, data, offset, frames, + transfer); snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end_unlock; - switch (runtime->status->state) { - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; + err = pcm_accessible_state(runtime); + if (err < 0) goto _end_unlock; - default: - break; - } appl_ptr += frames; if (appl_ptr >= runtime->boundary) appl_ptr -= runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - if (substream->ops->ack) - substream->ops->ack(substream); + err = pcm_lib_apply_appl_ptr(substream, appl_ptr); + if (err < 0) + goto _end_unlock; offset += frames; size -= frames; xfer += frames; avail -= frames; + if (is_playback && + runtime->status->state == SNDRV_PCM_STATE_PREPARED && + snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; + } } _end_unlock: runtime->twake = 0; @@ -2335,83 +2249,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, snd_pcm_stream_unlock_irq(substream); return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } - -snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size) -{ - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - nonblock = !!(substream->f_flags & O_NONBLOCK); - if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) - return -EINVAL; - return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_read); - -static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; - int channels = runtime->channels; - int c; - if (substream->ops->copy) { - for (c = 0; c < channels; ++c, ++bufs) { - char __user *buf; - if (*bufs == NULL) - continue; - buf = *bufs + samples_to_bytes(runtime, off); - if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) - return err; - } - } else { - snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c, ++bufs) { - char *hwbuf; - char __user *buf; - if (*bufs == NULL) - continue; - - hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); - buf = *bufs + samples_to_bytes(runtime, off); - if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) - return -EFAULT; - } - } - return 0; -} - -snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, - void __user **bufs, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) - return -EBADFD; - - nonblock = !!(substream->f_flags & O_NONBLOCK); - if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) - return -EINVAL; - return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_readv); +EXPORT_SYMBOL(__snd_pcm_lib_xfer); /* * standard channel mapping helpers diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h new file mode 100644 index 000000000000..16f254732b2a --- /dev/null +++ b/sound/core/pcm_local.h @@ -0,0 +1,50 @@ +/* + * pcm_local.h - a local header file for snd-pcm module. + * + * Copyright (c) Takashi Sakamoto <o-takashi@sakamocchi.jp> + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#ifndef __SOUND_CORE_PCM_LOCAL_H +#define __SOUND_CORE_PCM_LOCAL_H + +extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; + +void snd_interval_mul(const struct snd_interval *a, + const struct snd_interval *b, struct snd_interval *c); +void snd_interval_div(const struct snd_interval *a, + const struct snd_interval *b, struct snd_interval *c); +void snd_interval_muldivk(const struct snd_interval *a, + const struct snd_interval *b, + unsigned int k, struct snd_interval *c); +void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, + const struct snd_interval *b, struct snd_interval *c); + +int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); +int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); + +int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, + snd_pcm_hw_param_t var, u_int32_t mask); + +int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t appl_ptr); +int snd_pcm_update_state(struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime); +int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); + +void snd_pcm_playback_silence(struct snd_pcm_substream *substream, + snd_pcm_uframes_t new_hw_ptr); + +#ifdef CONFIG_SND_PCM_TIMER +void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); +void snd_pcm_timer_init(struct snd_pcm_substream *substream); +void snd_pcm_timer_done(struct snd_pcm_substream *substream); +#else +static inline void +snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {} +static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {} +static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {} +#endif + +#endif /* __SOUND_CORE_PCM_LOCAL_H */ diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index b45f6aa32264..ae33e456708c 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -120,7 +120,6 @@ int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free(substream); return 0; } - EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); #ifdef CONFIG_SND_VERBOSE_PROCFS @@ -263,7 +262,6 @@ int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, substream->dma_buffer.dev.dev = data; return snd_pcm_lib_preallocate_pages1(substream, size, max); } - EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); /** @@ -292,7 +290,6 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, return err; return 0; } - EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); #ifdef CONFIG_SND_DMA_SGBUF @@ -314,7 +311,6 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne return NULL; return sgbuf->page_table[idx]; } - EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); #endif /* CONFIG_SND_DMA_SGBUF */ @@ -370,7 +366,6 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) runtime->dma_bytes = size; return 1; /* area was changed */ } - EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); /** @@ -398,7 +393,6 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream) snd_pcm_set_runtime_buffer(substream, NULL); return 0; } - EXPORT_SYMBOL(snd_pcm_lib_free_pages); int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream, diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 53dc37357bca..9be81025372f 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -23,6 +23,9 @@ #include <linux/export.h> #include <sound/core.h> #include <sound/pcm.h> + +#include "pcm_local.h" + #define SND_PCM_FORMAT_UNKNOWN (-1) /* NOTE: "signed" prefix must be given below since the default char is @@ -245,7 +248,6 @@ int snd_pcm_format_signed(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_signed); /** @@ -264,7 +266,6 @@ int snd_pcm_format_unsigned(snd_pcm_format_t format) return val; return !val; } - EXPORT_SYMBOL(snd_pcm_format_unsigned); /** @@ -277,7 +278,6 @@ int snd_pcm_format_linear(snd_pcm_format_t format) { return snd_pcm_format_signed(format) >= 0; } - EXPORT_SYMBOL(snd_pcm_format_linear); /** @@ -296,7 +296,6 @@ int snd_pcm_format_little_endian(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_little_endian); /** @@ -315,7 +314,6 @@ int snd_pcm_format_big_endian(snd_pcm_format_t format) return val; return !val; } - EXPORT_SYMBOL(snd_pcm_format_big_endian); /** @@ -334,7 +332,6 @@ int snd_pcm_format_width(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_width); /** @@ -353,7 +350,6 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_physical_width); /** @@ -371,7 +367,6 @@ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) return -EINVAL; return samples * phys_width / 8; } - EXPORT_SYMBOL(snd_pcm_format_size); /** @@ -388,7 +383,6 @@ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) return NULL; return pcm_formats[(INT)format].silence; } - EXPORT_SYMBOL(snd_pcm_format_silence_64); /** @@ -459,7 +453,6 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int #endif return 0; } - EXPORT_SYMBOL(snd_pcm_format_set_silence); /** @@ -488,7 +481,6 @@ int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) } return 0; } - EXPORT_SYMBOL(snd_pcm_limit_hw_rates); /** diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index faa2e2be6f2e..b3d5bed75029 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -37,6 +37,18 @@ #include <sound/minors.h> #include <linux/uio.h> +#include "pcm_local.h" + +#ifdef CONFIG_SND_DEBUG +#define CREATE_TRACE_POINTS +#include "pcm_param_trace.h" +#else +#define trace_hw_mask_param_enabled() 0 +#define trace_hw_interval_param_enabled() 0 +#define trace_hw_mask_param(substream, type, index, prev, curr) +#define trace_hw_interval_param(substream, type, index, prev, curr) +#endif + /* * Compatibility */ @@ -181,20 +193,6 @@ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, } EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); -static inline mm_segment_t snd_enter_user(void) -{ - mm_segment_t fs = get_fs(); - set_fs(get_ds()); - return fs; -} - -static inline void snd_leave_user(mm_segment_t fs) -{ - set_fs(fs); -} - - - int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) { struct snd_pcm_runtime *runtime; @@ -214,11 +212,7 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) info->subdevices_avail = pstr->substream_count - pstr->substream_opened; strlcpy(info->subname, substream->name, sizeof(info->subname)); runtime = substream->runtime; - /* AB: FIXME!!! This is definitely nonsense */ - if (runtime) { - info->sync = runtime->sync; - substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); - } + return 0; } @@ -255,205 +249,268 @@ static bool hw_support_mmap(struct snd_pcm_substream *substream) return true; } -#undef RULES_DEBUG - -#ifdef RULES_DEBUG -#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v -static const char * const snd_pcm_hw_param_names[] = { - HW_PARAM(ACCESS), - HW_PARAM(FORMAT), - HW_PARAM(SUBFORMAT), - HW_PARAM(SAMPLE_BITS), - HW_PARAM(FRAME_BITS), - HW_PARAM(CHANNELS), - HW_PARAM(RATE), - HW_PARAM(PERIOD_TIME), - HW_PARAM(PERIOD_SIZE), - HW_PARAM(PERIOD_BYTES), - HW_PARAM(PERIODS), - HW_PARAM(BUFFER_TIME), - HW_PARAM(BUFFER_SIZE), - HW_PARAM(BUFFER_BYTES), - HW_PARAM(TICK_TIME), -}; -#endif - -int snd_pcm_hw_refine(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int constrain_mask_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { + struct snd_pcm_hw_constraints *constrs = + &substream->runtime->hw_constraints; + struct snd_mask *m; unsigned int k; - struct snd_pcm_hardware *hw; - struct snd_interval *i = NULL; - struct snd_mask *m = NULL; - struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; - unsigned int rstamps[constrs->rules_num]; - unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; - unsigned int stamp = 2; - int changed, again; - - params->info = 0; - params->fifo_size = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) - params->msbits = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { - params->rate_num = 0; - params->rate_den = 0; - } + struct snd_mask old_mask; + int changed; for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { m = hw_param_mask(params, k); if (snd_mask_empty(m)) return -EINVAL; + + /* This parameter is not requested to change by a caller. */ if (!(params->rmask & (1 << k))) continue; -#ifdef RULES_DEBUG - pr_debug("%s = ", snd_pcm_hw_param_names[k]); - pr_cont("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); -#endif + + if (trace_hw_mask_param_enabled()) + old_mask = *m; + changed = snd_mask_refine(m, constrs_mask(constrs, k)); -#ifdef RULES_DEBUG - pr_cont("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); -#endif - if (changed) - params->cmask |= 1 << k; if (changed < 0) return changed; + if (changed == 0) + continue; + + /* Set corresponding flag so that the caller gets it. */ + trace_hw_mask_param(substream, k, 0, &old_mask, m); + params->cmask |= 1 << k; } + return 0; +} + +static int constrain_interval_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_hw_constraints *constrs = + &substream->runtime->hw_constraints; + struct snd_interval *i; + unsigned int k; + struct snd_interval old_interval; + int changed; + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { i = hw_param_interval(params, k); if (snd_interval_empty(i)) return -EINVAL; + + /* This parameter is not requested to change by a caller. */ if (!(params->rmask & (1 << k))) continue; -#ifdef RULES_DEBUG - pr_debug("%s = ", snd_pcm_hw_param_names[k]); - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - pr_cont(" -> "); -#endif + + if (trace_hw_interval_param_enabled()) + old_interval = *i; + changed = snd_interval_refine(i, constrs_interval(constrs, k)); -#ifdef RULES_DEBUG - if (i->empty) - pr_cont("empty\n"); - else - pr_cont("%c%u %u%c\n", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); -#endif - if (changed) - params->cmask |= 1 << k; if (changed < 0) return changed; + if (changed == 0) + continue; + + /* Set corresponding flag so that the caller gets it. */ + trace_hw_interval_param(substream, k, 0, &old_interval, i); + params->cmask |= 1 << k; } + return 0; +} + +static int constrain_params_by_rules(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_hw_constraints *constrs = + &substream->runtime->hw_constraints; + unsigned int k; + unsigned int rstamps[constrs->rules_num]; + unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; + unsigned int stamp; + struct snd_pcm_hw_rule *r; + unsigned int d; + struct snd_mask old_mask; + struct snd_interval old_interval; + bool again; + int changed; + + /* + * Each application of rule has own sequence number. + * + * Each member of 'rstamps' array represents the sequence number of + * recent application of corresponding rule. + */ for (k = 0; k < constrs->rules_num; k++) rstamps[k] = 0; - for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) + + /* + * Each member of 'vstamps' array represents the sequence number of + * recent application of rule in which corresponding parameters were + * changed. + * + * In initial state, elements corresponding to parameters requested by + * a caller is 1. For unrequested parameters, corresponding members + * have 0 so that the parameters are never changed anymore. + */ + for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; - do { - again = 0; - for (k = 0; k < constrs->rules_num; k++) { - struct snd_pcm_hw_rule *r = &constrs->rules[k]; - unsigned int d; - int doit = 0; - if (r->cond && !(r->cond & params->flags)) - continue; - for (d = 0; r->deps[d] >= 0; d++) { - if (vstamps[r->deps[d]] > rstamps[k]) { - doit = 1; - break; - } - } - if (!doit) - continue; -#ifdef RULES_DEBUG - pr_debug("Rule %d [%p]: ", k, r->func); - if (r->var >= 0) { - pr_cont("%s = ", snd_pcm_hw_param_names[r->var]); - if (hw_is_mask(r->var)) { - m = hw_param_mask(params, r->var); - pr_cont("%x", *m->bits); - } else { - i = hw_param_interval(params, r->var); - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - } - } -#endif - changed = r->func(params, r); -#ifdef RULES_DEBUG - if (r->var >= 0) { - pr_cont(" -> "); - if (hw_is_mask(r->var)) - pr_cont("%x", *m->bits); - else { - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - } + + /* Due to the above design, actual sequence number starts at 2. */ + stamp = 2; +retry: + /* Apply all rules in order. */ + again = false; + for (k = 0; k < constrs->rules_num; k++) { + r = &constrs->rules[k]; + + /* + * Check condition bits of this rule. When the rule has + * some condition bits, parameter without the bits is + * never processed. SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP + * is an example of the condition bits. + */ + if (r->cond && !(r->cond & params->flags)) + continue; + + /* + * The 'deps' array includes maximum three dependencies + * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fourth + * member of this array is a sentinel and should be + * negative value. + * + * This rule should be processed in this time when dependent + * parameters were changed at former applications of the other + * rules. + */ + for (d = 0; r->deps[d] >= 0; d++) { + if (vstamps[r->deps[d]] > rstamps[k]) + break; + } + if (r->deps[d] < 0) + continue; + + if (trace_hw_mask_param_enabled()) { + if (hw_is_mask(r->var)) + old_mask = *hw_param_mask(params, r->var); + } + if (trace_hw_interval_param_enabled()) { + if (hw_is_interval(r->var)) + old_interval = *hw_param_interval(params, r->var); + } + + changed = r->func(params, r); + if (changed < 0) + return changed; + + /* + * When the parameter is changed, notify it to the caller + * by corresponding returned bit, then preparing for next + * iteration. + */ + if (changed && r->var >= 0) { + if (hw_is_mask(r->var)) { + trace_hw_mask_param(substream, r->var, + k + 1, &old_mask, + hw_param_mask(params, r->var)); } - pr_cont("\n"); -#endif - rstamps[k] = stamp; - if (changed && r->var >= 0) { - params->cmask |= (1 << r->var); - vstamps[r->var] = stamp; - again = 1; + if (hw_is_interval(r->var)) { + trace_hw_interval_param(substream, r->var, + k + 1, &old_interval, + hw_param_interval(params, r->var)); } - if (changed < 0) - return changed; - stamp++; + + params->cmask |= (1 << r->var); + vstamps[r->var] = stamp; + again = true; } - } while (again); + + rstamps[k] = stamp++; + } + + /* Iterate to evaluate all rules till no parameters are changed. */ + if (again) + goto retry; + + return 0; +} + +static int fixup_unreferenced_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + const struct snd_interval *i; + const struct snd_mask *m; + int err; + if (!params->msbits) { - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); if (snd_interval_single(i)) params->msbits = snd_interval_value(i); } if (!params->rate_den) { - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); if (snd_interval_single(i)) { params->rate_num = snd_interval_value(i); params->rate_den = 1; } } - hw = &substream->runtime->hw; + if (!params->fifo_size) { + m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); + i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + if (snd_mask_single(m) && snd_interval_single(i)) { + err = substream->ops->ioctl(substream, + SNDRV_PCM_IOCTL1_FIFO_SIZE, params); + if (err < 0) + return err; + } + } + if (!params->info) { - params->info = hw->info & ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | - SNDRV_PCM_INFO_DRAIN_TRIGGER); + params->info = substream->runtime->hw.info; + params->info &= ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | + SNDRV_PCM_INFO_DRAIN_TRIGGER); if (!hw_support_mmap(substream)) params->info &= ~(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID); } - if (!params->fifo_size) { - m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (snd_mask_min(m) == snd_mask_max(m) && - snd_interval_min(i) == snd_interval_max(i)) { - changed = substream->ops->ioctl(substream, - SNDRV_PCM_IOCTL1_FIFO_SIZE, params); - if (changed < 0) - return changed; - } + + return 0; +} + +int snd_pcm_hw_refine(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int err; + + params->info = 0; + params->fifo_size = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + params->msbits = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + params->rate_num = 0; + params->rate_den = 0; } + + err = constrain_mask_params(substream, params); + if (err < 0) + return err; + + err = constrain_interval_params(substream, params); + if (err < 0) + return err; + + err = constrain_params_by_rules(substream, params); + if (err < 0) + return err; + params->rmask = 0; + return 0; } - EXPORT_SYMBOL(snd_pcm_hw_refine); static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, @@ -467,11 +524,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, return PTR_ERR(params); err = snd_pcm_hw_refine(substream, params); - if (copy_to_user(_params, params, sizeof(*params))) { - if (!err) - err = -EFAULT; - } + if (err < 0) + goto end; + + err = fixup_unreferenced_params(substream, params); + if (err < 0) + goto end; + if (copy_to_user(_params, params, sizeof(*params))) + err = -EFAULT; +end: kfree(params); return err; } @@ -509,6 +571,70 @@ static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream, #endif } +/** + * snd_pcm_hw_param_choose - choose a configuration defined by @params + * @pcm: PCM instance + * @params: the hw_params instance + * + * Choose one configuration from configuration space defined by @params. + * The configuration chosen is that obtained fixing in this order: + * first access, first format, first subformat, min channels, + * min rate, min period time, max buffer size, min tick time + * + * Return: Zero if successful, or a negative error code on failure. + */ +static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params) +{ + static const int vars[] = { + SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_SUBFORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + SNDRV_PCM_HW_PARAM_TICK_TIME, + -1 + }; + const int *v; + struct snd_mask old_mask; + struct snd_interval old_interval; + int changed; + + for (v = vars; *v != -1; v++) { + /* Keep old parameter to trace. */ + if (trace_hw_mask_param_enabled()) { + if (hw_is_mask(*v)) + old_mask = *hw_param_mask(params, *v); + } + if (trace_hw_interval_param_enabled()) { + if (hw_is_interval(*v)) + old_interval = *hw_param_interval(params, *v); + } + if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) + changed = snd_pcm_hw_param_first(pcm, params, *v, NULL); + else + changed = snd_pcm_hw_param_last(pcm, params, *v, NULL); + if (snd_BUG_ON(changed < 0)) + return changed; + if (changed == 0) + continue; + + /* Trace the changed parameter. */ + if (hw_is_mask(*v)) { + trace_hw_mask_param(pcm, *v, 0, &old_mask, + hw_param_mask(params, *v)); + } + if (hw_is_interval(*v)) { + trace_hw_interval_param(pcm, *v, 0, &old_interval, + hw_param_interval(params, *v)); + } + } + + return 0; +} + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -546,6 +672,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, if (err < 0) goto _error; + err = fixup_unreferenced_params(substream, params); + if (err < 0) + goto _error; + if (substream->ops->hw_params != NULL) { err = substream->ops->hw_params(substream, params); if (err < 0) @@ -621,11 +751,12 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, return PTR_ERR(params); err = snd_pcm_hw_params(substream, params); - if (copy_to_user(_params, params, sizeof(*params))) { - if (!err) - err = -EFAULT; - } + if (err < 0) + goto end; + if (copy_to_user(_params, params, sizeof(*params))) + err = -EFAULT; +end: kfree(params); return err; } @@ -1081,6 +1212,7 @@ static const struct action_ops snd_pcm_action_start = { * @substream: the PCM substream instance * * Return: Zero if successful, or a negative error code. + * The stream lock must be acquired before calling this function. */ int snd_pcm_start(struct snd_pcm_substream *substream) { @@ -1088,6 +1220,13 @@ int snd_pcm_start(struct snd_pcm_substream *substream) SNDRV_PCM_STATE_RUNNING); } +/* take the stream lock and start the streams */ +static int snd_pcm_start_lock_irq(struct snd_pcm_substream *substream) +{ + return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, + SNDRV_PCM_STATE_RUNNING); +} + /* * stop callbacks */ @@ -1139,7 +1278,6 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) { return snd_pcm_action(&snd_pcm_action_stop, substream, state); } - EXPORT_SYMBOL(snd_pcm_stop); /** @@ -1314,7 +1452,6 @@ int snd_pcm_suspend(struct snd_pcm_substream *substream) snd_pcm_stream_unlock_irqrestore(substream, flags); return err; } - EXPORT_SYMBOL(snd_pcm_suspend); /** @@ -1346,7 +1483,6 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm) } return 0; } - EXPORT_SYMBOL(snd_pcm_suspend_all); /* resume */ @@ -1397,14 +1533,7 @@ static const struct action_ops snd_pcm_action_resume = { static int snd_pcm_resume(struct snd_pcm_substream *substream) { - struct snd_card *card = substream->pcm->card; - int res; - - snd_power_lock(card); - if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) - res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); - snd_power_unlock(card); - return res; + return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); } #else @@ -1423,17 +1552,9 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream) */ static int snd_pcm_xrun(struct snd_pcm_substream *substream) { - struct snd_card *card = substream->pcm->card; struct snd_pcm_runtime *runtime = substream->runtime; int result; - snd_power_lock(card); - if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { - result = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (result < 0) - goto _unlock; - } - snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: @@ -1446,8 +1567,6 @@ static int snd_pcm_xrun(struct snd_pcm_substream *substream) result = -EBADFD; } snd_pcm_stream_unlock_irq(substream); - _unlock: - snd_power_unlock(card); return result; } @@ -1551,8 +1670,6 @@ static const struct action_ops snd_pcm_action_prepare = { static int snd_pcm_prepare(struct snd_pcm_substream *substream, struct file *file) { - int res; - struct snd_card *card = substream->pcm->card; int f_flags; if (file) @@ -1560,12 +1677,19 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, else f_flags = substream->f_flags; - snd_power_lock(card); - if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) - res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, - substream, f_flags); - snd_power_unlock(card); - return res; + snd_pcm_stream_lock_irq(substream); + switch (substream->runtime->status->state) { + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* fallthru */ + case SNDRV_PCM_STATE_SUSPENDED: + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + } + snd_pcm_stream_unlock_irq(substream); + + return snd_pcm_action_nonatomic(&snd_pcm_action_prepare, + substream, f_flags); } /* @@ -1662,15 +1786,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - snd_power_lock(card); - if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { - result = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (result < 0) { - snd_power_unlock(card); - return result; - } - } - if (file) { if (file->f_flags & O_NONBLOCK) nonblock = 1; @@ -1753,7 +1868,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, unlock: snd_pcm_stream_unlock_irq(substream); up_read(&snd_pcm_link_rwsem); - snd_power_unlock(card); return result; } @@ -1773,8 +1887,7 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN || - runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED || - runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; snd_pcm_stream_lock_irq(substream); @@ -1940,7 +2053,8 @@ static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { unsigned int k; - struct snd_interval *i = hw_param_interval(params, rule->deps[0]); + const struct snd_interval *i = + hw_param_interval_c(params, rule->deps[0]); struct snd_mask m; struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); snd_mask_any(&m); @@ -1986,8 +2100,10 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, #error "Change this table" #endif -static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000 }; +static const unsigned int rates[] = { + 5512, 8000, 11025, 16000, 22050, 32000, 44100, + 48000, 64000, 88200, 96000, 176400, 192000 +}; const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { .count = ARRAY_SIZE(rates), @@ -2250,7 +2366,6 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) } snd_pcm_detach_substream(substream); } - EXPORT_SYMBOL(snd_pcm_release_substream); int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, @@ -2292,7 +2407,6 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, snd_pcm_release_substream(substream); return err; } - EXPORT_SYMBOL(snd_pcm_open_substream); static int snd_pcm_open_file(struct file *file, @@ -2428,50 +2542,84 @@ static int snd_pcm_release(struct inode *inode, struct file *file) return 0; } -static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream, - snd_pcm_uframes_t frames) +/* check and update PCM state; return 0 or a negative error + * call this inside PCM lock + */ +static int do_pcm_hwsync(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t appl_ptr; - snd_pcm_sframes_t ret; - snd_pcm_sframes_t hw_avail; - - if (frames == 0) - return 0; - - snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - break; + switch (substream->runtime->status->state) { case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_RUNNING: - if (snd_pcm_update_hw_ptr(substream) >= 0) - break; + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + return -EBADFD; /* Fall through */ - case SNDRV_PCM_STATE_XRUN: - ret = -EPIPE; - goto __end; + case SNDRV_PCM_STATE_RUNNING: + return snd_pcm_update_hw_ptr(substream); + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + return 0; case SNDRV_PCM_STATE_SUSPENDED: - ret = -ESTRPIPE; - goto __end; + return -ESTRPIPE; + case SNDRV_PCM_STATE_XRUN: + return -EPIPE; default: - ret = -EBADFD; - goto __end; + return -EBADFD; } +} - hw_avail = snd_pcm_playback_hw_avail(runtime); - if (hw_avail <= 0) { - ret = 0; - goto __end; - } - if (frames > (snd_pcm_uframes_t)hw_avail) - frames = hw_avail; +/* increase the appl_ptr; returns the processed frames or a negative error */ +static snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t frames, + snd_pcm_sframes_t avail) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + int ret; + + if (avail <= 0) + return 0; + if (frames > (snd_pcm_uframes_t)avail) + frames = avail; + appl_ptr = runtime->control->appl_ptr + frames; + if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) + appl_ptr -= runtime->boundary; + ret = pcm_lib_apply_appl_ptr(substream, appl_ptr); + return ret < 0 ? ret : frames; +} + +/* decrease the appl_ptr; returns the processed frames or a negative error */ +static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t frames, + snd_pcm_sframes_t avail) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + int ret; + + if (avail <= 0) + return 0; + if (frames > (snd_pcm_uframes_t)avail) + frames = avail; appl_ptr = runtime->control->appl_ptr - frames; if (appl_ptr < 0) appl_ptr += runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - ret = frames; - __end: + ret = pcm_lib_apply_appl_ptr(substream, appl_ptr); + return ret < 0 ? ret : frames; +} + +static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream, + snd_pcm_uframes_t frames) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_sframes_t ret; + + if (frames == 0) + return 0; + + snd_pcm_stream_lock_irq(substream); + ret = do_pcm_hwsync(substream); + if (!ret) + ret = rewind_appl_ptr(substream, frames, + snd_pcm_playback_hw_avail(runtime)); snd_pcm_stream_unlock_irq(substream); return ret; } @@ -2480,46 +2628,16 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t appl_ptr; snd_pcm_sframes_t ret; - snd_pcm_sframes_t hw_avail; if (frames == 0) return 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_DRAINING: - break; - case SNDRV_PCM_STATE_RUNNING: - if (snd_pcm_update_hw_ptr(substream) >= 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_XRUN: - ret = -EPIPE; - goto __end; - case SNDRV_PCM_STATE_SUSPENDED: - ret = -ESTRPIPE; - goto __end; - default: - ret = -EBADFD; - goto __end; - } - - hw_avail = snd_pcm_capture_hw_avail(runtime); - if (hw_avail <= 0) { - ret = 0; - goto __end; - } - if (frames > (snd_pcm_uframes_t)hw_avail) - frames = hw_avail; - appl_ptr = runtime->control->appl_ptr - frames; - if (appl_ptr < 0) - appl_ptr += runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - ret = frames; - __end: + ret = do_pcm_hwsync(substream); + if (!ret) + ret = rewind_appl_ptr(substream, frames, + snd_pcm_capture_hw_avail(runtime)); snd_pcm_stream_unlock_irq(substream); return ret; } @@ -2528,47 +2646,16 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t appl_ptr; snd_pcm_sframes_t ret; - snd_pcm_sframes_t avail; if (frames == 0) return 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_RUNNING: - if (snd_pcm_update_hw_ptr(substream) >= 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_XRUN: - ret = -EPIPE; - goto __end; - case SNDRV_PCM_STATE_SUSPENDED: - ret = -ESTRPIPE; - goto __end; - default: - ret = -EBADFD; - goto __end; - } - - avail = snd_pcm_playback_avail(runtime); - if (avail <= 0) { - ret = 0; - goto __end; - } - if (frames > (snd_pcm_uframes_t)avail) - frames = avail; - appl_ptr = runtime->control->appl_ptr + frames; - if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) - appl_ptr -= runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - ret = frames; - __end: + ret = do_pcm_hwsync(substream); + if (!ret) + ret = forward_appl_ptr(substream, frames, + snd_pcm_playback_avail(runtime)); snd_pcm_stream_unlock_irq(substream); return ret; } @@ -2577,123 +2664,47 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t appl_ptr; snd_pcm_sframes_t ret; - snd_pcm_sframes_t avail; if (frames == 0) return 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_RUNNING: - if (snd_pcm_update_hw_ptr(substream) >= 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_XRUN: - ret = -EPIPE; - goto __end; - case SNDRV_PCM_STATE_SUSPENDED: - ret = -ESTRPIPE; - goto __end; - default: - ret = -EBADFD; - goto __end; - } - - avail = snd_pcm_capture_avail(runtime); - if (avail <= 0) { - ret = 0; - goto __end; - } - if (frames > (snd_pcm_uframes_t)avail) - frames = avail; - appl_ptr = runtime->control->appl_ptr + frames; - if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) - appl_ptr -= runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - ret = frames; - __end: + ret = do_pcm_hwsync(substream); + if (!ret) + ret = forward_appl_ptr(substream, frames, + snd_pcm_capture_avail(runtime)); snd_pcm_stream_unlock_irq(substream); return ret; } static int snd_pcm_hwsync(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; int err; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_DRAINING: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - goto __badfd; - /* Fall through */ - case SNDRV_PCM_STATE_RUNNING: - if ((err = snd_pcm_update_hw_ptr(substream)) < 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_PREPARED: - err = 0; - break; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - break; - default: - __badfd: - err = -EBADFD; - break; - } + err = do_pcm_hwsync(substream); snd_pcm_stream_unlock_irq(substream); return err; } -static int snd_pcm_delay(struct snd_pcm_substream *substream, - snd_pcm_sframes_t __user *res) +static snd_pcm_sframes_t snd_pcm_delay(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; int err; snd_pcm_sframes_t n = 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_DRAINING: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - goto __badfd; - /* Fall through */ - case SNDRV_PCM_STATE_RUNNING: - if ((err = snd_pcm_update_hw_ptr(substream)) < 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_SUSPENDED: - err = 0; + err = do_pcm_hwsync(substream); + if (!err) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) n = snd_pcm_playback_hw_avail(runtime); else n = snd_pcm_capture_avail(runtime); n += runtime->delay; - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - break; - default: - __badfd: - err = -EBADFD; - break; } snd_pcm_stream_unlock_irq(substream); - if (!err) - if (put_user(n, res)) - err = -EFAULT; - return err; + return err < 0 ? err : n; } static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, @@ -2718,10 +2729,16 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, return err; } snd_pcm_stream_lock_irq(substream); - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) - control->appl_ptr = sync_ptr.c.control.appl_ptr; - else + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { + err = pcm_lib_apply_appl_ptr(substream, + sync_ptr.c.control.appl_ptr); + if (err < 0) { + snd_pcm_stream_unlock_irq(substream); + return err; + } + } else { sync_ptr.c.control.appl_ptr = control->appl_ptr; + } if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) control->avail_min = sync_ptr.c.control.avail_min; else @@ -2749,10 +2766,12 @@ static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg) return 0; } -static int snd_pcm_common_ioctl1(struct file *file, +static int snd_pcm_common_ioctl(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { + struct snd_pcm_file *pcm_file = file->private_data; + switch (cmd) { case SNDRV_PCM_IOCTL_PVERSION: return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; @@ -2762,6 +2781,11 @@ static int snd_pcm_common_ioctl1(struct file *file, return 0; case SNDRV_PCM_IOCTL_TTSTAMP: return snd_pcm_tstamp(substream, arg); + case SNDRV_PCM_IOCTL_USER_PVERSION: + if (get_user(pcm_file->user_pversion, + (unsigned int __user *)arg)) + return -EFAULT; + return 0; case SNDRV_PCM_IOCTL_HW_REFINE: return snd_pcm_hw_refine_user(substream, arg); case SNDRV_PCM_IOCTL_HW_PARAMS: @@ -2781,7 +2805,7 @@ static int snd_pcm_common_ioctl1(struct file *file, case SNDRV_PCM_IOCTL_RESET: return snd_pcm_reset(substream); case SNDRV_PCM_IOCTL_START: - return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING); + return snd_pcm_start_lock_irq(substream); case SNDRV_PCM_IOCTL_LINK: return snd_pcm_link(substream, (int)(unsigned long) arg); case SNDRV_PCM_IOCTL_UNLINK: @@ -2793,7 +2817,16 @@ static int snd_pcm_common_ioctl1(struct file *file, case SNDRV_PCM_IOCTL_HWSYNC: return snd_pcm_hwsync(substream); case SNDRV_PCM_IOCTL_DELAY: - return snd_pcm_delay(substream, arg); + { + snd_pcm_sframes_t delay = snd_pcm_delay(substream); + snd_pcm_sframes_t __user *res = arg; + + if (delay < 0) + return delay; + if (put_user(delay, res)) + return -EFAULT; + return 0; + } case SNDRV_PCM_IOCTL_SYNC_PTR: return snd_pcm_sync_ptr(substream, arg); #ifdef CONFIG_SND_SUPPORT_OLD_API @@ -2807,23 +2840,34 @@ static int snd_pcm_common_ioctl1(struct file *file, case SNDRV_PCM_IOCTL_DROP: return snd_pcm_drop(substream); case SNDRV_PCM_IOCTL_PAUSE: - { - int res; - snd_pcm_stream_lock_irq(substream); - res = snd_pcm_pause(substream, (int)(unsigned long)arg); - snd_pcm_stream_unlock_irq(substream); - return res; - } + return snd_pcm_action_lock_irq(&snd_pcm_action_pause, + substream, + (int)(unsigned long)arg); } pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd); return -ENOTTY; } +static int snd_pcm_common_ioctl1(struct file *file, + struct snd_pcm_substream *substream, + unsigned int cmd, void __user *arg) +{ + struct snd_card *card = substream->pcm->card; + int res; + + snd_power_lock(card); + res = snd_power_wait(card, SNDRV_CTL_POWER_D0); + if (res >= 0) + res = snd_pcm_common_ioctl(file, substream, cmd, arg); + snd_power_unlock(card); + return res; +} + static int snd_pcm_playback_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - if (snd_BUG_ON(!substream)) + if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) return -EINVAL; @@ -2903,7 +2947,7 @@ static int snd_pcm_capture_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - if (snd_BUG_ON(!substream)) + if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE)) return -EINVAL; @@ -3007,30 +3051,55 @@ static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd, (void __user *)arg); } +/** + * snd_pcm_kernel_ioctl - Execute PCM ioctl in the kernel-space + * @substream: PCM substream + * @cmd: IOCTL cmd + * @arg: IOCTL argument + * + * The function is provided primarily for OSS layer and USB gadget drivers, + * and it allows only the limited set of ioctls (hw_params, sw_params, + * prepare, start, drain, drop, forward). + */ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { - mm_segment_t fs; - int result; + snd_pcm_uframes_t *frames = arg; + snd_pcm_sframes_t result; - fs = snd_enter_user(); - switch (substream->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - result = snd_pcm_playback_ioctl1(NULL, substream, cmd, - (void __user *)arg); - break; - case SNDRV_PCM_STREAM_CAPTURE: - result = snd_pcm_capture_ioctl1(NULL, substream, cmd, - (void __user *)arg); - break; + switch (cmd) { + case SNDRV_PCM_IOCTL_FORWARD: + { + /* provided only for OSS; capture-only and no value returned */ + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + result = snd_pcm_capture_forward(substream, *frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_HW_PARAMS: + return snd_pcm_hw_params(substream, arg); + case SNDRV_PCM_IOCTL_SW_PARAMS: + return snd_pcm_sw_params(substream, arg); + case SNDRV_PCM_IOCTL_PREPARE: + return snd_pcm_prepare(substream, NULL); + case SNDRV_PCM_IOCTL_START: + return snd_pcm_start_lock_irq(substream); + case SNDRV_PCM_IOCTL_DRAIN: + return snd_pcm_drain(substream, NULL); + case SNDRV_PCM_IOCTL_DROP: + return snd_pcm_drop(substream); + case SNDRV_PCM_IOCTL_DELAY: + { + result = snd_pcm_delay(substream); + if (result < 0) + return result; + *frames = result; + return 0; + } default: - result = -EINVAL; - break; + return -EINVAL; } - snd_leave_user(fs); - return result; } - EXPORT_SYMBOL(snd_pcm_kernel_ioctl); static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, @@ -3314,10 +3383,41 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; return 0; } + +static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file) +{ + if (pcm_file->no_compat_mmap) + return false; + /* See pcm_control_mmap_allowed() below. + * Since older alsa-lib requires both status and control mmaps to be + * coupled, we have to disable the status mmap for old alsa-lib, too. + */ + if (pcm_file->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 14) && + (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR)) + return false; + return true; +} + +static bool pcm_control_mmap_allowed(struct snd_pcm_file *pcm_file) +{ + if (pcm_file->no_compat_mmap) + return false; + /* Disallow the control mmap when SYNC_APPLPTR flag is set; + * it enforces the user-space to fall back to snd_pcm_sync_ptr(), + * thus it effectively assures the manual update of appl_ptr. + */ + if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR) + return false; + return true; +} + #else /* ! coherent mmap */ /* * don't support mmap for status and control records. */ +#define pcm_status_mmap_allowed(pcm_file) false +#define pcm_control_mmap_allowed(pcm_file) false + static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area) { @@ -3437,7 +3537,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, area->vm_page_prot = pgprot_noncached(area->vm_page_prot); return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes); } - EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); #endif /* SNDRV_PCM_INFO_MMAP */ @@ -3486,7 +3585,6 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, atomic_inc(&substream->mmap_count); return err; } - EXPORT_SYMBOL(snd_pcm_mmap_data); static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) @@ -3503,11 +3601,11 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) offset = area->vm_pgoff << PAGE_SHIFT; switch (offset) { case SNDRV_PCM_MMAP_OFFSET_STATUS: - if (pcm_file->no_compat_mmap) + if (!pcm_status_mmap_allowed(pcm_file)) return -ENXIO; return snd_pcm_mmap_status(substream, file, area); case SNDRV_PCM_MMAP_OFFSET_CONTROL: - if (pcm_file->no_compat_mmap) + if (!pcm_control_mmap_allowed(pcm_file)) return -ENXIO; return snd_pcm_mmap_control(substream, file, area); default: @@ -3603,12 +3701,17 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, } snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_refine(substream, params); - snd_pcm_hw_convert_to_old_params(oparams, params); - if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { - if (!err) - err = -EFAULT; - } + if (err < 0) + goto out_old; + err = fixup_unreferenced_params(substream, params); + if (err < 0) + goto out_old; + + snd_pcm_hw_convert_to_old_params(oparams, params); + if (copy_to_user(_oparams, oparams, sizeof(*oparams))) + err = -EFAULT; +out_old: kfree(oparams); out: kfree(params); @@ -3631,14 +3734,16 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, err = PTR_ERR(oparams); goto out; } + snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_params(substream, params); - snd_pcm_hw_convert_to_old_params(oparams, params); - if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { - if (!err) - err = -EFAULT; - } + if (err < 0) + goto out_old; + snd_pcm_hw_convert_to_old_params(oparams, params); + if (copy_to_user(_oparams, oparams, sizeof(*oparams))) + err = -EFAULT; +out_old: kfree(oparams); out: kfree(params); diff --git a/sound/core/pcm_param_trace.h b/sound/core/pcm_param_trace.h new file mode 100644 index 000000000000..86c8d658a25c --- /dev/null +++ b/sound/core/pcm_param_trace.h @@ -0,0 +1,142 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM snd_pcm + +#if !defined(_PCM_PARAMS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _PCM_PARAMS_TRACE_H + +#include <linux/tracepoint.h> + +#define HW_PARAM_ENTRY(param) {SNDRV_PCM_HW_PARAM_##param, #param} +#define hw_param_labels \ + HW_PARAM_ENTRY(ACCESS), \ + HW_PARAM_ENTRY(FORMAT), \ + HW_PARAM_ENTRY(SUBFORMAT), \ + HW_PARAM_ENTRY(SAMPLE_BITS), \ + HW_PARAM_ENTRY(FRAME_BITS), \ + HW_PARAM_ENTRY(CHANNELS), \ + HW_PARAM_ENTRY(RATE), \ + HW_PARAM_ENTRY(PERIOD_TIME), \ + HW_PARAM_ENTRY(PERIOD_SIZE), \ + HW_PARAM_ENTRY(PERIOD_BYTES), \ + HW_PARAM_ENTRY(PERIODS), \ + HW_PARAM_ENTRY(BUFFER_TIME), \ + HW_PARAM_ENTRY(BUFFER_SIZE), \ + HW_PARAM_ENTRY(BUFFER_BYTES), \ + HW_PARAM_ENTRY(TICK_TIME) + +TRACE_EVENT(hw_mask_param, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_mask *prev, const struct snd_mask *curr), + TP_ARGS(substream, type, index, prev, curr), + TP_STRUCT__entry( + __field(int, card) + __field(int, device) + __field(int, subdevice) + __field(int, direction) + __field(snd_pcm_hw_param_t, type) + __field(int, index) + __field(int, total) + __array(__u32, prev_bits, 8) + __array(__u32, curr_bits, 8) + ), + TP_fast_assign( + __entry->card = substream->pcm->card->number; + __entry->device = substream->pcm->device; + __entry->subdevice = substream->number; + __entry->direction = substream->stream; + __entry->type = type; + __entry->index = index; + __entry->total = substream->runtime->hw_constraints.rules_num; + memcpy(__entry->prev_bits, prev->bits, sizeof(__u32) * 8); + memcpy(__entry->curr_bits, curr->bits, sizeof(__u32) * 8); + ), + TP_printk("pcmC%dD%d%s:%d %03d/%03d %s %08x%08x%08x%08x %08x%08x%08x%08x", + __entry->card, + __entry->device, + __entry->direction ? "c" : "p", + __entry->subdevice, + __entry->index, + __entry->total, + __print_symbolic(__entry->type, hw_param_labels), + __entry->prev_bits[3], __entry->prev_bits[2], + __entry->prev_bits[1], __entry->prev_bits[0], + __entry->curr_bits[3], __entry->curr_bits[2], + __entry->curr_bits[1], __entry->curr_bits[0] + ) +); + +TRACE_EVENT(hw_interval_param, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_interval *prev, const struct snd_interval *curr), + TP_ARGS(substream, type, index, prev, curr), + TP_STRUCT__entry( + __field(int, card) + __field(int, device) + __field(int, subdevice) + __field(int, direction) + __field(snd_pcm_hw_param_t, type) + __field(int, index) + __field(int, total) + __field(unsigned int, prev_min) + __field(unsigned int, prev_max) + __field(unsigned int, prev_openmin) + __field(unsigned int, prev_openmax) + __field(unsigned int, prev_integer) + __field(unsigned int, prev_empty) + __field(unsigned int, curr_min) + __field(unsigned int, curr_max) + __field(unsigned int, curr_openmin) + __field(unsigned int, curr_openmax) + __field(unsigned int, curr_integer) + __field(unsigned int, curr_empty) + ), + TP_fast_assign( + __entry->card = substream->pcm->card->number; + __entry->device = substream->pcm->device; + __entry->subdevice = substream->number; + __entry->direction = substream->stream; + __entry->type = type; + __entry->index = index; + __entry->total = substream->runtime->hw_constraints.rules_num; + __entry->prev_min = prev->min; + __entry->prev_max = prev->max; + __entry->prev_openmin = prev->openmin; + __entry->prev_openmax = prev->openmax; + __entry->prev_integer = prev->integer; + __entry->prev_empty = prev->empty; + __entry->curr_min = curr->min; + __entry->curr_max = curr->max; + __entry->curr_openmin = curr->openmin; + __entry->curr_openmax = curr->openmax; + __entry->curr_integer = curr->integer; + __entry->curr_empty = curr->empty; + ), + TP_printk("pcmC%dD%d%s:%d %03d/%03d %s %d %d %s%u %u%s %d %d %s%u %u%s", + __entry->card, + __entry->device, + __entry->direction ? "c" : "p", + __entry->subdevice, + __entry->index, + __entry->total, + __print_symbolic(__entry->type, hw_param_labels), + __entry->prev_empty, + __entry->prev_integer, + __entry->prev_openmin ? "(" : "[", + __entry->prev_min, + __entry->prev_max, + __entry->prev_openmax ? ")" : "]", + __entry->curr_empty, + __entry->curr_integer, + __entry->curr_openmin ? "(" : "[", + __entry->curr_min, + __entry->curr_max, + __entry->curr_openmax ? ")" : "]" + ) +); + +#endif /* _PCM_PARAMS_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE pcm_param_trace +#include <trace/define_trace.h> diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index 20ecd8f18080..11389f13de73 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -25,6 +25,8 @@ #include <sound/pcm.h> #include <sound/timer.h> +#include "pcm_local.h" + /* * Timer functions */ @@ -33,8 +35,8 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) { unsigned long rate, mult, fsize, l, post; struct snd_pcm_runtime *runtime = substream->runtime; - - mult = 1000000000; + + mult = 1000000000; rate = runtime->rate; if (snd_BUG_ON(!rate)) return; @@ -65,7 +67,7 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer) { struct snd_pcm_substream *substream; - + substream = timer->private_data; return substream->runtime ? substream->runtime->timer_resolution : 0; } @@ -73,7 +75,7 @@ static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer) static int snd_pcm_timer_start(struct snd_timer * timer) { struct snd_pcm_substream *substream; - + substream = snd_timer_chip(timer); substream->timer_running = 1; return 0; @@ -82,7 +84,7 @@ static int snd_pcm_timer_start(struct snd_timer * timer) static int snd_pcm_timer_stop(struct snd_timer * timer) { struct snd_pcm_substream *substream; - + substream = snd_timer_chip(timer); substream->timer_running = 0; return 0; @@ -112,7 +114,7 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream) { struct snd_timer_id tid; struct snd_timer *timer; - + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; tid.dev_class = SNDRV_TIMER_CLASS_PCM; tid.card = substream->pcm->card->number; diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h index b63b654da5ff..3ddec1b8ae46 100644 --- a/sound/core/pcm_trace.h +++ b/sound/core/pcm_trace.h @@ -34,9 +34,9 @@ TRACE_EVENT(hwptr, __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; ), - TP_printk("pcmC%dD%d%c/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu", + TP_printk("pcmC%dD%d%s/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu", __entry->card, __entry->device, - __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c", __entry->number, __entry->in_interrupt ? "IRQ" : "POS", (unsigned long)__entry->pos, @@ -69,9 +69,9 @@ TRACE_EVENT(xrun, __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; ), - TP_printk("pcmC%dD%d%c/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu", + TP_printk("pcmC%dD%d%s/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu", __entry->card, __entry->device, - __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c", __entry->number, (unsigned long)__entry->old_hw_ptr, (unsigned long)__entry->hw_ptr_base, @@ -96,12 +96,50 @@ TRACE_EVENT(hw_ptr_error, __entry->stream = (substream)->stream; __entry->reason = (why); ), - TP_printk("pcmC%dD%d%c/sub%d: ERROR: %s", + TP_printk("pcmC%dD%d%s/sub%d: ERROR: %s", __entry->card, __entry->device, - __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c", __entry->number, __entry->reason) ); +TRACE_EVENT(applptr, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t prev, snd_pcm_uframes_t curr), + TP_ARGS(substream, prev, curr), + TP_STRUCT__entry( + __field( unsigned int, card ) + __field( unsigned int, device ) + __field( unsigned int, number ) + __field( unsigned int, stream ) + __field( snd_pcm_uframes_t, prev ) + __field( snd_pcm_uframes_t, curr ) + __field( snd_pcm_uframes_t, avail ) + __field( snd_pcm_uframes_t, period_size ) + __field( snd_pcm_uframes_t, buffer_size ) + ), + TP_fast_assign( + __entry->card = (substream)->pcm->card->number; + __entry->device = (substream)->pcm->device; + __entry->number = (substream)->number; + __entry->stream = (substream)->stream; + __entry->prev = (prev); + __entry->curr = (curr); + __entry->avail = (substream)->stream ? snd_pcm_capture_avail(substream->runtime) : snd_pcm_playback_avail(substream->runtime); + __entry->period_size = (substream)->runtime->period_size; + __entry->buffer_size = (substream)->runtime->buffer_size; + ), + TP_printk("pcmC%dD%d%s/sub%d: prev=%lu, curr=%lu, avail=%lu, period=%lu, buf=%lu", + __entry->card, + __entry->device, + __entry->stream ? "c" : "p", + __entry->number, + __entry->prev, + __entry->curr, + __entry->avail, + __entry->period_size, + __entry->buffer_size + ) +); + #endif /* _PCM_TRACE_H */ /* This part must be outside protection */ diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 32588ad05653..b3b353d72527 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1610,7 +1610,7 @@ static int snd_rawmidi_dev_free(struct snd_device *device) return snd_rawmidi_free(rmidi); } -#if IS_REACHABLE(CONFIG_SND_SEQUENCER) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device) { struct snd_rawmidi *rmidi = device->private_data; @@ -1691,7 +1691,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device) } } rmidi->proc_entry = entry; -#if IS_REACHABLE(CONFIG_SND_SEQUENCER) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */ if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) { rmidi->seq_dev->private_data = rmidi; diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig index b851fd890a89..a536760a94c2 100644 --- a/sound/core/seq/Kconfig +++ b/sound/core/seq/Kconfig @@ -1,16 +1,62 @@ -# define SND_XXX_SEQ to min(SND_SEQUENCER,SND_XXX) +config SND_SEQUENCER + tristate "Sequencer support" + select SND_TIMER + select SND_SEQ_DEVICE + help + Say Y or M to enable MIDI sequencer and router support. This + feature allows routing and enqueueing of MIDI events. Events + can be processed at a given time. -config SND_RAWMIDI_SEQ - def_tristate SND_SEQUENCER && SND_RAWMIDI + Many programs require this feature, so you should enable it + unless you know what you're doing. -config SND_OPL3_LIB_SEQ - def_tristate SND_SEQUENCER && SND_OPL3_LIB +if SND_SEQUENCER -config SND_OPL4_LIB_SEQ - def_tristate SND_SEQUENCER && SND_OPL4_LIB +config SND_SEQ_DUMMY + tristate "Sequencer dummy client" + help + Say Y here to enable the dummy sequencer client. This client + is a simple MIDI-through client: all normal input events are + redirected to the output port immediately. -config SND_SBAWE_SEQ - def_tristate SND_SEQUENCER && SND_SBAWE + You don't need this unless you want to connect many MIDI + devices or applications together. -config SND_EMU10K1_SEQ - def_tristate SND_SEQUENCER && SND_EMU10K1 + To compile this driver as a module, choose M here: the module + will be called snd-seq-dummy. + +config SND_SEQUENCER_OSS + tristate "OSS Sequencer API" + depends on SND_OSSEMUL + select SND_SEQ_MIDI_EVENT + help + Say Y here to enable OSS sequencer emulation (both + /dev/sequencer and /dev/music interfaces). + + Many programs still use the OSS API, so say Y. + + To compile this driver as a module, choose M here: the module + will be called snd-seq-oss. + +config SND_SEQ_HRTIMER_DEFAULT + bool "Use HR-timer as default sequencer timer" + depends on SND_HRTIMER + default y + help + Say Y here to use the HR-timer backend as the default sequencer + timer. + +config SND_SEQ_MIDI_EVENT + def_tristate SND_RAWMIDI + +config SND_SEQ_MIDI + tristate + select SND_SEQ_MIDI_EVENT + +config SND_SEQ_MIDI_EMUL + tristate + +config SND_SEQ_VIRMIDI + tristate + +endif # SND_SEQUENCER diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index b65fa5a1943b..68fd367ac39c 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -3,7 +3,6 @@ # Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> # -snd-seq-device-objs := seq_device.o snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ seq_fifo.o seq_prioq.o seq_timer.o \ seq_system.o seq_ports.o @@ -14,17 +13,11 @@ snd-seq-midi-event-objs := seq_midi_event.o snd-seq-dummy-objs := seq_dummy.o snd-seq-virmidi-objs := seq_virmidi.o -obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o -ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) - obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o - obj-$(CONFIG_SND_SEQUENCER) += oss/ -endif -obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o +obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o +obj-$(CONFIG_SND_SEQUENCER_OSS) += oss/ -# Toplevel Module Dependency -obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o -obj-$(CONFIG_SND_RAWMIDI_SEQ) += snd-seq-midi.o snd-seq-midi-event.o -obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o -obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o -obj-$(CONFIG_SND_SBAWE_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o -obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o +obj-$(CONFIG_SND_SEQ_MIDI) += snd-seq-midi.o +obj-$(CONFIG_SND_SEQ_MIDI_EMUL) += snd-seq-midi-emul.o +obj-$(CONFIG_SND_SEQ_MIDI_EVENT) += snd-seq-midi-event.o +obj-$(CONFIG_SND_SEQ_VIRMIDI) += snd-seq-virmidi.o diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile index b38406b8463c..4ea4e3eea6b7 100644 --- a/sound/core/seq/oss/Makefile +++ b/sound/core/seq/oss/Makefile @@ -7,4 +7,4 @@ snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \ seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \ seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o -obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o +obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-oss.o diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index f3b1d7f50b81..272c55fe17c8 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1668,7 +1668,6 @@ int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo) return -EPERM; return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo); } - EXPORT_SYMBOL(snd_seq_set_queue_tempo); static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client, @@ -2200,7 +2199,6 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index, /* return client number to caller */ return client->number; } - EXPORT_SYMBOL(snd_seq_create_kernel_client); /* exported to kernel modules */ @@ -2219,7 +2217,6 @@ int snd_seq_delete_kernel_client(int client) kfree(ptr); return 0; } - EXPORT_SYMBOL(snd_seq_delete_kernel_client); /* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue @@ -2269,7 +2266,6 @@ int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event * ev, { return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop); } - EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); /* @@ -2283,7 +2279,6 @@ int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev { return kernel_client_enqueue(client, ev, file, 1, atomic, hop); } - EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking); /* @@ -2321,7 +2316,6 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev, snd_seq_client_unlock(cptr); return result; } - EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); /** @@ -2354,7 +2348,6 @@ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); return -ENOTTY; } - EXPORT_SYMBOL(snd_seq_kernel_client_ctl); /* exported (for OSS emulator) */ @@ -2372,7 +2365,6 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table return 1; return 0; } - EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); /*---------------------------------------------------------------------------*/ diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c index 12ba83367b1b..0ff7926a5a69 100644 --- a/sound/core/seq/seq_lock.c +++ b/sound/core/seq/seq_lock.c @@ -40,7 +40,6 @@ void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) schedule_timeout_uninterruptible(1); } } - EXPORT_SYMBOL(snd_use_lock_sync_helper); #endif diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index d6e9aacdc36b..f763682584a8 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -118,7 +118,6 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event, } return 0; } - EXPORT_SYMBOL(snd_seq_dump_var_event); @@ -169,7 +168,6 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char &buf); return err < 0 ? err : newlen; } - EXPORT_SYMBOL(snd_seq_expand_var_event); /* diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c index 7ba937399ac7..9e2912e3e80f 100644 --- a/sound/core/seq/seq_midi_emul.c +++ b/sound/core/seq/seq_midi_emul.c @@ -236,6 +236,7 @@ snd_midi_process_event(struct snd_midi_op *ops, break; } } +EXPORT_SYMBOL(snd_midi_process_event); /* @@ -409,6 +410,7 @@ snd_midi_channel_set_clear(struct snd_midi_channel_set *chset) chan->drum_channel = 0; } } +EXPORT_SYMBOL(snd_midi_channel_set_clear); /* * Process a rpn message. @@ -701,6 +703,7 @@ struct snd_midi_channel_set *snd_midi_channel_alloc_set(int n) } return chset; } +EXPORT_SYMBOL(snd_midi_channel_alloc_set); /* * Reset the midi controllers on a particular channel to default values. @@ -724,6 +727,7 @@ void snd_midi_channel_free_set(struct snd_midi_channel_set *chset) kfree(chset->channels); kfree(chset); } +EXPORT_SYMBOL(snd_midi_channel_free_set); static int __init alsa_seq_midi_emul_init(void) { @@ -736,8 +740,3 @@ static void __exit alsa_seq_midi_emul_exit(void) module_init(alsa_seq_midi_emul_init) module_exit(alsa_seq_midi_emul_exit) - -EXPORT_SYMBOL(snd_midi_process_event); -EXPORT_SYMBOL(snd_midi_channel_set_clear); -EXPORT_SYMBOL(snd_midi_channel_alloc_set); -EXPORT_SYMBOL(snd_midi_channel_free_set); diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c index 37db7ba492a6..90bbbdbeba03 100644 --- a/sound/core/seq/seq_midi_event.c +++ b/sound/core/seq/seq_midi_event.c @@ -134,6 +134,7 @@ int snd_midi_event_new(int bufsize, struct snd_midi_event **rdev) *rdev = dev; return 0; } +EXPORT_SYMBOL(snd_midi_event_new); void snd_midi_event_free(struct snd_midi_event *dev) { @@ -142,6 +143,7 @@ void snd_midi_event_free(struct snd_midi_event *dev) kfree(dev); } } +EXPORT_SYMBOL(snd_midi_event_free); /* * initialize record @@ -161,6 +163,7 @@ void snd_midi_event_reset_encode(struct snd_midi_event *dev) reset_encode(dev); spin_unlock_irqrestore(&dev->lock, flags); } +EXPORT_SYMBOL(snd_midi_event_reset_encode); void snd_midi_event_reset_decode(struct snd_midi_event *dev) { @@ -170,6 +173,7 @@ void snd_midi_event_reset_decode(struct snd_midi_event *dev) dev->lastcmd = 0xff; spin_unlock_irqrestore(&dev->lock, flags); } +EXPORT_SYMBOL(snd_midi_event_reset_decode); #if 0 void snd_midi_event_init(struct snd_midi_event *dev) @@ -183,6 +187,7 @@ void snd_midi_event_no_status(struct snd_midi_event *dev, int on) { dev->nostat = on ? 1 : 0; } +EXPORT_SYMBOL(snd_midi_event_no_status); /* * resize buffer @@ -232,6 +237,7 @@ long snd_midi_event_encode(struct snd_midi_event *dev, unsigned char *buf, long return result; } +EXPORT_SYMBOL(snd_midi_event_encode); /* * read one byte and encode to sequencer event: @@ -307,6 +313,7 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c, spin_unlock_irqrestore(&dev->lock, flags); return rc; } +EXPORT_SYMBOL(snd_midi_event_encode_byte); /* encode note event */ static void note_event(struct snd_midi_event *dev, struct snd_seq_event *ev) @@ -408,6 +415,7 @@ long snd_midi_event_decode(struct snd_midi_event *dev, unsigned char *buf, long return qlen; } } +EXPORT_SYMBOL(snd_midi_event_decode); /* decode note event */ @@ -524,19 +532,6 @@ static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf, return idx; } -/* - * exports - */ - -EXPORT_SYMBOL(snd_midi_event_new); -EXPORT_SYMBOL(snd_midi_event_free); -EXPORT_SYMBOL(snd_midi_event_reset_encode); -EXPORT_SYMBOL(snd_midi_event_reset_decode); -EXPORT_SYMBOL(snd_midi_event_no_status); -EXPORT_SYMBOL(snd_midi_event_encode); -EXPORT_SYMBOL(snd_midi_event_encode_byte); -EXPORT_SYMBOL(snd_midi_event_decode); - static int __init alsa_seq_midi_event_init(void) { return 0; diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index fe686ee41c6d..0a7020c82bfc 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -685,7 +685,6 @@ int snd_seq_event_port_attach(int client, return ret; } - EXPORT_SYMBOL(snd_seq_event_port_attach); /* @@ -706,5 +705,4 @@ int snd_seq_event_port_detach(int client, int port) return err; } - EXPORT_SYMBOL(snd_seq_event_port_detach); diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 52f31f1498f9..8d93a4021c78 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -534,6 +534,7 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi *rrmidi = rmidi; return 0; } +EXPORT_SYMBOL(snd_virmidi_new); /* * ENTRY functions @@ -550,5 +551,3 @@ static void __exit alsa_virmidi_exit(void) module_init(alsa_virmidi_init) module_exit(alsa_virmidi_exit) - -EXPORT_SYMBOL(snd_virmidi_new); diff --git a/sound/core/seq/seq_device.c b/sound/core/seq_device.c index c4acf17e9f5e..c4acf17e9f5e 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq_device.c diff --git a/sound/core/sound.c b/sound/core/sound.c index 175f9e4e01c8..b30f027eb0fe 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -74,7 +74,6 @@ void snd_request_card(int card) return; request_module("snd-card-%i", card); } - EXPORT_SYMBOL(snd_request_card); static void snd_request_other(int minor) @@ -124,7 +123,6 @@ void *snd_lookup_minor_data(unsigned int minor, int type) mutex_unlock(&sound_mutex); return private_data; } - EXPORT_SYMBOL(snd_lookup_minor_data); #ifdef CONFIG_MODULES diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 0ca9d72b2273..0a5c66229a22 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -55,7 +55,6 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type) mutex_unlock(&sound_oss_mutex); return private_data; } - EXPORT_SYMBOL(snd_lookup_oss_minor_data); static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev) @@ -159,7 +158,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev, kfree(preg); return -EBUSY; } - EXPORT_SYMBOL(snd_register_oss_device); int snd_unregister_oss_device(int type, struct snd_card *card, int dev) @@ -200,7 +198,6 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev) kfree(mptr); return 0; } - EXPORT_SYMBOL(snd_unregister_oss_device); /* diff --git a/sound/core/timer.c b/sound/core/timer.c index 884c3066b028..a9b9a277e00c 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -319,6 +319,7 @@ int snd_timer_open(struct snd_timer_instance **ti, *ti = timeri; return 0; } +EXPORT_SYMBOL(snd_timer_open); /* * close a timer instance @@ -384,6 +385,7 @@ int snd_timer_close(struct snd_timer_instance *timeri) mutex_unlock(®ister_mutex); return 0; } +EXPORT_SYMBOL(snd_timer_close); unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) { @@ -398,6 +400,7 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) } return 0; } +EXPORT_SYMBOL(snd_timer_resolution); static void snd_timer_notify1(struct snd_timer_instance *ti, int event) { @@ -589,6 +592,7 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) else return snd_timer_start1(timeri, true, ticks); } +EXPORT_SYMBOL(snd_timer_start); /* * stop the timer instance. @@ -602,6 +606,7 @@ int snd_timer_stop(struct snd_timer_instance *timeri) else return snd_timer_stop1(timeri, true); } +EXPORT_SYMBOL(snd_timer_stop); /* * start again.. the tick is kept. @@ -617,6 +622,7 @@ int snd_timer_continue(struct snd_timer_instance *timeri) else return snd_timer_start1(timeri, false, 0); } +EXPORT_SYMBOL(snd_timer_continue); /* * pause.. remember the ticks left @@ -628,6 +634,7 @@ int snd_timer_pause(struct snd_timer_instance * timeri) else return snd_timer_stop1(timeri, false); } +EXPORT_SYMBOL(snd_timer_pause); /* * reschedule the timer @@ -809,6 +816,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) if (use_tasklet) tasklet_schedule(&timer->task_queue); } +EXPORT_SYMBOL(snd_timer_interrupt); /* @@ -859,6 +867,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, *rtimer = timer; return 0; } +EXPORT_SYMBOL(snd_timer_new); static int snd_timer_free(struct snd_timer *timer) { @@ -978,6 +987,7 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam } spin_unlock_irqrestore(&timer->lock, flags); } +EXPORT_SYMBOL(snd_timer_notify); /* * exported functions for global timers @@ -993,11 +1003,13 @@ int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer) tid.subdevice = 0; return snd_timer_new(NULL, id, &tid, rtimer); } +EXPORT_SYMBOL(snd_timer_global_new); int snd_timer_global_free(struct snd_timer *timer) { return snd_timer_free(timer); } +EXPORT_SYMBOL(snd_timer_global_free); int snd_timer_global_register(struct snd_timer *timer) { @@ -1007,6 +1019,7 @@ int snd_timer_global_register(struct snd_timer *timer) dev.device_data = timer; return snd_timer_dev_register(&dev); } +EXPORT_SYMBOL(snd_timer_global_register); /* * System timer @@ -1327,6 +1340,33 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, wake_up(&tu->qchange_sleep); } +static int realloc_user_queue(struct snd_timer_user *tu, int size) +{ + struct snd_timer_read *queue = NULL; + struct snd_timer_tread *tqueue = NULL; + + if (tu->tread) { + tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); + if (!tqueue) + return -ENOMEM; + } else { + queue = kcalloc(size, sizeof(*queue), GFP_KERNEL); + if (!queue) + return -ENOMEM; + } + + spin_lock_irq(&tu->qlock); + kfree(tu->queue); + kfree(tu->tqueue); + tu->queue_size = size; + tu->queue = queue; + tu->tqueue = tqueue; + tu->qhead = tu->qtail = tu->qused = 0; + spin_unlock_irq(&tu->qlock); + + return 0; +} + static int snd_timer_user_open(struct inode *inode, struct file *file) { struct snd_timer_user *tu; @@ -1343,10 +1383,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file) init_waitqueue_head(&tu->qchange_sleep); mutex_init(&tu->ioctl_lock); tu->ticks = 1; - tu->queue_size = 128; - tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read), - GFP_KERNEL); - if (tu->queue == NULL) { + if (realloc_user_queue(tu, 128) < 0) { kfree(tu); return -ENOMEM; } @@ -1618,34 +1655,12 @@ static int snd_timer_user_tselect(struct file *file, if (err < 0) goto __err; - tu->qhead = tu->qtail = tu->qused = 0; - kfree(tu->queue); - tu->queue = NULL; - kfree(tu->tqueue); - tu->tqueue = NULL; - if (tu->tread) { - tu->tqueue = kmalloc(tu->queue_size * sizeof(struct snd_timer_tread), - GFP_KERNEL); - if (tu->tqueue == NULL) - err = -ENOMEM; - } else { - tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read), - GFP_KERNEL); - if (tu->queue == NULL) - err = -ENOMEM; - } - - if (err < 0) { - snd_timer_close(tu->timeri); - tu->timeri = NULL; - } else { - tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; - tu->timeri->callback = tu->tread + tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; + tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; - tu->timeri->ccallback = snd_timer_user_ccallback; - tu->timeri->callback_data = (void *)tu; - tu->timeri->disconnect = snd_timer_user_disconnect; - } + tu->timeri->ccallback = snd_timer_user_ccallback; + tu->timeri->callback_data = (void *)tu; + tu->timeri->disconnect = snd_timer_user_disconnect; __err: return err; @@ -1687,8 +1702,6 @@ static int snd_timer_user_params(struct file *file, struct snd_timer_user *tu; struct snd_timer_params params; struct snd_timer *t; - struct snd_timer_read *tr; - struct snd_timer_tread *ttr; int err; tu = file->private_data; @@ -1751,24 +1764,11 @@ static int snd_timer_user_params(struct file *file, spin_unlock_irq(&t->lock); if (params.queue_size > 0 && (unsigned int)tu->queue_size != params.queue_size) { - if (tu->tread) { - ttr = kmalloc(params.queue_size * sizeof(*ttr), - GFP_KERNEL); - if (ttr) { - kfree(tu->tqueue); - tu->queue_size = params.queue_size; - tu->tqueue = ttr; - } - } else { - tr = kmalloc(params.queue_size * sizeof(*tr), - GFP_KERNEL); - if (tr) { - kfree(tu->queue); - tu->queue_size = params.queue_size; - tu->queue = tr; - } - } + err = realloc_user_queue(tu, params.queue_size); + if (err < 0) + goto _end; } + spin_lock_irq(&tu->qlock); tu->qhead = tu->qtail = tu->qused = 0; if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { if (tu->tread) { @@ -1789,6 +1789,7 @@ static int snd_timer_user_params(struct file *file, } tu->filter = params.filter; tu->ticks = params.ticks; + spin_unlock_irq(&tu->qlock); err = 0; _end: if (copy_to_user(_params, ¶ms, sizeof(params))) @@ -1891,13 +1892,19 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, return snd_timer_user_next_device(argp); case SNDRV_TIMER_IOCTL_TREAD: { - int xarg; + int xarg, old_tread; if (tu->timeri) /* too late */ return -EBUSY; if (get_user(xarg, p)) return -EFAULT; + old_tread = tu->tread; tu->tread = xarg ? 1 : 0; + if (tu->tread != old_tread && + realloc_user_queue(tu, tu->queue_size) < 0) { + tu->tread = old_tread; + return -ENOMEM; + } return 0; } case SNDRV_TIMER_IOCTL_GINFO: @@ -2030,10 +2037,12 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait) poll_wait(file, &tu->qchange_sleep, wait); mask = 0; + spin_lock_irq(&tu->qlock); if (tu->qused) mask |= POLLIN | POLLRDNORM; if (tu->disconnected) mask |= POLLERR; + spin_unlock_irq(&tu->qlock); return mask; } @@ -2117,17 +2126,3 @@ static void __exit alsa_timer_exit(void) module_init(alsa_timer_init) module_exit(alsa_timer_exit) - -EXPORT_SYMBOL(snd_timer_open); -EXPORT_SYMBOL(snd_timer_close); -EXPORT_SYMBOL(snd_timer_resolution); -EXPORT_SYMBOL(snd_timer_start); -EXPORT_SYMBOL(snd_timer_stop); -EXPORT_SYMBOL(snd_timer_continue); -EXPORT_SYMBOL(snd_timer_pause); -EXPORT_SYMBOL(snd_timer_new); -EXPORT_SYMBOL(snd_timer_notify); -EXPORT_SYMBOL(snd_timer_global_new); -EXPORT_SYMBOL(snd_timer_global_free); -EXPORT_SYMBOL(snd_timer_global_register); -EXPORT_SYMBOL(snd_timer_interrupt); diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 8545da99b183..7144cc36e8ae 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -6,11 +6,24 @@ config SND_OPL3_LIB tristate select SND_TIMER select SND_HWDEP + select SND_SEQ_DEVICE if SND_SEQUENCER != n config SND_OPL4_LIB tristate select SND_TIMER select SND_HWDEP + select SND_SEQ_DEVICE if SND_SEQUENCER != n + +# select SEQ stuff to min(SND_SEQUENCER,SND_XXX) +config SND_OPL3_LIB_SEQ + def_tristate SND_SEQUENCER && SND_OPL3_LIB + select SND_SEQ_MIDI_EMUL + select SND_SEQ_MIDI_EVENT + +config SND_OPL4_LIB_SEQ + def_tristate SND_SEQUENCER && SND_OPL4_LIB + select SND_SEQ_MIDI_EMUL + select SND_SEQ_MIDI_EVENT config SND_VX_LIB tristate @@ -99,6 +112,8 @@ config SND_VIRMIDI depends on SND_SEQUENCER select SND_TIMER select SND_RAWMIDI + select SND_SEQ_VIRMIDI + select SND_SEQ_MIDI_EVENT help Say Y here to include the virtual MIDI driver. This driver allows to connect applications using raw MIDI devices to diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 172dacd925f5..dd5ed037adf2 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -644,15 +644,22 @@ static int alloc_fake_buffer(void) } static int dummy_pcm_copy(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - void __user *dst, snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long bytes) +{ + return 0; /* do nothing */ +} + +static int dummy_pcm_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long bytes) { return 0; /* do nothing */ } static int dummy_pcm_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + unsigned long bytes) { return 0; /* do nothing */ } @@ -683,8 +690,9 @@ static struct snd_pcm_ops dummy_pcm_ops_no_buf = { .prepare = dummy_pcm_prepare, .trigger = dummy_pcm_trigger, .pointer = dummy_pcm_pointer, - .copy = dummy_pcm_copy, - .silence = dummy_pcm_silence, + .copy_user = dummy_pcm_copy, + .copy_kernel = dummy_pcm_copy_kernel, + .fill_silence = dummy_pcm_silence, .page = dummy_pcm_page, }; diff --git a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile index 7f2c2a10c4e5..d72b1e7b51c4 100644 --- a/sound/drivers/opl3/Makefile +++ b/sound/drivers/opl3/Makefile @@ -5,7 +5,9 @@ snd-opl3-lib-objs := opl3_lib.o opl3_synth.o snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o -snd-opl3-synth-$(CONFIG_SND_SEQUENCER_OSS) += opl3_oss.o +ifneq ($(CONFIG_SND_SEQUENCER_OSS),) +snd-opl3-synth-y += opl3_oss.o +endif obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o obj-$(CONFIG_SND_OPL4_LIB) += snd-opl3-lib.o diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c index cd9e9f31720f..d5e5b4657b4b 100644 --- a/sound/drivers/opl3/opl3_lib.c +++ b/sound/drivers/opl3/opl3_lib.c @@ -528,7 +528,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, opl3->hwdep = hw; opl3->seq_dev_num = seq_device; -#if IS_REACHABLE(CONFIG_SND_SEQUENCER) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) { strcpy(opl3->seq_dev->name, hw->name); diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c index c1cb249acfaa..22c3e4bca220 100644 --- a/sound/drivers/opl3/opl3_oss.c +++ b/sound/drivers/opl3/opl3_oss.c @@ -27,20 +27,6 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, const char __user *buf, int offs, int count); static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg); -/* */ - -static inline mm_segment_t snd_enter_user(void) -{ - mm_segment_t fs = get_fs(); - set_fs(get_ds()); - return fs; -} - -static inline void snd_leave_user(mm_segment_t fs) -{ - set_fs(fs); -} - /* operators */ extern struct snd_midi_op opl3_ops; diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index fdae5d7f421f..d3e91be8b23a 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -252,7 +252,7 @@ static int snd_opl3_seq_probe(struct device *_dev) spin_lock_init(&opl3->sys_timer_lock); opl3->sys_timer_status = 0; -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) snd_opl3_init_seq_oss(opl3, name); #endif return 0; @@ -267,7 +267,7 @@ static int snd_opl3_seq_remove(struct device *_dev) if (opl3 == NULL) return -EINVAL; -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) snd_opl3_free_seq_oss(opl3); #endif if (opl3->seq_client >= 0) { diff --git a/sound/drivers/opl3/opl3_voice.h b/sound/drivers/opl3/opl3_voice.h index a371c075ac87..eaef435e0528 100644 --- a/sound/drivers/opl3/opl3_voice.h +++ b/sound/drivers/opl3/opl3_voice.h @@ -44,9 +44,12 @@ void snd_opl3_load_drums(struct snd_opl3 *opl3); void snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int on_off, int vel, struct snd_midi_channel *chan); /* Prototypes for opl3_oss.c */ -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) void snd_opl3_init_seq_oss(struct snd_opl3 *opl3, char *name); void snd_opl3_free_seq_oss(struct snd_opl3 *opl3); +#else +#define snd_opl3_init_seq_oss(opl3, name) /* NOP */ +#define snd_opl3_free_seq_oss(opl3) /* NOP */ #endif #endif diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c index 89c7aa04b3bc..bc345d564f8d 100644 --- a/sound/drivers/opl4/opl4_lib.c +++ b/sound/drivers/opl4/opl4_lib.c @@ -153,7 +153,7 @@ static int snd_opl4_detect(struct snd_opl4 *opl4) return 0; } -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) static void snd_opl4_seq_dev_free(struct snd_seq_device *seq_dev) { struct snd_opl4 *opl4 = seq_dev->private_data; @@ -249,7 +249,7 @@ int snd_opl4_create(struct snd_card *card, snd_opl4_create_mixer(opl4); snd_opl4_create_proc(opl4); -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) opl4->seq_client = -1; if (opl4->hardware < OPL3_HW_OPL4_ML) snd_opl4_create_seq_dev(opl4, seq_device); diff --git a/sound/drivers/opl4/opl4_local.h b/sound/drivers/opl4/opl4_local.h index 9a41bdebce6b..a16b4677c1e9 100644 --- a/sound/drivers/opl4/opl4_local.h +++ b/sound/drivers/opl4/opl4_local.h @@ -184,7 +184,7 @@ struct snd_opl4 { #endif struct mutex access_mutex; -#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) int used; int seq_dev_num; diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c index aca2d7d5f059..44b3632f6940 100644 --- a/sound/drivers/pcsp/pcsp_lib.c +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -323,7 +323,7 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream) return 0; } -static struct snd_pcm_ops snd_pcsp_playback_ops = { +static const struct snd_pcm_ops snd_pcsp_playback_ops = { .open = snd_pcsp_playback_open, .close = snd_pcsp_playback_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index be9477e30739..98a41ac40b60 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -455,7 +455,7 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele return 0; } -static struct snd_kcontrol_new vx_control_output_level = { +static const struct snd_kcontrol_new vx_control_output_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), @@ -514,7 +514,7 @@ static int vx_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v return 0; } -static struct snd_kcontrol_new vx_control_audio_src = { +static const struct snd_kcontrol_new vx_control_audio_src = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", .info = vx_audio_src_info, @@ -558,7 +558,7 @@ static int vx_clock_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ return 0; } -static struct snd_kcontrol_new vx_control_clock_mode = { +static const struct snd_kcontrol_new vx_control_clock_mode = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Clock Mode", .info = vx_clock_mode_info, @@ -717,7 +717,7 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ static const DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0); -static struct snd_kcontrol_new vx_control_audio_gain = { +static const struct snd_kcontrol_new vx_control_audio_gain = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), @@ -727,14 +727,14 @@ static struct snd_kcontrol_new vx_control_audio_gain = { .put = vx_audio_gain_put, .tlv = { .p = db_scale_audio_gain }, }; -static struct snd_kcontrol_new vx_control_output_switch = { +static const struct snd_kcontrol_new vx_control_output_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Switch", .info = vx_audio_sw_info, .get = vx_audio_sw_get, .put = vx_audio_sw_put }; -static struct snd_kcontrol_new vx_control_monitor_gain = { +static const struct snd_kcontrol_new vx_control_monitor_gain = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Monitoring Volume", .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | @@ -744,7 +744,7 @@ static struct snd_kcontrol_new vx_control_monitor_gain = { .put = vx_audio_monitor_put, .tlv = { .p = db_scale_audio_gain }, }; -static struct snd_kcontrol_new vx_control_monitor_switch = { +static const struct snd_kcontrol_new vx_control_monitor_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Monitoring Switch", .info = vx_audio_sw_info, /* shared */ @@ -805,7 +805,7 @@ static int vx_iec958_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu return 0; } -static struct snd_kcontrol_new vx_control_iec958_mask = { +static const struct snd_kcontrol_new vx_control_iec958_mask = { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), @@ -813,7 +813,7 @@ static struct snd_kcontrol_new vx_control_iec958_mask = { .get = vx_iec958_mask_get, }; -static struct snd_kcontrol_new vx_control_iec958 = { +static const struct snd_kcontrol_new vx_control_iec958 = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .info = vx_iec958_info, @@ -878,7 +878,7 @@ static int vx_saturation_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ return 0; } -static struct snd_kcontrol_new vx_control_vu_meter = { +static const struct snd_kcontrol_new vx_control_vu_meter = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, /* name will be filled later */ @@ -886,7 +886,7 @@ static struct snd_kcontrol_new vx_control_vu_meter = { .get = vx_vu_meter_get, }; -static struct snd_kcontrol_new vx_control_peak_meter = { +static const struct snd_kcontrol_new vx_control_peak_meter = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, /* name will be filled later */ @@ -894,7 +894,7 @@ static struct snd_kcontrol_new vx_control_peak_meter = { .get = vx_peak_meter_get, }; -static struct snd_kcontrol_new vx_control_saturation = { +static const struct snd_kcontrol_new vx_control_saturation = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Saturation", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index ea7b377f0378..d318a33b6cfb 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -873,7 +873,7 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs) /* * operators for PCM playback */ -static struct snd_pcm_ops vx_pcm_playback_ops = { +static const struct snd_pcm_ops vx_pcm_playback_ops = { .open = vx_pcm_playback_open, .close = vx_pcm_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1095,7 +1095,7 @@ static snd_pcm_uframes_t vx_pcm_capture_pointer(struct snd_pcm_substream *subs) /* * operators for PCM capture */ -static struct snd_pcm_ops vx_pcm_capture_ops = { +static const struct snd_pcm_ops vx_pcm_capture_ops = { .open = vx_pcm_capture_open, .close = vx_pcm_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index bebddc60fde8..23ccddb20de1 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -38,10 +38,6 @@ struct amdtp_am824 { u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM]; u8 midi_position; - void (*transfer_samples)(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - unsigned int frame_multiplier; }; @@ -177,32 +173,6 @@ static void write_pcm_s32(struct amdtp_stream *s, } } -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct amdtp_am824 *p = s->protocol; - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u16 *src; - - channels = p->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[p->pcm_positions[c]] = - cpu_to_be32((*src << 8) | 0x42000000); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) @@ -242,43 +212,6 @@ static void write_pcm_silence(struct amdtp_stream *s, } /** - * amdtp_am824_set_pcm_format - set the PCM format - * @s: the AMDTP stream to configure - * @format: the format of the ALSA PCM device - * - * The sample format must be set after the other parameters (rate/PCM channels/ - * MIDI) and before the stream is started, and must not be changed while the - * stream is running. - */ -void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) -{ - struct amdtp_am824 *p = s->protocol; - - if (WARN_ON(amdtp_stream_pcm_running(s))) - return; - - switch (format) { - default: - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S16: - if (s->direction == AMDTP_OUT_STREAM) { - p->transfer_samples = write_pcm_s16; - break; - } - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S32: - if (s->direction == AMDTP_OUT_STREAM) - p->transfer_samples = write_pcm_s32; - else - p->transfer_samples = read_pcm_s32; - break; - } -} -EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format); - -/** * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream * @s: the AMDTP stream for AM824 data block, must be initialized. * @runtime: the PCM substream runtime @@ -407,7 +340,7 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffe unsigned int pcm_frames; if (pcm) { - p->transfer_samples(s, pcm, buffer, data_blocks); + write_pcm_s32(s, pcm, buffer, data_blocks); pcm_frames = data_blocks * p->frame_multiplier; } else { write_pcm_silence(s, buffer, data_blocks); @@ -428,7 +361,7 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffe unsigned int pcm_frames; if (pcm) { - p->transfer_samples(s, pcm, buffer, data_blocks); + read_pcm_s32(s, pcm, buffer, data_blocks); pcm_frames = data_blocks * p->frame_multiplier; } else { pcm_frames = 0; diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index 73b07b3109db..b56e61fc997d 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -8,8 +8,7 @@ #define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 -#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ - SNDRV_PCM_FMTBIT_S32) +#define AM824_OUT_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 /* * This module supports maximum 64 PCM channels for one PCM stream @@ -41,9 +40,6 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s, int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); -void amdtp_am824_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format); - void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port, struct snd_rawmidi_substream *midi); diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 1e26854b3425..3fc581a5ad62 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -148,8 +148,27 @@ EXPORT_SYMBOL(amdtp_rate_table); int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime) { + struct snd_pcm_hardware *hw = &runtime->hw; int err; + hw->info = SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID; + + /* SNDRV_PCM_INFO_BATCH */ + hw->periods_min = 2; + hw->periods_max = UINT_MAX; + + /* bytes for a frame */ + hw->period_bytes_min = 4 * hw->channels_max; + + /* Just to prevent from allocating much pages. */ + hw->period_bytes_max = hw->period_bytes_min * 2048; + hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; + /* * Currently firewire-lib processes 16 packets in one software * interrupt callback. This equals to 2msec but actually the @@ -933,6 +952,25 @@ unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) EXPORT_SYMBOL(amdtp_stream_pcm_pointer); /** + * amdtp_stream_pcm_ack - acknowledge queued PCM frames + * @s: the AMDTP stream that transfers the PCM frames + * + * Returns zero always. + */ +int amdtp_stream_pcm_ack(struct amdtp_stream *s) +{ + /* + * Process isochronous packets for recent isochronous cycle to handle + * queued PCM frames. + */ + if (amdtp_stream_running(s)) + fw_iso_context_flush_completions(s->context); + + return 0; +} +EXPORT_SYMBOL(amdtp_stream_pcm_ack); + +/** * amdtp_stream_update - update the stream after a bus reset * @s: the AMDTP stream */ diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index ea1a91e99875..ed6eafd10992 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -168,6 +168,7 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, void amdtp_stream_pcm_prepare(struct amdtp_stream *s); unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); +int amdtp_stream_pcm_ack(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c index 07e5abdbceb5..d10208f92edf 100644 --- a/sound/firewire/bebob/bebob_maudio.c +++ b/sound/firewire/bebob/bebob_maudio.c @@ -396,7 +396,7 @@ static int special_clk_ctl_put(struct snd_kcontrol *kctl, return err; } -static struct snd_kcontrol_new special_clk_ctl = { +static const struct snd_kcontrol_new special_clk_ctl = { .name = "Clock Source", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -429,7 +429,7 @@ static int special_sync_ctl_get(struct snd_kcontrol *kctl, return 0; } -static struct snd_kcontrol_new special_sync_ctl = { +static const struct snd_kcontrol_new special_sync_ctl = { .name = "Sync Status", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READ, @@ -521,7 +521,7 @@ end: mutex_unlock(&bebob->mutex); return err; } -static struct snd_kcontrol_new special_dig_in_iface_ctl = { +static const struct snd_kcontrol_new special_dig_in_iface_ctl = { .name = "Digital Input Interface", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -577,7 +577,7 @@ static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl, mutex_unlock(&bebob->mutex); return err; } -static struct snd_kcontrol_new special_dig_out_iface_ctl = { +static const struct snd_kcontrol_new special_dig_out_iface_ctl = { .name = "Digital Output Interface", .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 9e27eb8e1dd4..e6adab3ef42e 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -92,19 +92,6 @@ limit_channels_and_rates(struct snd_pcm_hardware *hw, } } -static void -limit_period_and_buffer(struct snd_pcm_hardware *hw) -{ - hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ - hw->periods_max = UINT_MAX; - - hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */ - - /* Just to prevent from allocating much pages. */ - hw->period_bytes_max = hw->period_bytes_min * 2048; - hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; -} - static int pcm_init_hw_params(struct snd_bebob *bebob, struct snd_pcm_substream *substream) @@ -114,13 +101,6 @@ pcm_init_hw_params(struct snd_bebob *bebob, struct snd_bebob_stream_formation *formations; int err; - runtime->hw.info = SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_JOINT_DUPLEX | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &bebob->tx_stream; @@ -132,7 +112,6 @@ pcm_init_hw_params(struct snd_bebob *bebob, } limit_channels_and_rates(&runtime->hw, formations); - limit_period_and_buffer(&runtime->hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, formations, @@ -224,8 +203,6 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&bebob->mutex); } - amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params)); - return 0; } static int @@ -246,8 +223,6 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&bebob->mutex); } - amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params)); - return 0; } @@ -359,6 +334,20 @@ pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&bebob->rx_stream); } +static int pcm_capture_ack(struct snd_pcm_substream *substream) +{ + struct snd_bebob *bebob = substream->private_data; + + return amdtp_stream_pcm_ack(&bebob->tx_stream); +} + +static int pcm_playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_bebob *bebob = substream->private_data; + + return amdtp_stream_pcm_ack(&bebob->rx_stream); +} + int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { static const struct snd_pcm_ops capture_ops = { @@ -370,6 +359,7 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, + .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { @@ -381,6 +371,7 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, + .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 6074fe1f00f7..7cb9e9713ac3 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -51,18 +51,6 @@ static int limit_channels_and_rates(struct snd_dice *dice, return 0; } -static void limit_period_and_buffer(struct snd_pcm_hardware *hw) -{ - hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ - hw->periods_max = UINT_MAX; - - hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */ - - /* Just to prevent from allocating much pages. */ - hw->period_bytes_max = hw->period_bytes_min * 2048; - hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; -} - static int init_hw_info(struct snd_dice *dice, struct snd_pcm_substream *substream) { @@ -74,13 +62,6 @@ static int init_hw_info(struct snd_dice *dice, unsigned int count, size; int err; - hw->info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_JOINT_DUPLEX | - SNDRV_PCM_INFO_BLOCK_TRANSFER; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AM824_IN_PCM_FORMAT_BITS; dir = AMDTP_IN_STREAM; @@ -107,7 +88,6 @@ static int init_hw_info(struct snd_dice *dice, substream->pcm->device, size); if (err < 0) return err; - limit_period_and_buffer(hw); return amdtp_am824_add_pcm_hw_constraints(stream, runtime); } @@ -146,7 +126,6 @@ static int capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -160,15 +139,12 @@ static int capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_am824_set_pcm_format(stream, params_format(hw_params)); - return 0; } static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; - struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -182,8 +158,6 @@ static int playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_am824_set_pcm_format(stream, params_format(hw_params)); - return 0; } @@ -300,6 +274,22 @@ static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) return amdtp_stream_pcm_pointer(stream); } +static int capture_ack(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; + + return amdtp_stream_pcm_ack(stream); +} + +static int playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; + + return amdtp_stream_pcm_ack(stream); +} + int snd_dice_create_pcm(struct snd_dice *dice) { static const struct snd_pcm_ops capture_ops = { @@ -311,6 +301,7 @@ int snd_dice_create_pcm(struct snd_dice *dice) .prepare = capture_prepare, .trigger = capture_trigger, .pointer = capture_pointer, + .ack = capture_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; @@ -323,6 +314,7 @@ int snd_dice_create_pcm(struct snd_dice *dice) .prepare = playback_prepare, .trigger = playback_trigger, .pointer = playback_pointer, + .ack = playback_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index a4688545339c..1453c34ce99f 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -48,10 +48,6 @@ struct amdtp_dot { struct snd_rawmidi_substream *midi[MAX_MIDI_PORTS]; int midi_fifo_used[MAX_MIDI_PORTS]; int midi_fifo_limit; - - void (*transfer_samples)(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); }; /* @@ -173,32 +169,6 @@ static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, } } -static void write_pcm_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct amdtp_dot *p = s->protocol; - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u16 *src; - - channels = p->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - buffer++; - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[c] = cpu_to_be32((*src << 8) | 0x40000000); - dot_encode_step(&p->state, &buffer[c]); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) { @@ -351,33 +321,6 @@ int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, return amdtp_stream_add_pcm_hw_constraints(s, runtime); } -void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) -{ - struct amdtp_dot *p = s->protocol; - - if (WARN_ON(amdtp_stream_pcm_running(s))) - return; - - switch (format) { - default: - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S16: - if (s->direction == AMDTP_OUT_STREAM) { - p->transfer_samples = write_pcm_s16; - break; - } - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S32: - if (s->direction == AMDTP_OUT_STREAM) - p->transfer_samples = write_pcm_s32; - else - p->transfer_samples = read_pcm_s32; - break; - } -} - void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, struct snd_rawmidi_substream *midi) { @@ -392,13 +335,12 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, unsigned int data_blocks, unsigned int *syt) { - struct amdtp_dot *p = (struct amdtp_dot *)s->protocol; struct snd_pcm_substream *pcm; unsigned int pcm_frames; pcm = ACCESS_ONCE(s->pcm); if (pcm) { - p->transfer_samples(s, pcm, buffer, data_blocks); + read_pcm_s32(s, pcm, buffer, data_blocks); pcm_frames = data_blocks; } else { pcm_frames = 0; @@ -414,13 +356,12 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s, unsigned int data_blocks, unsigned int *syt) { - struct amdtp_dot *p = (struct amdtp_dot *)s->protocol; struct snd_pcm_substream *pcm; unsigned int pcm_frames; pcm = ACCESS_ONCE(s->pcm); if (pcm) { - p->transfer_samples(s, pcm, buffer, data_blocks); + write_pcm_s32(s, pcm, buffer, data_blocks); pcm_frames = data_blocks; } else { write_pcm_silence(s, buffer, data_blocks); diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index 68d1c52db051..796f4b4645f5 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -58,41 +58,29 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, static int pcm_init_hw_params(struct snd_dg00x *dg00x, struct snd_pcm_substream *substream) { - static const struct snd_pcm_hardware hardware = { - .info = SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_JOINT_DUPLEX | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000, - .rate_min = 44100, - .rate_max = 96000, - .channels_min = 10, - .channels_max = 18, - .period_bytes_min = 4 * 18, - .period_bytes_max = 4 * 18 * 2048, - .buffer_bytes_max = 4 * 18 * 2048 * 2, - .periods_min = 2, - .periods_max = UINT_MAX, - }; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hardware *hw = &runtime->hw; struct amdtp_stream *s; int err; - substream->runtime->hw = hardware; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &dg00x->tx_stream; } else { - substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 | - SNDRV_PCM_FMTBIT_S32; + substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &dg00x->rx_stream; } + hw->channels_min = 10; + hw->channels_max = 18; + + hw->rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + snd_pcm_limit_hw_rates(runtime); + err = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, NULL, @@ -184,8 +172,6 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dg00x->mutex); } - amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params)); - return 0; } @@ -206,8 +192,6 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dg00x->mutex); } - amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params)); - return 0; } @@ -329,6 +313,20 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&dg00x->rx_stream); } +static int pcm_capture_ack(struct snd_pcm_substream *substream) +{ + struct snd_dg00x *dg00x = substream->private_data; + + return amdtp_stream_pcm_ack(&dg00x->tx_stream); +} + +static int pcm_playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_dg00x *dg00x = substream->private_data; + + return amdtp_stream_pcm_ack(&dg00x->rx_stream); +} + int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) { static const struct snd_pcm_ops capture_ops = { @@ -340,6 +338,7 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, + .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { @@ -351,6 +350,7 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, + .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 1275a50956c0..4dd1bbf2ed3c 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -121,7 +121,6 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, void amdtp_dot_reset(struct amdtp_stream *s); int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); -void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, struct snd_rawmidi_substream *midi); diff --git a/sound/firewire/fireface/ff-midi.c b/sound/firewire/fireface/ff-midi.c index 29ee0a7365c3..949ee56b4e0e 100644 --- a/sound/firewire/fireface/ff-midi.c +++ b/sound/firewire/fireface/ff-midi.c @@ -74,18 +74,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&ff->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, -}; - static void set_midi_substream_names(struct snd_rawmidi_str *stream, const char *const name) { @@ -99,6 +87,16 @@ static void set_midi_substream_names(struct snd_rawmidi_str *stream, int snd_ff_create_midi_devices(struct snd_ff *ff) { + static const struct snd_rawmidi_ops midi_capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops midi_playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + }; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *stream; int err; diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index 93cee1978e8e..d12a0e3a4219 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -91,18 +91,6 @@ static void limit_channels_and_rates(struct snd_pcm_hardware *hw, } } -static void limit_period_and_buffer(struct snd_pcm_hardware *hw) -{ - hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ - hw->periods_max = UINT_MAX; - - hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */ - - /* Just to prevent from allocating much pages. */ - hw->period_bytes_max = hw->period_bytes_min * 2048; - hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; -} - static int pcm_init_hw_params(struct snd_ff *ff, struct snd_pcm_substream *substream) { @@ -111,13 +99,6 @@ static int pcm_init_hw_params(struct snd_ff *ff, const unsigned int *pcm_channels; int err; - runtime->hw.info = SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_JOINT_DUPLEX | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->tx_stream; @@ -128,9 +109,7 @@ static int pcm_init_hw_params(struct snd_ff *ff, pcm_channels = ff->spec->pcm_playback_channels; } - /* limit rates */ limit_channels_and_rates(&runtime->hw, pcm_channels); - limit_period_and_buffer(&runtime->hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, (void *)pcm_channels, @@ -365,33 +344,47 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&ff->rx_stream); } -static struct snd_pcm_ops pcm_capture_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, - .prepare = pcm_capture_prepare, - .trigger = pcm_capture_trigger, - .pointer = pcm_capture_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; - -static struct snd_pcm_ops pcm_playback_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, - .prepare = pcm_playback_prepare, - .trigger = pcm_playback_trigger, - .pointer = pcm_playback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; +static int pcm_capture_ack(struct snd_pcm_substream *substream) +{ + struct snd_ff *ff = substream->private_data; + + return amdtp_stream_pcm_ack(&ff->tx_stream); +} + +static int pcm_playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_ff *ff = substream->private_data; + + return amdtp_stream_pcm_ack(&ff->rx_stream); +} int snd_ff_create_pcm_devices(struct snd_ff *ff) { + static const struct snd_pcm_ops pcm_capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .ack = pcm_capture_ack, + .page = snd_pcm_lib_get_vmalloc_page, + }; + static const struct snd_pcm_ops pcm_playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .ack = pcm_playback_ack, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; struct snd_pcm *pcm; int err; diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index 9171702f7d0b..40faed5e6968 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -129,19 +129,6 @@ limit_channels(struct snd_pcm_hardware *hw, unsigned int *pcm_channels) } } -static void -limit_period_and_buffer(struct snd_pcm_hardware *hw) -{ - hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ - hw->periods_max = UINT_MAX; - - hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */ - - /* Just to prevent from allocating much pages. */ - hw->period_bytes_max = hw->period_bytes_min * 2048; - hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; -} - static int pcm_init_hw_params(struct snd_efw *efw, struct snd_pcm_substream *substream) @@ -151,13 +138,6 @@ pcm_init_hw_params(struct snd_efw *efw, unsigned int *pcm_channels; int err; - runtime->hw.info = SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_JOINT_DUPLEX | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &efw->tx_stream; @@ -173,7 +153,6 @@ pcm_init_hw_params(struct snd_efw *efw, snd_pcm_limit_hw_rates(runtime); limit_channels(&runtime->hw, pcm_channels); - limit_period_and_buffer(&runtime->hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, pcm_channels, @@ -257,8 +236,6 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&efw->mutex); } - amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params)); - return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, @@ -278,8 +255,6 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&efw->mutex); } - amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params)); - return 0; } @@ -383,6 +358,20 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&efw->rx_stream); } +static int pcm_capture_ack(struct snd_pcm_substream *substream) +{ + struct snd_efw *efw = substream->private_data; + + return amdtp_stream_pcm_ack(&efw->tx_stream); +} + +static int pcm_playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_efw *efw = substream->private_data; + + return amdtp_stream_pcm_ack(&efw->rx_stream); +} + int snd_efw_create_pcm_devices(struct snd_efw *efw) { static const struct snd_pcm_ops capture_ops = { @@ -394,6 +383,7 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw) .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, + .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { @@ -405,6 +395,7 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw) .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, + .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index 94558f3d218b..a2b50df70874 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -96,18 +96,6 @@ static void limit_channels_and_rates(struct snd_motu *motu, snd_pcm_limit_hw_rates(runtime); } -static void limit_period_and_buffer(struct snd_pcm_hardware *hw) -{ - hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ - hw->periods_max = UINT_MAX; - - hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */ - - /* Just to prevent from allocating much pages. */ - hw->period_bytes_max = hw->period_bytes_min * 2048; - hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; -} - static int init_hw_info(struct snd_motu *motu, struct snd_pcm_substream *substream) { @@ -117,13 +105,6 @@ static int init_hw_info(struct snd_motu *motu, struct snd_motu_packet_format *formats; int err; - hw->info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_JOINT_DUPLEX | - SNDRV_PCM_INFO_BLOCK_TRANSFER; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = SNDRV_PCM_FMTBIT_S32; stream = &motu->tx_stream; @@ -135,7 +116,6 @@ static int init_hw_info(struct snd_motu *motu, } limit_channels_and_rates(motu, runtime, formats); - limit_period_and_buffer(hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, motu_rate_constraint, formats, @@ -356,6 +336,20 @@ static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) return amdtp_stream_pcm_pointer(&motu->rx_stream); } +static int capture_ack(struct snd_pcm_substream *substream) +{ + struct snd_motu *motu = substream->private_data; + + return amdtp_stream_pcm_ack(&motu->tx_stream); +} + +static int playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_motu *motu = substream->private_data; + + return amdtp_stream_pcm_ack(&motu->rx_stream); +} + int snd_motu_create_pcm_devices(struct snd_motu *motu) { static struct snd_pcm_ops capture_ops = { @@ -367,6 +361,7 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu) .prepare = capture_prepare, .trigger = capture_trigger, .pointer = capture_pointer, + .ack = capture_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; @@ -379,6 +374,7 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu) .prepare = playback_prepare, .trigger = playback_trigger, .pointer = playback_pointer, + .ack = playback_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index f3530f89a025..3dd46285c0e2 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -106,18 +106,6 @@ static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats) } } -static void limit_period_and_buffer(struct snd_pcm_hardware *hw) -{ - hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ - hw->periods_max = UINT_MAX; - - hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */ - - /* Just to prevent from allocating much pages. */ - hw->period_bytes_max = hw->period_bytes_min * 2048; - hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; -} - static int init_hw_params(struct snd_oxfw *oxfw, struct snd_pcm_substream *substream) { @@ -126,13 +114,6 @@ static int init_hw_params(struct snd_oxfw *oxfw, struct amdtp_stream *stream; int err; - runtime->hw.info = SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_JOINT_DUPLEX | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; stream = &oxfw->tx_stream; @@ -144,7 +125,6 @@ static int init_hw_params(struct snd_oxfw *oxfw, } limit_channels_and_rates(&runtime->hw, formats); - limit_period_and_buffer(&runtime->hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, formats, @@ -244,8 +224,6 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&oxfw->mutex); } - amdtp_am824_set_pcm_format(&oxfw->tx_stream, params_format(hw_params)); - return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, @@ -265,8 +243,6 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&oxfw->mutex); } - amdtp_am824_set_pcm_format(&oxfw->rx_stream, params_format(hw_params)); - return 0; } @@ -386,6 +362,20 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm) return amdtp_stream_pcm_pointer(&oxfw->rx_stream); } +static int pcm_capture_ack(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + return amdtp_stream_pcm_ack(&oxfw->tx_stream); +} + +static int pcm_playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + return amdtp_stream_pcm_ack(&oxfw->rx_stream); +} + int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) { static const struct snd_pcm_ops capture_ops = { @@ -397,6 +387,7 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, + .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; @@ -409,6 +400,7 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, + .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c index 9dd0fccd5ccc..6aff1fc1c72d 100644 --- a/sound/firewire/tascam/amdtp-tascam.c +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -14,10 +14,6 @@ struct amdtp_tscm { unsigned int pcm_channels; - - void (*transfer_samples)(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); }; int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate) @@ -62,31 +58,6 @@ static void write_pcm_s32(struct amdtp_stream *s, } } -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct amdtp_tscm *p = s->protocol; - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u16 *src; - - channels = p->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[c] = cpu_to_be32(*src << 16); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames) @@ -146,44 +117,16 @@ int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, return amdtp_stream_add_pcm_hw_constraints(s, runtime); } -void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) -{ - struct amdtp_tscm *p = s->protocol; - - if (WARN_ON(amdtp_stream_pcm_running(s))) - return; - - switch (format) { - default: - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S16: - if (s->direction == AMDTP_OUT_STREAM) { - p->transfer_samples = write_pcm_s16; - break; - } - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S32: - if (s->direction == AMDTP_OUT_STREAM) - p->transfer_samples = write_pcm_s32; - else - p->transfer_samples = read_pcm_s32; - break; - } -} - static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, unsigned int data_blocks, unsigned int *syt) { - struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol; struct snd_pcm_substream *pcm; pcm = ACCESS_ONCE(s->pcm); if (data_blocks > 0 && pcm) - p->transfer_samples(s, pcm, buffer, data_blocks); + read_pcm_s32(s, pcm, buffer, data_blocks); /* A place holder for control messages. */ @@ -195,7 +138,6 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s, unsigned int data_blocks, unsigned int *syt) { - struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol; struct snd_pcm_substream *pcm; /* This field is not used. */ @@ -203,7 +145,7 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s, pcm = ACCESS_ONCE(s->pcm); if (pcm) - p->transfer_samples(s, pcm, buffer, data_blocks); + write_pcm_s32(s, pcm, buffer, data_blocks); else write_pcm_silence(s, buffer, data_blocks); diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index f5dd6ce6b6f1..6ec8ec634d4d 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -8,48 +8,20 @@ #include "tascam.h" -static void set_buffer_params(struct snd_pcm_hardware *hw) -{ - hw->period_bytes_min = 4 * hw->channels_min; - hw->period_bytes_max = hw->period_bytes_min * 2048; - hw->buffer_bytes_max = hw->period_bytes_max * 2; - - hw->periods_min = 2; - hw->periods_max = UINT_MAX; -} - static int pcm_init_hw_params(struct snd_tscm *tscm, struct snd_pcm_substream *substream) { - static const struct snd_pcm_hardware hardware = { - .info = SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_JOINT_DUPLEX | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000, - .rate_min = 44100, - .rate_max = 96000, - .channels_min = 10, - .channels_max = 18, - }; struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hardware *hw = &runtime->hw; struct amdtp_stream *stream; unsigned int pcm_channels; - runtime->hw = hardware; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; stream = &tscm->tx_stream; pcm_channels = tscm->spec->pcm_capture_analog_channels; } else { - runtime->hw.formats = - SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; stream = &tscm->rx_stream; pcm_channels = tscm->spec->pcm_playback_analog_channels; } @@ -60,7 +32,11 @@ static int pcm_init_hw_params(struct snd_tscm *tscm, pcm_channels += 2; runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels; - set_buffer_params(&runtime->hw); + hw->rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + snd_pcm_limit_hw_rates(runtime); return amdtp_tscm_add_pcm_hw_constraints(stream, runtime); } @@ -125,8 +101,6 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&tscm->mutex); } - amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params)); - return 0; } @@ -147,8 +121,6 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&tscm->mutex); } - amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params)); - return 0; } @@ -268,6 +240,20 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&tscm->rx_stream); } +static int pcm_capture_ack(struct snd_pcm_substream *substream) +{ + struct snd_tscm *tscm = substream->private_data; + + return amdtp_stream_pcm_ack(&tscm->tx_stream); +} + +static int pcm_playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_tscm *tscm = substream->private_data; + + return amdtp_stream_pcm_ack(&tscm->rx_stream); +} + int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) { static const struct snd_pcm_ops capture_ops = { @@ -279,6 +265,7 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, + .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { @@ -290,6 +277,7 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, + .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 08ecfae5c584..a5bd167eb5d9 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -131,7 +131,6 @@ int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate); int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); -void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate); int snd_tscm_stream_get_clock(struct snd_tscm *tscm, diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 0e81ea89a596..714a51721a31 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -212,5 +212,6 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus, bus->caddr_tbl[codec->addr] = NULL; clear_bit(codec->addr, &bus->codec_powered); bus->num_codecs--; + flush_work(&bus->unsol_work); } EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 03c9872c31cf..19deb306facb 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -159,6 +159,7 @@ void snd_hdac_device_unregister(struct hdac_device *codec) if (device_is_registered(&codec->dev)) { hda_widget_sysfs_exit(codec); device_del(&codec->dev); + snd_hdac_bus_remove_device(codec->bus, codec); } } EXPORT_SYMBOL_GPL(snd_hdac_device_unregister); diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c index 2183e9ebaa6d..4099e6062d3c 100644 --- a/sound/i2c/other/ak4113.c +++ b/sound/i2c/other/ak4113.c @@ -199,12 +199,11 @@ static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct ak4113 *chip = snd_kcontrol_chip(kcontrol); - long *ptr; spin_lock_irq(&chip->lock); - ptr = (long *)(((char *)chip) + kcontrol->private_value); - ucontrol->value.integer.value[0] = *ptr; - *ptr = 0; + ucontrol->value.integer.value[0] = + chip->errors[kcontrol->private_value]; + chip->errors[kcontrol->private_value] = 0; spin_unlock_irq(&chip->lock); return 0; } @@ -373,7 +372,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = { SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4113_in_error_info, .get = snd_ak4113_in_error_get, - .private_value = offsetof(struct ak4113, parity_errors), + .private_value = AK4113_PARITY_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -382,7 +381,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = { SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4113_in_error_info, .get = snd_ak4113_in_error_get, - .private_value = offsetof(struct ak4113, v_bit_errors), + .private_value = AK4113_V_BIT_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -391,7 +390,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = { SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4113_in_error_info, .get = snd_ak4113_in_error_get, - .private_value = offsetof(struct ak4113, ccrc_errors), + .private_value = AK4113_CCRC_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -400,7 +399,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = { SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4113_in_error_info, .get = snd_ak4113_in_error_get, - .private_value = offsetof(struct ak4113, qcrc_errors), + .private_value = AK4113_QCRC_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -551,13 +550,13 @@ int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags) rcs2 = reg_read(ak4113, AK4113_REG_RCS2); spin_lock_irqsave(&ak4113->lock, _flags); if (rcs0 & AK4113_PAR) - ak4113->parity_errors++; + ak4113->errors[AK4113_PARITY_ERRORS]++; if (rcs0 & AK4113_V) - ak4113->v_bit_errors++; + ak4113->errors[AK4113_V_BIT_ERRORS]++; if (rcs2 & AK4113_CCRC) - ak4113->ccrc_errors++; + ak4113->errors[AK4113_CCRC_ERRORS]++; if (rcs2 & AK4113_QCRC) - ak4113->qcrc_errors++; + ak4113->errors[AK4113_QCRC_ERRORS]++; c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC | AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^ (rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC | diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index d53c9bb36281..7fb1aeb46915 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -194,12 +194,11 @@ static int snd_ak4114_in_error_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct ak4114 *chip = snd_kcontrol_chip(kcontrol); - long *ptr; spin_lock_irq(&chip->lock); - ptr = (long *)(((char *)chip) + kcontrol->private_value); - ucontrol->value.integer.value[0] = *ptr; - *ptr = 0; + ucontrol->value.integer.value[0] = + chip->errors[kcontrol->private_value]; + chip->errors[kcontrol->private_value] = 0; spin_unlock_irq(&chip->lock); return 0; } @@ -341,7 +340,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4114_in_error_info, .get = snd_ak4114_in_error_get, - .private_value = offsetof(struct ak4114, parity_errors), + .private_value = AK4114_PARITY_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -349,7 +348,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4114_in_error_info, .get = snd_ak4114_in_error_get, - .private_value = offsetof(struct ak4114, v_bit_errors), + .private_value = AK4114_V_BIT_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -357,7 +356,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4114_in_error_info, .get = snd_ak4114_in_error_get, - .private_value = offsetof(struct ak4114, ccrc_errors), + .private_value = AK4114_CCRC_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -365,7 +364,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4114_in_error_info, .get = snd_ak4114_in_error_get, - .private_value = offsetof(struct ak4114, qcrc_errors), + .private_value = AK4114_QCRC_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -581,13 +580,13 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags) rcs0 = reg_read(ak4114, AK4114_REG_RCS0); spin_lock_irqsave(&ak4114->lock, _flags); if (rcs0 & AK4114_PAR) - ak4114->parity_errors++; + ak4114->errors[AK4114_PARITY_ERRORS]++; if (rcs1 & AK4114_V) - ak4114->v_bit_errors++; + ak4114->errors[AK4114_V_BIT_ERRORS]++; if (rcs1 & AK4114_CCRC) - ak4114->ccrc_errors++; + ak4114->errors[AK4114_CCRC_ERRORS]++; if (rcs1 & AK4114_QCRC) - ak4114->qcrc_errors++; + ak4114->errors[AK4114_QCRC_ERRORS]++; c0 = (ak4114->rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)) ^ (rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)); c1 = (ak4114->rcs1 & 0xf0) ^ (rcs1 & 0xf0); diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c index 0702f0552d19..3ab099fb8c15 100644 --- a/sound/i2c/other/ak4117.c +++ b/sound/i2c/other/ak4117.c @@ -168,12 +168,11 @@ static int snd_ak4117_in_error_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct ak4117 *chip = snd_kcontrol_chip(kcontrol); - long *ptr; spin_lock_irq(&chip->lock); - ptr = (long *)(((char *)chip) + kcontrol->private_value); - ucontrol->value.integer.value[0] = *ptr; - *ptr = 0; + ucontrol->value.integer.value[0] = + chip->errors[kcontrol->private_value]; + chip->errors[kcontrol->private_value] = 0; spin_unlock_irq(&chip->lock); return 0; } @@ -328,7 +327,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4117_in_error_info, .get = snd_ak4117_in_error_get, - .private_value = offsetof(struct ak4117, parity_errors), + .private_value = AK4117_PARITY_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -336,7 +335,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4117_in_error_info, .get = snd_ak4117_in_error_get, - .private_value = offsetof(struct ak4117, v_bit_errors), + .private_value = AK4117_V_BIT_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -344,7 +343,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4117_in_error_info, .get = snd_ak4117_in_error_get, - .private_value = offsetof(struct ak4117, ccrc_errors), + .private_value = AK4117_CCRC_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -352,7 +351,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = snd_ak4117_in_error_info, .get = snd_ak4117_in_error_get, - .private_value = offsetof(struct ak4117, qcrc_errors), + .private_value = AK4117_QCRC_ERRORS, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -470,13 +469,13 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags) // printk(KERN_DEBUG "AK IRQ: rcs0 = 0x%x, rcs1 = 0x%x, rcs2 = 0x%x\n", rcs0, rcs1, rcs2); spin_lock_irqsave(&ak4117->lock, _flags); if (rcs0 & AK4117_PAR) - ak4117->parity_errors++; + ak4117->errors[AK4117_PARITY_ERRORS]++; if (rcs0 & AK4117_V) - ak4117->v_bit_errors++; + ak4117->errors[AK4117_V_BIT_ERRORS]++; if (rcs2 & AK4117_CCRC) - ak4117->ccrc_errors++; + ak4117->errors[AK4117_CCRC_ERRORS]++; if (rcs2 & AK4117_QCRC) - ak4117->qcrc_errors++; + ak4117->errors[AK4117_QCRC_ERRORS]++; c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^ (rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)); c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^ diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index 37adcc6cbe6b..cb54d9c0a77f 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -377,6 +377,7 @@ config SND_SBAWE select SND_OPL3_LIB select SND_MPU401_UART select SND_SB16_DSP + select SND_SEQ_DEVICE if SND_SEQUENCER != n help Say Y here to include support for Sound Blaster AWE soundcards (including the Plug and Play version). @@ -384,6 +385,13 @@ config SND_SBAWE To compile this driver as a module, choose M here: the module will be called snd-sbawe. +# select SEQ stuff to min(SND_SEQUENCER,SND_XXX) +config SND_SBAWE_SEQ + def_tristate SND_SEQUENCER && SND_SBAWE + select SND_SEQ_MIDI_EMUL + select SND_SEQ_VIRMIDI + select SND_SYNTH_EMUX + config SND_SB16_CSP bool "Sound Blaster 16/AWE CSP support" depends on (SND_SB16 || SND_SBAWE) && (BROKEN || !PPC) diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c index 8e1756c3b9bb..d09e456107ad 100644 --- a/sound/isa/cmi8328.c +++ b/sound/isa/cmi8328.c @@ -26,7 +26,7 @@ MODULE_AUTHOR("Ondrej Zary <linux@rainbow-software.org>"); MODULE_DESCRIPTION("C-Media CMI8328"); MODULE_LICENSE("GPL"); -#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE) +#if IS_ENABLED(CONFIG_GAMEPORT) #define SUPPORT_JOYSTICK 1 #endif diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c index 2b7cc596f4c6..2012936f6756 100644 --- a/sound/isa/cs423x/cs4236_lib.c +++ b/sound/isa/cs423x/cs4236_lib.c @@ -138,7 +138,7 @@ static unsigned char snd_cs4236_ctrl_in(struct snd_wss *chip, unsigned char reg) #define CLOCKS 8 -static struct snd_ratnum clocks[CLOCKS] = { +static const struct snd_ratnum clocks[CLOCKS] = { { .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 }, { .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 }, { .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 }, @@ -149,7 +149,7 @@ static struct snd_ratnum clocks[CLOCKS] = { { .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 } }; -static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { +static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { .nrats = CLOCKS, .rats = clocks, }; diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index 81cf26fa28d6..5d3df96c5e5b 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -290,7 +290,7 @@ static int snd_es1688_init(struct snd_es1688 * chip, int enable) */ -static struct snd_ratnum clocks[2] = { +static const struct snd_ratnum clocks[2] = { { .num = 795444, .den_min = 1, @@ -305,7 +305,7 @@ static struct snd_ratnum clocks[2] = { } }; -static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { +static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { .nrats = 2, .rats = clocks, }; diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 0cabe2b8974f..ae17a6584061 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -369,7 +369,7 @@ static int snd_es18xx_reset_fifo(struct snd_es18xx *chip) return 0; } -static struct snd_ratnum new_clocks[2] = { +static const struct snd_ratnum new_clocks[2] = { { .num = 793800, .den_min = 1, @@ -384,12 +384,12 @@ static struct snd_ratnum new_clocks[2] = { } }; -static struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks = { +static const struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks = { .nrats = 2, .rats = new_clocks, }; -static struct snd_ratnum old_clocks[2] = { +static const struct snd_ratnum old_clocks[2] = { { .num = 795444, .den_min = 1, @@ -404,7 +404,7 @@ static struct snd_ratnum old_clocks[2] = { } }; -static struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks = { +static const struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks = { .nrats = 2, .rats = old_clocks, }; diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index 4490ee442ff4..3cf9b13c780a 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -82,7 +82,7 @@ static int snd_gus_joystick_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el return change; } -static struct snd_kcontrol_new snd_gus_joystick_control = { +static const struct snd_kcontrol_new snd_gus_joystick_control = { .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = "Joystick Speed", .info = snd_gus_joystick_info, diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index 06505999155f..6b3da01a93b7 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -61,8 +61,6 @@ struct gus_pcm_private { int final_volume; }; -static int snd_gf1_pcm_use_dma = 1; - static void snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus, void *private_data) { struct gus_pcm_private *pcmp = private_data; @@ -355,66 +353,83 @@ static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf, return 0; } -static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, - int voice, - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) +static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos, + unsigned int len) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct gus_pcm_private *pcmp = runtime->private_data; - unsigned int bpos, len; - - bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); - len = samples_to_bytes(runtime, count); + unsigned int bpos = pos + (voice * (pcmp->dma_size / 2)); if (snd_BUG_ON(bpos > pcmp->dma_size)) return -EIO; if (snd_BUG_ON(bpos + len > pcmp->dma_size)) return -EIO; + return bpos; +} + +static int playback_copy_ack(struct snd_pcm_substream *substream, + unsigned int bpos, unsigned int len) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct gus_pcm_private *pcmp = runtime->private_data; + struct snd_gus_card *gus = pcmp->gus; + int w16, invert; + + if (len > 32) + return snd_gf1_pcm_block_change(substream, bpos, + pcmp->memory + bpos, len); + + w16 = (snd_pcm_format_width(runtime->format) == 16); + invert = snd_pcm_format_unsigned(runtime->format); + return snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, + pcmp->memory + bpos, len, w16, invert); +} + +static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, + int voice, unsigned long pos, + void __user *src, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct gus_pcm_private *pcmp = runtime->private_data; + unsigned int len = count; + int bpos; + + bpos = get_bpos(pcmp, voice, pos, len); + if (bpos < 0) + return pos; if (copy_from_user(runtime->dma_area + bpos, src, len)) return -EFAULT; - if (snd_gf1_pcm_use_dma && len > 32) { - return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); - } else { - struct snd_gus_card *gus = pcmp->gus; - int err, w16, invert; + return playback_copy_ack(substream, bpos, len); +} - w16 = (snd_pcm_format_width(runtime->format) == 16); - invert = snd_pcm_format_unsigned(runtime->format); - if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) - return err; - } - return 0; +static int snd_gf1_pcm_playback_copy_kernel(struct snd_pcm_substream *substream, + int voice, unsigned long pos, + void *src, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct gus_pcm_private *pcmp = runtime->private_data; + unsigned int len = count; + int bpos; + + bpos = get_bpos(pcmp, voice, pos, len); + if (bpos < 0) + return pos; + memcpy(runtime->dma_area + bpos, src, len); + return playback_copy_ack(substream, bpos, len); } static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream, - int voice, - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int voice, unsigned long pos, + unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; - unsigned int bpos, len; + unsigned int len = count; + int bpos; - bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); - len = samples_to_bytes(runtime, count); - if (snd_BUG_ON(bpos > pcmp->dma_size)) - return -EIO; - if (snd_BUG_ON(bpos + len > pcmp->dma_size)) - return -EIO; - snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count); - if (snd_gf1_pcm_use_dma && len > 32) { - return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); - } else { - struct snd_gus_card *gus = pcmp->gus; - int err, w16, invert; - - w16 = (snd_pcm_format_width(runtime->format) == 16); - invert = snd_pcm_format_unsigned(runtime->format); - if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0) - return err; - } - return 0; + bpos = get_bpos(pcmp, voice, pos, len); + if (bpos < 0) + return pos; + snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, + bytes_to_samples(runtime, count)); + return playback_copy_ack(substream, bpos, len); } static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream, @@ -547,14 +562,14 @@ static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream * return pos; } -static struct snd_ratnum clock = { +static const struct snd_ratnum clock = { .num = 9878400/16, .den_min = 2, .den_max = 257, .den_step = 1, }; -static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { +static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { .nrats = 1, .rats = &clock, }; @@ -809,7 +824,7 @@ static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ return change; } -static struct snd_kcontrol_new snd_gf1_pcm_volume_control = +static const struct snd_kcontrol_new snd_gf1_pcm_volume_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", @@ -818,7 +833,7 @@ static struct snd_kcontrol_new snd_gf1_pcm_volume_control = .put = snd_gf1_pcm_volume_put }; -static struct snd_kcontrol_new snd_gf1_pcm_volume_control1 = +static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "GPCM Playback Volume", @@ -836,8 +851,9 @@ static struct snd_pcm_ops snd_gf1_pcm_playback_ops = { .prepare = snd_gf1_pcm_playback_prepare, .trigger = snd_gf1_pcm_playback_trigger, .pointer = snd_gf1_pcm_playback_pointer, - .copy = snd_gf1_pcm_playback_copy, - .silence = snd_gf1_pcm_playback_silence, + .copy_user = snd_gf1_pcm_playback_copy, + .copy_kernel = snd_gf1_pcm_playback_copy_kernel, + .fill_silence = snd_gf1_pcm_playback_silence, }; static struct snd_pcm_ops snd_gf1_pcm_capture_ops = { diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c index ec180708f160..d56973b770c7 100644 --- a/sound/isa/sb/emu8000.c +++ b/sound/isa/sb/emu8000.c @@ -1138,7 +1138,7 @@ snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports, snd_emu8000_free(hw); return err; } -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000, sizeof(struct snd_emu8000*), &awe) >= 0) { strcpy(awe->name, "EMU-8000"); diff --git a/sound/isa/sb/emu8000_callback.c b/sound/isa/sb/emu8000_callback.c index 72a9ac5efb40..d28d712f99f4 100644 --- a/sound/isa/sb/emu8000_callback.c +++ b/sound/isa/sb/emu8000_callback.c @@ -36,7 +36,7 @@ static void reset_voice(struct snd_emux *emu, int ch); static void terminate_voice(struct snd_emux_voice *vp); static void sysex(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_channel_set *chset); -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) static int oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2); #endif static int load_fx(struct snd_emux *emu, int type, int mode, @@ -76,7 +76,7 @@ static struct snd_emux_operators emu8000_ops = { .sample_reset = snd_emu8000_sample_reset, .load_fx = load_fx, .sysex = sysex, -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) .oss_ioctl = oss_ioctl, #endif }; @@ -477,7 +477,7 @@ sysex(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_chan } -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) /* * OSS ioctl callback */ diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c index 32f234f494e5..2ee8d67871ec 100644 --- a/sound/isa/sb/emu8000_pcm.c +++ b/sound/isa/sb/emu8000_pcm.c @@ -422,143 +422,148 @@ do { \ return -EAGAIN;\ } while (0) +enum { + COPY_USER, COPY_KERNEL, FILL_SILENCE, +}; + +#define GET_VAL(sval, buf, mode) \ + do { \ + switch (mode) { \ + case FILL_SILENCE: \ + sval = 0; \ + break; \ + case COPY_KERNEL: \ + sval = *buf++; \ + break; \ + default: \ + if (get_user(sval, (unsigned short __user *)buf)) \ + return -EFAULT; \ + buf++; \ + break; \ + } \ + } while (0) #ifdef USE_NONINTERLEAVE + +#define LOOP_WRITE(rec, offset, _buf, count, mode) \ + do { \ + struct snd_emu8000 *emu = (rec)->emu; \ + unsigned short *buf = (unsigned short *)(_buf); \ + snd_emu8000_write_wait(emu, 1); \ + EMU8000_SMALW_WRITE(emu, offset); \ + while (count > 0) { \ + unsigned short sval; \ + CHECK_SCHEDULER(); \ + GET_VAL(sval, buf, mode); \ + EMU8000_SMLD_WRITE(emu, sval); \ + count--; \ + } \ + } while (0) + /* copy one channel block */ -static int emu8k_transfer_block(struct snd_emu8000 *emu, int offset, unsigned short *buf, int count) +static int emu8k_pcm_copy(struct snd_pcm_substream *subs, + int voice, unsigned long pos, + void __user *src, unsigned long count) { - EMU8000_SMALW_WRITE(emu, offset); - while (count > 0) { - unsigned short sval; - CHECK_SCHEDULER(); - if (get_user(sval, buf)) - return -EFAULT; - EMU8000_SMLD_WRITE(emu, sval); - buf++; - count--; - } + struct snd_emu8k_pcm *rec = subs->runtime->private_data; + + /* convert to word unit */ + pos = (pos << 1) + rec->loop_start[voice]; + count <<= 1; + LOOP_WRITE(rec, pos, src, count, COPY_UESR); return 0; } -static int emu8k_pcm_copy(struct snd_pcm_substream *subs, - int voice, - snd_pcm_uframes_t pos, - void *src, - snd_pcm_uframes_t count) +static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs, + int voice, unsigned long pos, + void *src, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; - struct snd_emu8000 *emu = rec->emu; - - snd_emu8000_write_wait(emu, 1); - if (voice == -1) { - unsigned short *buf = src; - int i, err; - count /= rec->voices; - for (i = 0; i < rec->voices; i++) { - err = emu8k_transfer_block(emu, pos + rec->loop_start[i], buf, count); - if (err < 0) - return err; - buf += count; - } - return 0; - } else { - return emu8k_transfer_block(emu, pos + rec->loop_start[voice], src, count); - } -} -/* make a channel block silence */ -static int emu8k_silence_block(struct snd_emu8000 *emu, int offset, int count) -{ - EMU8000_SMALW_WRITE(emu, offset); - while (count > 0) { - CHECK_SCHEDULER(); - EMU8000_SMLD_WRITE(emu, 0); - count--; - } + /* convert to word unit */ + pos = (pos << 1) + rec->loop_start[voice]; + count <<= 1; + LOOP_WRITE(rec, pos, src, count, COPY_KERNEL); return 0; } +/* make a channel block silence */ static int emu8k_pcm_silence(struct snd_pcm_substream *subs, - int voice, - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int voice, unsigned long pos, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; - struct snd_emu8000 *emu = rec->emu; - - snd_emu8000_write_wait(emu, 1); - if (voice == -1 && rec->voices == 1) - voice = 0; - if (voice == -1) { - int err; - err = emu8k_silence_block(emu, pos + rec->loop_start[0], count / 2); - if (err < 0) - return err; - return emu8k_silence_block(emu, pos + rec->loop_start[1], count / 2); - } else { - return emu8k_silence_block(emu, pos + rec->loop_start[voice], count); - } + + /* convert to word unit */ + pos = (pos << 1) + rec->loop_start[voice]; + count <<= 1; + LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE); + return 0; } #else /* interleave */ +#define LOOP_WRITE(rec, pos, _buf, count, mode) \ + do { \ + struct snd_emu8000 *emu = rec->emu; \ + unsigned short *buf = (unsigned short *)(_buf); \ + snd_emu8000_write_wait(emu, 1); \ + EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \ + if (rec->voices > 1) \ + EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); \ + while (count > 0) { \ + unsigned short sval; \ + CHECK_SCHEDULER(); \ + GET_VAL(sval, buf, mode); \ + EMU8000_SMLD_WRITE(emu, sval); \ + if (rec->voices > 1) { \ + CHECK_SCHEDULER(); \ + GET_VAL(sval, buf, mode); \ + EMU8000_SMRD_WRITE(emu, sval); \ + } \ + count--; \ + } \ + } while (0) + + /* * copy the interleaved data can be done easily by using * DMA "left" and "right" channels on emu8k engine. */ static int emu8k_pcm_copy(struct snd_pcm_substream *subs, - int voice, - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) + int voice, unsigned long pos, + void __user *src, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; - struct snd_emu8000 *emu = rec->emu; - unsigned short __user *buf = src; - snd_emu8000_write_wait(emu, 1); - EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); - if (rec->voices > 1) - EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); - - while (count-- > 0) { - unsigned short sval; - CHECK_SCHEDULER(); - if (get_user(sval, buf)) - return -EFAULT; - EMU8000_SMLD_WRITE(emu, sval); - buf++; - if (rec->voices > 1) { - CHECK_SCHEDULER(); - if (get_user(sval, buf)) - return -EFAULT; - EMU8000_SMRD_WRITE(emu, sval); - buf++; - } - } + /* convert to frames */ + pos = bytes_to_frames(subs->runtime, pos); + count = bytes_to_frames(subs->runtime, count); + LOOP_WRITE(rec, pos, src, count, COPY_USER); + return 0; +} + +static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs, + int voice, unsigned long pos, + void *src, unsigned long count) +{ + struct snd_emu8k_pcm *rec = subs->runtime->private_data; + + /* convert to frames */ + pos = bytes_to_frames(subs->runtime, pos); + count = bytes_to_frames(subs->runtime, count); + LOOP_WRITE(rec, pos, src, count, COPY_KERNEL); return 0; } static int emu8k_pcm_silence(struct snd_pcm_substream *subs, - int voice, - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int voice, unsigned long pos, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; - struct snd_emu8000 *emu = rec->emu; - snd_emu8000_write_wait(emu, 1); - EMU8000_SMALW_WRITE(emu, rec->loop_start[0] + pos); - if (rec->voices > 1) - EMU8000_SMARW_WRITE(emu, rec->loop_start[1] + pos); - while (count-- > 0) { - CHECK_SCHEDULER(); - EMU8000_SMLD_WRITE(emu, 0); - if (rec->voices > 1) { - CHECK_SCHEDULER(); - EMU8000_SMRD_WRITE(emu, 0); - } - } + /* convert to frames */ + pos = bytes_to_frames(subs->runtime, pos); + count = bytes_to_frames(subs->runtime, count); + LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE); return 0; } #endif @@ -674,8 +679,9 @@ static struct snd_pcm_ops emu8k_pcm_ops = { .prepare = emu8k_pcm_prepare, .trigger = emu8k_pcm_trigger, .pointer = emu8k_pcm_pointer, - .copy = emu8k_pcm_copy, - .silence = emu8k_pcm_silence, + .copy_user = emu8k_pcm_copy, + .copy_kernel = emu8k_pcm_copy_kernel, + .fill_silence = emu8k_pcm_silence, }; diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c index 3b2e4f405ff2..917a93d696c3 100644 --- a/sound/isa/sb/sb16.c +++ b/sound/isa/sb/sb16.c @@ -62,7 +62,7 @@ MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB AWE 32}," #define SNDRV_DEBUG_IRQ #endif -#if defined(SNDRV_SBAWE) && (defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))) +#if defined(SNDRV_SBAWE) && IS_ENABLED(CONFIG_SND_SEQUENCER) #define SNDRV_SBAWE_EMU8000 #endif diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index 48da2276683d..fa5780bb0c68 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -1029,7 +1029,7 @@ static int snd_sb_qsound_space_put(struct snd_kcontrol *kcontrol, struct snd_ctl return change; } -static struct snd_kcontrol_new snd_sb_qsound_switch = { +static const struct snd_kcontrol_new snd_sb_qsound_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "3D Control - Switch", .info = snd_sb_qsound_switch_info, @@ -1037,7 +1037,7 @@ static struct snd_kcontrol_new snd_sb_qsound_switch = { .put = snd_sb_qsound_switch_put }; -static struct snd_kcontrol_new snd_sb_qsound_space = { +static const struct snd_kcontrol_new snd_sb_qsound_space = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "3D Control - Space", .info = snd_sb_qsound_space_info, diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index 8b2d6c6bfe97..4be1350f6649 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -737,7 +737,7 @@ static int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ct return change; } -static struct snd_kcontrol_new snd_sb16_dma_control = { +static const struct snd_kcontrol_new snd_sb16_dma_control = { .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = "16-bit DMA Allocation", .info = snd_sb16_dma_control_info, diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c index 9043397fe62f..0f302be4fb6d 100644 --- a/sound/isa/sb/sb8_main.c +++ b/sound/isa/sb/sb8_main.c @@ -46,19 +46,19 @@ MODULE_LICENSE("GPL"); #define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v)) #define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v)) -static struct snd_ratnum clock = { +static const struct snd_ratnum clock = { .num = SB8_CLOCK, .den_min = 1, .den_max = 256, .den_step = 1, }; -static struct snd_pcm_hw_constraint_ratnums hw_constraints_clock = { +static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clock = { .nrats = 1, .rats = &clock, }; -static struct snd_ratnum stereo_clocks[] = { +static const struct snd_ratnum stereo_clocks[] = { { .num = SB8_CLOCK, .den_min = SB8_DEN(22050), diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 54f5758a1bb3..1cd2908e4f12 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -671,7 +671,7 @@ __skip_change: return change; } -static struct snd_kcontrol_new midi_mixer_ctl = { +static const struct snd_kcontrol_new midi_mixer_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "MIDI", .info = sscape_midi_info, diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c index 913b731d2236..9f4e2e34cbe1 100644 --- a/sound/isa/wss/wss_lib.c +++ b/sound/isa/wss/wss_lib.c @@ -69,12 +69,12 @@ static unsigned char freq_bits[14] = { /* 48000 */ 0x0C | CS4231_XTAL1 }; -static unsigned int rates[14] = { +static const unsigned int rates[14] = { 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, 27042, 32000, 33075, 37800, 44100, 48000 }; -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { +static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c index 00fc9241d266..3318c15e324a 100644 --- a/sound/mips/hal2.c +++ b/sound/mips/hal2.c @@ -264,7 +264,7 @@ static int hal2_gain_put(struct snd_kcontrol *kcontrol, return old != new; } -static struct snd_kcontrol_new hal2_ctrl_headphone = { +static const struct snd_kcontrol_new hal2_ctrl_headphone = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -274,7 +274,7 @@ static struct snd_kcontrol_new hal2_ctrl_headphone = { .put = hal2_gain_put, }; -static struct snd_kcontrol_new hal2_ctrl_mic = { +static const struct snd_kcontrol_new hal2_ctrl_mic = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic Capture Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -461,15 +461,15 @@ static int hal2_alloc_dmabuf(struct hal2_codec *codec) int count = H2_BUF_SIZE / H2_BLOCK_SIZE; int i; - codec->buffer = dma_alloc_noncoherent(NULL, H2_BUF_SIZE, - &buffer_dma, GFP_KERNEL); + codec->buffer = dma_alloc_attrs(NULL, H2_BUF_SIZE, &buffer_dma, + GFP_KERNEL, DMA_ATTR_NON_CONSISTENT); if (!codec->buffer) return -ENOMEM; - desc = dma_alloc_noncoherent(NULL, count * sizeof(struct hal2_desc), - &desc_dma, GFP_KERNEL); + desc = dma_alloc_attrs(NULL, count * sizeof(struct hal2_desc), + &desc_dma, GFP_KERNEL, DMA_ATTR_NON_CONSISTENT); if (!desc) { - dma_free_noncoherent(NULL, H2_BUF_SIZE, - codec->buffer, buffer_dma); + dma_free_attrs(NULL, H2_BUF_SIZE, codec->buffer, buffer_dma, + DMA_ATTR_NON_CONSISTENT); return -ENOMEM; } codec->buffer_dma = buffer_dma; @@ -490,10 +490,10 @@ static int hal2_alloc_dmabuf(struct hal2_codec *codec) static void hal2_free_dmabuf(struct hal2_codec *codec) { - dma_free_noncoherent(NULL, codec->desc_count * sizeof(struct hal2_desc), - codec->desc, codec->desc_dma); - dma_free_noncoherent(NULL, H2_BUF_SIZE, codec->buffer, - codec->buffer_dma); + dma_free_attrs(NULL, codec->desc_count * sizeof(struct hal2_desc), + codec->desc, codec->desc_dma, DMA_ATTR_NON_CONSISTENT); + dma_free_attrs(NULL, H2_BUF_SIZE, codec->buffer, codec->buffer_dma, + DMA_ATTR_NON_CONSISTENT); } static struct snd_pcm_hardware hal2_pcm_hw = { @@ -616,10 +616,9 @@ static int hal2_playback_ack(struct snd_pcm_substream *substream) struct hal2_codec *dac = &hal2->dac; dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2; - snd_pcm_indirect_playback_transfer(substream, - &dac->pcm_indirect, - hal2_playback_transfer); - return 0; + return snd_pcm_indirect_playback_transfer(substream, + &dac->pcm_indirect, + hal2_playback_transfer); } static int hal2_capture_open(struct snd_pcm_substream *substream) @@ -707,10 +706,9 @@ static int hal2_capture_ack(struct snd_pcm_substream *substream) struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream); struct hal2_codec *adc = &hal2->adc; - snd_pcm_indirect_capture_transfer(substream, - &adc->pcm_indirect, - hal2_capture_transfer); - return 0; + return snd_pcm_indirect_capture_transfer(substream, + &adc->pcm_indirect, + hal2_capture_transfer); } static struct snd_pcm_ops hal2_playback_ops = { diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c index f07aa3993f83..0ebc1c3727df 100644 --- a/sound/mips/sgio2audio.c +++ b/sound/mips/sgio2audio.c @@ -230,7 +230,7 @@ static int sgio2audio_source_put(struct snd_kcontrol *kcontrol, } /* dac1/pcm0 mixer control */ -static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 = { +static const struct snd_kcontrol_new sgio2audio_ctrl_pcm0 = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .index = 0, @@ -242,7 +242,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 = { }; /* dac2/pcm1 mixer control */ -static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 = { +static const struct snd_kcontrol_new sgio2audio_ctrl_pcm1 = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .index = 1, @@ -254,7 +254,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 = { }; /* record level mixer control */ -static struct snd_kcontrol_new sgio2audio_ctrl_reclevel = { +static const struct snd_kcontrol_new sgio2audio_ctrl_reclevel = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -265,7 +265,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_reclevel = { }; /* record level source control */ -static struct snd_kcontrol_new sgio2audio_ctrl_recsource = { +static const struct snd_kcontrol_new sgio2audio_ctrl_recsource = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, @@ -275,7 +275,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_recsource = { }; /* line mixer control */ -static struct snd_kcontrol_new sgio2audio_ctrl_line = { +static const struct snd_kcontrol_new sgio2audio_ctrl_line = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line Playback Volume", .index = 0, @@ -287,7 +287,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_line = { }; /* cd mixer control */ -static struct snd_kcontrol_new sgio2audio_ctrl_cd = { +static const struct snd_kcontrol_new sgio2audio_ctrl_cd = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line Playback Volume", .index = 1, @@ -299,7 +299,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_cd = { }; /* mic mixer control */ -static struct snd_kcontrol_new sgio2audio_ctrl_mic = { +static const struct snd_kcontrol_new sgio2audio_ctrl_mic = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index 99b64cb3cef8..5911eb35c2a3 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -83,14 +83,14 @@ MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable); #define NAME "harmony" #define PFX NAME ": " -static unsigned int snd_harmony_rates[] = { +static const unsigned int snd_harmony_rates[] = { 5512, 6615, 8000, 9600, 11025, 16000, 18900, 22050, 27428, 32000, 33075, 37800, 44100, 48000 }; -static unsigned int rate_bits[14] = { +static const unsigned int rate_bits[14] = { HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ, HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ, HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ, @@ -98,7 +98,7 @@ static unsigned int rate_bits[14] = { HARMONY_SR_44KHZ, HARMONY_SR_48KHZ }; -static struct snd_pcm_hw_constraint_list hw_constraint_rates = { +static const struct snd_pcm_hw_constraint_list hw_constraint_rates = { .count = ARRAY_SIZE(snd_harmony_rates), .list = snd_harmony_rates, .mask = 0, diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 32151d8c6bb8..d9f3fdb777e4 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -465,6 +465,7 @@ config SND_EMU10K1 select SND_RAWMIDI select SND_AC97_CODEC select SND_TIMER + select SND_SEQ_DEVICE if SND_SEQUENCER != n depends on ZONE_DMA help Say Y to include support for Sound Blaster PCI 512, Live!, @@ -477,6 +478,13 @@ config SND_EMU10K1 To compile this driver as a module, choose M here: the module will be called snd-emu10k1. +# select SEQ stuff to min(SND_SEQUENCER,SND_XXX) +config SND_EMU10K1_SEQ + def_tristate SND_SEQUENCER && SND_EMU10K1 + select SND_SEQ_MIDI_EMUL + select SND_SEQ_VIRMIDI + select SND_SYNTH_EMUX + config SND_EMU10K1X tristate "Emu10k1X (Dell OEM Version)" select SND_AC97_CODEC diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 34bbc2e730a6..8567f1e5b9cf 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1602,8 +1602,8 @@ static struct snd_pcm_hardware snd_ali_modem = static int snd_ali_modem_open(struct snd_pcm_substream *substream, int rec, int channel) { - static unsigned int rates[] = {8000, 9600, 12000, 16000}; - static struct snd_pcm_hw_constraint_list hw_constraint_rates = { + static const unsigned int rates[] = {8000, 9600, 12000, 16000}; + static const struct snd_pcm_hw_constraint_list hw_constraint_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 40152feef1e7..52e0ea7b9b80 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -860,8 +860,8 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream, struct atiixp_modem *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; - static unsigned int rates[] = { 8000, 9600, 12000, 16000 }; - static struct snd_pcm_hw_constraint_list hw_constraints_rates = { + static const unsigned int rates[] = { 8000, 9600, 12000, 16000 }; + static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 335979a753fe..1aa97012451d 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -112,11 +112,11 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_wt = { }; #endif #ifdef CHIP_AU8830 -static unsigned int au8830_channels[3] = { +static const unsigned int au8830_channels[3] = { 1, 2, 4, }; -static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = { +static const struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = { .count = ARRAY_SIZE(au8830_channels), .list = au8830_channels, .mask = 0, diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 79b2e6b7d88b..fc18c29a8173 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -2014,7 +2014,7 @@ static const struct snd_pcm_hardware snd_azf3328_hardware = }; -static unsigned int snd_azf3328_fixed_rates[] = { +static const unsigned int snd_azf3328_fixed_rates[] = { AZF_FREQ_4000, AZF_FREQ_4800, AZF_FREQ_5512, @@ -2031,7 +2031,7 @@ static unsigned int snd_azf3328_fixed_rates[] = { AZF_FREQ_66200 }; -static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = { +static const struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = { .count = ARRAY_SIZE(snd_azf3328_fixed_rates), .list = snd_azf3328_fixed_rates, .mask = 0, diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 099efb046b1c..de0234294c25 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -401,13 +401,13 @@ static int snd_bt87x_set_digital_hw(struct snd_bt87x *chip, struct snd_pcm_runti static int snd_bt87x_set_analog_hw(struct snd_bt87x *chip, struct snd_pcm_runtime *runtime) { - static struct snd_ratnum analog_clock = { + static const struct snd_ratnum analog_clock = { .num = ANALOG_CLOCK, .den_min = CLOCK_DIV_MIN, .den_max = CLOCK_DIV_MAX, .den_step = 1 }; - static struct snd_pcm_hw_constraint_ratnums constraint_rates = { + static const struct snd_pcm_hw_constraint_ratnums constraint_rates = { .nrats = 1, .rats = &analog_clock }; diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 745a0a3743b4..a460cb63e971 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -719,18 +719,18 @@ static int snd_cmipci_hw_free(struct snd_pcm_substream *substream) /* */ -static unsigned int hw_channels[] = {1, 2, 4, 6, 8}; -static struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = { +static const unsigned int hw_channels[] = {1, 2, 4, 6, 8}; +static const struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = { .count = 3, .list = hw_channels, .mask = 0, }; -static struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = { +static const struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = { .count = 4, .list = hw_channels, .mask = 0, }; -static struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = { +static const struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = { .count = 5, .list = hw_channels, .mask = 0, @@ -1597,9 +1597,9 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif = .fifo_size = 0, }; -static unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050, +static const unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 128000 }; -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { +static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rate_constraints), .list = rate_constraints, .mask = 0, diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index f870697aca67..ee7ba4b0b47b 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1296,7 +1296,7 @@ static void snd_cs4281_free_gameport(struct cs4281 *chip) #else static inline int snd_cs4281_create_gameport(struct cs4281 *chip) { return -ENOSYS; } static inline void snd_cs4281_free_gameport(struct cs4281 *chip) { } -#endif /* CONFIG_GAMEPORT || (MODULE && CONFIG_GAMEPORT_MODULE) */ +#endif /* IS_REACHABLE(CONFIG_GAMEPORT) */ static int snd_cs4281_free(struct cs4281 *chip) { diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index e4cf3187b4dd..709fb1a503b7 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -887,8 +887,8 @@ static int snd_cs46xx_playback_transfer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_cs46xx_pcm * cpcm = runtime->private_data; - snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec, snd_cs46xx_pb_trans_copy); - return 0; + return snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec, + snd_cs46xx_pb_trans_copy); } static void snd_cs46xx_cp_trans_copy(struct snd_pcm_substream *substream, @@ -903,8 +903,8 @@ static void snd_cs46xx_cp_trans_copy(struct snd_pcm_substream *substream, static int snd_cs46xx_capture_transfer(struct snd_pcm_substream *substream) { struct snd_cs46xx *chip = snd_pcm_substream_chip(substream); - snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec, snd_cs46xx_cp_trans_copy); - return 0; + return snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec, + snd_cs46xx_cp_trans_copy); } static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(struct snd_pcm_substream *substream) @@ -1482,9 +1482,9 @@ static struct snd_pcm_hardware snd_cs46xx_capture = #ifdef CONFIG_SND_CS46XX_NEW_DSP -static unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 }; +static const unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 }; -static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = { +static const struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = { .count = ARRAY_SIZE(period_sizes), .list = period_sizes, .mask = 0 @@ -2371,7 +2371,7 @@ static int snd_cs46xx_front_dup_put(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] ? 0 : 0x200); } -static struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = { +static const struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Duplicate Front", .info = snd_mixer_boolean_info, diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 6a0e49ac5273..d3203df50a1a 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -37,7 +37,7 @@ MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS}," "{Creative Labs,SB Audigy}}"); -#if IS_REACHABLE(CONFIG_SND_SEQUENCER) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) #define ENABLE_SYNTH #include <sound/emu10k1_synth.h> #endif diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 56fc47bd6dba..dc585959ca32 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -311,21 +311,6 @@ static const u32 onoff_table[2] = { }; /* - */ - -static inline mm_segment_t snd_enter_user(void) -{ - mm_segment_t fs = get_fs(); - set_fs(get_ds()); - return fs; -} - -static inline void snd_leave_user(mm_segment_t fs) -{ - set_fs(fs); -} - -/* * controls */ @@ -538,7 +523,8 @@ unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc) } static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu, - struct snd_emu10k1_fx8010_code *icode) + struct snd_emu10k1_fx8010_code *icode, + bool in_kernel) { int gpr; u32 val; @@ -546,7 +532,9 @@ static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu, for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) { if (!test_bit(gpr, icode->gpr_valid)) continue; - if (get_user(val, &icode->gpr_map[gpr])) + if (in_kernel) + val = *(u32 *)&icode->gpr_map[gpr]; + else if (get_user(val, &icode->gpr_map[gpr])) return -EFAULT; snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, val); } @@ -569,7 +557,8 @@ static int snd_emu10k1_gpr_peek(struct snd_emu10k1 *emu, } static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu, - struct snd_emu10k1_fx8010_code *icode) + struct snd_emu10k1_fx8010_code *icode, + bool in_kernel) { int tram; u32 addr, val; @@ -577,9 +566,14 @@ static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu, for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) { if (!test_bit(tram, icode->tram_valid)) continue; - if (get_user(val, &icode->tram_data_map[tram]) || - get_user(addr, &icode->tram_addr_map[tram])) - return -EFAULT; + if (in_kernel) { + val = *(u32 *)&icode->tram_data_map[tram]; + addr = *(u32 *)&icode->tram_addr_map[tram]; + } else { + if (get_user(val, &icode->tram_data_map[tram]) || + get_user(addr, &icode->tram_addr_map[tram])) + return -EFAULT; + } snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, val); if (!emu->audigy) { snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr); @@ -615,16 +609,22 @@ static int snd_emu10k1_tram_peek(struct snd_emu10k1 *emu, } static int snd_emu10k1_code_poke(struct snd_emu10k1 *emu, - struct snd_emu10k1_fx8010_code *icode) + struct snd_emu10k1_fx8010_code *icode, + bool in_kernel) { u32 pc, lo, hi; for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) { if (!test_bit(pc / 2, icode->code_valid)) continue; - if (get_user(lo, &icode->code[pc + 0]) || - get_user(hi, &icode->code[pc + 1])) - return -EFAULT; + if (in_kernel) { + lo = *(u32 *)&icode->code[pc + 0]; + hi = *(u32 *)&icode->code[pc + 1]; + } else { + if (get_user(lo, &icode->code[pc + 0]) || + get_user(hi, &icode->code[pc + 1])) + return -EFAULT; + } snd_emu10k1_efx_write(emu, pc + 0, lo); snd_emu10k1_efx_write(emu, pc + 1, hi); } @@ -665,14 +665,16 @@ snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id) #define MAX_TLV_SIZE 256 -static unsigned int *copy_tlv(const unsigned int __user *_tlv) +static unsigned int *copy_tlv(const unsigned int __user *_tlv, bool in_kernel) { unsigned int data[2]; unsigned int *tlv; if (!_tlv) return NULL; - if (copy_from_user(data, _tlv, sizeof(data))) + if (in_kernel) + memcpy(data, (void *)_tlv, sizeof(data)); + else if (copy_from_user(data, _tlv, sizeof(data))) return NULL; if (data[1] >= MAX_TLV_SIZE) return NULL; @@ -680,7 +682,9 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv) if (!tlv) return NULL; memcpy(tlv, data, sizeof(data)); - if (copy_from_user(tlv + 2, _tlv + 2, data[1])) { + if (in_kernel) { + memcpy(tlv + 2, (void *)(_tlv + 2), data[1]); + } else if (copy_from_user(tlv + 2, _tlv + 2, data[1])) { kfree(tlv); return NULL; } @@ -690,7 +694,7 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv) static int copy_gctl(struct snd_emu10k1 *emu, struct snd_emu10k1_fx8010_control_gpr *gctl, struct snd_emu10k1_fx8010_control_gpr __user *_gctl, - int idx) + int idx, bool in_kernel) { struct snd_emu10k1_fx8010_control_old_gpr __user *octl; @@ -718,7 +722,8 @@ static int copy_gctl_to_user(struct snd_emu10k1 *emu, } static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, - struct snd_emu10k1_fx8010_code *icode) + struct snd_emu10k1_fx8010_code *icode, + bool in_kernel) { unsigned int i; struct snd_ctl_elem_id __user *_id; @@ -728,7 +733,9 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, for (i = 0, _id = icode->gpr_del_controls; i < icode->gpr_del_control_count; i++, _id++) { - if (copy_from_user(&id, _id, sizeof(id))) + if (in_kernel) + id = *(struct snd_ctl_elem_id *)_id; + else if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; if (snd_emu10k1_look_for_ctl(emu, &id) == NULL) return -ENOENT; @@ -738,7 +745,8 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, return -ENOMEM; err = 0; for (i = 0; i < icode->gpr_add_control_count; i++) { - if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) { + if (copy_gctl(emu, gctl, icode->gpr_add_controls, i, + in_kernel)) { err = -EFAULT; goto __error; } @@ -759,7 +767,8 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, } for (i = 0; i < icode->gpr_list_control_count; i++) { /* FIXME: we need to check the WRITE access */ - if (copy_gctl(emu, gctl, icode->gpr_list_controls, i)) { + if (copy_gctl(emu, gctl, icode->gpr_list_controls, i, + in_kernel)) { err = -EFAULT; goto __error; } @@ -781,7 +790,8 @@ static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl) } static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, - struct snd_emu10k1_fx8010_code *icode) + struct snd_emu10k1_fx8010_code *icode, + bool in_kernel) { unsigned int i, j; struct snd_emu10k1_fx8010_control_gpr *gctl; @@ -800,7 +810,8 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, } for (i = 0; i < icode->gpr_add_control_count; i++) { - if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) { + if (copy_gctl(emu, gctl, icode->gpr_add_controls, i, + in_kernel)) { err = -EFAULT; goto __error; } @@ -821,7 +832,7 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, knew.device = gctl->id.device; knew.subdevice = gctl->id.subdevice; knew.info = snd_emu10k1_gpr_ctl_info; - knew.tlv.p = copy_tlv(gctl->tlv); + knew.tlv.p = copy_tlv(gctl->tlv, in_kernel); if (knew.tlv.p) knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ; @@ -873,7 +884,8 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, } static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu, - struct snd_emu10k1_fx8010_code *icode) + struct snd_emu10k1_fx8010_code *icode, + bool in_kernel) { unsigned int i; struct snd_ctl_elem_id id; @@ -883,7 +895,9 @@ static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu, for (i = 0, _id = icode->gpr_del_controls; i < icode->gpr_del_control_count; i++, _id++) { - if (copy_from_user(&id, _id, sizeof(id))) + if (in_kernel) + id = *(struct snd_ctl_elem_id *)_id; + else if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; down_write(&card->controls_rwsem); ctl = snd_emu10k1_look_for_ctl(emu, &id); @@ -941,12 +955,14 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu, } static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu, - struct snd_emu10k1_fx8010_code *icode) + struct snd_emu10k1_fx8010_code *icode, + bool in_kernel) { int err = 0; mutex_lock(&emu->fx8010.lock); - if ((err = snd_emu10k1_verify_controls(emu, icode)) < 0) + err = snd_emu10k1_verify_controls(emu, icode, in_kernel); + if (err < 0) goto __error; strlcpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name)); /* stop FX processor - this may be dangerous, but it's better to miss @@ -956,11 +972,20 @@ static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu, else snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP); /* ok, do the main job */ - if ((err = snd_emu10k1_del_controls(emu, icode)) < 0 || - (err = snd_emu10k1_gpr_poke(emu, icode)) < 0 || - (err = snd_emu10k1_tram_poke(emu, icode)) < 0 || - (err = snd_emu10k1_code_poke(emu, icode)) < 0 || - (err = snd_emu10k1_add_controls(emu, icode)) < 0) + err = snd_emu10k1_del_controls(emu, icode, in_kernel); + if (err < 0) + goto __error; + err = snd_emu10k1_gpr_poke(emu, icode, in_kernel); + if (err < 0) + goto __error; + err = snd_emu10k1_tram_poke(emu, icode, in_kernel); + if (err < 0) + goto __error; + err = snd_emu10k1_code_poke(emu, icode, in_kernel); + if (err < 0) + goto __error; + err = snd_emu10k1_add_controls(emu, icode, in_kernel); + if (err < 0) goto __error; /* start FX processor when the DSP code is updated */ if (emu->audigy) @@ -1179,7 +1204,6 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) struct snd_emu10k1_fx8010_code *icode = NULL; struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl; u32 *gpr_map; - mm_segment_t seg; err = -ENOMEM; icode = kzalloc(sizeof(*icode), GFP_KERNEL); @@ -1739,13 +1763,11 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) while (ptr < 0x400) A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0); - seg = snd_enter_user(); icode->gpr_add_control_count = nctl; icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls; emu->support_tlv = 1; /* support TLV */ - err = snd_emu10k1_icode_poke(emu, icode); + err = snd_emu10k1_icode_poke(emu, icode, true); emu->support_tlv = 0; /* clear again */ - snd_leave_user(seg); __err: kfree(controls); @@ -1817,7 +1839,6 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) struct snd_emu10k1_fx8010_pcm_rec *ipcm = NULL; struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl; u32 *gpr_map; - mm_segment_t seg; err = -ENOMEM; icode = kzalloc(sizeof(*icode), GFP_KERNEL); @@ -2368,13 +2389,11 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0) goto __err; - seg = snd_enter_user(); icode->gpr_add_control_count = i; icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls; emu->support_tlv = 1; /* support TLV */ - err = snd_emu10k1_icode_poke(emu, icode); + err = snd_emu10k1_icode_poke(emu, icode, true); emu->support_tlv = 0; /* clear again */ - snd_leave_user(seg); if (err >= 0) err = snd_emu10k1_ipcm_poke(emu, ipcm); __err: @@ -2537,7 +2556,7 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un icode = memdup_user(argp, sizeof(*icode)); if (IS_ERR(icode)) return PTR_ERR(icode); - res = snd_emu10k1_icode_poke(emu, icode); + res = snd_emu10k1_icode_poke(emu, icode, false); kfree(icode); return res; case SNDRV_EMU10K1_IOCTL_CODE_PEEK: diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index ef1cf530c929..5c9054a9f69e 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -164,7 +164,7 @@ static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voic return 0; } -static unsigned int capture_period_sizes[31] = { +static const unsigned int capture_period_sizes[31] = { 384, 448, 512, 640, 384*2, 448*2, 512*2, 640*2, 384*4, 448*4, 512*4, 640*4, @@ -175,17 +175,17 @@ static unsigned int capture_period_sizes[31] = { 384*128,448*128,512*128 }; -static struct snd_pcm_hw_constraint_list hw_constraints_capture_period_sizes = { +static const struct snd_pcm_hw_constraint_list hw_constraints_capture_period_sizes = { .count = 31, .list = capture_period_sizes, .mask = 0 }; -static unsigned int capture_rates[8] = { +static const unsigned int capture_rates[8] = { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000 }; -static struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = { +static const struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = { .count = 8, .list = capture_rates, .mask = 0 @@ -1632,8 +1632,8 @@ static int snd_emu10k1_fx8010_playback_transfer(struct snd_pcm_substream *substr struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number]; - snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, fx8010_pb_trans_copy); - return 0; + return snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, + fx8010_pb_trans_copy); } static int snd_emu10k1_fx8010_playback_hw_params(struct snd_pcm_substream *substream, diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 09a63ef41ef2..f0d978e3b274 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -467,41 +467,41 @@ MODULE_DEVICE_TABLE(pci, snd_audiopci_ids); #define POLL_COUNT 0xa000 #ifdef CHIP1370 -static unsigned int snd_es1370_fixed_rates[] = +static const unsigned int snd_es1370_fixed_rates[] = {5512, 11025, 22050, 44100}; -static struct snd_pcm_hw_constraint_list snd_es1370_hw_constraints_rates = { +static const struct snd_pcm_hw_constraint_list snd_es1370_hw_constraints_rates = { .count = 4, .list = snd_es1370_fixed_rates, .mask = 0, }; -static struct snd_ratnum es1370_clock = { +static const struct snd_ratnum es1370_clock = { .num = ES_1370_SRCLOCK, .den_min = 29, .den_max = 353, .den_step = 1, }; -static struct snd_pcm_hw_constraint_ratnums snd_es1370_hw_constraints_clock = { +static const struct snd_pcm_hw_constraint_ratnums snd_es1370_hw_constraints_clock = { .nrats = 1, .rats = &es1370_clock, }; #else -static struct snd_ratden es1371_dac_clock = { +static const struct snd_ratden es1371_dac_clock = { .num_min = 3000 * (1 << 15), .num_max = 48000 * (1 << 15), .num_step = 3000, .den = 1 << 15, }; -static struct snd_pcm_hw_constraint_ratdens snd_es1371_hw_constraints_dac_clock = { +static const struct snd_pcm_hw_constraint_ratdens snd_es1371_hw_constraints_dac_clock = { .nrats = 1, .rats = &es1371_dac_clock, }; -static struct snd_ratnum es1371_adc_clock = { +static const struct snd_ratnum es1371_adc_clock = { .num = 48000 << 15, .den_min = 32768, .den_max = 393216, .den_step = 1, }; -static struct snd_pcm_hw_constraint_ratnums snd_es1371_hw_constraints_adc_clock = { +static const struct snd_pcm_hw_constraint_ratnums snd_es1371_hw_constraints_adc_clock = { .nrats = 1, .rats = &es1371_adc_clock, }; diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index e8d943071a8c..069902a06f00 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -436,7 +436,7 @@ static void snd_es1938_reset_fifo(struct es1938 *chip) outb(0, SLSB_REG(chip, RESET)); } -static struct snd_ratnum clocks[2] = { +static const struct snd_ratnum clocks[2] = { { .num = 793800, .den_min = 1, @@ -451,7 +451,7 @@ static struct snd_ratnum clocks[2] = { } }; -static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { +static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { .nrats = 2, .rats = clocks, }; @@ -839,15 +839,12 @@ static snd_pcm_uframes_t snd_es1938_playback_pointer(struct snd_pcm_substream *s } static int snd_es1938_capture_copy(struct snd_pcm_substream *substream, - int channel, - snd_pcm_uframes_t pos, - void __user *dst, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct es1938 *chip = snd_pcm_substream_chip(substream); - pos <<= chip->dma1_shift; - count <<= chip->dma1_shift; + if (snd_BUG_ON(pos + count > chip->dma1_size)) return -EINVAL; if (pos + count < chip->dma1_size) { @@ -856,12 +853,31 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream, } else { if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1)) return -EFAULT; - if (put_user(runtime->dma_area[0], ((unsigned char __user *)dst) + count - 1)) + if (put_user(runtime->dma_area[0], + ((unsigned char __user *)dst) + count - 1)) return -EFAULT; } return 0; } +static int snd_es1938_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct es1938 *chip = snd_pcm_substream_chip(substream); + + if (snd_BUG_ON(pos + count > chip->dma1_size)) + return -EINVAL; + if (pos + count < chip->dma1_size) { + memcpy(dst, runtime->dma_area + pos + 1, count); + } else { + memcpy(dst, runtime->dma_area + pos + 1, count - 1); + runtime->dma_area[0] = *((unsigned char *)dst + count - 1); + } + return 0; +} + /* * buffer management */ @@ -1012,7 +1028,8 @@ static const struct snd_pcm_ops snd_es1938_capture_ops = { .prepare = snd_es1938_capture_prepare, .trigger = snd_es1938_capture_trigger, .pointer = snd_es1938_capture_pointer, - .copy = snd_es1938_capture_copy, + .copy_user = snd_es1938_capture_copy, + .copy_kernel = snd_es1938_capture_copy_kernel, }; static int snd_es1938_new_pcm(struct es1938 *chip, int device) diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index c47287d79306..2e402ece4c86 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -334,23 +334,23 @@ static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short return fm801_readw(chip, AC97_DATA); } -static unsigned int rates[] = { +static const unsigned int rates[] = { 5500, 8000, 9600, 11025, 16000, 19200, 22050, 32000, 38400, 44100, 48000 }; -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { +static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; -static unsigned int channels[] = { +static const unsigned int channels[] = { 2, 4, 6 }; -static struct snd_pcm_hw_constraint_list hw_constraints_channels = { +static const struct snd_pcm_hw_constraint_list hw_constraints_channels = { .count = ARRAY_SIZE(channels), .list = channels, .mask = 0, diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 70bb365a08d2..821aad374a06 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -19,13 +19,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <linux/mm.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/module.h> -#include <linux/async.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <sound/core.h> @@ -1477,18 +1475,8 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put); -/** - * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume - * @kcontrol: ctl element - * @op_flag: operation flag - * @size: byte size of input TLV - * @_tlv: TLV data - * - * The control element is supposed to have the private_value field - * set up via HDA_COMPOSE_AMP_VAL*() or related macros. - */ -int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *_tlv) +/* inquiry the amp caps and convert to TLV */ +static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); @@ -1497,8 +1485,6 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, bool min_mute = get_amp_min_mute(kcontrol); u32 caps, val1, val2; - if (size < 4 * sizeof(unsigned int)) - return -ENOMEM; caps = query_amp_caps(codec, nid, dir); val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; val2 = (val2 + 1) * 25; @@ -1507,13 +1493,31 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, val1 = ((int)val1) * ((int)val2); if (min_mute || (caps & AC_AMPCAP_MIN_MUTE)) val2 |= TLV_DB_SCALE_MUTE; - if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) - return -EFAULT; - if (put_user(2 * sizeof(unsigned int), _tlv + 1)) - return -EFAULT; - if (put_user(val1, _tlv + 2)) - return -EFAULT; - if (put_user(val2, _tlv + 3)) + tlv[0] = SNDRV_CTL_TLVT_DB_SCALE; + tlv[1] = 2 * sizeof(unsigned int); + tlv[2] = val1; + tlv[3] = val2; +} + +/** + * snd_hda_mixer_amp_tlv - TLV callback for a standard AMP mixer volume + * @kcontrol: ctl element + * @op_flag: operation flag + * @size: byte size of input TLV + * @_tlv: TLV data + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ +int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *_tlv) +{ + unsigned int tlv[4]; + + if (size < 4 * sizeof(unsigned int)) + return -ENOMEM; + get_ctl_amp_tlv(kcontrol, tlv); + if (copy_to_user(_tlv, tlv, sizeof(tlv))) return -EFAULT; return 0; } @@ -1807,13 +1811,10 @@ static int get_kctl_0dB_offset(struct hda_codec *codec, const int *tlv = NULL; int val = -1; - if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - /* FIXME: set_fs() hack for obtaining user-space TLV data */ - mm_segment_t fs = get_fs(); - set_fs(get_ds()); - if (!kctl->tlv.c(kctl, 0, sizeof(_tlv), _tlv)) - tlv = _tlv; - set_fs(fs); + if ((kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) && + kctl->tlv.c == snd_hda_mixer_amp_tlv) { + get_ctl_amp_tlv(kctl, _tlv); + tlv = _tlv; } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) tlv = kctl->tlv.p; if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) { @@ -2118,196 +2119,6 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put); /* - * bound volume controls - * - * bind multiple volumes (# indices, from 0) - */ - -#define AMP_VAL_IDX_SHIFT 19 -#define AMP_VAL_IDX_MASK (0x0f<<19) - -/** - * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_MUTE*() macros. - */ -int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned long pval; - int err; - - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ - err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - return err; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_get); - -/** - * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_MUTE*() macros. - */ -int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned long pval; - int i, indices, err = 0, change = 0; - - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; - for (i = 0; i < indices; i++) { - kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | - (i << AMP_VAL_IDX_SHIFT); - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (err < 0) - break; - change |= err; - } - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - return err < 0 ? err : change; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_put); - -/** - * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control - * @kcontrol: referred ctl element - * @uinfo: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. - */ -int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_bind_ctls *c; - int err; - - mutex_lock(&codec->control_mutex); - c = (struct hda_bind_ctls *)kcontrol->private_value; - kcontrol->private_value = *c->values; - err = c->ops->info(kcontrol, uinfo); - kcontrol->private_value = (long)c; - mutex_unlock(&codec->control_mutex); - return err; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_info); - -/** - * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. - */ -int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_bind_ctls *c; - int err; - - mutex_lock(&codec->control_mutex); - c = (struct hda_bind_ctls *)kcontrol->private_value; - kcontrol->private_value = *c->values; - err = c->ops->get(kcontrol, ucontrol); - kcontrol->private_value = (long)c; - mutex_unlock(&codec->control_mutex); - return err; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_get); - -/** - * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control - * @kcontrol: ctl element - * @ucontrol: pointer to get/store the data - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. - */ -int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_bind_ctls *c; - unsigned long *vals; - int err = 0, change = 0; - - mutex_lock(&codec->control_mutex); - c = (struct hda_bind_ctls *)kcontrol->private_value; - for (vals = c->values; *vals; vals++) { - kcontrol->private_value = *vals; - err = c->ops->put(kcontrol, ucontrol); - if (err < 0) - break; - change |= err; - } - kcontrol->private_value = (long)c; - mutex_unlock(&codec->control_mutex); - return err < 0 ? err : change; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_put); - -/** - * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control - * @kcontrol: ctl element - * @op_flag: operation flag - * @size: byte size of input TLV - * @tlv: TLV data - * - * The control element is supposed to have the private_value field - * set up via HDA_BIND_VOL() macro. - */ -int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_bind_ctls *c; - int err; - - mutex_lock(&codec->control_mutex); - c = (struct hda_bind_ctls *)kcontrol->private_value; - kcontrol->private_value = *c->values; - err = c->ops->tlv(kcontrol, op_flag, size, tlv); - kcontrol->private_value = (long)c; - mutex_unlock(&codec->control_mutex); - return err; -} -EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_tlv); - -struct hda_ctl_ops snd_hda_bind_vol = { - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = snd_hda_mixer_amp_volume_put, - .tlv = snd_hda_mixer_amp_tlv -}; -EXPORT_SYMBOL_GPL(snd_hda_bind_vol); - -struct hda_ctl_ops snd_hda_bind_sw = { - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = snd_hda_mixer_amp_switch_put, - .tlv = snd_hda_mixer_amp_tlv -}; -EXPORT_SYMBOL_GPL(snd_hda_bind_sw); - -/* * SPDIF out controls */ diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 1c60beb5b70a..d1eb14842340 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1345,6 +1345,9 @@ int azx_codec_configure(struct azx *chip) list_for_each_codec_safe(codec, next, &chip->bus) { snd_hda_codec_configure(codec); } + + if (!azx_bus(chip)->num_codecs) + return -ENODEV; return 0; } EXPORT_SYMBOL_GPL(azx_codec_configure); diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 35a9ab2cac46..a68e75b00ea3 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -32,7 +32,11 @@ #define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */ #define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */ #define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */ -/* 13 unused */ +#ifdef CONFIG_SND_HDA_I915 +#define AZX_DCAPS_I915_COMPONENT (1 << 13) /* bind with i915 gfx */ +#else +#define AZX_DCAPS_I915_COMPONENT 0 /* NOP */ +#endif /* 14 unused */ #define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ #define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 71545b56b4c8..28e265a88383 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -948,6 +948,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_get(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); @@ -970,7 +972,7 @@ static const struct snd_kcontrol_new control_templates[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_bind_switch_get, + .get = hda_gen_bind_mute_get, .put = hda_gen_bind_mute_put, /* replaced */ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), }, @@ -1101,11 +1103,51 @@ static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); } +/* + * Bound mute controls + */ +#define AMP_VAL_IDX_SHIFT 19 +#define AMP_VAL_IDX_MASK (0x0f<<19) + +static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned long pval; + int err; + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */ + err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + return err; +} + static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned long pval; + int i, indices, err = 0, change = 0; + sync_auto_mute_bits(kcontrol, ucontrol); - return snd_hda_mixer_bind_switch_put(kcontrol, ucontrol); + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT; + for (i = 0; i < indices; i++) { + kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | + (i << AMP_VAL_IDX_SHIFT); + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (err < 0) + break; + change |= err; + } + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + return err < 0 ? err : change; } /* any ctl assigned to the path with the given index? */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 01eb1dc7b5b3..5ae8ddab6412 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -263,6 +263,7 @@ enum { AZX_DRIVER_ICH, AZX_DRIVER_PCH, AZX_DRIVER_SCH, + AZX_DRIVER_SKL, AZX_DRIVER_HDMI, AZX_DRIVER_ATI, AZX_DRIVER_ATIHDMI, @@ -292,38 +293,43 @@ enum { (AZX_DCAPS_NO_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\ AZX_DCAPS_SNOOP_TYPE(SCH)) -/* PCH up to IVB; no runtime PM */ +/* PCH up to IVB; no runtime PM; bind with i915 gfx */ #define AZX_DCAPS_INTEL_PCH_NOPM \ - (AZX_DCAPS_INTEL_PCH_BASE) + (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT) /* PCH for HSW/BDW; with runtime PM */ +/* no i915 binding for this as HSW/BDW has another controller for HDMI */ #define AZX_DCAPS_INTEL_PCH \ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME) /* HSW HDMI */ #define AZX_DCAPS_INTEL_HASWELL \ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\ - AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ - AZX_DCAPS_SNOOP_TYPE(SCH)) + AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\ + AZX_DCAPS_I915_POWERWELL | AZX_DCAPS_SNOOP_TYPE(SCH)) /* Broadwell HDMI can't use position buffer reliably, force to use LPIB */ #define AZX_DCAPS_INTEL_BROADWELL \ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_POSFIX_LPIB |\ - AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ - AZX_DCAPS_SNOOP_TYPE(SCH)) + AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\ + AZX_DCAPS_I915_POWERWELL | AZX_DCAPS_SNOOP_TYPE(SCH)) #define AZX_DCAPS_INTEL_BAYTRAIL \ - (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_I915_POWERWELL) + (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT |\ + AZX_DCAPS_I915_POWERWELL) #define AZX_DCAPS_INTEL_BRASWELL \ - (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_I915_POWERWELL) + (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ + AZX_DCAPS_I915_COMPONENT | AZX_DCAPS_I915_POWERWELL) #define AZX_DCAPS_INTEL_SKYLAKE \ - (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG |\ + (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ + AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT |\ AZX_DCAPS_I915_POWERWELL) #define AZX_DCAPS_INTEL_BROXTON \ - (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG |\ + (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ + AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT |\ AZX_DCAPS_I915_POWERWELL) /* quirks for ATI SB / AMD Hudson */ @@ -364,23 +370,13 @@ enum { ((pci)->device == 0x0d0c) || \ ((pci)->device == 0x160c)) -#define IS_SKL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa170) -#define IS_SKL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d70) -#define IS_KBL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa171) -#define IS_KBL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d71) -#define IS_KBL_H(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa2f0) #define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98) -#define IS_BXT_T(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x1a98) -#define IS_GLK(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x3198) -#define IS_CFL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa348) -#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci) || \ - IS_BXT_T(pci) || IS_KBL(pci) || IS_KBL_LP(pci) || \ - IS_KBL_H(pci) || IS_GLK(pci) || IS_CFL(pci)) static char *driver_short_names[] = { [AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_PCH] = "HDA Intel PCH", [AZX_DRIVER_SCH] = "HDA Intel MID", + [AZX_DRIVER_SKL] = "HDA Intel PCH", /* kept old name for compatibility */ [AZX_DRIVER_HDMI] = "HDA Intel HDMI", [AZX_DRIVER_ATI] = "HDA ATI SB", [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", @@ -644,13 +640,13 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset) if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) snd_hdac_set_codec_wakeup(bus, true); - if (IS_SKL_PLUS(pci)) { + if (chip->driver_type == AZX_DRIVER_SKL) { pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val); val = val & ~INTEL_HDA_CGCTL_MISCBDCGE; pci_write_config_dword(pci, INTEL_HDA_CGCTL, val); } azx_init_chip(chip, full_reset); - if (IS_SKL_PLUS(pci)) { + if (chip->driver_type == AZX_DRIVER_SKL) { pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val); val = val | INTEL_HDA_CGCTL_MISCBDCGE; pci_write_config_dword(pci, INTEL_HDA_CGCTL, val); @@ -1017,7 +1013,7 @@ static int azx_suspend(struct device *dev) if (chip->msi) pci_disable_msi(chip->pci); - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL + if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) && hda->need_i915_power) snd_hdac_display_power(bus, false); @@ -1075,9 +1071,11 @@ static int azx_resume(struct device *dev) */ static int azx_freeze_noirq(struct device *dev) { + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; struct pci_dev *pci = to_pci_dev(dev); - if (IS_SKL_PLUS(pci)) + if (chip->driver_type == AZX_DRIVER_SKL) pci_set_power_state(pci, PCI_D3hot); return 0; @@ -1085,9 +1083,11 @@ static int azx_freeze_noirq(struct device *dev) static int azx_thaw_noirq(struct device *dev) { + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; struct pci_dev *pci = to_pci_dev(dev); - if (IS_SKL_PLUS(pci)) + if (chip->driver_type == AZX_DRIVER_SKL) pci_set_power_state(pci, PCI_D0); return 0; @@ -1119,7 +1119,7 @@ static int azx_runtime_suspend(struct device *dev) azx_stop_chip(chip); azx_enter_link_reset(chip); azx_clear_irq_pending(chip); - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL + if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) && hda->need_i915_power) snd_hdac_display_power(azx_bus(chip), false); @@ -1384,8 +1384,9 @@ static int azx_free(struct azx *chip) if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { if (hda->need_i915_power) snd_hdac_display_power(bus, false); - snd_hdac_i915_exit(bus); } + if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) + snd_hdac_i915_exit(bus); kfree(hda); return 0; @@ -1497,7 +1498,7 @@ static int check_position_fix(struct azx *chip, int fix) dev_dbg(chip->card->dev, "Using LPIB position fix\n"); return POS_FIX_LPIB; } - if (IS_SKL_PLUS(chip->pci)) { + if (chip->driver_type == AZX_DRIVER_SKL) { dev_dbg(chip->card->dev, "Using SKL position fix\n"); return POS_FIX_SKL; } @@ -1798,7 +1799,7 @@ static int azx_first_init(struct azx *chip) return -ENXIO; } - if (IS_SKL_PLUS(pci)) + if (chip->driver_type == AZX_DRIVER_SKL) snd_hdac_bus_parse_capabilities(bus); /* @@ -2201,16 +2202,8 @@ static int azx_probe_continue(struct azx *chip) hda->probe_continued = 1; - /* Request display power well for the HDA controller or codec. For - * Haswell/Broadwell, both the display HDA controller and codec need - * this power. For other platforms, like Baytrail/Braswell, only the - * display codec needs the power and it can be released after probe. - */ - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - /* HSW/BDW controllers need this power */ - if (CONTROLLER_IN_GPU(pci)) - hda->need_i915_power = 1; - + /* bind with i915 if needed */ + if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) { err = snd_hdac_i915_init(bus); if (err < 0) { /* if the controller is bound only with HDMI/DP @@ -2222,9 +2215,23 @@ static int azx_probe_continue(struct azx *chip) dev_err(chip->card->dev, "HSW/BDW HD-audio HDMI/DP requires binding with gfx driver\n"); goto out_free; - } else - goto skip_i915; + } else { + /* don't bother any longer */ + chip->driver_caps &= + ~(AZX_DCAPS_I915_COMPONENT | AZX_DCAPS_I915_POWERWELL); + } } + } + + /* Request display power well for the HDA controller or codec. For + * Haswell/Broadwell, both the display HDA controller and codec need + * this power. For other platforms, like Baytrail/Braswell, only the + * display codec needs the power and it can be released after probe. + */ + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { + /* HSW/BDW controllers need this power */ + if (CONTROLLER_IN_GPU(pci)) + hda->need_i915_power = 1; err = snd_hdac_display_power(bus, true); if (err < 0) { @@ -2234,7 +2241,6 @@ static int azx_probe_continue(struct azx *chip) } } - skip_i915: err = azx_first_init(chip); if (err < 0) goto out_free; @@ -2277,7 +2283,7 @@ static int azx_probe_continue(struct azx *chip) pm_runtime_put_autosuspend(&pci->dev); out_free: - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL + if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) && !hda->need_i915_power) snd_hdac_display_power(bus, false); @@ -2367,31 +2373,31 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Sunrise Point */ { PCI_DEVICE(0x8086, 0xa170), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, /* Kabylake */ { PCI_DEVICE(0x8086, 0xa171), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, /* Kabylake-LP */ { PCI_DEVICE(0x8086, 0x9d71), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, /* Kabylake-H */ { PCI_DEVICE(0x8086, 0xa2f0), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, /* Coffelake */ { PCI_DEVICE(0x8086, 0xa348), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE}, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Broxton-P(Apollolake) */ { PCI_DEVICE(0x8086, 0x5a98), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BROXTON }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, /* Broxton-T */ { PCI_DEVICE(0x8086, 0x1a98), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BROXTON }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, /* Gemini-Lake */ { PCI_DEVICE(0x8086, 0x3198), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BROXTON }, + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c), .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index d0e066e4c985..5b5c324c99b9 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -178,67 +178,6 @@ void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook); #define HDA_AMP_UNMUTE 0x00 #define HDA_AMP_VOLMASK 0x7f -/* mono switch binding multiple inputs */ -#define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ - .info = snd_hda_mixer_amp_switch_info, \ - .get = snd_hda_mixer_bind_switch_get, \ - .put = snd_hda_mixer_bind_switch_put, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, indices, direction) } - -/* stereo switch binding multiple inputs */ -#define HDA_BIND_MUTE(xname,nid,indices,dir) \ - HDA_BIND_MUTE_MONO(xname,nid,3,indices,dir) - -int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -/* more generic bound controls */ -struct hda_ctl_ops { - snd_kcontrol_info_t *info; - snd_kcontrol_get_t *get; - snd_kcontrol_put_t *put; - snd_kcontrol_tlv_rw_t *tlv; -}; - -extern struct hda_ctl_ops snd_hda_bind_vol; /* for bind-volume with TLV */ -extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */ - -struct hda_bind_ctls { - struct hda_ctl_ops *ops; - unsigned long values[]; -}; - -int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv); - -#define HDA_BIND_VOL(xname, bindrec) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ - SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,\ - .info = snd_hda_mixer_bind_ctls_info,\ - .get = snd_hda_mixer_bind_ctls_get,\ - .put = snd_hda_mixer_bind_ctls_put,\ - .tlv = { .c = snd_hda_mixer_bind_tlv },\ - .private_value = (long) (bindrec) } -#define HDA_BIND_SW(xname, bindrec) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ - .name = xname, \ - .info = snd_hda_mixer_bind_ctls_info,\ - .get = snd_hda_mixer_bind_ctls_get,\ - .put = snd_hda_mixer_bind_ctls_put,\ - .private_value = (long) (bindrec) } - /* * SPDIF I/O */ diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index 9739fce9e032..9b7efece4484 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -761,7 +761,7 @@ static struct attribute *hda_dev_attrs[] = { NULL }; -static struct attribute_group hda_dev_attr_group = { +static const struct attribute_group hda_dev_attr_group = { .attrs = hda_dev_attrs, }; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 90e4ff87445e..76c85f08bea6 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -174,7 +174,6 @@ struct hdmi_spec { /* i915/powerwell (Haswell+/Valleyview+) specific */ bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */ struct i915_audio_component_audio_ops i915_audio_ops; - bool i915_bound; /* was i915 bound in this driver? */ struct hdac_chmap chmap; hda_nid_t vendor_nid; @@ -2234,8 +2233,6 @@ static void generic_spec_free(struct hda_codec *codec) struct hdmi_spec *spec = codec->spec; if (spec) { - if (spec->i915_bound) - snd_hdac_i915_exit(&codec->bus->core); hdmi_array_free(spec); kfree(spec); codec->spec = NULL; @@ -2506,19 +2503,41 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec, } } -/* Intel Haswell and onwards; audio component with eld notifier */ -static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid) +/* precondition and allocation for Intel codecs */ +static int alloc_intel_hdmi(struct hda_codec *codec) { - struct hdmi_spec *spec; - int err; - - /* HSW+ requires i915 binding */ + /* requires i915 binding */ if (!codec->bus->core.audio_component) { codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); return -ENODEV; } - err = alloc_generic_hdmi(codec); + return alloc_generic_hdmi(codec); +} + +/* parse and post-process for Intel codecs */ +static int parse_intel_hdmi(struct hda_codec *codec) +{ + int err; + + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); + return err; + } + + generic_hdmi_init_per_pins(codec); + register_i915_notifier(codec); + return 0; +} + +/* Intel Haswell and onwards; audio component with eld notifier */ +static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid) +{ + struct hdmi_spec *spec; + int err; + + err = alloc_intel_hdmi(codec); if (err < 0) return err; spec = codec->spec; @@ -2542,15 +2561,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid) spec->ops.setup_stream = i915_hsw_setup_stream; spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - register_i915_notifier(codec); - return 0; + return parse_intel_hdmi(codec); } static int patch_i915_hsw_hdmi(struct hda_codec *codec) @@ -2569,13 +2580,7 @@ static int patch_i915_byt_hdmi(struct hda_codec *codec) struct hdmi_spec *spec; int err; - /* requires i915 binding */ - if (!codec->bus->core.audio_component) { - codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); - return -ENODEV; - } - - err = alloc_generic_hdmi(codec); + err = alloc_intel_hdmi(codec); if (err < 0) return err; spec = codec->spec; @@ -2590,49 +2595,18 @@ static int patch_i915_byt_hdmi(struct hda_codec *codec) spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - register_i915_notifier(codec); - return 0; + return parse_intel_hdmi(codec); } /* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */ static int patch_i915_cpt_hdmi(struct hda_codec *codec) { - struct hdmi_spec *spec; int err; - /* no i915 component should have been bound before this */ - if (WARN_ON(codec->bus->core.audio_component)) - return -EBUSY; - - err = alloc_generic_hdmi(codec); + err = alloc_intel_hdmi(codec); if (err < 0) return err; - spec = codec->spec; - - /* Try to bind with i915 now */ - err = snd_hdac_i915_init(&codec->bus->core); - if (err < 0) - goto error; - spec->i915_bound = true; - - err = hdmi_parse_codec(codec); - if (err < 0) - goto error; - - generic_hdmi_init_per_pins(codec); - register_i915_notifier(codec); - return 0; - - error: - generic_spec_free(codec); - return err; + return parse_intel_hdmi(codec); } /* @@ -2782,21 +2756,21 @@ static int nvhdmi_7x_init_8ch(struct hda_codec *codec) return 0; } -static unsigned int channels_2_6_8[] = { +static const unsigned int channels_2_6_8[] = { 2, 6, 8 }; -static unsigned int channels_2_8[] = { +static const unsigned int channels_2_8[] = { 2, 8 }; -static struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = { +static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = { .count = ARRAY_SIZE(channels_2_6_8), .list = channels_2_6_8, .mask = 0, }; -static struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = { +static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = { .count = ARRAY_SIZE(channels_2_8), .list = channels_2_8, .mask = 0, @@ -2807,7 +2781,7 @@ static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hdmi_spec *spec = codec->spec; - struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL; + const struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL; switch (codec->preset->vendor_id) { case 0x10de0002: diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cbeebc0a9711..cd6987b5c6d9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -41,9 +41,6 @@ /* keep halting ALC5505 DSP, for power saving */ #define HALT_REALTEK_ALC5505 -/* for GPIO Poll */ -#define GPIO_MASK 0x03 - /* extra amp-initialization sequence types */ enum { ALC_INIT_NONE, @@ -327,6 +324,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0292: alc_update_coef_idx(codec, 0x4, 1<<15, 0); break; + case 0x10ec0215: case 0x10ec0225: case 0x10ec0233: case 0x10ec0255: @@ -335,12 +333,13 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0283: case 0x10ec0286: case 0x10ec0288: + case 0x10ec0285: case 0x10ec0295: case 0x10ec0298: + case 0x10ec0289: case 0x10ec0299: alc_update_coef_idx(codec, 0x10, 1<<9, 0); break; - case 0x10ec0285: case 0x10ec0293: alc_update_coef_idx(codec, 0xa, 1<<13, 0); break; @@ -2575,18 +2574,37 @@ static int patch_alc262(struct hda_codec *codec) * ALC268 */ /* bind Beep switches of both NID 0x0f and 0x10 */ -static const struct hda_bind_ctls alc268_bind_beep_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT), - HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT), - 0 - }, -}; +static int alc268_beep_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned long pval; + int err; + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = (pval & ~0xff) | 0x0f; + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (err >= 0) { + kcontrol->private_value = (pval & ~0xff) | 0x10; + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + } + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + return err; +} static const struct snd_kcontrol_new alc268_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), - HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Beep Playback Switch", + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_beep_switch_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT) + }, { } }; @@ -2719,11 +2737,12 @@ enum { ALC269_TYPE_ALC282, ALC269_TYPE_ALC283, ALC269_TYPE_ALC284, - ALC269_TYPE_ALC285, + ALC269_TYPE_ALC293, ALC269_TYPE_ALC286, ALC269_TYPE_ALC298, ALC269_TYPE_ALC255, ALC269_TYPE_ALC256, + ALC269_TYPE_ALC215, ALC269_TYPE_ALC225, ALC269_TYPE_ALC294, ALC269_TYPE_ALC700, @@ -2745,7 +2764,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC269VC: case ALC269_TYPE_ALC280: case ALC269_TYPE_ALC284: - case ALC269_TYPE_ALC285: + case ALC269_TYPE_ALC293: ssids = alc269va_ssids; break; case ALC269_TYPE_ALC269VB: @@ -2756,6 +2775,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC298: case ALC269_TYPE_ALC255: case ALC269_TYPE_ALC256: + case ALC269_TYPE_ALC215: case ALC269_TYPE_ALC225: case ALC269_TYPE_ALC294: case ALC269_TYPE_ALC700: @@ -3043,6 +3063,135 @@ static void alc283_shutup(struct hda_codec *codec) alc_write_coef_idx(codec, 0x43, 0x9614); } +static void alc256_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; + + if (!hp_pin) + return; + + msleep(30); + + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + if (hp_pin_sense) + msleep(2); + + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ + + 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(100); + + alc_update_coef_idx(codec, 0x46, 3 << 12, 0); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ +} + +static void alc256_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; + + if (!hp_pin) { + alc269_shutup(codec); + return; + } + + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + if (hp_pin_sense) + msleep(2); + + 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); + + alc_update_coef_idx(codec, 0x46, 0, 3 << 12); /* 3k pull low control for Headset jack. */ + + if (hp_pin_sense) + msleep(100); + + alc_auto_setup_eapd(codec, false); + snd_hda_shutup_pins(codec); +} + +static void alc_default_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; + + if (!hp_pin) + return; + + msleep(30); + + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + if (hp_pin_sense) + msleep(2); + + 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(100); +} + +static void alc_default_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; + + if (!hp_pin) { + alc269_shutup(codec); + return; + } + + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + if (hp_pin_sense) + msleep(2); + + 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); + + if (hp_pin_sense) + msleep(100); + + alc_auto_setup_eapd(codec, false); + snd_hda_shutup_pins(codec); +} + static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, unsigned int val) { @@ -3754,6 +3903,16 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) UPDATE_COEF(0x4a, 3<<10, 0), {} }; + static struct coef_fw coef0274[] = { + UPDATE_COEF(0x4a, 0x0100, 0), + UPDATE_COEFEX(0x57, 0x05, 0x4000, 0), + UPDATE_COEF(0x6b, 0xf000, 0x5000), + UPDATE_COEF(0x4a, 0x0010, 0), + UPDATE_COEF(0x4a, 0x0c00, 0x0c00), + WRITE_COEF(0x45, 0x5289), + UPDATE_COEF(0x4a, 0x0c00, 0), + {} + }; switch (codec->core.vendor_id) { case 0x10ec0255: @@ -3764,6 +3923,11 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) alc_process_coef_fw(codec, coef0256); alc_process_coef_fw(codec, coef0255); break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_process_coef_fw(codec, coef0274); + break; case 0x10ec0233: case 0x10ec0283: alc_process_coef_fw(codec, coef0233); @@ -3841,7 +4005,12 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, UPDATE_COEF(0x63, 3<<14, 0), {} }; - + static struct coef_fw coef0274[] = { + UPDATE_COEFEX(0x57, 0x05, 0x4000, 0x4000), + UPDATE_COEF(0x4a, 0x0010, 0), + UPDATE_COEF(0x6b, 0xf000, 0), + {} + }; switch (codec->core.vendor_id) { case 0x10ec0255: @@ -3851,6 +4020,14 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, alc_process_coef_fw(codec, coef0255); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_write_coef_idx(codec, 0x45, 0x4689); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0274); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; case 0x10ec0233: case 0x10ec0283: alc_write_coef_idx(codec, 0x45, 0xc429); @@ -3948,6 +4125,13 @@ static void alc_headset_mode_default(struct hda_codec *codec) WRITE_COEF(0xb7, 0x802b), {} }; + static struct coef_fw coef0274[] = { + WRITE_COEF(0x45, 0x4289), + UPDATE_COEF(0x4a, 0x0010, 0x0010), + UPDATE_COEF(0x6b, 0x0f00, 0), + UPDATE_COEF(0x49, 0x0300, 0x0300), + {} + }; switch (codec->core.vendor_id) { case 0x10ec0225: @@ -3959,6 +4143,11 @@ static void alc_headset_mode_default(struct hda_codec *codec) case 0x10ec0256: alc_process_coef_fw(codec, coef0255); break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_process_coef_fw(codec, coef0274); + break; case 0x10ec0233: case 0x10ec0283: alc_process_coef_fw(codec, coef0233); @@ -4044,6 +4233,11 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) case 0x10ec0256: alc_process_coef_fw(codec, coef0256); break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_write_coef_idx(codec, 0x45, 0xd689); + break; case 0x10ec0233: case 0x10ec0283: alc_process_coef_fw(codec, coef0233); @@ -4138,6 +4332,11 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) case 0x10ec0256: alc_process_coef_fw(codec, coef0256); break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_write_coef_idx(codec, 0x45, 0xe689); + break; case 0x10ec0233: case 0x10ec0283: alc_process_coef_fw(codec, coef0233); @@ -4201,6 +4400,13 @@ static void alc_determine_headset_type(struct hda_codec *codec) UPDATE_COEF(0x49, 1<<8, 1<<8), {} }; + static struct coef_fw coef0274[] = { + UPDATE_COEF(0x4a, 0x0010, 0), + UPDATE_COEF(0x4a, 0x8000, 0), + WRITE_COEF(0x45, 0xd289), + UPDATE_COEF(0x49, 0x0300, 0x0300), + {} + }; switch (codec->core.vendor_id) { case 0x10ec0255: @@ -4210,6 +4416,14 @@ static void alc_determine_headset_type(struct hda_codec *codec) val = alc_read_coef_idx(codec, 0x46); is_ctia = (val & 0x0070) == 0x0070; break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_process_coef_fw(codec, coef0274); + msleep(80); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x00f0) == 0x00f0; + break; case 0x10ec0233: case 0x10ec0283: alc_write_coef_idx(codec, 0x45, 0xd029); @@ -4892,6 +5106,7 @@ enum { ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, ALC269_FIXUP_HEADSET_MODE, ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, ALC269_FIXUP_ASPIRE_HEADSET_MIC, @@ -5192,6 +5407,16 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, + [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1b, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, [ALC269_FIXUP_HEADSET_MODE] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode, @@ -6322,6 +6547,11 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x17, 0x90170110}, {0x1a, 0x03011020}, {0x21, 0x03211030}), + SND_HDA_PIN_QUIRK(0x10ec0299, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + ALC225_STANDARD_PINS, + {0x12, 0xb7a60130}, + {0x13, 0xb8a61140}, + {0x17, 0x90170110}), {} }; @@ -6384,7 +6614,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; + spec->shutup = alc_default_shutup; + spec->init_hook = alc_default_init; snd_hda_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); @@ -6424,6 +6655,7 @@ static int patch_alc269(struct hda_codec *codec) } if (err < 0) goto error; + spec->shutup = alc269_shutup; spec->init_hook = alc269_fill_coef; alc269_fill_coef(codec); break; @@ -6447,9 +6679,8 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0292: spec->codec_variant = ALC269_TYPE_ALC284; break; - case 0x10ec0285: case 0x10ec0293: - spec->codec_variant = ALC269_TYPE_ALC285; + spec->codec_variant = ALC269_TYPE_ALC293; break; case 0x10ec0286: case 0x10ec0288: @@ -6464,9 +6695,17 @@ static int patch_alc269(struct hda_codec *codec) break; case 0x10ec0256: spec->codec_variant = ALC269_TYPE_ALC256; + spec->shutup = alc256_shutup; + spec->init_hook = alc256_init; spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/ break; + case 0x10ec0215: + case 0x10ec0285: + case 0x10ec0289: + spec->codec_variant = ALC269_TYPE_ALC215; + spec->gen.mixer_nid = 0; + break; case 0x10ec0225: case 0x10ec0295: spec->codec_variant = ALC269_TYPE_ALC225; @@ -6479,6 +6718,8 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0274: case 0x10ec0294: spec->codec_variant = ALC269_TYPE_ALC294; + spec->gen.mixer_nid = 0; /* ALC2x4 does not have any loopback mixer path */ + alc_update_coef_idx(codec, 0x6b, 0x0018, (1<<4) | (1<<3)); /* UAJ MIC Vref control by verb */ break; case 0x10ec0700: case 0x10ec0701: @@ -7495,6 +7736,7 @@ static int patch_alc680(struct hda_codec *codec) * patch entries */ static const struct hda_device_id snd_hda_id_realtek[] = { + HDA_CODEC_ENTRY(0x10ec0215, "ALC215", patch_alc269), HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269), HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269), HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), @@ -7520,6 +7762,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269), HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269), HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0289, "ALC289", patch_alc269), HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269), HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269), diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index ffda38c45509..f63acb1b965c 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -169,8 +169,8 @@ static int si3054_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - static unsigned int rates[] = { 8000, 9600, 16000 }; - static struct snd_pcm_hw_constraint_list hw_constraints_rates = { + static const unsigned int rates[] = { 8000, 9600, 16000 }; + static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 1d8612cabb9e..0e66afa403a3 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -932,10 +932,10 @@ static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device) * PCM code - professional part (multitrack) */ -static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, +static const unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 }; -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { +static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, @@ -1401,7 +1401,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] = { }, }; -static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch = { +static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "H/W Multi Capture Switch", .info = snd_ice1712_pro_mixer_switch_info, @@ -1420,7 +1420,7 @@ static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch = { .count = 2, }; -static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume = { +static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), @@ -2165,7 +2165,7 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route = { +static const struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "H/W Playback Route", .info = snd_ice1712_pro_route_info, diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 58f8f2ae758d..5cfba09c9761 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -348,7 +348,7 @@ struct snd_ice1712 { struct mutex open_mutex; struct snd_pcm_substream *pcm_reserved[4]; - struct snd_pcm_hw_constraint_list *hw_rates; /* card-specific rate constraints */ + const struct snd_pcm_hw_constraint_list *hw_rates; /* card-specific rate constraints */ unsigned int akm_codecs; struct snd_akm4xxx *akm; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 9cd6e55c0642..057c2f394ea7 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -521,25 +521,25 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id) * PCM code - professional part (multitrack) */ -static unsigned int rates[] = { +static const unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, }; -static struct snd_pcm_hw_constraint_list hw_constraints_rates_96 = { +static const struct snd_pcm_hw_constraint_list hw_constraints_rates_96 = { .count = ARRAY_SIZE(rates) - 2, /* up to 96000 */ .list = rates, .mask = 0, }; -static struct snd_pcm_hw_constraint_list hw_constraints_rates_48 = { +static const struct snd_pcm_hw_constraint_list hw_constraints_rates_48 = { .count = ARRAY_SIZE(rates) - 5, /* up to 48000 */ .list = rates, .mask = 0, }; -static struct snd_pcm_hw_constraint_list hw_constraints_rates_192 = { +static const struct snd_pcm_hw_constraint_list hw_constraints_rates_192 = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, @@ -2142,7 +2142,7 @@ static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol, digital_route_shift(idx)); } -static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route = +static const struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "H/W Playback Route", diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index 4f0213427152..5bb146703738 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -133,19 +133,19 @@ struct juli_spec { /* * Initial setup of the conversion array GPIO <-> rate */ -static unsigned int juli_rates[] = { +static const unsigned int juli_rates[] = { 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, }; -static unsigned int gpio_vals[] = { +static const unsigned int gpio_vals[] = { GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000, GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200, GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000, }; -static struct snd_pcm_hw_constraint_list juli_rates_info = { +static const struct snd_pcm_hw_constraint_list juli_rates_info = { .count = ARRAY_SIZE(juli_rates), .list = juli_rates, .mask = 0, diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c index 7de25c4807fd..0e30419f6bbd 100644 --- a/sound/pci/ice1712/maya44.c +++ b/sound/pci/ice1712/maya44.c @@ -661,12 +661,12 @@ static void set_rate(struct snd_ice1712 *ice, unsigned int rate) * supported sample rates (to override the default one) */ -static unsigned int rates[] = { +static const unsigned int rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 }; /* playback rates: 32..192 kHz */ -static struct snd_pcm_hw_constraint_list dac_rates = { +static const struct snd_pcm_hw_constraint_list dac_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0 diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c index 7c387b04067e..f1b3732cc6d2 100644 --- a/sound/pci/ice1712/quartet.c +++ b/sound/pci/ice1712/quartet.c @@ -231,17 +231,17 @@ static char *get_binary(char *buffer, int value) /* * Initial setup of the conversion array GPIO <-> rate */ -static unsigned int qtet_rates[] = { +static const unsigned int qtet_rates[] = { 44100, 48000, 88200, 96000, 176400, 192000, }; -static unsigned int cks_vals[] = { +static const unsigned int cks_vals[] = { CPLD_CKS_44100HZ, CPLD_CKS_48000HZ, CPLD_CKS_88200HZ, CPLD_CKS_96000HZ, CPLD_CKS_176400HZ, CPLD_CKS_192000HZ, }; -static struct snd_pcm_hw_constraint_list qtet_rates_info = { +static const struct snd_pcm_hw_constraint_list qtet_rates_info = { .count = ARRAY_SIZE(qtet_rates), .list = qtet_rates, .mask = 0, diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 6d17b171c17b..a8d7092e93dd 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1136,31 +1136,31 @@ static struct snd_pcm_hardware snd_intel8x0_stream = .fifo_size = 0, }; -static unsigned int channels4[] = { +static const unsigned int channels4[] = { 2, 4, }; -static struct snd_pcm_hw_constraint_list hw_constraints_channels4 = { +static const struct snd_pcm_hw_constraint_list hw_constraints_channels4 = { .count = ARRAY_SIZE(channels4), .list = channels4, .mask = 0, }; -static unsigned int channels6[] = { +static const unsigned int channels6[] = { 2, 4, 6, }; -static struct snd_pcm_hw_constraint_list hw_constraints_channels6 = { +static const struct snd_pcm_hw_constraint_list hw_constraints_channels6 = { .count = ARRAY_SIZE(channels6), .list = channels6, .mask = 0, }; -static unsigned int channels8[] = { +static const unsigned int channels8[] = { 2, 4, 6, 8, }; -static struct snd_pcm_hw_constraint_list hw_constraints_channels8 = { +static const struct snd_pcm_hw_constraint_list hw_constraints_channels8 = { .count = ARRAY_SIZE(channels8), .list = channels8, .mask = 0, diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 1bc98c867133..18ff668ce7a5 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -635,8 +635,8 @@ static struct snd_pcm_hardware snd_intel8x0m_stream = static int snd_intel8x0m_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev) { - static unsigned int rates[] = { 8000, 9600, 12000, 16000 }; - static struct snd_pcm_hw_constraint_list hw_constraints_rates = { + static const unsigned int rates[] = { 8000, 9600, 12000, 16000 }; + static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 1e25095fd144..b28fe4914d6b 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -1299,13 +1299,21 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun return 0; } -static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst, int pos, int count, int offset, int size) +static int snd_korg1212_copy_to(struct snd_pcm_substream *substream, + void __user *dst, int pos, int count, + bool in_kernel) { - struct KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos; - int i, rc; - - K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d offset=%d size=%d\n", - pos, offset, size); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); + struct KorgAudioFrame *src; + int i, size; + + pos = bytes_to_frames(runtime, pos); + count = bytes_to_frames(runtime, count); + size = korg1212->channels * 2; + src = korg1212->recordDataBufsPtr[0].bufferData + pos; + K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d size=%d count=%d\n", + pos, size, count); if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES)) return -EINVAL; @@ -1317,11 +1325,10 @@ static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst, return -EFAULT; } #endif - rc = copy_to_user(dst + offset, src, size); - if (rc) { - K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i); + if (in_kernel) + memcpy((void *)dst, src, size); + else if (copy_to_user(dst, src, size)) return -EFAULT; - } src++; dst += size; } @@ -1329,13 +1336,22 @@ static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst, return 0; } -static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *src, int pos, int count, int offset, int size) +static int snd_korg1212_copy_from(struct snd_pcm_substream *substream, + void __user *src, int pos, int count, + bool in_kernel) { - struct KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos; - int i, rc; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); + struct KorgAudioFrame *dst; + int i, size; - K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d offset=%d size=%d count=%d\n", - pos, offset, size, count); + pos = bytes_to_frames(runtime, pos); + count = bytes_to_frames(runtime, count); + size = korg1212->channels * 2; + dst = korg1212->playDataBufsPtr[0].bufferData + pos; + + K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d size=%d count=%d\n", + pos, size, count); if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES)) return -EINVAL; @@ -1348,11 +1364,10 @@ static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *sr return -EFAULT; } #endif - rc = copy_from_user((void*) dst + offset, src, size); - if (rc) { - K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i); + if (in_kernel) + memcpy((void *)dst, src, size); + else if (copy_from_user(dst, src, size)) return -EFAULT; - } dst++; src += size; } @@ -1640,45 +1655,46 @@ static snd_pcm_uframes_t snd_korg1212_capture_pointer(struct snd_pcm_substream * } static int snd_korg1212_playback_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *src, unsigned long count) { - struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); - - K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n", - stateName[korg1212->cardState], pos, count); - - return snd_korg1212_copy_from(korg1212, src, pos, count, 0, korg1212->channels * 2); + return snd_korg1212_copy_from(substream, src, pos, count, false); +} +static int snd_korg1212_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + return snd_korg1212_copy_from(substream, (void __user *)src, + pos, count, true); } static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + unsigned long pos, + unsigned long count) { + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); - K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_playback_silence [%s]\n", - stateName[korg1212->cardState]); - - return snd_korg1212_silence(korg1212, pos, count, 0, korg1212->channels * 2); + return snd_korg1212_silence(korg1212, bytes_to_frames(runtime, pos), + bytes_to_frames(runtime, count), + 0, korg1212->channels * 2); } static int snd_korg1212_capture_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *dst, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long count) { - struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); - - K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n", - stateName[korg1212->cardState], pos, count); + return snd_korg1212_copy_to(substream, dst, pos, count, false); +} - return snd_korg1212_copy_to(korg1212, dst, pos, count, 0, korg1212->channels * 2); +static int snd_korg1212_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + return snd_korg1212_copy_to(substream, (void __user *)dst, + pos, count, true); } static const struct snd_pcm_ops snd_korg1212_playback_ops = { @@ -1689,8 +1705,9 @@ static const struct snd_pcm_ops snd_korg1212_playback_ops = { .prepare = snd_korg1212_prepare, .trigger = snd_korg1212_trigger, .pointer = snd_korg1212_playback_pointer, - .copy = snd_korg1212_playback_copy, - .silence = snd_korg1212_playback_silence, + .copy_user = snd_korg1212_playback_copy, + .copy_kernel = snd_korg1212_playback_copy_kernel, + .fill_silence = snd_korg1212_playback_silence, }; static const struct snd_pcm_ops snd_korg1212_capture_ops = { @@ -1701,7 +1718,8 @@ static const struct snd_pcm_ops snd_korg1212_capture_ops = { .prepare = snd_korg1212_prepare, .trigger = snd_korg1212_trigger, .pointer = snd_korg1212_capture_pointer, - .copy = snd_korg1212_capture_copy, + .copy_user = snd_korg1212_capture_copy, + .copy_kernel = snd_korg1212_capture_copy_kernel, }; /* diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c index 4a4616aac787..2b9496a66c77 100644 --- a/sound/pci/mixart/mixart_mixer.c +++ b/sound/pci/mixart/mixart_mixer.c @@ -404,7 +404,7 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e static const DECLARE_TLV_DB_SCALE(db_scale_analog, -9600, 50, 0); -static struct snd_kcontrol_new mixart_control_analog_level = { +static const struct snd_kcontrol_new mixart_control_analog_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), @@ -897,7 +897,7 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem static const DECLARE_TLV_DB_SCALE(db_scale_digital, -10950, 50, 0); -static struct snd_kcontrol_new snd_mixart_pcm_vol = +static const struct snd_kcontrol_new snd_mixart_pcm_vol = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | @@ -951,7 +951,7 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ return changed; } -static struct snd_kcontrol_new mixart_control_pcm_switch = { +static const struct snd_kcontrol_new mixart_control_pcm_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* name will be filled later */ .count = MIXART_PLAYBACK_STREAMS, diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 103fe311e5a9..0ef8054c3936 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -398,10 +398,10 @@ snd_nm256_load_coefficient(struct nm256 *chip, int stream, int number) /* The actual rates supported by the card. */ -static unsigned int samplerates[8] = { +static const unsigned int samplerates[8] = { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, }; -static struct snd_pcm_hw_constraint_list constraints_rates = { +static const struct snd_pcm_hw_constraint_list constraints_rates = { .count = ARRAY_SIZE(samplerates), .list = samplerates, .mask = 0, @@ -695,53 +695,68 @@ snd_nm256_capture_pointer(struct snd_pcm_substream *substream) */ static int snd_nm256_playback_silence(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int channel, unsigned long pos, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - count = frames_to_bytes(runtime, count); - pos = frames_to_bytes(runtime, pos); + memset_io(s->bufptr + pos, 0, count); return 0; } static int snd_nm256_playback_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - count = frames_to_bytes(runtime, count); - pos = frames_to_bytes(runtime, pos); + if (copy_from_user_toio(s->bufptr + pos, src, count)) return -EFAULT; return 0; } +static int +snd_nm256_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct nm256_stream *s = runtime->private_data; + + memcpy_toio(s->bufptr + pos, src, count); + return 0; +} + /* * copy to user */ static int snd_nm256_capture_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *dst, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - count = frames_to_bytes(runtime, count); - pos = frames_to_bytes(runtime, pos); + if (copy_to_user_fromio(dst, s->bufptr + pos, count)) return -EFAULT; return 0; } +static int +snd_nm256_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct nm256_stream *s = runtime->private_data; + + memcpy_fromio(dst, s->bufptr + pos, count); + return 0; +} + #endif /* !__i386__ */ @@ -911,8 +926,9 @@ static const struct snd_pcm_ops snd_nm256_playback_ops = { .trigger = snd_nm256_playback_trigger, .pointer = snd_nm256_playback_pointer, #ifndef __i386__ - .copy = snd_nm256_playback_copy, - .silence = snd_nm256_playback_silence, + .copy_user = snd_nm256_playback_copy, + .copy_kernel = snd_nm256_playback_copy_kernel, + .fill_silence = snd_nm256_playback_silence, #endif .mmap = snd_pcm_lib_mmap_iomem, }; @@ -926,7 +942,8 @@ static const struct snd_pcm_ops snd_nm256_capture_ops = { .trigger = snd_nm256_capture_trigger, .pointer = snd_nm256_capture_pointer, #ifndef __i386__ - .copy = snd_nm256_capture_copy, + .copy_user = snd_nm256_capture_copy, + .copy_kernel = snd_nm256_capture_copy_kernel, #endif .mmap = snd_pcm_lib_mmap_iomem, }; diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 96d15db65dfd..e4cdef94e4a2 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -254,39 +254,46 @@ static inline unsigned int snd_rme32_pcm_byteptr(struct rme32 * rme32) } /* silence callback for halfduplex mode */ -static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) +static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + unsigned long count) { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - count <<= rme32->playback_frlog; - pos <<= rme32->playback_frlog; + memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count); return 0; } /* copy callback for halfduplex mode */ -static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *src, snd_pcm_uframes_t count) +static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - count <<= rme32->playback_frlog; - pos <<= rme32->playback_frlog; + if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, - src, count)) + src, count)) return -EFAULT; return 0; } +static int snd_rme32_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + struct rme32 *rme32 = snd_pcm_substream_chip(substream); + + memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, src, count); + return 0; +} + /* copy callback for halfduplex mode */ -static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *dst, snd_pcm_uframes_t count) +static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *dst, unsigned long count) { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - count <<= rme32->capture_frlog; - pos <<= rme32->capture_frlog; + if (copy_to_user_fromio(dst, rme32->iobase + RME32_IO_DATA_BUFFER + pos, count)) @@ -294,6 +301,16 @@ static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int chann return 0; } +static int snd_rme32_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct rme32 *rme32 = snd_pcm_substream_chip(substream); + + memcpy_fromio(dst, rme32->iobase + RME32_IO_DATA_BUFFER + pos, count); + return 0; +} + /* * SPDIF I/O capabilities (half-duplex mode) */ @@ -819,10 +836,9 @@ static irqreturn_t snd_rme32_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static unsigned int period_bytes[] = { RME32_BLOCK_SIZE }; - +static const unsigned int period_bytes[] = { RME32_BLOCK_SIZE }; -static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = { +static const struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = { .count = ARRAY_SIZE(period_bytes), .list = period_bytes, .mask = 0 @@ -1157,9 +1173,8 @@ static int snd_rme32_playback_fd_ack(struct snd_pcm_substream *substream) if (rme32->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) rec->hw_queue_size -= cprec->hw_ready; spin_unlock(&rme32->lock); - snd_pcm_indirect_playback_transfer(substream, rec, - snd_rme32_pb_trans_copy); - return 0; + return snd_pcm_indirect_playback_transfer(substream, rec, + snd_rme32_pb_trans_copy); } static void snd_rme32_cp_trans_copy(struct snd_pcm_substream *substream, @@ -1174,9 +1189,8 @@ static void snd_rme32_cp_trans_copy(struct snd_pcm_substream *substream, static int snd_rme32_capture_fd_ack(struct snd_pcm_substream *substream) { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - snd_pcm_indirect_capture_transfer(substream, &rme32->capture_pcm, - snd_rme32_cp_trans_copy); - return 0; + return snd_pcm_indirect_capture_transfer(substream, &rme32->capture_pcm, + snd_rme32_cp_trans_copy); } static snd_pcm_uframes_t @@ -1205,8 +1219,9 @@ static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = { .prepare = snd_rme32_playback_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, - .copy = snd_rme32_playback_copy, - .silence = snd_rme32_playback_silence, + .copy_user = snd_rme32_playback_copy, + .copy_kernel = snd_rme32_playback_copy_kernel, + .fill_silence = snd_rme32_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1219,7 +1234,8 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = { .prepare = snd_rme32_capture_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, - .copy = snd_rme32_capture_copy, + .copy_user = snd_rme32_capture_copy, + .copy_kernel = snd_rme32_capture_copy_kernel, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1231,8 +1247,9 @@ static const struct snd_pcm_ops snd_rme32_playback_adat_ops = { .prepare = snd_rme32_playback_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, - .copy = snd_rme32_playback_copy, - .silence = snd_rme32_playback_silence, + .copy_user = snd_rme32_playback_copy, + .copy_kernel = snd_rme32_playback_copy_kernel, + .fill_silence = snd_rme32_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1244,7 +1261,8 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_ops = { .prepare = snd_rme32_capture_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, - .copy = snd_rme32_capture_copy, + .copy_user = snd_rme32_capture_copy, + .copy_kernel = snd_rme32_capture_copy_kernel, .mmap = snd_pcm_lib_mmap_iomem, }; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 05b9da30990d..2e19ba55e754 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -327,13 +327,10 @@ snd_rme96_capture_ptr(struct rme96 *rme96) static int snd_rme96_playback_silence(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int channel, unsigned long pos, unsigned long count) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - count <<= rme96->playback_frlog; - pos <<= rme96->playback_frlog; + memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, 0, count); return 0; @@ -341,32 +338,49 @@ snd_rme96_playback_silence(struct snd_pcm_substream *substream, static int snd_rme96_playback_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - count <<= rme96->playback_frlog; - pos <<= rme96->playback_frlog; - return copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, - count); + + return copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, + src, count); +} + +static int +snd_rme96_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + struct rme96 *rme96 = snd_pcm_substream_chip(substream); + + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, count); + return 0; } static int snd_rme96_capture_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *dst, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long count) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - count <<= rme96->capture_frlog; - pos <<= rme96->capture_frlog; - return copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, + + return copy_to_user_fromio(dst, + rme96->iobase + RME96_IO_REC_BUFFER + pos, count); } +static int +snd_rme96_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct rme96 *rme96 = snd_pcm_substream_chip(substream); + + memcpy_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, count); + return 0; +} + /* * Digital output capabilities (S/PDIF) */ @@ -1149,9 +1163,9 @@ snd_rme96_interrupt(int irq, return IRQ_HANDLED; } -static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE }; +static const unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE }; -static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = { +static const struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = { .count = ARRAY_SIZE(period_bytes), .list = period_bytes, .mask = 0 @@ -1513,8 +1527,9 @@ static const struct snd_pcm_ops snd_rme96_playback_spdif_ops = { .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, - .copy = snd_rme96_playback_copy, - .silence = snd_rme96_playback_silence, + .copy_user = snd_rme96_playback_copy, + .copy_kernel = snd_rme96_playback_copy_kernel, + .fill_silence = snd_rme96_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1526,7 +1541,8 @@ static const struct snd_pcm_ops snd_rme96_capture_spdif_ops = { .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, - .copy = snd_rme96_capture_copy, + .copy_user = snd_rme96_capture_copy, + .copy_kernel = snd_rme96_capture_copy_kernel, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1538,8 +1554,9 @@ static const struct snd_pcm_ops snd_rme96_playback_adat_ops = { .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, - .copy = snd_rme96_playback_copy, - .silence = snd_rme96_playback_silence, + .copy_user = snd_rme96_playback_copy, + .copy_kernel = snd_rme96_playback_copy_kernel, + .fill_silence = snd_rme96_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1551,7 +1568,8 @@ static const struct snd_pcm_ops snd_rme96_capture_adat_ops = { .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, - .copy = snd_rme96_capture_copy, + .copy_user = snd_rme96_capture_copy, + .copy_kernel = snd_rme96_capture_copy_kernel, .mmap = snd_pcm_lib_mmap_iomem, }; diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index fc0face6cdc6..fe36d44d16c6 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -3913,42 +3913,73 @@ static char *hdsp_channel_buffer_location(struct hdsp *hdsp, return hdsp->playback_buffer + (mapped_channel * HDSP_CHANNEL_BUFFER_BYTES); } -static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) +static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); char *channel_buf; - if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4)) + if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES)) return -EINVAL; channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_from_user(channel_buf + pos * 4, src, count * 4)) + if (copy_from_user(channel_buf + pos, src, count)) return -EFAULT; - return count; + return 0; } -static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count) +static int snd_hdsp_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); char *channel_buf; - if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4)) + channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel); + if (snd_BUG_ON(!channel_buf)) + return -EIO; + memcpy(channel_buf + pos, src, count); + return 0; +} + +static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *dst, unsigned long count) +{ + struct hdsp *hdsp = snd_pcm_substream_chip(substream); + char *channel_buf; + + if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES)) return -EINVAL; channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_to_user(dst, channel_buf + pos * 4, count * 4)) + if (copy_to_user(dst, channel_buf + pos, count)) return -EFAULT; - return count; + return 0; } -static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +static int snd_hdsp_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct hdsp *hdsp = snd_pcm_substream_chip(substream); + char *channel_buf; + + channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel); + if (snd_BUG_ON(!channel_buf)) + return -EIO; + memcpy(dst, channel_buf + pos, count); + return 0; +} + +static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + unsigned long count) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); char *channel_buf; @@ -3956,8 +3987,8 @@ static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, int channel, channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - memset(channel_buf + pos * 4, 0, count * 4); - return count; + memset(channel_buf + pos, 0, count); + return 0; } static int snd_hdsp_reset(struct snd_pcm_substream *substream) @@ -4239,17 +4270,17 @@ static struct snd_pcm_hardware snd_hdsp_capture_subinfo = .fifo_size = 0 }; -static unsigned int hdsp_period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; +static const unsigned int hdsp_period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; -static struct snd_pcm_hw_constraint_list hdsp_hw_constraints_period_sizes = { +static const struct snd_pcm_hw_constraint_list hdsp_hw_constraints_period_sizes = { .count = ARRAY_SIZE(hdsp_period_sizes), .list = hdsp_period_sizes, .mask = 0 }; -static unsigned int hdsp_9632_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 }; +static const unsigned int hdsp_9632_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 }; -static struct snd_pcm_hw_constraint_list hdsp_hw_constraints_9632_sample_rates = { +static const struct snd_pcm_hw_constraint_list hdsp_hw_constraints_9632_sample_rates = { .count = ARRAY_SIZE(hdsp_9632_sample_rates), .list = hdsp_9632_sample_rates, .mask = 0 @@ -4869,8 +4900,9 @@ static const struct snd_pcm_ops snd_hdsp_playback_ops = { .prepare = snd_hdsp_prepare, .trigger = snd_hdsp_trigger, .pointer = snd_hdsp_hw_pointer, - .copy = snd_hdsp_playback_copy, - .silence = snd_hdsp_hw_silence, + .copy_user = snd_hdsp_playback_copy, + .copy_kernel = snd_hdsp_playback_copy_kernel, + .fill_silence = snd_hdsp_hw_silence, }; static const struct snd_pcm_ops snd_hdsp_capture_ops = { @@ -4881,7 +4913,8 @@ static const struct snd_pcm_ops snd_hdsp_capture_ops = { .prepare = snd_hdsp_prepare, .trigger = snd_hdsp_trigger, .pointer = snd_hdsp_hw_pointer, - .copy = snd_hdsp_capture_copy, + .copy_user = snd_hdsp_capture_copy, + .copy_kernel = snd_hdsp_capture_copy_kernel, }; static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index c48acdb0e186..254c3d040118 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6040,11 +6040,11 @@ static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params, } -static unsigned int hdspm_aes32_sample_rates[] = { +static const unsigned int hdspm_aes32_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 }; -static struct snd_pcm_hw_constraint_list +static const struct snd_pcm_hw_constraint_list hdspm_hw_constraints_aes32_sample_rates = { .count = ARRAY_SIZE(hdspm_aes32_sample_rates), .list = hdspm_aes32_sample_rates, diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 55172c689991..150d08898db8 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -1883,13 +1883,14 @@ static char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652, } } -static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) +static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); char *channel_buf; - if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4)) + if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES)) return -EINVAL; channel_buf = rme9652_channel_buffer_location (rme9652, @@ -1897,18 +1898,35 @@ static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, int ch channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_from_user(channel_buf + pos * 4, src, count * 4)) + if (copy_from_user(channel_buf + pos, src, count)) return -EFAULT; - return count; + return 0; } -static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count) +static int snd_rme9652_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) { struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); char *channel_buf; - if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4)) + channel_buf = rme9652_channel_buffer_location(rme9652, + substream->pstr->stream, + channel); + if (snd_BUG_ON(!channel_buf)) + return -EIO; + memcpy(channel_buf + pos, src, count); + return 0; +} + +static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *dst, unsigned long count) +{ + struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); + char *channel_buf; + + if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES)) return -EINVAL; channel_buf = rme9652_channel_buffer_location (rme9652, @@ -1916,13 +1934,30 @@ static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, int cha channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_to_user(dst, channel_buf + pos * 4, count * 4)) + if (copy_to_user(dst, channel_buf + pos, count)) return -EFAULT; - return count; + return 0; } -static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +static int snd_rme9652_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); + char *channel_buf; + + channel_buf = rme9652_channel_buffer_location(rme9652, + substream->pstr->stream, + channel); + if (snd_BUG_ON(!channel_buf)) + return -EIO; + memcpy(dst, channel_buf + pos, count); + return 0; +} + +static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + unsigned long count) { struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); char *channel_buf; @@ -1932,8 +1967,8 @@ static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, int chann channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - memset(channel_buf + pos * 4, 0, count * 4); - return count; + memset(channel_buf + pos, 0, count); + return 0; } static int snd_rme9652_reset(struct snd_pcm_substream *substream) @@ -2193,9 +2228,9 @@ static struct snd_pcm_hardware snd_rme9652_capture_subinfo = .fifo_size = 0, }; -static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; +static const unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; -static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = { +static const struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = { .count = ARRAY_SIZE(period_sizes), .list = period_sizes, .mask = 0 @@ -2376,8 +2411,9 @@ static const struct snd_pcm_ops snd_rme9652_playback_ops = { .prepare = snd_rme9652_prepare, .trigger = snd_rme9652_trigger, .pointer = snd_rme9652_hw_pointer, - .copy = snd_rme9652_playback_copy, - .silence = snd_rme9652_hw_silence, + .copy_user = snd_rme9652_playback_copy, + .copy_kernel = snd_rme9652_playback_copy_kernel, + .fill_silence = snd_rme9652_hw_silence, }; static const struct snd_pcm_ops snd_rme9652_capture_ops = { @@ -2388,7 +2424,8 @@ static const struct snd_pcm_ops snd_rme9652_capture_ops = { .prepare = snd_rme9652_prepare, .trigger = snd_rme9652_trigger, .pointer = snd_rme9652_hw_pointer, - .copy = snd_rme9652_capture_copy, + .copy_user = snd_rme9652_capture_copy, + .copy_kernel = snd_rme9652_capture_copy_kernel, }; static int snd_rme9652_create_pcm(struct snd_card *card, diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 8e3d4ec39c35..784d762f18a7 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -248,13 +248,13 @@ static const struct pci_device_id snd_sonic_ids[] = { MODULE_DEVICE_TABLE(pci, snd_sonic_ids); -static struct snd_ratden sonicvibes_adc_clock = { +static const struct snd_ratden sonicvibes_adc_clock = { .num_min = 4000 * 65536, .num_max = 48000UL * 65536, .num_step = 1, .den = 65536, }; -static struct snd_pcm_hw_constraint_ratdens snd_sonicvibes_hw_constraints_adc_clock = { +static const struct snd_pcm_hw_constraint_ratdens snd_sonicvibes_hw_constraints_adc_clock = { .nrats = 1, .rats = &sonicvibes_adc_clock, }; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index b6c84d15b10b..c767b8664359 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1286,10 +1286,10 @@ static int snd_via8233_multi_open(struct snd_pcm_substream *substream) /* channels constraint for VIA8233A * 3 and 5 channels are not supported */ - static unsigned int channels[] = { + static const unsigned int channels[] = { 1, 2, 4, 6 }; - static struct snd_pcm_hw_constraint_list hw_constraints_channels = { + static const struct snd_pcm_hw_constraint_list hw_constraints_channels = { .count = ARRAY_SIZE(channels), .list = channels, .mask = 0, diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 2f6d40f10618..55f79b2599e7 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -744,8 +744,8 @@ static int snd_via82xx_modem_pcm_open(struct via82xx_modem *chip, struct viadev { struct snd_pcm_runtime *runtime = substream->runtime; int err; - static unsigned int rates[] = { 8000, 9600, 12000, 16000 }; - static struct snd_pcm_hw_constraint_list hw_constraints_rates = { + static const unsigned int rates[] = { 8000, 9600, 12000, 16000 }; + static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index ecbaf473fc1e..55861849d7df 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -116,7 +116,7 @@ static struct snd_vx_hardware vx222_mic_hw = { */ static int snd_vx222_free(struct vx_core *chip) { - struct snd_vx222 *vx = (struct snd_vx222 *)chip; + struct snd_vx222 *vx = to_vx222(chip); if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); @@ -158,7 +158,7 @@ static int snd_vx222_create(struct snd_card *card, struct pci_dev *pci, pci_disable_device(pci); return -ENOMEM; } - vx = (struct snd_vx222 *)chip; + vx = to_vx222(chip); vx->pci = pci; if ((err = pci_request_regions(pci, CARD_NAME)) < 0) { diff --git a/sound/pci/vx222/vx222.h b/sound/pci/vx222/vx222.h index 2f0d78f609a6..cae355c8ed28 100644 --- a/sound/pci/vx222/vx222.h +++ b/sound/pci/vx222/vx222.h @@ -39,6 +39,8 @@ struct snd_vx222 { int mic_level; /* mic level for vx222 mic */ }; +#define to_vx222(x) container_of(x, struct snd_vx222, core) + /* we use a lookup table with 148 values, see vx_mixer.c */ #define VX2_AKM_LEVEL_MAX 0x93 diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 7df1663ea510..d4298af6d3ee 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -86,7 +86,7 @@ static int vx2_reg_index[VX_REG_MAX] = { static inline unsigned long vx2_reg_addr(struct vx_core *_chip, int reg) { - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); return chip->port[vx2_reg_index[reg]] + vx2_reg_offset[reg]; } @@ -159,7 +159,7 @@ static void vx2_outl(struct vx_core *chip, int offset, unsigned int val) static void vx2_reset_dsp(struct vx_core *_chip) { - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); /* set the reset dsp bit to 0 */ vx_outl(chip, CDSP, chip->regCDSP & ~VX_CDSP_DSP_RESET_MASK); @@ -174,7 +174,7 @@ static void vx2_reset_dsp(struct vx_core *_chip) static int vx2_test_xilinx(struct vx_core *_chip) { - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); unsigned int data; dev_dbg(_chip->card->dev, "testing xilinx...\n"); @@ -479,7 +479,7 @@ static int vx2_test_and_ack(struct vx_core *chip) */ static void vx2_validate_irq(struct vx_core *_chip, int enable) { - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); /* Set the interrupt enable bit to 1 in CDSP register */ if (enable) { @@ -730,7 +730,7 @@ static void vx2_old_write_codec_bit(struct vx_core *chip, int codec, unsigned in */ static void vx2_reset_codec(struct vx_core *_chip) { - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); /* Set the reset CODEC bit to 0. */ vx_outl(chip, CDSP, chip->regCDSP &~ VX_CDSP_CODEC_RESET_MASK); @@ -772,7 +772,7 @@ static void vx2_reset_codec(struct vx_core *_chip) */ static void vx2_change_audio_source(struct vx_core *_chip, int src) { - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); switch (src) { case VX_AUDIO_SRC_DIGITAL: @@ -791,7 +791,7 @@ static void vx2_change_audio_source(struct vx_core *_chip, int src) */ static void vx2_set_clock_source(struct vx_core *_chip, int source) { - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); if (source == INTERNAL_QUARTZ) chip->regCFG &= ~VX_CFG_CLOCKIN_SEL_MASK; @@ -805,7 +805,7 @@ static void vx2_set_clock_source(struct vx_core *_chip, int source) */ static void vx2_reset_board(struct vx_core *_chip, int cold_reset) { - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); /* initialize the register values */ chip->regCDSP = VX_CDSP_CODEC_RESET_MASK | VX_CDSP_DSP_RESET_MASK ; @@ -878,7 +878,7 @@ static int vx_input_level_info(struct snd_kcontrol *kcontrol, struct snd_ctl_ele static int vx_input_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); mutex_lock(&_chip->mixer_mutex); ucontrol->value.integer.value[0] = chip->input_level[0]; ucontrol->value.integer.value[1] = chip->input_level[1]; @@ -889,7 +889,7 @@ static int vx_input_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); if (ucontrol->value.integer.value[0] < 0 || ucontrol->value.integer.value[0] > MIC_LEVEL_MAX) return -EINVAL; @@ -922,7 +922,7 @@ static int vx_mic_level_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ static int vx_mic_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); ucontrol->value.integer.value[0] = chip->mic_level; return 0; } @@ -930,7 +930,7 @@ static int vx_mic_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); if (ucontrol->value.integer.value[0] < 0 || ucontrol->value.integer.value[0] > MIC_LEVEL_MAX) return -EINVAL; @@ -973,7 +973,7 @@ static const struct snd_kcontrol_new vx_control_mic_level = { static int vx2_add_mic_controls(struct vx_core *_chip) { - struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + struct snd_vx222 *chip = to_vx222(_chip); int err; if (_chip->type != VX_TYPE_MIC) diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c index a4a664259f0d..304b153005a5 100644 --- a/sound/pcmcia/vx/vxp_mixer.c +++ b/sound/pcmcia/vx/vxp_mixer.c @@ -43,7 +43,7 @@ static int vx_mic_level_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ static int vx_mic_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); ucontrol->value.integer.value[0] = chip->mic_level; return 0; } @@ -51,7 +51,7 @@ static int vx_mic_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); unsigned int val = ucontrol->value.integer.value[0]; if (val > MIC_LEVEL_MAX) @@ -69,7 +69,7 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v static const DECLARE_TLV_DB_SCALE(db_scale_mic, -21, 3, 0); -static struct snd_kcontrol_new vx_control_mic_level = { +static const struct snd_kcontrol_new vx_control_mic_level = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ), @@ -88,7 +88,7 @@ static struct snd_kcontrol_new vx_control_mic_level = { static int vx_mic_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); ucontrol->value.integer.value[0] = chip->mic_level; return 0; } @@ -96,7 +96,7 @@ static int vx_mic_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v static int vx_mic_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); int val = !!ucontrol->value.integer.value[0]; mutex_lock(&_chip->mixer_mutex); if (chip->mic_level != val) { @@ -109,7 +109,7 @@ static int vx_mic_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v return 0; } -static struct snd_kcontrol_new vx_control_mic_boost = { +static const struct snd_kcontrol_new vx_control_mic_boost = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic Boost", .info = vx_mic_boost_info, @@ -120,7 +120,7 @@ static struct snd_kcontrol_new vx_control_mic_boost = { int vxp_add_mic_controls(struct vx_core *_chip) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); int err; /* mute input levels */ diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index 5f97791f00d7..8cde40226355 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -50,7 +50,7 @@ static int vxp_reg_offset[VX_REG_MAX] = { static inline unsigned long vxp_reg_addr(struct vx_core *_chip, int reg) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); return chip->port + vxp_reg_offset[reg]; } @@ -110,7 +110,7 @@ static int vx_check_magic(struct vx_core *chip) static void vxp_reset_dsp(struct vx_core *_chip) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); /* set the reset dsp bit to 1 */ vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK); @@ -128,7 +128,7 @@ static void vxp_reset_dsp(struct vx_core *_chip) */ static void vxp_reset_codec(struct vx_core *_chip) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); /* Set the reset CODEC bit to 1. */ vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK); @@ -147,7 +147,7 @@ static void vxp_reset_codec(struct vx_core *_chip) */ static int vxp_load_xilinx_binary(struct vx_core *_chip, const struct firmware *fw) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); unsigned int i; int c; int regCSUER, regRUER; @@ -280,7 +280,7 @@ static int vxp_load_dsp(struct vx_core *vx, int index, const struct firmware *fw */ static int vxp_test_and_ack(struct vx_core *_chip) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); /* not booted yet? */ if (! (_chip->chip_status & VX_STAT_XILINX_LOADED)) @@ -307,7 +307,7 @@ static int vxp_test_and_ack(struct vx_core *_chip) */ static void vxp_validate_irq(struct vx_core *_chip, int enable) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); /* Set the interrupt enable bit to 1 in CDSP register */ if (enable) @@ -323,7 +323,7 @@ static void vxp_validate_irq(struct vx_core *_chip, int enable) */ static void vx_setup_pseudo_dma(struct vx_core *_chip, int do_write) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); /* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */ vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ); @@ -343,7 +343,7 @@ static void vx_setup_pseudo_dma(struct vx_core *_chip, int do_write) */ static void vx_release_pseudo_dma(struct vx_core *_chip) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); /* Disable DMA and 16-bit accesses */ chip->regDIALOG &= ~(VXP_DLG_DMAWRITE_SEL_MASK| @@ -403,7 +403,7 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, struct vx_pipe *pipe, int count) { - struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; + struct snd_vxpocket *pchip = to_vxpocket(chip); long port = vxp_reg_addr(chip, VX_DMA); int offset = pipe->hw_ptr; unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); @@ -467,7 +467,7 @@ static void vxp_write_codec_reg(struct vx_core *chip, int codec, unsigned int da */ void vx_set_mic_boost(struct vx_core *chip, int boost) { - struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; + struct snd_vxpocket *pchip = to_vxpocket(chip); if (chip->chip_status & VX_STAT_IS_STALE) return; @@ -509,7 +509,7 @@ static int vx_compute_mic_level(int level) */ void vx_set_mic_level(struct vx_core *chip, int level) { - struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; + struct snd_vxpocket *pchip = to_vxpocket(chip); if (chip->chip_status & VX_STAT_IS_STALE) return; @@ -528,7 +528,7 @@ void vx_set_mic_level(struct vx_core *chip, int level) */ static void vxp_change_audio_source(struct vx_core *_chip, int src) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); switch (src) { case VX_AUDIO_SRC_DIGITAL: @@ -568,7 +568,7 @@ static void vxp_change_audio_source(struct vx_core *_chip, int src) */ static void vxp_set_clock_source(struct vx_core *_chip, int source) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); if (source == INTERNAL_QUARTZ) chip->regCDSP &= ~VXP_CDSP_CLOCKIN_SEL_MASK; @@ -583,7 +583,7 @@ static void vxp_set_clock_source(struct vx_core *_chip, int source) */ static void vxp_reset_board(struct vx_core *_chip, int cold_reset) { - struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + struct snd_vxpocket *chip = to_vxpocket(_chip); chip->regCDSP = 0; chip->regDIALOG = 0; diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index b16f42deed67..ca0d19e723fd 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -155,7 +155,7 @@ static int snd_vxpocket_new(struct snd_card *card, int ibl, } chip->ibl.size = ibl; - vxp = (struct snd_vxpocket *)chip; + vxp = to_vxpocket(chip); vxp->p_dev = link; link->priv = chip; @@ -187,7 +187,7 @@ static int snd_vxpocket_assign_resources(struct vx_core *chip, int port, int irq { int err; struct snd_card *card = chip->card; - struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; + struct snd_vxpocket *vxp = to_vxpocket(chip); snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq); vxp->port = port; diff --git a/sound/pcmcia/vx/vxpocket.h b/sound/pcmcia/vx/vxpocket.h index 13d658c1a216..26f4255e132e 100644 --- a/sound/pcmcia/vx/vxpocket.h +++ b/sound/pcmcia/vx/vxpocket.h @@ -43,6 +43,8 @@ struct snd_vxpocket { struct pcmcia_device *p_dev; }; +#define to_vxpocket(x) container_of(x, struct snd_vxpocket, core) + extern struct snd_vx_ops snd_vxpocket_ops; void vx_set_mic_boost(struct vx_core *chip, int boost); diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 1468e4b7bf93..d1e4ef1c5c30 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -514,7 +514,7 @@ static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] = { }, }; -static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw = { +static const struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Playback Switch", .info = snd_pmac_boolean_stereo_info, @@ -523,7 +523,7 @@ static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw = { .private_value = AMP_CH_HD, }; -static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw = { +static const struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Speaker Playback Switch", .info = snd_pmac_boolean_stereo_info, diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index d3524f9fa05d..f19eb3e39937 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -206,7 +206,7 @@ static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol, return oval != chip->beep->volume; } -static struct snd_kcontrol_new snd_pmac_beep_mixer = { +static const struct snd_kcontrol_new snd_pmac_beep_mixer = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Beep Playback Volume", .info = snd_pmac_info_beep, diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 58ee8089bbf9..0779a2912237 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -897,7 +897,7 @@ static struct snd_kcontrol_new snapper_mixers[] = { }, }; -static struct snd_kcontrol_new tumbler_hp_sw = { +static const struct snd_kcontrol_new tumbler_hp_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Playback Switch", .info = snd_pmac_boolean_mono_info, @@ -905,7 +905,7 @@ static struct snd_kcontrol_new tumbler_hp_sw = { .put = tumbler_put_mute_switch, .private_value = TUMBLER_MUTE_HP, }; -static struct snd_kcontrol_new tumbler_speaker_sw = { +static const struct snd_kcontrol_new tumbler_speaker_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Speaker Playback Switch", .info = snd_pmac_boolean_mono_info, @@ -913,7 +913,7 @@ static struct snd_kcontrol_new tumbler_speaker_sw = { .put = tumbler_put_mute_switch, .private_value = TUMBLER_MUTE_AMP, }; -static struct snd_kcontrol_new tumbler_lineout_sw = { +static const struct snd_kcontrol_new tumbler_lineout_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line Out Playback Switch", .info = snd_pmac_boolean_mono_info, @@ -921,7 +921,7 @@ static struct snd_kcontrol_new tumbler_lineout_sw = { .put = tumbler_put_mute_switch, .private_value = TUMBLER_MUTE_LINE, }; -static struct snd_kcontrol_new tumbler_drc_sw = { +static const struct snd_kcontrol_new tumbler_drc_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "DRC Switch", .info = snd_pmac_boolean_mono_info, diff --git a/sound/sh/aica.c b/sound/sh/aica.c index fbbc25279559..ab4802df62e1 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -535,7 +535,7 @@ static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new snd_aica_pcmswitch_control = { +static const struct snd_kcontrol_new snd_aica_pcmswitch_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Switch", .index = 0, @@ -544,7 +544,7 @@ static struct snd_kcontrol_new snd_aica_pcmswitch_control = { .put = aica_pcmswitch_put }; -static struct snd_kcontrol_new snd_aica_pcmvolume_control = { +static const struct snd_kcontrol_new snd_aica_pcmvolume_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .index = 0, diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index 461b310c7872..c1e00ed715ee 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -184,23 +184,36 @@ static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) +static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count) { /* channel is not used (interleaved data) */ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - ssize_t b_count = frames_to_bytes(runtime , count); - ssize_t b_pos = frames_to_bytes(runtime , pos); - if (count < 0) - return -EINVAL; + if (copy_from_user_toio(chip->data_buffer + pos, src, count)) + return -EFAULT; + chip->buffer_end = chip->data_buffer + pos + count; - if (!count) - return 0; + if (chip->empty) { + chip->empty = 0; + dac_audio_start_timer(chip); + } + + return 0; +} - memcpy_toio(chip->data_buffer + b_pos, src, b_count); - chip->buffer_end = chip->data_buffer + b_pos + b_count; +static int snd_sh_dac_pcm_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + /* channel is not used (interleaved data) */ + struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + memcpy_toio(chip->data_buffer + pos, src, count); + chip->buffer_end = chip->data_buffer + pos + count; if (chip->empty) { chip->empty = 0; @@ -211,23 +224,15 @@ static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel, } static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + unsigned long count) { /* channel is not used (interleaved data) */ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - ssize_t b_count = frames_to_bytes(runtime , count); - ssize_t b_pos = frames_to_bytes(runtime , pos); - - if (count < 0) - return -EINVAL; - - if (!count) - return 0; - memset_io(chip->data_buffer + b_pos, 0, b_count); - chip->buffer_end = chip->data_buffer + b_pos + b_count; + memset_io(chip->data_buffer + pos, 0, count); + chip->buffer_end = chip->data_buffer + pos + count; if (chip->empty) { chip->empty = 0; @@ -256,8 +261,9 @@ static struct snd_pcm_ops snd_sh_dac_pcm_ops = { .prepare = snd_sh_dac_pcm_prepare, .trigger = snd_sh_dac_pcm_trigger, .pointer = snd_sh_dac_pcm_pointer, - .copy = snd_sh_dac_pcm_copy, - .silence = snd_sh_dac_pcm_silence, + .copy_user = snd_sh_dac_pcm_copy, + .copy_kernel = snd_sh_dac_pcm_copy_kernel, + .fill_silence = snd_sh_dac_pcm_silence, .mmap = snd_pcm_lib_mmap_iomem, }; diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index 6eaf081cad50..4b27aed40a51 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h @@ -83,8 +83,7 @@ struct atmel_pcm_dma_params { #define ssc_readx(base, reg) (__raw_readl((base) + (reg))) #define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg)) -#if defined(CONFIG_SND_ATMEL_SOC_PDC) || \ - defined(CONFIG_SND_ATMEL_SOC_PDC_MODULE) +#if IS_ENABLED(CONFIG_SND_ATMEL_SOC_PDC) int atmel_pcm_pdc_platform_register(struct device *dev); void atmel_pcm_pdc_platform_unregister(struct device *dev); #else @@ -97,8 +96,7 @@ static inline void atmel_pcm_pdc_platform_unregister(struct device *dev) } #endif -#if defined(CONFIG_SND_ATMEL_SOC_DMA) || \ - defined(CONFIG_SND_ATMEL_SOC_DMA_MODULE) +#if IS_ENABLED(CONFIG_SND_ATMEL_SOC_DMA) int atmel_pcm_dma_platform_register(struct device *dev); void atmel_pcm_dma_platform_unregister(struct device *dev); #else diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c index a72c7d642026..3a1393283156 100644 --- a/sound/soc/atmel/tse850-pcm5142.c +++ b/sound/soc/atmel/tse850-pcm5142.c @@ -227,7 +227,7 @@ int tse850_put_ana(struct snd_kcontrol *kctrl, static const char * const mux_text[] = { "Mixer", "Loop" }; static const struct soc_enum mux_enum = - SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, mux_text); + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(mux_text), mux_text); static const struct snd_kcontrol_new mux1 = SOC_DAPM_ENUM_EXT("MUX1", mux_enum, tse850_get_mux1, tse850_put_mux1); @@ -252,7 +252,7 @@ static const char * const ana_text[] = { }; static const struct soc_enum ana_enum = - SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 9, ana_text); + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(ana_text), ana_text); static const struct snd_kcontrol_new out = SOC_DAPM_ENUM_EXT("ANA", ana_enum, tse850_get_ana, tse850_put_ana); diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 02ad2606fa19..913e29275f4e 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -279,23 +279,33 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, return 0 ; } #else -static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, - void __user *buf, snd_pcm_uframes_t count) +static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *buf, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1]; + struct ac97_frame *dst; + pr_debug("%s copy pos:0x%lx count:0x%lx\n", substream->stream ? "Capture" : "Playback", pos, count); + dst = (struct ac97_frame *)runtime->dma_area + + bytes_to_frames(runtime, pos); + count = bytes_to_frames(runtime, count); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos, - (__u16 *)buf, count, chan_mask); + bf5xx_pcm_to_ac97(dst, buf, count, chan_mask); else - bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos, - (__u16 *)buf, count); + bf5xx_ac97_to_pcm(dst, buf, count); return 0; } + +static int bf5xx_pcm_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *buf, unsigned long count) +{ + return bf5xx_pcm_copy(substream, channel, pos, (void *)buf, count); +} #endif static struct snd_pcm_ops bf5xx_pcm_ac97_ops = { @@ -309,7 +319,8 @@ static struct snd_pcm_ops bf5xx_pcm_ac97_ops = { #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) .mmap = bf5xx_pcm_mmap, #else - .copy = bf5xx_pcm_copy, + .copy_user = bf5xx_pcm_copy_user, + .copy_kernel = bf5xx_pcm_copy, #endif }; diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 6cba211da32e..470d99abf6f6 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -225,8 +225,9 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, return 0 ; } -static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) +static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *buf, unsigned long count) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -238,6 +239,8 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); if (dma_data->tdm_mode) { + pos = bytes_to_frames(runtime, pos); + count = bytes_to_frames(runtime, count); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { src = buf; dst = runtime->dma_area; @@ -269,21 +272,29 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { src = buf; dst = runtime->dma_area; - dst += frames_to_bytes(runtime, pos); + dst += pos; } else { src = runtime->dma_area; - src += frames_to_bytes(runtime, pos); + src += pos; dst = buf; } - memcpy(dst, src, frames_to_bytes(runtime, count)); + memcpy(dst, src, count); } return 0; } +static int bf5xx_pcm_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *buf, unsigned long count) +{ + return bf5xx_pcm_copy(substream, channel, pos, (void *)buf, count); +} + static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) + int channel, unsigned long pos, + unsigned long count) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -295,11 +306,11 @@ static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); if (dma_data->tdm_mode) { - offset = pos * 8 * sample_size; - samples = count * 8; + offset = bytes_to_frames(runtime, pos) * 8 * sample_size; + samples = bytes_to_frames(runtime, count) * 8; } else { - offset = frames_to_bytes(runtime, pos); - samples = count * runtime->channels; + offset = pos; + samples = bytes_to_samples(runtime, count); } snd_pcm_format_set_silence(runtime->format, buf + offset, samples); @@ -316,8 +327,9 @@ static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { .trigger = bf5xx_pcm_trigger, .pointer = bf5xx_pcm_pointer, .mmap = bf5xx_pcm_mmap, - .copy = bf5xx_pcm_copy, - .silence = bf5xx_pcm_silence, + .copy_user = bf5xx_pcm_copy_user, + .copy_kernel = bf5xx_pcm_copy, + .fill_silence = bf5xx_pcm_silence, }; static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 883ed4c8a551..6c78b0b49b81 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -72,6 +72,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_DA9055 if I2C select SND_SOC_DIO2125 select SND_SOC_DMIC + select SND_SOC_ES8316 if I2C select SND_SOC_ES8328_SPI if SPI_MASTER select SND_SOC_ES8328_I2C if I2C select SND_SOC_ES7134 @@ -543,6 +544,10 @@ config SND_SOC_HDMI_CODEC config SND_SOC_ES7134 tristate "Everest Semi ES7134 CODEC" +config SND_SOC_ES8316 + tristate "Everest Semi ES8316 CODEC" + depends on I2C + config SND_SOC_ES8328 tristate @@ -1114,6 +1119,11 @@ config SND_SOC_WM9713 tristate select REGMAP_AC97 +config SND_SOC_ZX_AUD96P22 + tristate "ZTE ZX AUD96P22 CODEC" + depends on I2C + select REGMAP_I2C + # Amp config SND_SOC_LM4857 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 28a63fdaf982..1755a54e3dc9 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -65,6 +65,7 @@ snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-dmic-objs := dmic.o snd-soc-es7134-objs := es7134.o +snd-soc-es8316-objs := es8316.o snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o @@ -224,6 +225,7 @@ snd-soc-wm9705-objs := wm9705.o snd-soc-wm9712-objs := wm9712.o snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o +snd-soc-zx-aud96p22-objs := zx_aud96p22.o # Amp snd-soc-dio2125-objs := dio2125.o snd-soc-max9877-objs := max9877.o @@ -300,6 +302,7 @@ obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o +obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o @@ -455,6 +458,7 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o +obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o # Amp obj-$(CONFIG_SND_SOC_DIO2125) += snd-soc-dio2125.o diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index b2dfddead227..690edebf029e 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -94,6 +94,8 @@ struct ak4613_interface { struct ak4613_priv { struct mutex lock; const struct ak4613_interface *iface; + struct snd_pcm_hw_constraint_list constraint; + unsigned int sysclk; unsigned int fmt; u8 oc; @@ -139,9 +141,7 @@ static const struct reg_default ak4613_reg[] = { #define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt } static const struct ak4613_interface ak4613_iface[] = { /* capture */ /* playback */ - [0] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(16, RIGHT_J) }, - [1] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(20, RIGHT_J) }, - [2] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, RIGHT_J) }, + /* [0] - [2] are not supported */ [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) }, [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) }, }; @@ -254,6 +254,74 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, mutex_unlock(&priv->lock); } +static void ak4613_hw_constraints(struct ak4613_priv *priv, + struct snd_pcm_runtime *runtime) +{ + static const unsigned int ak4613_rates[] = { + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 176400, + 192000, + }; + struct snd_pcm_hw_constraint_list *constraint = &priv->constraint; + unsigned int fs; + int i; + + constraint->list = ak4613_rates; + constraint->mask = 0; + constraint->count = 0; + + /* + * Slave Mode + * Normal: [32kHz, 48kHz] : 256fs,384fs or 512fs + * Double: [64kHz, 96kHz] : 256fs + * Quad : [128kHz,192kHz]: 128fs + * + * Master mode + * Normal: [32kHz, 48kHz] : 256fs or 512fs + * Double: [64kHz, 96kHz] : 256fs + * Quad : [128kHz,192kHz]: 128fs + */ + for (i = 0; i < ARRAY_SIZE(ak4613_rates); i++) { + /* minimum fs on each range */ + fs = (ak4613_rates[i] <= 96000) ? 256 : 128; + + if (priv->sysclk >= ak4613_rates[i] * fs) + constraint->count = i + 1; + } + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, constraint); +} + +static int ak4613_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + + priv->cnt++; + + ak4613_hw_constraints(priv, substream->runtime); + + return 0; +} + +static int ak4613_dai_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + + priv->sysclk = freq; + + return 0; +} + static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; @@ -262,11 +330,9 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) fmt &= SND_SOC_DAIFMT_FORMAT_MASK; switch (fmt) { - case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_I2S: priv->fmt = fmt; - break; default: return -EINVAL; @@ -286,13 +352,8 @@ static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface, if (fmts->fmt != fmt) return false; - if (fmt == SND_SOC_DAIFMT_RIGHT_J) { - if (fmts->width != width) - return false; - } else { - if (fmts->width < width) - return false; - } + if (fmts->width != width) + return false; return true; } @@ -319,6 +380,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, case 48000: ctrl2 = DFS_NORMAL_SPEED; break; + case 64000: case 88200: case 96000: ctrl2 = DFS_DOUBLE_SPEED; @@ -345,7 +407,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width)) iface = priv->iface; } else { - for (i = ARRAY_SIZE(ak4613_iface); i >= 0; i--) { + for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { if (!ak4613_dai_fmt_matching(ak4613_iface + i, is_play, fmt, width)) @@ -358,7 +420,6 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, if ((priv->iface == NULL) || (priv->iface == iface)) { priv->iface = iface; - priv->cnt++; ret = 0; } mutex_unlock(&priv->lock); @@ -407,7 +468,9 @@ static int ak4613_set_bias_level(struct snd_soc_codec *codec, } static const struct snd_soc_dai_ops ak4613_dai_ops = { + .startup = ak4613_dai_startup, .shutdown = ak4613_dai_shutdown, + .set_sysclk = ak4613_dai_set_sysclk, .set_fmt = ak4613_dai_set_fmt, .hw_params = ak4613_dai_hw_params, }; @@ -420,8 +483,7 @@ static const struct snd_soc_dai_ops ak4613_dai_ops = { SNDRV_PCM_RATE_96000 |\ SNDRV_PCM_RATE_176400 |\ SNDRV_PCM_RATE_192000) -#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE) +#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_driver ak4613_dai = { .name = "ak4613-hifi", @@ -527,6 +589,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, priv->iface = NULL; priv->cnt = 0; + priv->sysclk = 0; mutex_init(&priv->lock); diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 23ab9646c351..66de8a2013a6 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -433,7 +433,7 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) static int ak4642_set_mcko(struct snd_soc_codec *codec, u32 frequency) { - u32 fs_list[] = { + static const u32 fs_list[] = { [0] = 8000, [1] = 12000, [2] = 16000, @@ -447,7 +447,7 @@ static int ak4642_set_mcko(struct snd_soc_codec *codec, [14] = 29400, [15] = 44100, }; - u32 ps_list[] = { + static const u32 ps_list[] = { [0] = 256, [1] = 128, [2] = 64, diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index 7c5d1510cf2c..0a747c66cc6c 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -567,12 +567,12 @@ static int cs35l34_pcm_hw_params(struct snd_pcm_substream *substream, return ret; } -static unsigned int cs35l34_src_rates[] = { +static const unsigned int cs35l34_src_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }; -static struct snd_pcm_hw_constraint_list cs35l34_constraints = { +static const struct snd_pcm_hw_constraint_list cs35l34_constraints = { .count = ARRAY_SIZE(cs35l34_src_rates), .list = cs35l34_src_rates, }; diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index f8aef5869b03..f1ee184ecab2 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -162,6 +162,14 @@ static bool cs35l35_precious_register(struct device *dev, unsigned int reg) } } +static void cs35l35_reset(struct cs35l35_private *cs35l35) +{ + gpiod_set_value_cansleep(cs35l35->reset_gpio, 0); + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cs35l35->reset_gpio, 1); + usleep_range(1000, 1100); +} + static int cs35l35_wait_for_pdn(struct cs35l35_private *cs35l35) { int ret; @@ -756,6 +764,76 @@ static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec, return ret; } +static int cs35l35_boost_inductor(struct cs35l35_private *cs35l35, + int inductor) +{ + struct regmap *regmap = cs35l35->regmap; + unsigned int bst_ipk = 0; + + /* + * Digital Boost Converter Configuration for feedback, + * ramping, switching frequency, and estimation block seeding. + */ + + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_SWFREQ_MASK, 0x00); + + regmap_read(regmap, CS35L35_BST_PEAK_I, &bst_ipk); + bst_ipk &= CS35L35_BST_IPK_MASK; + + switch (inductor) { + case 1000: /* 1 uH */ + regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x24); + regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x24); + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_LBST_MASK, 0x00); + + if (bst_ipk < 0x04) + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B); + else + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x4E); + break; + case 1200: /* 1.2 uH */ + regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x20); + regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x20); + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_LBST_MASK, 0x01); + + if (bst_ipk < 0x04) + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B); + else + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x47); + break; + case 1500: /* 1.5uH */ + regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x20); + regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x20); + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_LBST_MASK, 0x02); + + if (bst_ipk < 0x04) + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B); + else + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x3C); + break; + case 2200: /* 2.2uH */ + regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x19); + regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x25); + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_LBST_MASK, 0x03); + + if (bst_ipk < 0x04) + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B); + else + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x23); + break; + default: + dev_err(cs35l35->dev, "Invalid Inductor Value %d uH\n", + inductor); + return -EINVAL; + } + return 0; +} + static int cs35l35_codec_probe(struct snd_soc_codec *codec) { struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec); @@ -775,6 +853,10 @@ static int cs35l35_codec_probe(struct snd_soc_codec *codec) cs35l35->pdata.bst_ipk << CS35L35_BST_IPK_SHIFT); + ret = cs35l35_boost_inductor(cs35l35, cs35l35->pdata.boost_ind); + if (ret) + return ret; + if (cs35l35->pdata.gain_zc) regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL, CS35L35_AMP_GAIN_ZC_MASK, @@ -1195,7 +1277,15 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, return -EINVAL; } - pdata->bst_ipk = (val32 - 1680) / 110; + pdata->bst_ipk = ((val32 - 1680) / 110) | CS35L35_VALID_PDATA; + } + + ret = of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val32); + if (ret >= 0) { + pdata->boost_ind = val32; + } else { + dev_err(&i2c_client->dev, "Inductor not specified.\n"); + return -EINVAL; } if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0) @@ -1454,7 +1544,7 @@ static int cs35l35_i2c_probe(struct i2c_client *i2c_client, } } - gpiod_set_value_cansleep(cs35l35->reset_gpio, 1); + cs35l35_reset(cs35l35); init_completion(&cs35l35->pdn_done); diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h index 5a6e43a87c4d..621bfef70d03 100644 --- a/sound/soc/codecs/cs35l35.h +++ b/sound/soc/codecs/cs35l35.h @@ -200,6 +200,12 @@ #define CS35L35_SP_I2S_DRV_MASK 0x03 #define CS35L35_SP_I2S_DRV_SHIFT 0 +/* Boost Converter Config */ +#define CS35L35_BST_CONV_COEFF_MASK 0xFF +#define CS35L35_BST_CONV_SLOPE_MASK 0xFF +#define CS35L35_BST_CONV_LBST_MASK 0x03 +#define CS35L35_BST_CONV_SWFREQ_MASK 0xF0 + /* Class H Algorithm Control */ #define CS35L35_CH_STEREO_MASK 0x40 #define CS35L35_CH_STEREO_SHIFT 6 diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index e78b5f055f25..d8824773dc29 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -674,8 +674,6 @@ static int cs4271_common_probe(struct device *dev, cs4271->gpio_nreset = cs4271plat->gpio_nreset; if (gpio_is_valid(cs4271->gpio_nreset)) { - int ret; - ret = devm_gpio_request(dev, cs4271->gpio_nreset, "CS4271 Reset"); if (ret < 0) diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index 1e0d5973b758..06933a5d0a75 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -747,7 +747,7 @@ static unsigned int const cs53l30_src_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }; -static struct snd_pcm_hw_constraint_list src_constraints = { +static const struct snd_pcm_hw_constraint_list src_constraints = { .count = ARRAY_SIZE(cs53l30_src_rates), .list = cs53l30_src_rates, }; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 024d83fa6a7f..c3e11897f8ae 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -13,6 +13,8 @@ */ #include <linux/acpi.h> +#include <linux/of_device.h> +#include <linux/property.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -1606,12 +1608,12 @@ static enum da7213_dmic_clk_rate } static struct da7213_platform_data - *da7213_of_to_pdata(struct snd_soc_codec *codec) + *da7213_fw_to_pdata(struct snd_soc_codec *codec) { - struct device_node *np = codec->dev->of_node; + struct device *dev = codec->dev; struct da7213_platform_data *pdata; - const char *of_str; - u32 of_val32; + const char *fw_str; + u32 fw_val32; pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { @@ -1619,29 +1621,29 @@ static struct da7213_platform_data return NULL; } - if (of_property_read_u32(np, "dlg,micbias1-lvl", &of_val32) >= 0) - pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, of_val32); + if (device_property_read_u32(dev, "dlg,micbias1-lvl", &fw_val32) >= 0) + pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, fw_val32); else pdata->micbias1_lvl = DA7213_MICBIAS_2_2V; - if (of_property_read_u32(np, "dlg,micbias2-lvl", &of_val32) >= 0) - pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, of_val32); + if (device_property_read_u32(dev, "dlg,micbias2-lvl", &fw_val32) >= 0) + pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, fw_val32); else pdata->micbias2_lvl = DA7213_MICBIAS_2_2V; - if (!of_property_read_string(np, "dlg,dmic-data-sel", &of_str)) - pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, of_str); + if (!device_property_read_string(dev, "dlg,dmic-data-sel", &fw_str)) + pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, fw_str); else pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL; - if (!of_property_read_string(np, "dlg,dmic-samplephase", &of_str)) + if (!device_property_read_string(dev, "dlg,dmic-samplephase", &fw_str)) pdata->dmic_samplephase = - da7213_of_dmic_samplephase(codec, of_str); + da7213_of_dmic_samplephase(codec, fw_str); else pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE; - if (of_property_read_u32(np, "dlg,dmic-clkrate", &of_val32) >= 0) - pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, of_val32); + if (device_property_read_u32(dev, "dlg,dmic-clkrate", &fw_val32) >= 0) + pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, fw_val32); else pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ; @@ -1713,10 +1715,9 @@ static int da7213_probe(struct snd_soc_codec *codec) DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE); /* Handle DT/Platform data */ - if (codec->dev->of_node) - da7213->pdata = da7213_of_to_pdata(codec); - else - da7213->pdata = dev_get_platdata(codec->dev); + da7213->pdata = dev_get_platdata(codec->dev); + if (!da7213->pdata) + da7213->pdata = da7213_fw_to_pdata(codec); /* Set platform data values */ if (da7213->pdata) { diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index d256ebf9e309..6e1940eb0653 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -1457,7 +1457,7 @@ static int da7218_dai_event(struct snd_soc_dapm_widget *w, ++i; msleep(DA7218_SRM_CHECK_DELAY); } - } while ((i < DA7218_SRM_CHECK_TRIES) & (!success)); + } while ((i < DA7218_SRM_CHECK_TRIES) && (!success)); if (!success) dev_warn(codec->dev, "SRM failed to lock\n"); diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index 6274d79c1353..1d1d10dd92ae 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -115,19 +115,21 @@ static void da7219_aad_hptest_work(struct work_struct *work) struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); u16 tonegen_freq_hptest; - u8 pll_srm_sts, gain_ramp_ctrl, accdet_cfg8; + u8 pll_srm_sts, pll_ctrl, gain_ramp_ctrl, accdet_cfg8; int report = 0, ret = 0; - /* Lock DAPM and any Kcontrols that are affected by this test */ + /* Lock DAPM, Kcontrols affected by this test and the PLL */ snd_soc_dapm_mutex_lock(dapm); - mutex_lock(&da7219->lock); + mutex_lock(&da7219->ctrl_lock); + mutex_lock(&da7219->pll_lock); /* Ensure MCLK is available for HP test procedure */ if (da7219->mclk) { ret = clk_prepare_enable(da7219->mclk); if (ret) { dev_err(codec->dev, "Failed to enable mclk - %d\n", ret); - mutex_unlock(&da7219->lock); + mutex_unlock(&da7219->pll_lock); + mutex_unlock(&da7219->ctrl_lock); snd_soc_dapm_mutex_unlock(dapm); return; } @@ -136,12 +138,21 @@ static void da7219_aad_hptest_work(struct work_struct *work) /* * If MCLK not present, then we're using the internal oscillator and * require different frequency settings to achieve the same result. + * + * If MCLK is present, but PLL is not enabled then we enable it here to + * ensure a consistent detection procedure. */ pll_srm_sts = snd_soc_read(codec, DA7219_PLL_SRM_STS); - if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) + if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) { tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ); - else + + pll_ctrl = snd_soc_read(codec, DA7219_PLL_CTRL); + if ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS) + da7219_set_pll(codec, DA7219_SYSCLK_PLL, + DA7219_PLL_FREQ_OUT_98304); + } else { tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC); + } /* Ensure gain ramping at fastest rate */ gain_ramp_ctrl = snd_soc_read(codec, DA7219_GAIN_RAMP_CTRL); @@ -302,11 +313,17 @@ static void da7219_aad_hptest_work(struct work_struct *work) snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK, DA7219_HP_R_AMP_OE_MASK); + /* Restore PLL to previous configuration, if re-configured */ + if ((pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) && + ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS)) + da7219_set_pll(codec, DA7219_SYSCLK_MCLK, 0); + /* Remove MCLK, if previously enabled */ if (da7219->mclk) clk_disable_unprepare(da7219->mclk); - mutex_unlock(&da7219->lock); + mutex_unlock(&da7219->pll_lock); + mutex_unlock(&da7219->ctrl_lock); snd_soc_dapm_mutex_unlock(dapm); /* diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 99601627f83c..f71d72c22bfc 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -260,9 +260,9 @@ static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); int ret; - mutex_lock(&da7219->lock); + mutex_lock(&da7219->ctrl_lock); ret = snd_soc_get_volsw(kcontrol, ucontrol); - mutex_unlock(&da7219->lock); + mutex_unlock(&da7219->ctrl_lock); return ret; } @@ -274,9 +274,9 @@ static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); int ret; - mutex_lock(&da7219->lock); + mutex_lock(&da7219->ctrl_lock); ret = snd_soc_put_volsw(kcontrol, ucontrol); - mutex_unlock(&da7219->lock); + mutex_unlock(&da7219->ctrl_lock); return ret; } @@ -288,9 +288,9 @@ static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); int ret; - mutex_lock(&da7219->lock); + mutex_lock(&da7219->ctrl_lock); ret = snd_soc_get_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7219->lock); + mutex_unlock(&da7219->ctrl_lock); return ret; } @@ -302,9 +302,9 @@ static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); int ret; - mutex_lock(&da7219->lock); + mutex_lock(&da7219->ctrl_lock); ret = snd_soc_put_enum_double(kcontrol, ucontrol); - mutex_unlock(&da7219->lock); + mutex_unlock(&da7219->ctrl_lock); return ret; } @@ -424,9 +424,9 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, u16 val; int ret; - mutex_lock(&da7219->lock); + mutex_lock(&da7219->ctrl_lock); ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); - mutex_unlock(&da7219->lock); + mutex_unlock(&da7219->ctrl_lock); if (ret) return ret; @@ -458,9 +458,9 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, */ val = cpu_to_le16(ucontrol->value.integer.value[0]); - mutex_lock(&da7219->lock); + mutex_lock(&da7219->ctrl_lock); ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val)); - mutex_unlock(&da7219->lock); + mutex_unlock(&da7219->ctrl_lock); return ret; } @@ -801,7 +801,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w, ++i; msleep(50); } - } while ((i < DA7219_SRM_CHECK_RETRIES) && (!srm_lock)); + } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock)); if (!srm_lock) dev_warn(codec->dev, "SRM failed to lock\n"); @@ -1129,6 +1129,8 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, return -EINVAL; } + mutex_lock(&da7219->pll_lock); + switch (clk_id) { case DA7219_CLKSRC_MCLK_SQR: snd_soc_update_bits(codec, DA7219_PLL_CTRL, @@ -1141,6 +1143,7 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, break; default: dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + mutex_unlock(&da7219->pll_lock); return -EINVAL; } @@ -1152,19 +1155,20 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, if (ret) { dev_err(codec_dai->dev, "Failed to set clock rate %d\n", freq); + mutex_unlock(&da7219->pll_lock); return ret; } } da7219->mclk_rate = freq; + mutex_unlock(&da7219->pll_lock); + return 0; } -static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, - int source, unsigned int fref, unsigned int fout) +int da7219_set_pll(struct snd_soc_codec *codec, int source, unsigned int fout) { - struct snd_soc_codec *codec = codec_dai->codec; struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); u8 pll_ctrl, indiv_bits, indiv; @@ -1237,6 +1241,20 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, return 0; } +static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&da7219->pll_lock); + ret = da7219_set_pll(codec, source, fout); + mutex_unlock(&da7219->pll_lock); + + return ret; +} + static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; @@ -1741,7 +1759,8 @@ static int da7219_probe(struct snd_soc_codec *codec) unsigned int rev; int ret; - mutex_init(&da7219->lock); + mutex_init(&da7219->ctrl_lock); + mutex_init(&da7219->pll_lock); /* Regulator configuration */ ret = da7219_handle_supplies(codec); diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index 6baba7455fa1..8d6c3c8c8026 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -810,7 +810,8 @@ struct da7219_priv { bool wakeup_source; struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES]; struct regmap *regmap; - struct mutex lock; + struct mutex ctrl_lock; + struct mutex pll_lock; struct clk *mclk; unsigned int mclk_rate; @@ -821,4 +822,6 @@ struct da7219_priv { u8 gain_ramp_ctrl; }; +int da7219_set_pll(struct snd_soc_codec *codec, int source, unsigned int fout); + #endif /* __DA7219_H */ diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c new file mode 100644 index 000000000000..ecc02449c569 --- /dev/null +++ b/sound/soc/codecs/es8316.c @@ -0,0 +1,637 @@ +/* + * es8316.c -- es8316 ALSA SoC audio driver + * Copyright Everest Semiconductor Co.,Ltd + * + * Authors: David Yang <yangxiaohua@everest-semi.com>, + * Daniel Drake <drake@endlessm.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/acpi.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include "es8316.h" + +/* In slave mode at single speed, the codec is documented as accepting 5 + * MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on + * Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK). + */ +#define NR_SUPPORTED_MCLK_LRCK_RATIOS 6 +static const unsigned int supported_mclk_lrck_ratios[] = { + 256, 384, 400, 512, 768, 1024 +}; + +struct es8316_priv { + unsigned int sysclk; + unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS]; + struct snd_pcm_hw_constraint_list sysclk_constraints; +}; + +/* + * ES8316 controls + */ +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_target_tlv, -1650, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(hpmixer_gain_tlv, -1200, 150, 0); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(adc_pga_gain_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-350, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(0, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(250, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0), + 4, 4, TLV_DB_SCALE_ITEM(700, 0, 0), + 5, 5, TLV_DB_SCALE_ITEM(1000, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(1300, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(1600, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(1800, 0, 0), + 9, 9, TLV_DB_SCALE_ITEM(2100, 0, 0), + 10, 10, TLV_DB_SCALE_ITEM(2400, 0, 0), +); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpout_vol_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-4800, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(-2400, 1200, 0), +); + +static const char * const ng_type_txt[] = + { "Constant PGA Gain", "Mute ADC Output" }; +static const struct soc_enum ng_type = + SOC_ENUM_SINGLE(ES8316_ADC_ALC_NG, 6, 2, ng_type_txt); + +static const char * const adcpol_txt[] = { "Normal", "Invert" }; +static const struct soc_enum adcpol = + SOC_ENUM_SINGLE(ES8316_ADC_MUTE, 1, 2, adcpol_txt); +static const char *const dacpol_txt[] = + { "Normal", "R Invert", "L Invert", "L + R Invert" }; +static const struct soc_enum dacpol = + SOC_ENUM_SINGLE(ES8316_DAC_SET1, 0, 4, dacpol_txt); + +static const struct snd_kcontrol_new es8316_snd_controls[] = { + SOC_DOUBLE_TLV("Headphone Playback Volume", ES8316_CPHP_ICAL_VOL, + 4, 0, 3, 1, hpout_vol_tlv), + SOC_DOUBLE_TLV("Headphone Mixer Volume", ES8316_HPMIX_VOL, + 0, 4, 7, 0, hpmixer_gain_tlv), + + SOC_ENUM("Playback Polarity", dacpol), + SOC_DOUBLE_R_TLV("DAC Playback Volume", ES8316_DAC_VOLL, + ES8316_DAC_VOLR, 0, 0xc0, 1, dac_vol_tlv), + SOC_SINGLE("DAC Soft Ramp Switch", ES8316_DAC_SET1, 4, 1, 1), + SOC_SINGLE("DAC Soft Ramp Rate", ES8316_DAC_SET1, 2, 4, 0), + SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0), + SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0), + SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0), + + SOC_ENUM("Capture Polarity", adcpol), + SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0), + SOC_SINGLE_TLV("ADC Capture Volume", ES8316_ADC_VOLUME, + 0, 0xc0, 1, adc_vol_tlv), + SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8316_ADC_PGAGAIN, + 4, 10, 0, adc_pga_gain_tlv), + SOC_SINGLE("ADC Soft Ramp Switch", ES8316_ADC_MUTE, 4, 1, 0), + SOC_SINGLE("ADC Double Fs Switch", ES8316_ADC_DMIC, 4, 1, 0), + + SOC_SINGLE("ALC Capture Switch", ES8316_ADC_ALC1, 6, 1, 0), + SOC_SINGLE_TLV("ALC Capture Max Volume", ES8316_ADC_ALC1, 0, 28, 0, + alc_max_gain_tlv), + SOC_SINGLE_TLV("ALC Capture Min Volume", ES8316_ADC_ALC2, 0, 28, 0, + alc_min_gain_tlv), + SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 10, 0, + alc_target_tlv), + SOC_SINGLE("ALC Capture Hold Time", ES8316_ADC_ALC3, 0, 10, 0), + SOC_SINGLE("ALC Capture Decay Time", ES8316_ADC_ALC4, 4, 10, 0), + SOC_SINGLE("ALC Capture Attack Time", ES8316_ADC_ALC4, 0, 10, 0), + SOC_SINGLE("ALC Capture Noise Gate Switch", ES8316_ADC_ALC_NG, + 5, 1, 0), + SOC_SINGLE("ALC Capture Noise Gate Threshold", ES8316_ADC_ALC_NG, + 0, 31, 0), + SOC_ENUM("ALC Capture Noise Gate Type", ng_type), +}; + +/* Analog Input Mux */ +static const char * const es8316_analog_in_txt[] = { + "lin1-rin1", + "lin2-rin2", + "lin1-rin1 with 20db Boost", + "lin2-rin2 with 20db Boost" +}; +static const unsigned int es8316_analog_in_values[] = { 0, 1, 2, 3 }; +static const struct soc_enum es8316_analog_input_enum = + SOC_VALUE_ENUM_SINGLE(ES8316_ADC_PDN_LINSEL, 4, 3, + ARRAY_SIZE(es8316_analog_in_txt), + es8316_analog_in_txt, + es8316_analog_in_values); +static const struct snd_kcontrol_new es8316_analog_in_mux_controls = + SOC_DAPM_ENUM("Route", es8316_analog_input_enum); + +static const char * const es8316_dmic_txt[] = { + "dmic disable", + "dmic data at high level", + "dmic data at low level", +}; +static const unsigned int es8316_dmic_values[] = { 0, 1, 2 }; +static const struct soc_enum es8316_dmic_src_enum = + SOC_VALUE_ENUM_SINGLE(ES8316_ADC_DMIC, 0, 3, + ARRAY_SIZE(es8316_dmic_txt), + es8316_dmic_txt, + es8316_dmic_values); +static const struct snd_kcontrol_new es8316_dmic_src_controls = + SOC_DAPM_ENUM("Route", es8316_dmic_src_enum); + +/* hp mixer mux */ +static const char * const es8316_hpmux_texts[] = { + "lin1-rin1", + "lin2-rin2", + "lin-rin with Boost", + "lin-rin with Boost and PGA" +}; + +static const unsigned int es8316_hpmux_values[] = { 0, 1, 2, 3 }; + +static SOC_ENUM_SINGLE_DECL(es8316_left_hpmux_enum, ES8316_HPMIX_SEL, + 4, es8316_hpmux_texts); + +static const struct snd_kcontrol_new es8316_left_hpmux_controls = + SOC_DAPM_ENUM("Route", es8316_left_hpmux_enum); + +static SOC_ENUM_SINGLE_DECL(es8316_right_hpmux_enum, ES8316_HPMIX_SEL, + 0, es8316_hpmux_texts); + +static const struct snd_kcontrol_new es8316_right_hpmux_controls = + SOC_DAPM_ENUM("Route", es8316_right_hpmux_enum); + +/* headphone Output Mixer */ +static const struct snd_kcontrol_new es8316_out_left_mix[] = { + SOC_DAPM_SINGLE("LLIN Switch", ES8316_HPMIX_SWITCH, 6, 1, 0), + SOC_DAPM_SINGLE("Left DAC Switch", ES8316_HPMIX_SWITCH, 7, 1, 0), +}; +static const struct snd_kcontrol_new es8316_out_right_mix[] = { + SOC_DAPM_SINGLE("RLIN Switch", ES8316_HPMIX_SWITCH, 2, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", ES8316_HPMIX_SWITCH, 3, 1, 0), +}; + +/* DAC data source mux */ +static const char * const es8316_dacsrc_texts[] = { + "LDATA TO LDAC, RDATA TO RDAC", + "LDATA TO LDAC, LDATA TO RDAC", + "RDATA TO LDAC, RDATA TO RDAC", + "RDATA TO LDAC, LDATA TO RDAC", +}; + +static const unsigned int es8316_dacsrc_values[] = { 0, 1, 2, 3 }; + +static SOC_ENUM_SINGLE_DECL(es8316_dacsrc_mux_enum, ES8316_DAC_SET1, + 6, es8316_dacsrc_texts); + +static const struct snd_kcontrol_new es8316_dacsrc_mux_controls = + SOC_DAPM_ENUM("Route", es8316_dacsrc_mux_enum); + +static const struct snd_soc_dapm_widget es8316_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Bias", ES8316_SYS_PDN, 3, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Analog power", ES8316_SYS_PDN, 4, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias", ES8316_SYS_PDN, 5, 1, NULL, 0), + + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + + /* Input Mux */ + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &es8316_analog_in_mux_controls), + + SND_SOC_DAPM_SUPPLY("ADC Vref", ES8316_SYS_PDN, 1, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC bias", ES8316_SYS_PDN, 2, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Clock", ES8316_CLKMGR_CLKSW, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Line input PGA", ES8316_ADC_PDN_LINSEL, + 7, 1, NULL, 0), + SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8316_ADC_PDN_LINSEL, 6, 1), + SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0, + &es8316_dmic_src_controls), + + /* Digital Interface */ + SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 1, + ES8316_SERDATA_ADC, 6, 1), + SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("DAC Source Mux", SND_SOC_NOPM, 0, 0, + &es8316_dacsrc_mux_controls), + + SND_SOC_DAPM_SUPPLY("DAC Vref", ES8316_SYS_PDN, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Clock", ES8316_CLKMGR_CLKSW, 2, 0, NULL, 0), + SND_SOC_DAPM_DAC("Right DAC", NULL, ES8316_DAC_PDN, 0, 1), + SND_SOC_DAPM_DAC("Left DAC", NULL, ES8316_DAC_PDN, 4, 1), + + /* Headphone Output Side */ + SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, + &es8316_left_hpmux_controls), + SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, + &es8316_right_hpmux_controls), + SND_SOC_DAPM_MIXER("Left Headphone Mixer", ES8316_HPMIX_PDN, + 5, 1, &es8316_out_left_mix[0], + ARRAY_SIZE(es8316_out_left_mix)), + SND_SOC_DAPM_MIXER("Right Headphone Mixer", ES8316_HPMIX_PDN, + 1, 1, &es8316_out_right_mix[0], + ARRAY_SIZE(es8316_out_right_mix)), + SND_SOC_DAPM_PGA("Left Headphone Mixer Out", ES8316_HPMIX_PDN, + 4, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right Headphone Mixer Out", ES8316_HPMIX_PDN, + 0, 1, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("Left Headphone Charge Pump", ES8316_CPHP_OUTEN, + 6, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Right Headphone Charge Pump", ES8316_CPHP_OUTEN, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", ES8316_CPHP_PDN2, + 5, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Charge Pump Clock", ES8316_CLKMGR_CLKSW, + 4, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("Left Headphone Driver", ES8316_CPHP_OUTEN, + 5, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Right Headphone Driver", ES8316_CPHP_OUTEN, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Out", ES8316_CPHP_PDN1, 2, 1, NULL, 0), + + /* pdn_Lical and pdn_Rical bits are documented as Reserved, but must + * be explicitly unset in order to enable HP output + */ + SND_SOC_DAPM_SUPPLY("Left Headphone ical", ES8316_CPHP_ICAL_VOL, + 7, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Right Headphone ical", ES8316_CPHP_ICAL_VOL, + 3, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route es8316_dapm_routes[] = { + /* Recording */ + {"MIC1", NULL, "Mic Bias"}, + {"MIC2", NULL, "Mic Bias"}, + {"MIC1", NULL, "Bias"}, + {"MIC2", NULL, "Bias"}, + {"MIC1", NULL, "Analog power"}, + {"MIC2", NULL, "Analog power"}, + + {"Differential Mux", "lin1-rin1", "MIC1"}, + {"Differential Mux", "lin2-rin2", "MIC2"}, + {"Line input PGA", NULL, "Differential Mux"}, + + {"Mono ADC", NULL, "ADC Clock"}, + {"Mono ADC", NULL, "ADC Vref"}, + {"Mono ADC", NULL, "ADC bias"}, + {"Mono ADC", NULL, "Line input PGA"}, + + /* It's not clear why, but to avoid recording only silence, + * the DAC clock must be running for the ADC to work. + */ + {"Mono ADC", NULL, "DAC Clock"}, + + {"Digital Mic Mux", "dmic disable", "Mono ADC"}, + + {"I2S OUT", NULL, "Digital Mic Mux"}, + + /* Playback */ + {"DAC Source Mux", "LDATA TO LDAC, RDATA TO RDAC", "I2S IN"}, + + {"Left DAC", NULL, "DAC Clock"}, + {"Right DAC", NULL, "DAC Clock"}, + + {"Left DAC", NULL, "DAC Vref"}, + {"Right DAC", NULL, "DAC Vref"}, + + {"Left DAC", NULL, "DAC Source Mux"}, + {"Right DAC", NULL, "DAC Source Mux"}, + + {"Left Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"}, + {"Right Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"}, + + {"Left Headphone Mixer", "LLIN Switch", "Left Headphone Mux"}, + {"Left Headphone Mixer", "Left DAC Switch", "Left DAC"}, + + {"Right Headphone Mixer", "RLIN Switch", "Right Headphone Mux"}, + {"Right Headphone Mixer", "Right DAC Switch", "Right DAC"}, + + {"Left Headphone Mixer Out", NULL, "Left Headphone Mixer"}, + {"Right Headphone Mixer Out", NULL, "Right Headphone Mixer"}, + + {"Left Headphone Charge Pump", NULL, "Left Headphone Mixer Out"}, + {"Right Headphone Charge Pump", NULL, "Right Headphone Mixer Out"}, + + {"Left Headphone Charge Pump", NULL, "Headphone Charge Pump"}, + {"Right Headphone Charge Pump", NULL, "Headphone Charge Pump"}, + + {"Left Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"}, + {"Right Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"}, + + {"Left Headphone Driver", NULL, "Left Headphone Charge Pump"}, + {"Right Headphone Driver", NULL, "Right Headphone Charge Pump"}, + + {"HPOL", NULL, "Left Headphone Driver"}, + {"HPOR", NULL, "Right Headphone Driver"}, + + {"HPOL", NULL, "Left Headphone ical"}, + {"HPOR", NULL, "Right Headphone ical"}, + + {"Headphone Out", NULL, "Bias"}, + {"Headphone Out", NULL, "Analog power"}, + {"HPOL", NULL, "Headphone Out"}, + {"HPOR", NULL, "Headphone Out"}, +}; + +static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct es8316_priv *es8316 = snd_soc_codec_get_drvdata(codec); + int i; + int count = 0; + + es8316->sysclk = freq; + + if (freq == 0) + return 0; + + /* Limit supported sample rates to ones that can be autodetected + * by the codec running in slave mode. + */ + for (i = 0; i < NR_SUPPORTED_MCLK_LRCK_RATIOS; i++) { + const unsigned int ratio = supported_mclk_lrck_ratios[i]; + + if (freq % ratio == 0) + es8316->allowed_rates[count++] = freq / ratio; + } + + es8316->sysclk_constraints.list = es8316->allowed_rates; + es8316->sysclk_constraints.count = count; + + return 0; +} + +static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u8 serdata1 = 0; + u8 serdata2 = 0; + u8 clksw; + u8 mask; + + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "Codec driver only supports slave mode\n"); + return -EINVAL; + } + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) { + dev_err(codec->dev, "Codec driver only supports I2S format\n"); + return -EINVAL; + } + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + serdata1 |= ES8316_SERDATA1_BCLK_INV; + serdata2 |= ES8316_SERDATA2_ADCLRP; + break; + case SND_SOC_DAIFMT_IB_NF: + serdata1 |= ES8316_SERDATA1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + serdata2 |= ES8316_SERDATA2_ADCLRP; + break; + default: + return -EINVAL; + } + + mask = ES8316_SERDATA1_MASTER | ES8316_SERDATA1_BCLK_INV; + snd_soc_update_bits(codec, ES8316_SERDATA1, mask, serdata1); + + mask = ES8316_SERDATA2_FMT_MASK | ES8316_SERDATA2_ADCLRP; + snd_soc_update_bits(codec, ES8316_SERDATA_ADC, mask, serdata2); + snd_soc_update_bits(codec, ES8316_SERDATA_DAC, mask, serdata2); + + /* Enable BCLK and MCLK inputs in slave mode */ + clksw = ES8316_CLKMGR_CLKSW_MCLK_ON | ES8316_CLKMGR_CLKSW_BCLK_ON; + snd_soc_update_bits(codec, ES8316_CLKMGR_CLKSW, clksw, clksw); + + return 0; +} + +static int es8316_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct es8316_priv *es8316 = snd_soc_codec_get_drvdata(codec); + + if (es8316->sysclk == 0) { + dev_err(codec->dev, "No sysclk provided\n"); + return -EINVAL; + } + + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC. + */ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &es8316->sysclk_constraints); + + return 0; +} + +static int es8316_pcm_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 snd_soc_codec *codec = rtd->codec; + struct es8316_priv *es8316 = snd_soc_codec_get_drvdata(codec); + u8 wordlen = 0; + + if (!es8316->sysclk) { + dev_err(codec->dev, "No MCLK configured\n"); + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + wordlen = ES8316_SERDATA2_LEN_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + wordlen = ES8316_SERDATA2_LEN_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + wordlen = ES8316_SERDATA2_LEN_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + wordlen = ES8316_SERDATA2_LEN_32; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ES8316_SERDATA_DAC, + ES8316_SERDATA2_LEN_MASK, wordlen); + snd_soc_update_bits(codec, ES8316_SERDATA_ADC, + ES8316_SERDATA2_LEN_MASK, wordlen); + return 0; +} + +static int es8316_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, ES8316_DAC_SET1, 0x20, + mute ? 0x20 : 0); + return 0; +} + +#define ES8316_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops es8316_ops = { + .startup = es8316_pcm_startup, + .hw_params = es8316_pcm_hw_params, + .set_fmt = es8316_set_dai_fmt, + .set_sysclk = es8316_set_dai_sysclk, + .digital_mute = es8316_mute, +}; + +static struct snd_soc_dai_driver es8316_dai = { + .name = "ES8316 HiFi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ES8316_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ES8316_FORMATS, + }, + .ops = &es8316_ops, + .symmetric_rates = 1, +}; + +static int es8316_probe(struct snd_soc_codec *codec) +{ + /* Reset codec and enable current state machine */ + snd_soc_write(codec, ES8316_RESET, 0x3f); + usleep_range(5000, 5500); + snd_soc_write(codec, ES8316_RESET, ES8316_RESET_CSM_ON); + msleep(30); + + /* + * Documentation is unclear, but this value from the vendor driver is + * needed otherwise audio output is silent. + */ + snd_soc_write(codec, ES8316_SYS_VMIDSEL, 0xff); + + /* + * Documentation for this register is unclear and incomplete, + * but here is a vendor-provided value that improves volume + * and quality for Intel CHT platforms. + */ + snd_soc_write(codec, ES8316_CLKMGR_ADCOSR, 0x32); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_es8316 = { + .probe = es8316_probe, + .idle_bias_off = true, + + .component_driver = { + .controls = es8316_snd_controls, + .num_controls = ARRAY_SIZE(es8316_snd_controls), + .dapm_widgets = es8316_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8316_dapm_widgets), + .dapm_routes = es8316_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8316_dapm_routes), + }, +}; + +static const struct regmap_config es8316_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x53, + .cache_type = REGCACHE_RBTREE, +}; + +static int es8316_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct es8316_priv *es8316; + struct regmap *regmap; + + es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv), + GFP_KERNEL); + if (es8316 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c_client, es8316); + + regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return snd_soc_register_codec(&i2c_client->dev, &soc_codec_dev_es8316, + &es8316_dai, 1); +} + +static int es8316_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id es8316_i2c_id[] = { + {"es8316", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, es8316_i2c_id); + +static const struct of_device_id es8316_of_match[] = { + { .compatible = "everest,es8316", }, + {}, +}; +MODULE_DEVICE_TABLE(of, es8316_of_match); + +static const struct acpi_device_id es8316_acpi_match[] = { + {"ESSX8316", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, es8316_acpi_match); + +static struct i2c_driver es8316_i2c_driver = { + .driver = { + .name = "es8316", + .acpi_match_table = ACPI_PTR(es8316_acpi_match), + .of_match_table = of_match_ptr(es8316_of_match), + }, + .probe = es8316_i2c_probe, + .remove = es8316_i2c_remove, + .id_table = es8316_i2c_id, +}; +module_i2c_driver(es8316_i2c_driver); + +MODULE_DESCRIPTION("Everest Semi ES8316 ALSA SoC Codec Driver"); +MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/es8316.h b/sound/soc/codecs/es8316.h new file mode 100644 index 000000000000..6bcdd63ea459 --- /dev/null +++ b/sound/soc/codecs/es8316.h @@ -0,0 +1,129 @@ +/* + * Copyright Everest Semiconductor Co.,Ltd + * + * Author: David Yang <yangxiaohua@everest-semi.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 _ES8316_H +#define _ES8316_H + +/* + * ES8316 register space + */ + +/* Reset Control */ +#define ES8316_RESET 0x00 + +/* Clock Management */ +#define ES8316_CLKMGR_CLKSW 0x01 +#define ES8316_CLKMGR_CLKSEL 0x02 +#define ES8316_CLKMGR_ADCOSR 0x03 +#define ES8316_CLKMGR_ADCDIV1 0x04 +#define ES8316_CLKMGR_ADCDIV2 0x05 +#define ES8316_CLKMGR_DACDIV1 0x06 +#define ES8316_CLKMGR_DACDIV2 0x07 +#define ES8316_CLKMGR_CPDIV 0x08 + +/* Serial Data Port Control */ +#define ES8316_SERDATA1 0x09 +#define ES8316_SERDATA_ADC 0x0a +#define ES8316_SERDATA_DAC 0x0b + +/* System Control */ +#define ES8316_SYS_VMIDSEL 0x0c +#define ES8316_SYS_PDN 0x0d +#define ES8316_SYS_LP1 0x0e +#define ES8316_SYS_LP2 0x0f +#define ES8316_SYS_VMIDLOW 0x10 +#define ES8316_SYS_VSEL 0x11 +#define ES8316_SYS_REF 0x12 + +/* Headphone Mixer */ +#define ES8316_HPMIX_SEL 0x13 +#define ES8316_HPMIX_SWITCH 0x14 +#define ES8316_HPMIX_PDN 0x15 +#define ES8316_HPMIX_VOL 0x16 + +/* Charge Pump Headphone driver */ +#define ES8316_CPHP_OUTEN 0x17 +#define ES8316_CPHP_ICAL_VOL 0x18 +#define ES8316_CPHP_PDN1 0x19 +#define ES8316_CPHP_PDN2 0x1a +#define ES8316_CPHP_LDOCTL 0x1b + +/* Calibration */ +#define ES8316_CAL_TYPE 0x1c +#define ES8316_CAL_SET 0x1d +#define ES8316_CAL_HPLIV 0x1e +#define ES8316_CAL_HPRIV 0x1f +#define ES8316_CAL_HPLMV 0x20 +#define ES8316_CAL_HPRMV 0x21 + +/* ADC Control */ +#define ES8316_ADC_PDN_LINSEL 0x22 +#define ES8316_ADC_PGAGAIN 0x23 +#define ES8316_ADC_D2SEPGA 0x24 +#define ES8316_ADC_DMIC 0x25 +#define ES8316_ADC_MUTE 0x26 +#define ES8316_ADC_VOLUME 0x27 +#define ES8316_ADC_ALC1 0x29 +#define ES8316_ADC_ALC2 0x2a +#define ES8316_ADC_ALC3 0x2b +#define ES8316_ADC_ALC4 0x2c +#define ES8316_ADC_ALC5 0x2d +#define ES8316_ADC_ALC_NG 0x2e + +/* DAC Control */ +#define ES8316_DAC_PDN 0x2f +#define ES8316_DAC_SET1 0x30 +#define ES8316_DAC_SET2 0x31 +#define ES8316_DAC_SET3 0x32 +#define ES8316_DAC_VOLL 0x33 +#define ES8316_DAC_VOLR 0x34 + +/* GPIO */ +#define ES8316_GPIO_SEL 0x4d +#define ES8316_GPIO_DEBOUNCE 0x4e +#define ES8316_GPIO_FLAG 0x4f + +/* Test mode */ +#define ES8316_TESTMODE 0x50 +#define ES8316_TEST1 0x51 +#define ES8316_TEST2 0x52 +#define ES8316_TEST3 0x53 + +/* + * Field definitions + */ + +/* ES8316_RESET */ +#define ES8316_RESET_CSM_ON 0x80 + +/* ES8316_CLKMGR_CLKSW */ +#define ES8316_CLKMGR_CLKSW_MCLK_ON 0x40 +#define ES8316_CLKMGR_CLKSW_BCLK_ON 0x20 + +/* ES8316_SERDATA1 */ +#define ES8316_SERDATA1_MASTER 0x80 +#define ES8316_SERDATA1_BCLK_INV 0x20 + +/* ES8316_SERDATA_ADC and _DAC */ +#define ES8316_SERDATA2_FMT_MASK 0x3 +#define ES8316_SERDATA2_FMT_I2S 0x00 +#define ES8316_SERDATA2_FMT_LEFTJ 0x01 +#define ES8316_SERDATA2_FMT_RIGHTJ 0x02 +#define ES8316_SERDATA2_FMT_PCM 0x03 +#define ES8316_SERDATA2_ADCLRP 0x20 +#define ES8316_SERDATA2_LEN_MASK 0x1c +#define ES8316_SERDATA2_LEN_24 0x00 +#define ES8316_SERDATA2_LEN_20 0x04 +#define ES8316_SERDATA2_LEN_18 0x08 +#define ES8316_SERDATA2_LEN_16 0x0c +#define ES8316_SERDATA2_LEN_32 0x10 + +#endif diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 8c5ae1fc23a9..22ed0dc88f0a 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -25,17 +25,6 @@ #include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */ -struct hdmi_device { - struct device *dev; - struct list_head list; - int cnt; -}; -#define pos_to_hdmi_device(pos) container_of((pos), struct hdmi_device, list) -LIST_HEAD(hdmi_device_list); -static DEFINE_MUTEX(hdmi_mutex); - -#define DAI_NAME_SIZE 16 - #define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1 struct hdmi_codec_channel_map_table { @@ -293,7 +282,6 @@ struct hdmi_codec_priv { struct hdmi_codec_daifmt daifmt[2]; struct mutex current_stream_lock; struct snd_pcm_substream *current_stream; - struct snd_pcm_hw_constraint_list ratec; uint8_t eld[MAX_ELD_BYTES]; struct snd_pcm_chmap *chmap_info; unsigned int chmap_idx; @@ -702,6 +690,7 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, } static struct snd_soc_dai_driver hdmi_i2s_dai = { + .name = "i2s-hifi", .id = DAI_ID_I2S, .playback = { .stream_name = "Playback", @@ -716,6 +705,7 @@ static struct snd_soc_dai_driver hdmi_i2s_dai = { }; static const struct snd_soc_dai_driver hdmi_spdif_dai = { + .name = "spdif-hifi", .id = DAI_ID_SPDIF, .playback = { .stream_name = "Playback", @@ -728,30 +718,16 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = { .pcm_new = hdmi_codec_pcm_new, }; -static char hdmi_dai_name[][DAI_NAME_SIZE] = { - "hdmi-hifi.0", - "hdmi-hifi.1", - "hdmi-hifi.2", - "hdmi-hifi.3", -}; - -static int hdmi_of_xlate_dai_name(struct snd_soc_component *component, - struct of_phandle_args *args, - const char **dai_name) +static int hdmi_of_xlate_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) { - int id; + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + int ret = -ENOTSUPP; /* see snd_soc_get_dai_id() */ - if (args->args_count) - id = args->args[0]; - else - id = 0; + if (hcp->hcd.ops->get_dai_id) + ret = hcp->hcd.ops->get_dai_id(component, endpoint); - if (id < ARRAY_SIZE(hdmi_dai_name)) { - *dai_name = hdmi_dai_name[id]; - return 0; - } - - return -EAGAIN; + return ret; } static struct snd_soc_codec_driver hdmi_codec = { @@ -762,7 +738,7 @@ static struct snd_soc_codec_driver hdmi_codec = { .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), .dapm_routes = hdmi_routes, .num_dapm_routes = ARRAY_SIZE(hdmi_routes), - .of_xlate_dai_name = hdmi_of_xlate_dai_name, + .of_xlate_dai_id = hdmi_of_xlate_dai_id, }, }; @@ -771,8 +747,6 @@ static int hdmi_codec_probe(struct platform_device *pdev) struct hdmi_codec_pdata *hcd = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct hdmi_codec_priv *hcp; - struct hdmi_device *hd; - struct list_head *pos; int dai_count, i = 0; int ret; @@ -794,35 +768,6 @@ static int hdmi_codec_probe(struct platform_device *pdev) if (!hcp) return -ENOMEM; - hd = NULL; - mutex_lock(&hdmi_mutex); - list_for_each(pos, &hdmi_device_list) { - struct hdmi_device *tmp = pos_to_hdmi_device(pos); - - if (tmp->dev == dev->parent) { - hd = tmp; - break; - } - } - - if (!hd) { - hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL); - if (!hd) { - mutex_unlock(&hdmi_mutex); - return -ENOMEM; - } - - hd->dev = dev->parent; - - list_add_tail(&hd->list, &hdmi_device_list); - } - mutex_unlock(&hdmi_mutex); - - if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) { - dev_err(dev, "too many hdmi codec are deteced\n"); - return -EINVAL; - } - hcp->hcd = *hcd; mutex_init(&hcp->current_stream_lock); @@ -835,14 +780,11 @@ static int hdmi_codec_probe(struct platform_device *pdev) hcp->daidrv[i] = hdmi_i2s_dai; hcp->daidrv[i].playback.channels_max = hcd->max_i2s_channels; - hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++]; i++; } - if (hcd->spdif) { + if (hcd->spdif) hcp->daidrv[i] = hdmi_spdif_dai; - hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++]; - } ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv, dai_count); @@ -859,20 +801,8 @@ static int hdmi_codec_probe(struct platform_device *pdev) static int hdmi_codec_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct list_head *pos; struct hdmi_codec_priv *hcp; - mutex_lock(&hdmi_mutex); - list_for_each(pos, &hdmi_device_list) { - struct hdmi_device *tmp = pos_to_hdmi_device(pos); - - if (tmp->dev == dev->parent) { - list_del(pos); - break; - } - } - mutex_unlock(&hdmi_mutex); - hcp = dev_get_drvdata(dev); kfree(hcp->chmap_info); snd_soc_unregister_codec(dev); diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 0247edc9c84e..2a40a69a7513 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -132,7 +132,7 @@ enum rates { pcm_rate_48, max_pcm_rate, }; -struct ni_div_rates { +static const struct ni_div_rates { u32 mclk; u16 ni[max_pcm_rate]; } ni_div[] = { diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index d8e8590746af..a78802920c3c 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -223,8 +223,8 @@ struct pm8916_wcd_analog_priv { u16 codec_version; struct clk *mclk; struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; - bool micbias1_cap_mode; - bool micbias2_cap_mode; + unsigned int micbias1_cap_mode; + unsigned int micbias2_cap_mode; }; static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" }; @@ -285,7 +285,7 @@ static void pm8916_wcd_analog_micbias_enable(struct snd_soc_codec *codec) static int pm8916_wcd_analog_enable_micbias_ext(struct snd_soc_codec *codec, int event, - int reg, u32 cap_mode) + int reg, unsigned int cap_mode) { switch (event) { case SND_SOC_DAPM_POST_PMU: diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index cca974d26136..3a309b18035e 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -1125,6 +1125,57 @@ static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } /** + * nau8824_set_tdm_slot - configure DAI TDM. + * @dai: DAI + * @tx_mask: Bitmask representing active TX slots. Ex. + * 0xf for normal 4 channel TDM. + * 0xf0 for shifted 4 channel TDM + * @rx_mask: Bitmask [0:1] representing active DACR RX slots. + * Bitmask [2:3] representing active DACL RX slots. + * 00=CH0,01=CH1,10=CH2,11=CH3. Ex. + * 0xf for DACL/R selecting TDM CH3. + * 0xf0 for DACL/R selecting shifted TDM CH3. + * @slots: Number of slots in use. + * @slot_width: Width in bits for each slot. + * + * Configures a DAI for TDM operation. Only support 4 slots TDM. + */ +static int nau8824_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + unsigned int tslot_l = 0, ctrl_val = 0; + + if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)) || + ((rx_mask & 0xf0) && (rx_mask & 0xf)) || + ((rx_mask & 0xf0) && (tx_mask & 0xf)) || + ((rx_mask & 0xf) && (tx_mask & 0xf0))) + return -EINVAL; + + ctrl_val |= (NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN); + if (tx_mask & 0xf0) { + tslot_l = 4 * slot_width; + ctrl_val |= (tx_mask >> 4); + } else { + ctrl_val |= tx_mask; + } + if (rx_mask & 0xf0) + ctrl_val |= ((rx_mask >> 4) << NAU8824_TDM_DACR_RX_SFT); + else + ctrl_val |= (rx_mask << NAU8824_TDM_DACR_RX_SFT); + + regmap_update_bits(nau8824->regmap, NAU8824_REG_TDM_CTRL, + NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN | + NAU8824_TDM_DACL_RX_MASK | NAU8824_TDM_DACR_RX_MASK | + NAU8824_TDM_TX_MASK, ctrl_val); + regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_LEFT_TIME_SLOT, + NAU8824_TSLOT_L_MASK, tslot_l); + + return 0; +} + +/** * nau8824_calc_fll_param - Calculate FLL parameters. * @fll_in: external clock provided to codec. * @fs: sampling rate. @@ -1440,6 +1491,7 @@ static struct snd_soc_codec_driver nau8824_codec_driver = { static const struct snd_soc_dai_ops nau8824_dai_ops = { .hw_params = nau8824_hw_params, .set_fmt = nau8824_set_fmt, + .set_tdm_slot = nau8824_set_tdm_slot, }; #define NAU8824_RATES SNDRV_PCM_RATE_8000_192000 diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h index 87ac9a382aed..21eae2431c83 100644 --- a/sound/soc/codecs/nau8824.h +++ b/sound/soc/codecs/nau8824.h @@ -258,6 +258,18 @@ #define NAU8824_I2S_MS_SLAVE (0 << NAU8824_I2S_MS_SFT) #define NAU8824_I2S_BLK_DIV_MASK 0x7 +/* PORT0_LEFT_TIME_SLOT (0x1E) */ +#define NAU8824_TSLOT_L_MASK 0x3ff + +/* TDM_CTRL (0x20) */ +#define NAU8824_TDM_MODE (0x1 << 15) +#define NAU8824_TDM_OFFSET_EN (0x1 << 14) +#define NAU8824_TDM_DACL_RX_SFT 6 +#define NAU8824_TDM_DACL_RX_MASK (0x3 << NAU8824_TDM_DACL_RX_SFT) +#define NAU8824_TDM_DACR_RX_SFT 4 +#define NAU8824_TDM_DACR_RX_MASK (0x3 << NAU8824_TDM_DACR_RX_SFT) +#define NAU8824_TDM_TX_MASK 0xf + /* ADC_FILTER_CTRL (0x24) */ #define NAU8824_ADC_SYNC_DOWN_MASK 0x3 #define NAU8824_ADC_SYNC_DOWN_32 0 diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 97fbeba9498f..46a30eaa7ace 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1612,7 +1612,6 @@ static int nau8825_jack_insert(struct nau8825 *nau8825) snd_soc_dapm_sync(dapm); break; case 2: - case 3: dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n"); type = SND_JACK_HEADSET; @@ -1632,6 +1631,11 @@ static int nau8825_jack_insert(struct nau8825 *nau8825) snd_soc_dapm_force_enable_pin(dapm, "SAR"); snd_soc_dapm_sync(dapm); break; + case 3: + /* detect error case */ + dev_err(nau8825->dev, "detection error; disable mic function\n"); + type = SND_JACK_HEADPHONE; + break; } /* Leaving HPOL/R grounded after jack insert by default. They will be @@ -1682,7 +1686,7 @@ static irqreturn_t nau8825_interrupt(int irq, void *data) } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) { if (nau8825_is_jack_inserted(regmap)) { event |= nau8825_jack_insert(nau8825); - if (!nau8825->high_imped) { + if (!nau8825->xtalk_bypass && !nau8825->high_imped) { /* Apply the cross talk suppression in the * headset without high impedance. */ @@ -2328,6 +2332,13 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: + /* Reset the configuration of jack type for detection */ + /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0); + /* ground HPL/HPR, MICGRND1/2 */ + regmap_update_bits(nau8825->regmap, + NAU8825_REG_HSD_CTRL, 0xf, 0xf); /* Cancel and reset cross talk detection funciton */ nau8825_xtalk_cancel(nau8825); /* Turn off all interruptions before system shutdown. Keep the @@ -2351,6 +2362,10 @@ static int __maybe_unused nau8825_suspend(struct snd_soc_codec *codec) disable_irq(nau8825->irq); snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); + /* Power down codec power; don't suppoet button wakeup */ + snd_soc_dapm_disable_pin(nau8825->dapm, "SAR"); + snd_soc_dapm_disable_pin(nau8825->dapm, "MICBIAS"); + snd_soc_dapm_sync(nau8825->dapm); regcache_cache_only(nau8825->regmap, true); regcache_mark_dirty(nau8825->regmap); @@ -2425,10 +2440,13 @@ static void nau8825_print_device_properties(struct nau8825 *nau8825) nau8825->jack_insert_debounce); dev_dbg(dev, "jack-eject-debounce: %d\n", nau8825->jack_eject_debounce); + dev_dbg(dev, "crosstalk-bypass: %d\n", + nau8825->xtalk_bypass); } static int nau8825_read_device_properties(struct device *dev, struct nau8825 *nau8825) { + int ret; nau8825->jkdet_enable = device_property_read_bool(dev, "nuvoton,jkdet-enable"); @@ -2436,30 +2454,60 @@ static int nau8825_read_device_properties(struct device *dev, "nuvoton,jkdet-pull-enable"); nau8825->jkdet_pull_up = device_property_read_bool(dev, "nuvoton,jkdet-pull-up"); - device_property_read_u32(dev, "nuvoton,jkdet-polarity", + ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity", &nau8825->jkdet_polarity); - device_property_read_u32(dev, "nuvoton,micbias-voltage", + if (ret) + nau8825->jkdet_polarity = 1; + ret = device_property_read_u32(dev, "nuvoton,micbias-voltage", &nau8825->micbias_voltage); - device_property_read_u32(dev, "nuvoton,vref-impedance", + if (ret) + nau8825->micbias_voltage = 6; + ret = device_property_read_u32(dev, "nuvoton,vref-impedance", &nau8825->vref_impedance); - device_property_read_u32(dev, "nuvoton,sar-threshold-num", + if (ret) + nau8825->vref_impedance = 2; + ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num", &nau8825->sar_threshold_num); - device_property_read_u32_array(dev, "nuvoton,sar-threshold", + if (ret) + nau8825->sar_threshold_num = 4; + ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold", nau8825->sar_threshold, nau8825->sar_threshold_num); - device_property_read_u32(dev, "nuvoton,sar-hysteresis", + if (ret) { + nau8825->sar_threshold[0] = 0x08; + nau8825->sar_threshold[1] = 0x12; + nau8825->sar_threshold[2] = 0x26; + nau8825->sar_threshold[3] = 0x73; + } + ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis", &nau8825->sar_hysteresis); - device_property_read_u32(dev, "nuvoton,sar-voltage", + if (ret) + nau8825->sar_hysteresis = 0; + ret = device_property_read_u32(dev, "nuvoton,sar-voltage", &nau8825->sar_voltage); - device_property_read_u32(dev, "nuvoton,sar-compare-time", + if (ret) + nau8825->sar_voltage = 6; + ret = device_property_read_u32(dev, "nuvoton,sar-compare-time", &nau8825->sar_compare_time); - device_property_read_u32(dev, "nuvoton,sar-sampling-time", + if (ret) + nau8825->sar_compare_time = 1; + ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time", &nau8825->sar_sampling_time); - device_property_read_u32(dev, "nuvoton,short-key-debounce", + if (ret) + nau8825->sar_sampling_time = 1; + ret = device_property_read_u32(dev, "nuvoton,short-key-debounce", &nau8825->key_debounce); - device_property_read_u32(dev, "nuvoton,jack-insert-debounce", + if (ret) + nau8825->key_debounce = 3; + ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce", &nau8825->jack_insert_debounce); - device_property_read_u32(dev, "nuvoton,jack-eject-debounce", + if (ret) + nau8825->jack_insert_debounce = 7; + ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce", &nau8825->jack_eject_debounce); + if (ret) + nau8825->jack_eject_debounce = 0; + nau8825->xtalk_bypass = device_property_read_bool(dev, + "nuvoton,crosstalk-bypass"); nau8825->mclk = devm_clk_get(dev, "mclk"); if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) { diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index 514fd13c2f46..8aee5c8647ae 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -476,6 +476,7 @@ struct nau8825 { int xtalk_event_mask; bool xtalk_protect; int imp_rms[NAU8825_XTALK_IMM]; + int xtalk_bypass; }; int nau8825_enable_jack_detect(struct snd_soc_codec *codec, diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index f91221b1ddf0..1b6796c4c471 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include <linux/acpi.h> #include <linux/fs.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -395,14 +396,14 @@ static const char * const rt5514_dmic_src[] = { "DMIC1", "DMIC2" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL, RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); static const struct snd_kcontrol_new rt5514_sto1_dmic_mux = SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL, RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); @@ -906,9 +907,23 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, if (rx_mask || tx_mask) val |= RT5514_TDM_MODE; - if (slots == 4) + switch (slots) { + case 4: val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH; + break; + + case 6: + val |= RT5514_TDMSLOT_SEL_RX_6CH | RT5514_TDMSLOT_SEL_TX_6CH; + break; + case 8: + val |= RT5514_TDMSLOT_SEL_RX_8CH | RT5514_TDMSLOT_SEL_TX_8CH; + break; + + case 2: + default: + break; + } switch (slot_width) { case 20: @@ -919,6 +934,10 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24; break; + case 25: + val |= RT5514_TDM_MODE2; + break; + case 32: val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32; break; @@ -930,7 +949,8 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE | RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK | - RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK, val); + RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK | + RT5514_TDM_MODE2, val); return 0; } @@ -1076,6 +1096,14 @@ static const struct of_device_id rt5514_of_match[] = { MODULE_DEVICE_TABLE(of, rt5514_of_match); #endif +#ifdef CONFIG_ACPI +static struct acpi_device_id rt5514_acpi_match[] = { + { "10EC5514", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5514_acpi_match); +#endif + static int rt5514_parse_dt(struct rt5514_priv *rt5514, struct device *dev) { device_property_read_u32(dev, "realtek,dmic-init-delay-ms", @@ -1179,6 +1207,7 @@ static const struct dev_pm_ops rt5514_i2_pm_ops = { static struct i2c_driver rt5514_i2c_driver = { .driver = { .name = "rt5514", + .acpi_match_table = ACPI_PTR(rt5514_acpi_match), .of_match_table = of_match_ptr(rt5514_of_match), .pm = &rt5514_i2_pm_ops, }, diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h index 5d343fb6d125..02bc212a86d9 100644 --- a/sound/soc/codecs/rt5514.h +++ b/sound/soc/codecs/rt5514.h @@ -117,6 +117,8 @@ #define RT5514_POW_ADCFEDL_BIT 0 /* RT5514_I2S_CTRL1 (0x2010) */ +#define RT5514_TDM_MODE2 (0x1 << 30) +#define RT5514_TDM_MODE2_SFT 30 #define RT5514_TDM_MODE (0x1 << 28) #define RT5514_TDM_MODE_SFT 28 #define RT5514_I2S_LR_MASK (0x1 << 26) @@ -136,6 +138,8 @@ #define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10) #define RT5514_TDMSLOT_SEL_RX_SFT 10 #define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10) +#define RT5514_TDMSLOT_SEL_RX_6CH (0x2 << 10) +#define RT5514_TDMSLOT_SEL_RX_8CH (0x3 << 10) #define RT5514_CH_LEN_RX_MASK (0x3 << 8) #define RT5514_CH_LEN_RX_SFT 8 #define RT5514_CH_LEN_RX_16 (0x0 << 8) @@ -145,6 +149,8 @@ #define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6) #define RT5514_TDMSLOT_SEL_TX_SFT 6 #define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6) +#define RT5514_TDMSLOT_SEL_TX_6CH (0x2 << 6) +#define RT5514_TDMSLOT_SEL_TX_8CH (0x3 << 6) #define RT5514_CH_LEN_TX_MASK (0x3 << 4) #define RT5514_CH_LEN_TX_SFT 4 #define RT5514_CH_LEN_TX_16 (0x0 << 4) diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 87844a45886a..9ec58166f7c4 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -34,6 +34,17 @@ #include "rl6231.h" #include "rt5645.h" +#define QUIRK_INV_JD1_1(q) ((q) & 1) +#define QUIRK_LEVEL_IRQ(q) (((q) >> 1) & 1) +#define QUIRK_IN2_DIFF(q) (((q) >> 2) & 1) +#define QUIRK_JD_MODE(q) (((q) >> 4) & 7) +#define QUIRK_DMIC1_DATA_PIN(q) (((q) >> 8) & 3) +#define QUIRK_DMIC2_DATA_PIN(q) (((q) >> 12) & 3) + +static unsigned int quirk = -1; +module_param(quirk, uint, 0444); +MODULE_PARM_DESC(quirk, "RT5645 pdata quirk override"); + #define RT5645_DEVICE_ID 0x6308 #define RT5650_DEVICE_ID 0x6419 @@ -59,7 +70,7 @@ static const struct regmap_range_cfg rt5645_ranges[] = { static const struct reg_sequence init_list[] = { {RT5645_PR_BASE + 0x3d, 0x3600}, - {RT5645_PR_BASE + 0x1c, 0xfd20}, + {RT5645_PR_BASE + 0x1c, 0xfd70}, {RT5645_PR_BASE + 0x20, 0x611f}, {RT5645_PR_BASE + 0x21, 0x4040}, {RT5645_PR_BASE + 0x23, 0x0004}, @@ -3151,7 +3162,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) snd_soc_dapm_sync(dapm); rt5645->jack_type = SND_JACK_HEADPHONE; } - if (rt5645->pdata.jd_invert) + if (rt5645->pdata.level_trigger_irq) regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR); } else { /* jack out */ @@ -3172,7 +3183,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) snd_soc_dapm_disable_pin(dapm, "LDO2"); snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); snd_soc_dapm_sync(dapm); - if (rt5645->pdata.jd_invert) + if (rt5645->pdata.level_trigger_irq) regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); } @@ -3238,24 +3249,16 @@ static void rt5645_jack_detect_work(struct work_struct *work) snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); return; - case 1: /* 2 port */ - val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0070; - break; - default: /* 1 port */ - val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0020; + default: /* read rt5645 jd1_1 status */ + val = snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x1000; break; } - switch (val) { - /* jack in */ - case 0x30: /* 2 port */ - case 0x0: /* 1 port or 2 port */ - if (rt5645->jack_type == 0) { - report = rt5645_jack_detect(rt5645->codec, 1); - /* for push button and jack out */ - break; - } + if (!val && (rt5645->jack_type == 0)) { /* jack in */ + report = rt5645_jack_detect(rt5645->codec, 1); + } else if (!val && rt5645->jack_type != 0) { + /* for push button and jack out */ btn_type = 0; if (snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x4) { /* button pressed */ @@ -3302,19 +3305,12 @@ static void rt5645_jack_detect_work(struct work_struct *work) mod_timer(&rt5645->btn_check_timer, msecs_to_jiffies(100)); } - - break; - /* jack out */ - case 0x70: /* 2 port */ - case 0x10: /* 2 port */ - case 0x20: /* 1 port */ + } else { + /* jack out */ report = 0; snd_soc_update_bits(rt5645->codec, RT5645_INT_IRQ_ST, 0x1, 0x0); rt5645_jack_detect(rt5645->codec, 0); - break; - default: - break; } snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); @@ -3601,7 +3597,7 @@ static struct rt5645_platform_data buddy_platform_data = { .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, .jd_mode = 3, - .jd_invert = true, + .level_trigger_irq = true, }; static struct dmi_system_id dmi_platform_intel_broadwell[] = { @@ -3614,6 +3610,33 @@ static struct dmi_system_id dmi_platform_intel_broadwell[] = { { } }; +static struct rt5645_platform_data gpd_win_platform_data = { + .jd_mode = 3, + .inv_jd1_1 = true, +}; + +static const struct dmi_system_id dmi_platform_gpd_win[] = { + { + /* + * Match for the GPDwin which unfortunately uses somewhat + * generic dmi strings, which is why we test for 4 strings. + * Comparing against 23 other byt/cht boards, board_vendor + * and board_name are unique to the GPDwin, where as only one + * other board has the same board_serial and 3 others have + * the same default product_name. Also the GPDwin is the + * only device to have both board_ and product_name not set. + */ + .ident = "GPD Win", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Default string"), + DMI_MATCH(DMI_BOARD_SERIAL, "Default string"), + DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), + }, + }, + {} +}; + static bool rt5645_check_dp(struct device *dev) { if (device_property_present(dev, "realtek,in2-differential") || @@ -3664,6 +3687,17 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, rt5645_parse_dt(rt5645, &i2c->dev); else if (dmi_check_system(dmi_platform_intel_braswell)) rt5645->pdata = general_platform_data; + else if (dmi_check_system(dmi_platform_gpd_win)) + rt5645->pdata = gpd_win_platform_data; + + if (quirk != -1) { + rt5645->pdata.in2_diff = QUIRK_IN2_DIFF(quirk); + rt5645->pdata.level_trigger_irq = QUIRK_LEVEL_IRQ(quirk); + rt5645->pdata.inv_jd1_1 = QUIRK_INV_JD1_1(quirk); + rt5645->pdata.jd_mode = QUIRK_JD_MODE(quirk); + rt5645->pdata.dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk); + rt5645->pdata.dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk); + } rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect", GPIOD_IN); @@ -3745,6 +3779,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, ret); } + regmap_update_bits(rt5645->regmap, RT5645_CLSD_OUT_CTRL, 0xc0, 0xc0); + if (rt5645->pdata.in2_diff) regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL, RT5645_IN_DF2, RT5645_IN_DF2); @@ -3848,12 +3884,16 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, default: break; } + if (rt5645->pdata.inv_jd1_1) { + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); + } } regmap_update_bits(rt5645->regmap, RT5645_ADDA_CLK1, RT5645_I2S_PD1_MASK, RT5645_I2S_PD1_2); - if (rt5645->pdata.jd_invert) { + if (rt5645->pdata.level_trigger_irq) { regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); } @@ -3897,6 +3937,7 @@ static int rt5645_i2c_remove(struct i2c_client *i2c) cancel_delayed_work_sync(&rt5645->jack_detect_work); cancel_delayed_work_sync(&rt5645->rcclock_work); + del_timer_sync(&rt5645->btn_check_timer); snd_soc_unregister_codec(&i2c->dev); regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index f5d34153e21f..db05b60d5002 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -586,44 +586,6 @@ static const struct snd_kcontrol_new hpo_r_mute_control = SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL, RT5651_R_MUTE_SFT, 1, 1); -/* INL/R source */ -static const char * const rt5651_inl_src[] = {"IN2P", "HPOVOLLP"}; - -static SOC_ENUM_SINGLE_DECL( - rt5651_inl_enum, RT5651_INL1_INR1_VOL, - RT5651_INL_SEL_SFT, rt5651_inl_src); - -static const struct snd_kcontrol_new rt5651_inl1_mux = - SOC_DAPM_ENUM("INL1 source", rt5651_inl_enum); - -static const char * const rt5651_inr1_src[] = {"IN2N", "HPOVOLRP"}; - -static SOC_ENUM_SINGLE_DECL( - rt5651_inr1_enum, RT5651_INL1_INR1_VOL, - RT5651_INR_SEL_SFT, rt5651_inr1_src); - -static const struct snd_kcontrol_new rt5651_inr1_mux = - SOC_DAPM_ENUM("INR1 source", rt5651_inr1_enum); - -static const char * const rt5651_inl2_src[] = {"IN3P", "OUTVOLLP"}; - -static SOC_ENUM_SINGLE_DECL( - rt5651_inl2_enum, RT5651_INL2_INR2_VOL, - RT5651_INL_SEL_SFT, rt5651_inl2_src); - -static const struct snd_kcontrol_new rt5651_inl2_mux = - SOC_DAPM_ENUM("INL2 source", rt5651_inl2_enum); - -static const char * const rt5651_inr2_src[] = {"IN3N", "OUTVOLRP"}; - -static SOC_ENUM_SINGLE_DECL( - rt5651_inr2_enum, RT5651_INL2_INR2_VOL, - RT5651_INR_SEL_SFT, rt5651_inr2_src); - -static const struct snd_kcontrol_new rt5651_inr2_mux = - SOC_DAPM_ENUM("INR2 source", rt5651_inr2_enum); - - /* Stereo ADC source */ static const char * const rt5651_stereo1_adc1_src[] = {"DD MIX", "ADC"}; @@ -955,11 +917,7 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = { RT5651_PWR_IN2_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR2 VOL", RT5651_PWR_VOL, RT5651_PWR_IN2_R_BIT, 0, NULL, 0), - /* IN Mux */ - SND_SOC_DAPM_MUX("INL1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl1_mux), - SND_SOC_DAPM_MUX("INR1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr1_mux), - SND_SOC_DAPM_MUX("INL2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl2_mux), - SND_SOC_DAPM_MUX("INR2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr2_mux), + /* REC Mixer */ SND_SOC_DAPM_MIXER("RECMIXL", RT5651_PWR_MIXER, RT5651_PWR_RM_L_BIT, 0, rt5651_rec_l_mix, ARRAY_SIZE(rt5651_rec_l_mix)), diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index a32508d7dcfd..a33202affeb1 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -2847,6 +2847,8 @@ static int rt5663_resume(struct snd_soc_codec *codec) regcache_cache_only(rt5663->regmap, false); regcache_sync(rt5663->regmap); + rt5663_irq(0, rt5663); + return 0; } #else @@ -3141,7 +3143,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5663->regmap, RT5663_DIG_MISC, RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN); regmap_update_bits(rt5663->regmap, RT5663_AUTO_1MRC_CLK, - RT5663_IRQ_POW_SAV_MASK, RT5663_IRQ_POW_SAV_EN); + RT5663_IRQ_MANUAL_MASK, RT5663_IRQ_MANUAL_EN); regmap_update_bits(rt5663->regmap, RT5663_IRQ_1, RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN); regmap_update_bits(rt5663->regmap, RT5663_GPIO_1, diff --git a/sound/soc/codecs/rt5663.h b/sound/soc/codecs/rt5663.h index d77fae619f2f..4621812c94d8 100644 --- a/sound/soc/codecs/rt5663.h +++ b/sound/soc/codecs/rt5663.h @@ -590,6 +590,10 @@ #define RT5663_IRQ_POW_SAV_JD1_SHIFT 14 #define RT5663_IRQ_POW_SAV_JD1_DIS (0x0 << 14) #define RT5663_IRQ_POW_SAV_JD1_EN (0x1 << 14) +#define RT5663_IRQ_MANUAL_MASK (0x1 << 8) +#define RT5663_IRQ_MANUAL_SHIFT 8 +#define RT5663_IRQ_MANUAL_DIS (0x0 << 8) +#define RT5663_IRQ_MANUAL_EN (0x1 << 8) /* IRQ Control 1 (0x00b6) */ #define RT5663_EN_CB_JD_MASK (0x1 << 3) diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 8cd22307f5b6..370ed54d1e15 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -70,6 +70,7 @@ struct rt5665_priv { int jack_type; int irq_work_delay_time; unsigned int sar_adc_value; + bool calibration_done; }; static const struct reg_default rt5665_reg[] = { @@ -912,46 +913,46 @@ static const char * const rt5665_data_select[] = { "L/R", "R/L", "L/L", "R/R" }; -static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_01_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_01_adc_enum, RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT01_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_23_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_23_adc_enum, RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT23_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_45_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_45_adc_enum, RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT45_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if1_1_67_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_67_adc_enum, RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT67_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_01_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_01_adc_enum, RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT01_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_23_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_23_adc_enum, RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT23_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_45_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_45_adc_enum, RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT45_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if1_2_67_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_67_adc_enum, RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT67_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if2_1_dac_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if2_1_dac_enum, RT5665_DIG_INF2_DATA, RT5665_IF2_1_DAC_SEL_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if2_1_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if2_1_adc_enum, RT5665_DIG_INF2_DATA, RT5665_IF2_1_ADC_SEL_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if2_2_dac_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if2_2_dac_enum, RT5665_DIG_INF2_DATA, RT5665_IF2_2_DAC_SEL_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if2_2_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if2_2_adc_enum, RT5665_DIG_INF2_DATA, RT5665_IF2_2_ADC_SEL_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if3_dac_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if3_dac_enum, RT5665_DIG_INF3_DATA, RT5665_IF3_DAC_SEL_SFT, rt5665_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5665_if3_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5665_if3_adc_enum, RT5665_DIG_INF3_DATA, RT5665_IF3_ADC_SEL_SFT, rt5665_data_select); static const struct snd_kcontrol_new rt5665_if1_1_01_adc_swap_mux = @@ -1305,6 +1306,11 @@ static void rt5665_jack_detect_handler(struct work_struct *work) usleep_range(10000, 15000); } + while (!rt5665->calibration_done) { + pr_debug("%s calibration not ready\n", __func__); + usleep_range(10000, 15000); + } + mutex_lock(&rt5665->calibrate_mutex); val = snd_soc_read(rt5665->codec, RT5665_AJD1_CTRL) & 0x0010; @@ -1819,14 +1825,14 @@ static const char * const rt5665_dac2_src[] = { "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "Mono ADC MIX" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_dac_l2_enum, RT5665_DAC2_CTRL, RT5665_DAC_L2_SEL_SFT, rt5665_dac2_src); static const struct snd_kcontrol_new rt5665_dac_l2_mux = SOC_DAPM_ENUM("Digital DAC L2 Source", rt5665_dac_l2_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_dac_r2_enum, RT5665_DAC2_CTRL, RT5665_DAC_R2_SEL_SFT, rt5665_dac2_src); @@ -1839,14 +1845,14 @@ static const char * const rt5665_dac3_src[] = { "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "STO2 ADC MIX" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_dac_l3_enum, RT5665_DAC3_CTRL, RT5665_DAC_L3_SEL_SFT, rt5665_dac3_src); static const struct snd_kcontrol_new rt5665_dac_l3_mux = SOC_DAPM_ENUM("Digital DAC L3 Source", rt5665_dac_l3_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_dac_r3_enum, RT5665_DAC3_CTRL, RT5665_DAC_R3_SEL_SFT, rt5665_dac3_src); @@ -1859,14 +1865,14 @@ static const char * const rt5665_sto1_adc1_src[] = { "DD Mux", "ADC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto1_adc1l_enum, RT5665_STO1_ADC_MIXER, RT5665_STO1_ADC1L_SRC_SFT, rt5665_sto1_adc1_src); static const struct snd_kcontrol_new rt5665_sto1_adc1l_mux = SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5665_sto1_adc1l_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto1_adc1r_enum, RT5665_STO1_ADC_MIXER, RT5665_STO1_ADC1R_SRC_SFT, rt5665_sto1_adc1_src); @@ -1879,14 +1885,14 @@ static const char * const rt5665_sto1_adc_src[] = { "ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto1_adcl_enum, RT5665_STO1_ADC_MIXER, RT5665_STO1_ADCL_SRC_SFT, rt5665_sto1_adc_src); static const struct snd_kcontrol_new rt5665_sto1_adcl_mux = SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5665_sto1_adcl_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto1_adcr_enum, RT5665_STO1_ADC_MIXER, RT5665_STO1_ADCR_SRC_SFT, rt5665_sto1_adc_src); @@ -1899,14 +1905,14 @@ static const char * const rt5665_sto1_adc2_src[] = { "DAC MIX", "DMIC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto1_adc2l_enum, RT5665_STO1_ADC_MIXER, RT5665_STO1_ADC2L_SRC_SFT, rt5665_sto1_adc2_src); static const struct snd_kcontrol_new rt5665_sto1_adc2l_mux = SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5665_sto1_adc2l_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto1_adc2r_enum, RT5665_STO1_ADC_MIXER, RT5665_STO1_ADC2R_SRC_SFT, rt5665_sto1_adc2_src); @@ -1919,7 +1925,7 @@ static const char * const rt5665_sto1_dmic_src[] = { "DMIC1", "DMIC2" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto1_dmic_enum, RT5665_STO1_ADC_MIXER, RT5665_STO1_DMIC_SRC_SFT, rt5665_sto1_dmic_src); @@ -1931,7 +1937,7 @@ static const char * const rt5665_sto1_dd_l_src[] = { "STO2 DAC", "MONO DAC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto1_dd_l_enum, RT5665_STO1_ADC_MIXER, RT5665_STO1_DD_L_SRC_SFT, rt5665_sto1_dd_l_src); @@ -1943,7 +1949,7 @@ static const char * const rt5665_sto1_dd_r_src[] = { "STO2 DAC", "MONO DAC", "AEC REF" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto1_dd_r_enum, RT5665_STO1_ADC_MIXER, RT5665_STO1_DD_R_SRC_SFT, rt5665_sto1_dd_r_src); @@ -1956,7 +1962,7 @@ static const char * const rt5665_mono_adc_l2_src[] = { "DAC MIXL", "DMIC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_adc_l2_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_ADC_L2_SRC_SFT, rt5665_mono_adc_l2_src); @@ -1970,7 +1976,7 @@ static const char * const rt5665_mono_adc_l1_src[] = { "DD Mux", "ADC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_adc_l1_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_ADC_L1_SRC_SFT, rt5665_mono_adc_l1_src); @@ -1982,14 +1988,14 @@ static const char * const rt5665_mono_dd_src[] = { "STO2 DAC", "MONO DAC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_dd_l_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_DD_L_SRC_SFT, rt5665_mono_dd_src); static const struct snd_kcontrol_new rt5665_mono_dd_l_mux = SOC_DAPM_ENUM("Mono DD L Source", rt5665_mono_dd_l_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_dd_r_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_DD_R_SRC_SFT, rt5665_mono_dd_src); @@ -2002,14 +2008,14 @@ static const char * const rt5665_mono_adc_src[] = { "ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_adc_l_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_ADC_L_SRC_SFT, rt5665_mono_adc_src); static const struct snd_kcontrol_new rt5665_mono_adc_l_mux = SOC_DAPM_ENUM("Mono ADC L Source", rt5665_mono_adc_l_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_adcr_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_ADC_R_SRC_SFT, rt5665_mono_adc_src); @@ -2022,7 +2028,7 @@ static const char * const rt5665_mono_dmic_l_src[] = { "DMIC1 L", "DMIC2 L" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_dmic_l_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_DMIC_L_SRC_SFT, rt5665_mono_dmic_l_src); @@ -2035,7 +2041,7 @@ static const char * const rt5665_mono_adc_r2_src[] = { "DAC MIXR", "DMIC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_adc_r2_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_ADC_R2_SRC_SFT, rt5665_mono_adc_r2_src); @@ -2048,7 +2054,7 @@ static const char * const rt5665_mono_adc_r1_src[] = { "DD Mux", "ADC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_adc_r1_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_ADC_R1_SRC_SFT, rt5665_mono_adc_r1_src); @@ -2061,7 +2067,7 @@ static const char * const rt5665_mono_dmic_r_src[] = { "DMIC1 R", "DMIC2 R" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_mono_dmic_r_enum, RT5665_MONO_ADC_MIXER, RT5665_MONO_DMIC_R_SRC_SFT, rt5665_mono_dmic_r_src); @@ -2075,14 +2081,14 @@ static const char * const rt5665_sto2_adc1_src[] = { "DD Mux", "ADC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto2_adc1l_enum, RT5665_STO2_ADC_MIXER, RT5665_STO2_ADC1L_SRC_SFT, rt5665_sto2_adc1_src); static const struct snd_kcontrol_new rt5665_sto2_adc1l_mux = SOC_DAPM_ENUM("Stereo2 ADC1L Source", rt5665_sto2_adc1l_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto2_adc1r_enum, RT5665_STO2_ADC_MIXER, RT5665_STO2_ADC1R_SRC_SFT, rt5665_sto2_adc1_src); @@ -2095,14 +2101,14 @@ static const char * const rt5665_sto2_adc_src[] = { "ADC1 L", "ADC1 R", "ADC2 L" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto2_adcl_enum, RT5665_STO2_ADC_MIXER, RT5665_STO2_ADCL_SRC_SFT, rt5665_sto2_adc_src); static const struct snd_kcontrol_new rt5665_sto2_adcl_mux = SOC_DAPM_ENUM("Stereo2 ADCL Source", rt5665_sto2_adcl_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto2_adcr_enum, RT5665_STO2_ADC_MIXER, RT5665_STO2_ADCR_SRC_SFT, rt5665_sto2_adc_src); @@ -2115,14 +2121,14 @@ static const char * const rt5665_sto2_adc2_src[] = { "DAC MIX", "DMIC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto2_adc2l_enum, RT5665_STO2_ADC_MIXER, RT5665_STO2_ADC2L_SRC_SFT, rt5665_sto2_adc2_src); static const struct snd_kcontrol_new rt5665_sto2_adc2l_mux = SOC_DAPM_ENUM("Stereo2 ADC2L Source", rt5665_sto2_adc2l_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto2_adc2r_enum, RT5665_STO2_ADC_MIXER, RT5665_STO2_ADC2R_SRC_SFT, rt5665_sto2_adc2_src); @@ -2135,7 +2141,7 @@ static const char * const rt5665_sto2_dmic_src[] = { "DMIC1", "DMIC2" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto2_dmic_enum, RT5665_STO2_ADC_MIXER, RT5665_STO2_DMIC_SRC_SFT, rt5665_sto2_dmic_src); @@ -2147,7 +2153,7 @@ static const char * const rt5665_sto2_dd_l_src[] = { "STO2 DAC", "MONO DAC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto2_dd_l_enum, RT5665_STO2_ADC_MIXER, RT5665_STO2_DD_L_SRC_SFT, rt5665_sto2_dd_l_src); @@ -2159,7 +2165,7 @@ static const char * const rt5665_sto2_dd_r_src[] = { "STO2 DAC", "MONO DAC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_sto2_dd_r_enum, RT5665_STO2_ADC_MIXER, RT5665_STO2_DD_R_SRC_SFT, rt5665_sto2_dd_r_src); @@ -2172,14 +2178,14 @@ static const char * const rt5665_dac1_src[] = { "IF1 DAC1", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_dac_r1_enum, RT5665_AD_DA_MIXER, RT5665_DAC1_R_SEL_SFT, rt5665_dac1_src); static const struct snd_kcontrol_new rt5665_dac_r1_mux = SOC_DAPM_ENUM("DAC R1 Source", rt5665_dac_r1_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_dac_l1_enum, RT5665_AD_DA_MIXER, RT5665_DAC1_L_SEL_SFT, rt5665_dac1_src); @@ -2192,14 +2198,14 @@ static const char * const rt5665_dig_dac_mix_src[] = { "Stereo1 DAC Mixer", "Stereo2 DAC Mixer", "Mono DAC Mixer" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_dig_dac_mixl_enum, RT5665_A_DAC1_MUX, RT5665_DAC_MIX_L_SFT, rt5665_dig_dac_mix_src); static const struct snd_kcontrol_new rt5665_dig_dac_mixl_mux = SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5665_dig_dac_mixl_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_dig_dac_mixr_enum, RT5665_A_DAC1_MUX, RT5665_DAC_MIX_R_SFT, rt5665_dig_dac_mix_src); @@ -2212,14 +2218,14 @@ static const char * const rt5665_alg_dac1_src[] = { "Stereo1 DAC Mixer", "DAC1", "DMIC1" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_alg_dac_l1_enum, RT5665_A_DAC1_MUX, RT5665_A_DACL1_SFT, rt5665_alg_dac1_src); static const struct snd_kcontrol_new rt5665_alg_dac_l1_mux = SOC_DAPM_ENUM("Analog DAC L1 Source", rt5665_alg_dac_l1_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_alg_dac_r1_enum, RT5665_A_DAC1_MUX, RT5665_A_DACR1_SFT, rt5665_alg_dac1_src); @@ -2232,14 +2238,14 @@ static const char * const rt5665_alg_dac2_src[] = { "Mono DAC Mixer", "DAC2" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_alg_dac_l2_enum, RT5665_A_DAC2_MUX, RT5665_A_DACL2_SFT, rt5665_alg_dac2_src); static const struct snd_kcontrol_new rt5665_alg_dac_l2_mux = SOC_DAPM_ENUM("Analog DAC L2 Source", rt5665_alg_dac_l2_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_alg_dac_r2_enum, RT5665_A_DAC2_MUX, RT5665_A_DACR2_SFT, rt5665_alg_dac2_src); @@ -2253,7 +2259,7 @@ static const char * const rt5665_if2_1_adc_in_src[] = { "IF1 DAC2", "IF2_2 DAC", "IF3 DAC", "DAC1 MIX" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if2_1_adc_in_enum, RT5665_DIG_INF2_DATA, RT5665_IF2_1_ADC_IN_SFT, rt5665_if2_1_adc_in_src); @@ -2266,7 +2272,7 @@ static const char * const rt5665_if2_2_adc_in_src[] = { "IF1 DAC2", "IF2_1 DAC", "IF3 DAC", "DAC1 MIX" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if2_2_adc_in_enum, RT5665_DIG_INF2_DATA, RT5665_IF2_2_ADC_IN_SFT, rt5665_if2_2_adc_in_src); @@ -2280,7 +2286,7 @@ static const char * const rt5665_if3_adc_in_src[] = { "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "DAC1 MIX" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if3_adc_in_enum, RT5665_DIG_INF3_DATA, RT5665_IF3_ADC_IN_SFT, rt5665_if3_adc_in_src); @@ -2293,14 +2299,14 @@ static const char * const rt5665_pdm_src[] = { "Stereo1 DAC", "Stereo2 DAC", "Mono DAC" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_pdm_l_enum, RT5665_PDM_OUT_CTRL, RT5665_PDM1_L_SFT, rt5665_pdm_src); static const struct snd_kcontrol_new rt5665_pdm_l_mux = SOC_DAPM_ENUM("PDM L Source", rt5665_pdm_l_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_pdm_r_enum, RT5665_PDM_OUT_CTRL, RT5665_PDM1_R_SFT, rt5665_pdm_src); @@ -2314,7 +2320,7 @@ static const char * const rt5665_if1_1_adc1_data_src[] = { "STO1 ADC", "IF2_1 DAC", }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if1_1_adc1_data_enum, RT5665_TDM_CTRL_3, RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_1_adc1_data_src); @@ -2326,7 +2332,7 @@ static const char * const rt5665_if1_1_adc2_data_src[] = { "STO2 ADC", "IF2_2 DAC", }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if1_1_adc2_data_enum, RT5665_TDM_CTRL_3, RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_1_adc2_data_src); @@ -2338,7 +2344,7 @@ static const char * const rt5665_if1_1_adc3_data_src[] = { "MONO ADC", "IF3 DAC", }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if1_1_adc3_data_enum, RT5665_TDM_CTRL_3, RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_1_adc3_data_src); @@ -2350,7 +2356,7 @@ static const char * const rt5665_if1_2_adc1_data_src[] = { "STO1 ADC", "IF1 DAC", }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if1_2_adc1_data_enum, RT5665_TDM_CTRL_4, RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_2_adc1_data_src); @@ -2362,7 +2368,7 @@ static const char * const rt5665_if1_2_adc2_data_src[] = { "STO2 ADC", "IF2_1 DAC", }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if1_2_adc2_data_enum, RT5665_TDM_CTRL_4, RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_2_adc2_data_src); @@ -2374,7 +2380,7 @@ static const char * const rt5665_if1_2_adc3_data_src[] = { "MONO ADC", "IF2_2 DAC", }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if1_2_adc3_data_enum, RT5665_TDM_CTRL_4, RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_2_adc3_data_src); @@ -2386,7 +2392,7 @@ static const char * const rt5665_if1_2_adc4_data_src[] = { "DAC1", "IF3 DAC", }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_if1_2_adc4_data_enum, RT5665_TDM_CTRL_4, RT5665_IF1_ADC4_SEL_SFT, rt5665_if1_2_adc4_data_src); @@ -2401,14 +2407,14 @@ static const char * const rt5665_tdm_adc_data_src[] = { "4123", "4132", "4213", "4231", "4312", "4321" }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_tdm1_adc_data_enum, RT5665_TDM_CTRL_3, RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src); static const struct snd_kcontrol_new rt5665_tdm1_adc_mux = SOC_DAPM_ENUM("TDM1 ADC Mux", rt5665_tdm1_adc_data_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL( rt5665_tdm2_adc_data_enum, RT5665_TDM_CTRL_4, RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src); @@ -2607,7 +2613,7 @@ static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - unsigned int val1, val2, mask1, mask2 = 0; + unsigned int val1, val2, mask1 = 0, mask2 = 0; switch (w->shift) { case RT5665_PWR_I2S2_1_BIT: @@ -2635,13 +2641,17 @@ static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w, } switch (event) { case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, val1); + if (mask1) + snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, + mask1, val1); if (mask2) snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2, mask2, val2); break; case SND_SOC_DAPM_POST_PMD: - snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, 0); + if (mask1) + snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, + mask1, 0); if (mask2) snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2, mask2, 0); @@ -2684,6 +2694,8 @@ static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = { RT5665_DAC_MONO_R_ASRC_SFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5665_ASRC_1, RT5665_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5665_ASRC_1, + RT5665_ADC_STO2_ASRC_SFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("ADC Mono L ASRC", 1, RT5665_ASRC_1, RT5665_ADC_MONO_L_ASRC_SFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("ADC Mono R ASRC", 1, RT5665_ASRC_1, @@ -3227,6 +3239,7 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = { /*ASRC*/ {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc}, {"ADC Mono Left Filter", NULL, "ADC Mono L ASRC", is_using_asrc}, {"ADC Mono Right Filter", NULL, "ADC Mono R ASRC", is_using_asrc}, {"DAC Mono Left Filter", NULL, "DAC Mono L ASRC", is_using_asrc}, @@ -4688,6 +4701,7 @@ static void rt5665_calibrate(struct rt5665_priv *rt5665) regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120); out_unlock: + rt5665->calibration_done = true; mutex_unlock(&rt5665->calibrate_mutex); } @@ -4922,7 +4936,7 @@ static struct acpi_device_id rt5665_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5665_acpi_match); #endif -struct i2c_driver rt5665_i2c_driver = { +static struct i2c_driver rt5665_i2c_driver = { .driver = { .name = "rt5665", .of_match_table = of_match_ptr(rt5665_of_match), diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index e27c5a4a0a15..0ec7985ed306 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -1717,7 +1717,6 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = { SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), /* DSP */ SND_SOC_DAPM_PGA("TxDP_ADC", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -2023,7 +2022,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, { "Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter" }, - { "ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll }, { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, { "Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter" }, @@ -2062,7 +2060,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" }, { "Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter" }, - { "ADC Stereo2 Filter", NULL, "PLL1", is_sys_clk_from_pll }, { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" }, { "Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter" }, @@ -2086,13 +2083,13 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { { "IF1 ADC1 IN1 Mux", "IF1_ADC3", "IF1_ADC3" }, { "IF1 ADC1 IN2 Mux", "IF1_ADC1_IN1", "IF1 ADC1 IN1 Mux" }, - { "IF1 ADC1 IN2 Mux", "IF1_ADC4", "IF1_ADC4" }, + { "IF1 ADC1 IN2 Mux", "IF1_ADC4", "TxDP_ADC" }, { "IF1 ADC2 IN Mux", "IF_ADC2", "IF_ADC2" }, { "IF1 ADC2 IN Mux", "VAD_ADC", "VAD_ADC" }, { "IF1 ADC2 IN1 Mux", "IF1_ADC2_IN", "IF1 ADC2 IN Mux" }, - { "IF1 ADC2 IN1 Mux", "IF1_ADC4", "IF1_ADC4" }, + { "IF1 ADC2 IN1 Mux", "IF1_ADC4", "TxDP_ADC" }, { "IF1_ADC1" , NULL, "IF1 ADC1 IN2 Mux" }, { "IF1_ADC2" , NULL, "IF1 ADC2 IN1 Mux" }, @@ -2445,10 +2442,9 @@ static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) +static int rt5670_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) { - struct snd_soc_codec *codec = dai->codec; struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); unsigned int reg_val = 0; @@ -2472,7 +2468,7 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, if (clk_id != RT5670_SCLK_S_RCCLK) rt5670->sysclk_src = clk_id; - dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + dev_dbg(codec->dev, "Sysclk : %dHz clock id : %d\n", freq, clk_id); return 0; } @@ -2724,7 +2720,6 @@ static int rt5670_resume(struct snd_soc_codec *codec) static const struct snd_soc_dai_ops rt5670_aif_dai_ops = { .hw_params = rt5670_hw_params, .set_fmt = rt5670_set_dai_fmt, - .set_sysclk = rt5670_set_dai_sysclk, .set_tdm_slot = rt5670_set_tdm_slot, .set_pll = rt5670_set_dai_pll, }; @@ -2777,6 +2772,7 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5670 = { .resume = rt5670_resume, .set_bias_level = rt5670_set_bias_level, .idle_bias_off = true, + .set_sysclk = rt5670_set_codec_sysclk, .component_driver = { .controls = rt5670_snd_controls, .num_controls = ARRAY_SIZE(rt5670_snd_controls), @@ -2849,6 +2845,10 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), }, }, + {} +}; + +static const struct dmi_system_id dmi_platform_intel_bytcht_jdmode2[] = { { .ident = "Lenovo Thinkpad Tablet 10", .matches = { @@ -2883,6 +2883,11 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; rt5670->pdata.dev_gpio = true; rt5670->pdata.jd_mode = 1; + } else if (dmi_check_system(dmi_platform_intel_bytcht_jdmode2)) { + rt5670->pdata.dmic_en = true; + rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; + rt5670->pdata.dev_gpio = true; + rt5670->pdata.jd_mode = 2; } rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap); diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 65ac4518ad06..36e530a36c82 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -41,15 +41,6 @@ #define RT5677_PR_BASE (RT5677_PR_RANGE_BASE + (0 * RT5677_PR_SPACING)) -/* GPIO indexes defined by ACPI */ -enum { - RT5677_GPIO_PLUG_DET = 0, - RT5677_GPIO_MIC_PRESENT_L = 1, - RT5677_GPIO_HOTWORD_DET_L = 2, - RT5677_GPIO_DSP_INT = 3, - RT5677_GPIO_HP_AMP_SHDN_L = 4, -}; - static const struct regmap_range_cfg rt5677_ranges[] = { { .name = "PR", @@ -5030,7 +5021,6 @@ static const struct regmap_config rt5677_regmap = { static const struct i2c_device_id rt5677_i2c_id[] = { { "rt5677", RT5677 }, { "rt5676", RT5676 }, - { "RT5677CE:00", RT5677 }, { } }; MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); @@ -5041,28 +5031,19 @@ static const struct of_device_id rt5677_of_match[] = { }; MODULE_DEVICE_TABLE(of, rt5677_of_match); -static const struct acpi_gpio_params plug_det_gpio = { RT5677_GPIO_PLUG_DET, 0, false }; -static const struct acpi_gpio_params mic_present_gpio = { RT5677_GPIO_MIC_PRESENT_L, 0, false }; -static const struct acpi_gpio_params headphone_enable_gpio = { RT5677_GPIO_HP_AMP_SHDN_L, 0, false }; - -static const struct acpi_gpio_mapping bdw_rt5677_gpios[] = { - { "plug-det-gpios", &plug_det_gpio, 1 }, - { "mic-present-gpios", &mic_present_gpio, 1 }, - { "headphone-enable-gpios", &headphone_enable_gpio, 1 }, - { NULL }, +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5677_acpi_match[] = { + { "RT5677CE", RT5677 }, + { } }; +MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match); +#endif static void rt5677_read_acpi_properties(struct rt5677_priv *rt5677, struct device *dev) { - int ret; u32 val; - ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), - bdw_rt5677_gpios); - if (ret) - dev_warn(dev, "Failed to add driver gpios\n"); - if (!device_property_read_u32(dev, "DCLK", &val)) rt5677->pdata.dmic2_clk_pin = val; @@ -5301,6 +5282,7 @@ static struct i2c_driver rt5677_i2c_driver = { .driver = { .name = "rt5677", .of_match_table = rt5677_of_match, + .acpi_match_table = ACPI_PTR(rt5677_acpi_match), }, .probe = rt5677_i2c_probe, .remove = rt5677_i2c_remove, diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 5a2702edeb77..8f6814c1eb6b 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -74,6 +74,20 @@ static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_DAP_AVC_DECAY, 0x0050 }, }; +/* AVC: Threshold dB -> register: pre-calculated values */ +static const u16 avc_thr_db2reg[97] = { + 0x5168, 0x488E, 0x40AA, 0x39A1, 0x335D, 0x2DC7, 0x28CC, 0x245D, 0x2068, + 0x1CE2, 0x19BE, 0x16F1, 0x1472, 0x1239, 0x103E, 0x0E7A, 0x0CE6, 0x0B7F, + 0x0A3F, 0x0922, 0x0824, 0x0741, 0x0677, 0x05C3, 0x0522, 0x0493, 0x0414, + 0x03A2, 0x033D, 0x02E3, 0x0293, 0x024B, 0x020B, 0x01D2, 0x019F, 0x0172, + 0x014A, 0x0126, 0x0106, 0x00E9, 0x00D0, 0x00B9, 0x00A5, 0x0093, 0x0083, + 0x0075, 0x0068, 0x005D, 0x0052, 0x0049, 0x0041, 0x003A, 0x0034, 0x002E, + 0x0029, 0x0025, 0x0021, 0x001D, 0x001A, 0x0017, 0x0014, 0x0012, 0x0010, + 0x000E, 0x000D, 0x000B, 0x000A, 0x0009, 0x0008, 0x0007, 0x0006, 0x0005, + 0x0005, 0x0004, 0x0004, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, 0x0002, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; + /* regulator supplies for sgtl5000, VDDD is an optional external supply */ enum sgtl5000_regulator_supplies { VDDA, @@ -382,6 +396,65 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol, return 0; } +/* + * custom function to get AVC threshold + * + * The threshold dB is calculated by rearranging the calculation from the + * avc_put_threshold function: register_value = 10^(dB/20) * 0.636 * 2^15 ==> + * dB = ( fls(register_value) - 14.347 ) * 6.02 + * + * As this calculation is expensive and the threshold dB values may not exeed + * 0 to 96 we use pre-calculated values. + */ +static int avc_get_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int db, i; + u16 reg = snd_soc_read(codec, SGTL5000_DAP_AVC_THRESHOLD); + + /* register value 0 => -96dB */ + if (!reg) { + ucontrol->value.integer.value[0] = 96; + ucontrol->value.integer.value[1] = 96; + return 0; + } + + /* get dB from register value (rounded down) */ + for (i = 0; avc_thr_db2reg[i] > reg; i++) + ; + db = i; + + ucontrol->value.integer.value[0] = db; + ucontrol->value.integer.value[1] = db; + + return 0; +} + +/* + * custom function to put AVC threshold + * + * The register value is calculated by following formula: + * register_value = 10^(dB/20) * 0.636 * 2^15 + * As this calculation is expensive and the threshold dB values may not exeed + * 0 to 96 we use pre-calculated values. + */ +static int avc_put_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int db; + u16 reg; + + db = (int)ucontrol->value.integer.value[0]; + if (db < 0 || db > 96) + return -EINVAL; + reg = avc_thr_db2reg[db]; + snd_soc_write(codec, SGTL5000_DAP_AVC_THRESHOLD, reg); + + return 0; +} + static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0); /* tlv for mic gain, 0db 20db 30db 40db */ @@ -396,6 +469,12 @@ static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0); /* tlv for lineout volume, 31 steps of .5db each */ static const DECLARE_TLV_DB_SCALE(lineout_volume, -1550, 50, 0); +/* tlv for dap avc max gain, 0db, 6db, 12db */ +static const DECLARE_TLV_DB_SCALE(avc_max_gain, 0, 600, 0); + +/* tlv for dap avc threshold, */ +static const DECLARE_TLV_DB_MINMAX(avc_threshold, 0, 9600); + static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { /* SOC_DOUBLE_S8_TLV with invert */ { @@ -434,6 +513,16 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { 0x1f, 1, lineout_volume), SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1), + + /* Automatic Volume Control (DAP AVC) */ + SOC_SINGLE("AVC Switch", SGTL5000_DAP_AVC_CTRL, 0, 1, 0), + SOC_SINGLE("AVC Hard Limiter Switch", SGTL5000_DAP_AVC_CTRL, 5, 1, 0), + SOC_SINGLE_TLV("AVC Max Gain Volume", SGTL5000_DAP_AVC_CTRL, 12, 2, 0, + avc_max_gain), + SOC_SINGLE("AVC Integrator Response", SGTL5000_DAP_AVC_CTRL, 8, 3, 0), + SOC_SINGLE_EXT_TLV("AVC Threshold Volume", SGTL5000_DAP_AVC_THRESHOLD, + 0, 96, 0, avc_get_threshold, avc_put_threshold, + avc_threshold), }; /* mute the codec used by alsa core */ diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index f8a90ba8cd71..d7d03c92cb8a 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1210,7 +1210,7 @@ static const struct snd_soc_dai_ops aic31xx_dai_ops = { static struct snd_soc_dai_driver dac31xx_dai_driver[] = { { - .name = "tlv32dac31xx-hifi", + .name = "tlv320dac31xx-hifi", .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 20695b691aff..65c059b5ffd7 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -482,8 +482,6 @@ struct wm_coeff_ctl_ops { struct snd_ctl_elem_value *ucontrol); int (*xput)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); - int (*xinfo)(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); }; struct wm_coeff_ctl { @@ -1890,7 +1888,7 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, } if (be32_to_cpu(val) != 0xbedead) - adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", + adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", pos + len, be32_to_cpu(val)); alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); @@ -2654,7 +2652,7 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; char preload[32]; - snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", mc->shift); + snprintf(preload, ARRAY_SIZE(preload), "DSP%u Preload", mc->shift); dsp->preloaded = ucontrol->value.integer.value[0]; diff --git a/sound/soc/codecs/zx_aud96p22.c b/sound/soc/codecs/zx_aud96p22.c new file mode 100644 index 000000000000..032fb7cf6cbd --- /dev/null +++ b/sound/soc/codecs/zx_aud96p22.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2017 Sanechips Technology Co., Ltd. + * Copyright 2017 Linaro Ltd. + * + * Author: Baoyou Xie <baoyou.xie@linaro.org> + * + * 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/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/tlv.h> + +#define AUD96P22_RESET 0x00 +#define RST_DAC_DPZ BIT(0) +#define RST_ADC_DPZ BIT(1) +#define AUD96P22_I2S1_CONFIG_0 0x03 +#define I2S1_MS_MODE BIT(3) +#define I2S1_MODE_MASK 0x7 +#define I2S1_MODE_RIGHT_J 0x0 +#define I2S1_MODE_I2S 0x1 +#define I2S1_MODE_LEFT_J 0x2 +#define AUD96P22_PD_0 0x15 +#define AUD96P22_PD_1 0x16 +#define AUD96P22_PD_3 0x18 +#define AUD96P22_PD_4 0x19 +#define AUD96P22_MUTE_0 0x1d +#define AUD96P22_MUTE_2 0x1f +#define AUD96P22_MUTE_4 0x21 +#define AUD96P22_RECVOL_0 0x24 +#define AUD96P22_RECVOL_1 0x25 +#define AUD96P22_PGA1VOL_0 0x26 +#define AUD96P22_PGA1VOL_1 0x27 +#define AUD96P22_LMVOL_0 0x34 +#define AUD96P22_LMVOL_1 0x35 +#define AUD96P22_HS1VOL_0 0x38 +#define AUD96P22_HS1VOL_1 0x39 +#define AUD96P22_PGA1SEL_0 0x47 +#define AUD96P22_PGA1SEL_1 0x48 +#define AUD96P22_LDR1SEL_0 0x59 +#define AUD96P22_LDR1SEL_1 0x60 +#define AUD96P22_LDR2SEL_0 0x5d +#define AUD96P22_REG_MAX 0xfb + +struct aud96p22_priv { + struct regmap *regmap; +}; + +static int aud96p22_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aud96p22_priv *priv = snd_soc_codec_get_drvdata(codec); + struct regmap *regmap = priv->regmap; + + if (event != SND_SOC_DAPM_POST_PMU) + return -EINVAL; + + /* Assert/de-assert the bit to reset ADC data path */ + regmap_update_bits(regmap, AUD96P22_RESET, RST_ADC_DPZ, 0); + regmap_update_bits(regmap, AUD96P22_RESET, RST_ADC_DPZ, RST_ADC_DPZ); + + return 0; +} + +static int aud96p22_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct aud96p22_priv *priv = snd_soc_codec_get_drvdata(codec); + struct regmap *regmap = priv->regmap; + + if (event != SND_SOC_DAPM_POST_PMU) + return -EINVAL; + + /* Assert/de-assert the bit to reset DAC data path */ + regmap_update_bits(regmap, AUD96P22_RESET, RST_DAC_DPZ, 0); + regmap_update_bits(regmap, AUD96P22_RESET, RST_DAC_DPZ, RST_DAC_DPZ); + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(lm_tlv, -11550, 50, 0); +static const DECLARE_TLV_DB_SCALE(hs_tlv, -3900, 300, 0); +static const DECLARE_TLV_DB_SCALE(rec_tlv, -9550, 50, 0); +static const DECLARE_TLV_DB_SCALE(pga_tlv, -1800, 100, 0); + +static const struct snd_kcontrol_new aud96p22_snd_controls[] = { + /* Volume control */ + SOC_DOUBLE_R_TLV("Master Playback Volume", AUD96P22_LMVOL_0, + AUD96P22_LMVOL_1, 0, 0xff, 0, lm_tlv), + SOC_DOUBLE_R_TLV("Headphone Volume", AUD96P22_HS1VOL_0, + AUD96P22_HS1VOL_1, 0, 0xf, 0, hs_tlv), + SOC_DOUBLE_R_TLV("Master Capture Volume", AUD96P22_RECVOL_0, + AUD96P22_RECVOL_1, 0, 0xff, 0, rec_tlv), + SOC_DOUBLE_R_TLV("Analogue Capture Volume", AUD96P22_PGA1VOL_0, + AUD96P22_PGA1VOL_1, 0, 0x37, 0, pga_tlv), + + /* Mute control */ + SOC_DOUBLE("Master Playback Switch", AUD96P22_MUTE_2, 0, 1, 1, 1), + SOC_DOUBLE("Headphone Switch", AUD96P22_MUTE_2, 4, 5, 1, 1), + SOC_DOUBLE("Line Out Switch", AUD96P22_MUTE_4, 0, 1, 1, 1), + SOC_DOUBLE("Speaker Switch", AUD96P22_MUTE_4, 2, 3, 1, 1), + SOC_DOUBLE("Master Capture Switch", AUD96P22_MUTE_0, 0, 1, 1, 1), + SOC_DOUBLE("Analogue Capture Switch", AUD96P22_MUTE_0, 2, 3, 1, 1), +}; + +/* Input mux kcontrols */ +static const unsigned int ain_mux_values[] = { + 0, 1, 3, 4, 5, +}; + +static const char * const ainl_mux_texts[] = { + "AINL1 differential", + "AINL1 single-ended", + "AINL3 single-ended", + "AINL2 differential", + "AINL2 single-ended", +}; + +static const char * const ainr_mux_texts[] = { + "AINR1 differential", + "AINR1 single-ended", + "AINR3 single-ended", + "AINR2 differential", + "AINR2 single-ended", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(ainl_mux_enum, AUD96P22_PGA1SEL_0, + 0, 0x7, ainl_mux_texts, ain_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(ainr_mux_enum, AUD96P22_PGA1SEL_1, + 0, 0x7, ainr_mux_texts, ain_mux_values); + +static const struct snd_kcontrol_new ainl_mux_kcontrol = + SOC_DAPM_ENUM("AINL Mux", ainl_mux_enum); +static const struct snd_kcontrol_new ainr_mux_kcontrol = + SOC_DAPM_ENUM("AINR Mux", ainr_mux_enum); + +/* Output mixer kcontrols */ +static const struct snd_kcontrol_new ld1_left_kcontrols[] = { + SOC_DAPM_SINGLE("DACL LD1L Switch", AUD96P22_LDR1SEL_0, 0, 1, 0), + SOC_DAPM_SINGLE("AINL LD1L Switch", AUD96P22_LDR1SEL_0, 1, 1, 0), + SOC_DAPM_SINGLE("AINR LD1L Switch", AUD96P22_LDR1SEL_0, 2, 1, 0), +}; + +static const struct snd_kcontrol_new ld1_right_kcontrols[] = { + SOC_DAPM_SINGLE("DACR LD1R Switch", AUD96P22_LDR1SEL_1, 8, 1, 0), + SOC_DAPM_SINGLE("AINR LD1R Switch", AUD96P22_LDR1SEL_1, 9, 1, 0), + SOC_DAPM_SINGLE("AINL LD1R Switch", AUD96P22_LDR1SEL_1, 10, 1, 0), +}; + +static const struct snd_kcontrol_new ld2_kcontrols[] = { + SOC_DAPM_SINGLE("DACL LD2 Switch", AUD96P22_LDR2SEL_0, 0, 1, 0), + SOC_DAPM_SINGLE("AINL LD2 Switch", AUD96P22_LDR2SEL_0, 1, 1, 0), + SOC_DAPM_SINGLE("DACR LD2 Switch", AUD96P22_LDR2SEL_0, 2, 1, 0), +}; + +static const struct snd_soc_dapm_widget aud96p22_dapm_widgets[] = { + /* Overall power bit */ + SND_SOC_DAPM_SUPPLY("POWER", AUD96P22_PD_0, 0, 0, NULL, 0), + + /* Input pins */ + SND_SOC_DAPM_INPUT("AINL1P"), + SND_SOC_DAPM_INPUT("AINL2P"), + SND_SOC_DAPM_INPUT("AINL3"), + SND_SOC_DAPM_INPUT("AINL1N"), + SND_SOC_DAPM_INPUT("AINL2N"), + SND_SOC_DAPM_INPUT("AINR2N"), + SND_SOC_DAPM_INPUT("AINR1N"), + SND_SOC_DAPM_INPUT("AINR3"), + SND_SOC_DAPM_INPUT("AINR2P"), + SND_SOC_DAPM_INPUT("AINR1P"), + + /* Input muxes */ + SND_SOC_DAPM_MUX("AINLMUX", AUD96P22_PD_1, 2, 0, &ainl_mux_kcontrol), + SND_SOC_DAPM_MUX("AINRMUX", AUD96P22_PD_1, 3, 0, &ainr_mux_kcontrol), + + /* ADCs */ + SND_SOC_DAPM_ADC_E("ADCL", "Capture Left", AUD96P22_PD_1, 0, 0, + aud96p22_adc_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC_E("ADCR", "Capture Right", AUD96P22_PD_1, 1, 0, + aud96p22_adc_event, SND_SOC_DAPM_POST_PMU), + + /* DACs */ + SND_SOC_DAPM_DAC_E("DACL", "Playback Left", AUD96P22_PD_3, 0, 0, + aud96p22_dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_DAC_E("DACR", "Playback Right", AUD96P22_PD_3, 1, 0, + aud96p22_dac_event, SND_SOC_DAPM_POST_PMU), + + /* Output mixers */ + SND_SOC_DAPM_MIXER("LD1L", AUD96P22_PD_3, 6, 0, ld1_left_kcontrols, + ARRAY_SIZE(ld1_left_kcontrols)), + SND_SOC_DAPM_MIXER("LD1R", AUD96P22_PD_3, 7, 0, ld1_right_kcontrols, + ARRAY_SIZE(ld1_right_kcontrols)), + SND_SOC_DAPM_MIXER("LD2", AUD96P22_PD_4, 2, 0, ld2_kcontrols, + ARRAY_SIZE(ld2_kcontrols)), + + /* Headset power switch */ + SND_SOC_DAPM_SUPPLY("HS1L", AUD96P22_PD_3, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HS1R", AUD96P22_PD_3, 5, 0, NULL, 0), + + /* Output pins */ + SND_SOC_DAPM_OUTPUT("HSOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTMP"), + SND_SOC_DAPM_OUTPUT("LINEOUTMN"), + SND_SOC_DAPM_OUTPUT("LINEOUTR"), + SND_SOC_DAPM_OUTPUT("HSOUTR"), +}; + +static const struct snd_soc_dapm_route aud96p22_dapm_routes[] = { + { "AINLMUX", "AINL1 differential", "AINL1N" }, + { "AINLMUX", "AINL1 single-ended", "AINL1P" }, + { "AINLMUX", "AINL3 single-ended", "AINL3" }, + { "AINLMUX", "AINL2 differential", "AINL2N" }, + { "AINLMUX", "AINL2 single-ended", "AINL2P" }, + + { "AINRMUX", "AINR1 differential", "AINR1N" }, + { "AINRMUX", "AINR1 single-ended", "AINR1P" }, + { "AINRMUX", "AINR3 single-ended", "AINR3" }, + { "AINRMUX", "AINR2 differential", "AINR2N" }, + { "AINRMUX", "AINR2 single-ended", "AINR2P" }, + + { "ADCL", NULL, "AINLMUX" }, + { "ADCR", NULL, "AINRMUX" }, + + { "ADCL", NULL, "POWER" }, + { "ADCR", NULL, "POWER" }, + { "DACL", NULL, "POWER" }, + { "DACR", NULL, "POWER" }, + + { "LD1L", "DACL LD1L Switch", "DACL" }, + { "LD1L", "AINL LD1L Switch", "AINLMUX" }, + { "LD1L", "AINR LD1L Switch", "AINRMUX" }, + + { "LD1R", "DACR LD1R Switch", "DACR" }, + { "LD1R", "AINR LD1R Switch", "AINRMUX" }, + { "LD1R", "AINL LD1R Switch", "AINLMUX" }, + + { "LD2", "DACL LD2 Switch", "DACL" }, + { "LD2", "AINL LD2 Switch", "AINLMUX" }, + { "LD2", "DACR LD2 Switch", "DACR" }, + + { "HSOUTL", NULL, "LD1L" }, + { "HSOUTR", NULL, "LD1R" }, + { "HSOUTL", NULL, "HS1L" }, + { "HSOUTR", NULL, "HS1R" }, + + { "LINEOUTL", NULL, "LD1L" }, + { "LINEOUTR", NULL, "LD1R" }, + + { "LINEOUTMP", NULL, "LD2" }, + { "LINEOUTMN", NULL, "LD2" }, +}; + +static struct snd_soc_codec_driver aud96p22_driver = { + .component_driver = { + .controls = aud96p22_snd_controls, + .num_controls = ARRAY_SIZE(aud96p22_snd_controls), + .dapm_widgets = aud96p22_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aud96p22_dapm_widgets), + .dapm_routes = aud96p22_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aud96p22_dapm_routes), + }, +}; + +static int aud96p22_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct aud96p22_priv *priv = snd_soc_codec_get_drvdata(dai->codec); + struct regmap *regmap = priv->regmap; + unsigned int val; + + /* Master/slave mode */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + val = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + val = I2S1_MS_MODE; + break; + default: + return -EINVAL; + } + + regmap_update_bits(regmap, AUD96P22_I2S1_CONFIG_0, I2S1_MS_MODE, val); + + /* Audio format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = I2S1_MODE_RIGHT_J; + break; + case SND_SOC_DAIFMT_I2S: + val = I2S1_MODE_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = I2S1_MODE_LEFT_J; + break; + default: + return -EINVAL; + } + + regmap_update_bits(regmap, AUD96P22_I2S1_CONFIG_0, I2S1_MODE_MASK, val); + + return 0; +} + +static struct snd_soc_dai_ops aud96p22_dai_ops = { + .set_fmt = aud96p22_set_fmt, +}; + +#define AUD96P22_RATES SNDRV_PCM_RATE_8000_192000 +#define AUD96P22_FORMATS (\ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver aud96p22_dai = { + .name = "aud96p22-dai", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AUD96P22_RATES, + .formats = AUD96P22_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AUD96P22_RATES, + .formats = AUD96P22_FORMATS, + }, + .ops = &aud96p22_dai_ops, +}; + +static const struct regmap_config aud96p22_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AUD96P22_REG_MAX, + .cache_type = REGCACHE_RBTREE, +}; + +static int aud96p22_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct aud96p22_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->regmap = devm_regmap_init_i2c(i2c, &aud96p22_regmap); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(dev, "failed to init i2c regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, priv); + + ret = snd_soc_register_codec(dev, &aud96p22_driver, &aud96p22_dai, 1); + if (ret) { + dev_err(dev, "failed to register codec: %d\n", ret); + return ret; + } + + return 0; +} + +static int aud96p22_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + return 0; +} + +const struct of_device_id aud96p22_dt_ids[] = { + { .compatible = "zte,zx-aud96p22", }, + { } +}; +MODULE_DEVICE_TABLE(of, aud96p22_dt_ids); + +static struct i2c_driver aud96p22_i2c_driver = { + .driver = { + .name = "zx_aud96p22", + .of_match_table = aud96p22_dt_ids, + }, + .probe = aud96p22_i2c_probe, + .remove = aud96p22_i2c_remove, +}; +module_i2c_driver(aud96p22_i2c_driver); + +MODULE_DESCRIPTION("ZTE ASoC AUD96P22 CODEC driver"); +MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 3c5a9804d3f5..56ec1d301ac2 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -629,7 +629,7 @@ static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream, if (mcasp->tdm_mask[stream]) slots = hweight32(mcasp->tdm_mask[stream]); - for (i = 2; i <= slots; i++) + for (i = 1; i <= slots; i++) list[count++] = i; for (i = 2; i <= serializers; i++) @@ -1297,7 +1297,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, - 2, max_channels); + 0, max_channels); snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, @@ -1459,13 +1459,13 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { .suspend = davinci_mcasp_suspend, .resume = davinci_mcasp_resume, .playback = { - .channels_min = 2, + .channels_min = 1, .channels_max = 32 * 16, .rates = DAVINCI_MCASP_RATES, .formats = DAVINCI_MCASP_PCM_FMTS, }, .capture = { - .channels_min = 2, + .channels_min = 1, .channels_max = 32 * 16, .rates = DAVINCI_MCASP_RATES, .formats = DAVINCI_MCASP_PCM_FMTS, @@ -1971,12 +1971,12 @@ static int davinci_mcasp_probe(struct platform_device *pdev) */ mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) * - (32 + mcasp->num_serializer - 2), + (32 + mcasp->num_serializer - 1), GFP_KERNEL); mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) * - (32 + mcasp->num_serializer - 2), + (32 + mcasp->num_serializer - 1), GFP_KERNEL); if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list || diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 9c46e4112026..916067638180 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -496,6 +496,8 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, idx = COMP1_TX_WORDSIZE_0(comp1); if (WARN_ON(idx >= ARRAY_SIZE(formats))) return -EINVAL; + if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) + idx = 1; dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->playback.channels_max = 1 << (COMP1_TX_CHANNELS(comp1) + 1); @@ -508,6 +510,8 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, idx = COMP2_RX_WORDSIZE_0(comp2); if (WARN_ON(idx >= ARRAY_SIZE(formats))) return -EINVAL; + if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) + idx = 1; dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->capture.channels_max = 1 << (COMP1_RX_CHANNELS(comp1) + 1); @@ -543,6 +547,8 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, if (ret < 0) return ret; + if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) + idx = 1; /* Set DMA slaves info */ dev->play_dma_data.pd.data = pdata->play_dma_data; dev->capture_dma_data.pd.data = pdata->capture_dma_data; diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 0b82e209b6e3..1f7e70bfbd55 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -302,7 +302,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd) struct snd_card *card = rtd->card->snd_card; struct snd_soc_dai *dai = rtd->cpu_dai; struct snd_pcm *pcm = rtd->pcm; - struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); size_t size = psc_dma_hardware.buffer_bytes_max; int rc; diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index d023959b8cd6..c954be0a0f96 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -14,3 +14,20 @@ config SND_SIMPLE_SCU_CARD help This option enables generic simple SCU sound card support. It supports DPCM of multi CPU single Codec system. + +config SND_AUDIO_GRAPH_CARD + tristate "ASoC Audio Graph sound card support" + depends on OF + select SND_SIMPLE_CARD_UTILS + help + This option enables generic simple simple sound card support + with OF-graph DT bindings. + +config SND_AUDIO_GRAPH_SCU_CARD + tristate "ASoC Audio Graph SCU sound card support" + depends on OF + select SND_SIMPLE_CARD_UTILS + help + This option enables generic simple SCU sound card support + with OF-graph DT bindings. + It supports DPCM of multi CPU single Codec ststem. diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index ee750f3023ba..9e000523a3b4 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -1,7 +1,11 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o snd-soc-simple-scu-card-objs := simple-scu-card.o +snd-soc-audio-graph-card-objs := audio-graph-card.o +snd-soc-audio-graph-scu-card-objs := audio-graph-scu-card.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o +obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o +obj-$(CONFIG_SND_AUDIO_GRAPH_SCU_CARD) += snd-soc-audio-graph-scu-card.o diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c new file mode 100644 index 000000000000..105ec3a6e30d --- /dev/null +++ b/sound/soc/generic/audio-graph-card.c @@ -0,0 +1,338 @@ +/* + * ASoC audio graph sound card support + * + * Copyright (C) 2016 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * based on ${LINUX}/sound/soc/generic/simple-card.c + * + * 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/clk.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <sound/jack.h> +#include <sound/simple_card_utils.h> + +struct graph_card_data { + struct snd_soc_card snd_card; + struct graph_dai_props { + struct asoc_simple_dai cpu_dai; + struct asoc_simple_dai codec_dai; + } *dai_props; + struct snd_soc_dai_link *dai_link; + struct gpio_desc *pa_gpio; +}; + +static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct graph_card_data *priv = snd_soc_card_get_drvdata(dapm->card); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + gpiod_set_value_cansleep(priv->pa_gpio, 1); + break; + case SND_SOC_DAPM_PRE_PMD: + gpiod_set_value_cansleep(priv->pa_gpio, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = { + SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM, + 0, 0, NULL, 0, asoc_graph_card_outdrv_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +}; + +#define graph_priv_to_card(priv) (&(priv)->snd_card) +#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) +#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) +#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) + +static int asoc_graph_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + int ret; + + ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai); + if (ret) + return ret; + + ret = asoc_simple_card_clk_enable(&dai_props->codec_dai); + if (ret) + asoc_simple_card_clk_disable(&dai_props->cpu_dai); + + return ret; +} + +static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + + asoc_simple_card_clk_disable(&dai_props->cpu_dai); + + asoc_simple_card_clk_disable(&dai_props->codec_dai); +} + +static struct snd_soc_ops asoc_graph_card_ops = { + .startup = asoc_graph_card_startup, + .shutdown = asoc_graph_card_shutdown, +}; + +static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + struct graph_dai_props *dai_props = + graph_priv_to_props(priv, rtd->num); + int ret; + + ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + if (ret < 0) + return ret; + + return 0; +} + +static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, + struct graph_card_data *priv, + int idx) +{ + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); + struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; + struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; + struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL); + struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep); + struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep); + int ret; + + if (rcpu_ep != cpu_ep) { + dev_err(dev, "remote-endpoint mismatch (%s/%s/%s)\n", + cpu_ep->name, codec_ep->name, rcpu_ep->name); + ret = -EINVAL; + goto dai_link_of_err; + } + + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, + NULL, &dai_link->dai_fmt); + if (ret < 0) + goto dai_link_of_err; + + /* + * we need to consider "mclk-fs" around here + * see simple-card + */ + + ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_of_parse_tdm(cpu_ep, cpu_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_of_parse_tdm(codec_ep, codec_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "%s-%s", + dai_link->cpu_dai_name, + dai_link->codec_dai_name); + if (ret < 0) + goto dai_link_of_err; + + dai_link->ops = &asoc_graph_card_ops; + dai_link->init = asoc_graph_card_dai_init; + + asoc_simple_card_canonicalize_cpu(dai_link, + of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); + +dai_link_of_err: + of_node_put(cpu_ep); + of_node_put(rcpu_ep); + of_node_put(codec_ep); + + return ret; +} + +static int asoc_graph_card_parse_of(struct graph_card_data *priv) +{ + struct of_phandle_iterator it; + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_card *card = graph_priv_to_card(priv); + struct device_node *node = dev->of_node; + int rc, idx = 0; + int ret; + + ret = asoc_simple_card_of_parse_widgets(card, NULL); + if (ret < 0) + return ret; + + ret = asoc_simple_card_of_parse_routing(card, NULL, 1); + if (ret < 0) + return ret; + + /* + * we need to consider "mclk-fs" around here + * see simple-card + */ + + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + ret = asoc_graph_card_dai_link_of(it.node, priv, idx++); + of_node_put(it.node); + if (ret < 0) + return ret; + } + + return asoc_simple_card_parse_card_name(card, NULL); +} + +static int asoc_graph_get_dais_count(struct device *dev) +{ + struct of_phandle_iterator it; + struct device_node *node = dev->of_node; + int count = 0; + int rc; + + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + count++; + of_node_put(it.node); + } + + return count; +} + +static int asoc_graph_card_probe(struct platform_device *pdev) +{ + struct graph_card_data *priv; + struct snd_soc_dai_link *dai_link; + struct graph_dai_props *dai_props; + struct device *dev = &pdev->dev; + struct snd_soc_card *card; + int num, ret; + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + num = asoc_graph_get_dais_count(dev); + if (num == 0) + return -EINVAL; + + dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); + dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + if (!dai_props || !dai_link) + return -ENOMEM; + + priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); + if (IS_ERR(priv->pa_gpio)) { + ret = PTR_ERR(priv->pa_gpio); + dev_err(dev, "failed to get amplifier gpio: %d\n", ret); + return ret; + } + + priv->dai_props = dai_props; + priv->dai_link = dai_link; + + /* Init snd_soc_card */ + card = graph_priv_to_card(priv); + card->owner = THIS_MODULE; + card->dev = dev; + card->dai_link = dai_link; + card->num_links = num; + card->dapm_widgets = asoc_graph_card_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets); + + ret = asoc_graph_card_parse_of(priv); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + goto err; + } + + snd_soc_card_set_drvdata(card, priv); + + ret = devm_snd_soc_register_card(dev, card); + if (ret < 0) + goto err; + + return 0; +err: + asoc_simple_card_clean_reference(card); + + return ret; +} + +static int asoc_graph_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + return asoc_simple_card_clean_reference(card); +} + +static const struct of_device_id asoc_graph_of_match[] = { + { .compatible = "audio-graph-card", }, + {}, +}; +MODULE_DEVICE_TABLE(of, asoc_graph_of_match); + +static struct platform_driver asoc_graph_card = { + .driver = { + .name = "asoc-audio-graph-card", + .of_match_table = asoc_graph_of_match, + }, + .probe = asoc_graph_card_probe, + .remove = asoc_graph_card_remove, +}; +module_platform_driver(asoc_graph_card); + +MODULE_ALIAS("platform:asoc-audio-graph-card"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Audio Graph Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c new file mode 100644 index 000000000000..dcd2df37bc3b --- /dev/null +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -0,0 +1,411 @@ +/* + * ASoC audio graph SCU sound card support + * + * Copyright (C) 2017 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * based on + * ${LINUX}/sound/soc/generic/simple-scu-card.c + * ${LINUX}/sound/soc/generic/audio-graph-card.c + * + * 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/clk.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <sound/jack.h> +#include <sound/simple_card_utils.h> + +struct graph_card_data { + struct snd_soc_card snd_card; + struct snd_soc_codec_conf codec_conf; + struct asoc_simple_dai *dai_props; + struct snd_soc_dai_link *dai_link; + struct asoc_simple_card_data adata; +}; + +#define graph_priv_to_card(priv) (&(priv)->snd_card) +#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) +#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) +#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) + +static int asoc_graph_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num); + + return asoc_simple_card_clk_enable(dai_props); +} + +static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num); + + asoc_simple_card_clk_disable(dai_props); +} + +static struct snd_soc_ops asoc_graph_card_ops = { + .startup = asoc_graph_card_startup, + .shutdown = asoc_graph_card_shutdown, +}; + +static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai; + struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dai_props; + int num = rtd->num; + + dai_link = graph_priv_to_link(priv, num); + dai_props = graph_priv_to_props(priv, num); + dai = dai_link->dynamic ? + rtd->cpu_dai : + rtd->codec_dai; + + return asoc_simple_card_init_dai(dai, dai_props); +} + +static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + + asoc_simple_card_convert_fixup(&priv->adata, params); + + return 0; +} + +static int asoc_graph_card_dai_link_of(struct device_node *ep, + struct graph_card_data *priv, + unsigned int daifmt, + int idx, int is_fe) +{ + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); + struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, idx); + struct snd_soc_card *card = graph_priv_to_card(priv); + int ret; + + if (is_fe) { + /* BE is dummy */ + dai_link->codec_of_node = NULL; + dai_link->codec_dai_name = "snd-soc-dummy-dai"; + dai_link->codec_name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); + if (ret) + return ret; + + ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai_props); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "fe.%s", + dai_link->cpu_dai_name); + if (ret < 0) + return ret; + + /* card->num_links includes Codec */ + asoc_simple_card_canonicalize_cpu(dai_link, + of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); + } else { + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; + + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup; + + ret = asoc_simple_card_parse_graph_codec(ep, dai_link); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai_props); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "be.%s", + dai_link->codec_dai_name); + if (ret < 0) + return ret; + + snd_soc_of_parse_audio_prefix(card, + &priv->codec_conf, + dai_link->codec_of_node, + "prefix"); + } + + ret = asoc_simple_card_of_parse_tdm(ep, dai_props); + if (ret) + return ret; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + return ret; + + dai_link->dai_fmt = daifmt; + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->ops = &asoc_graph_card_ops; + dai_link->init = asoc_graph_card_dai_init; + + return 0; +} + +static int asoc_graph_card_parse_of(struct graph_card_data *priv) +{ + struct of_phandle_iterator it; + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_card *card = graph_priv_to_card(priv); + struct device_node *node = dev->of_node; + struct device_node *cpu_port; + struct device_node *cpu_ep; + struct device_node *codec_ep; + struct device_node *rcpu_ep; + struct device_node *codec_port; + struct device_node *codec_port_old; + unsigned int daifmt = 0; + int dai_idx, ret; + int rc, codec; + + if (!node) + return -EINVAL; + + /* + * we need to consider "widgets", "mclk-fs" around here + * see simple-card + */ + + ret = asoc_simple_card_of_parse_routing(card, NULL, 0); + if (ret < 0) + return ret; + + asoc_simple_card_parse_convert(dev, NULL, &priv->adata); + + /* + * it supports multi CPU, single CODEC only here + * see asoc_graph_get_dais_count + */ + + /* find 1st codec */ + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + cpu_port = it.node; + cpu_ep = of_get_next_child(cpu_port, NULL); + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + rcpu_ep = of_graph_get_remote_endpoint(codec_ep); + + of_node_put(cpu_port); + of_node_put(cpu_ep); + of_node_put(codec_ep); + of_node_put(rcpu_ep); + + if (!codec_ep) + continue; + + if (rcpu_ep != cpu_ep) { + dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n", + cpu_ep->name, codec_ep->name, rcpu_ep->name); + ret = -EINVAL; + goto parse_of_err; + } + + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, + NULL, &daifmt); + if (ret < 0) + goto parse_of_err; + } + + dai_idx = 0; + codec_port_old = NULL; + for (codec = 0; codec < 2; codec++) { + /* + * To listup valid sounds continuously, + * detect all CPU-dummy first, and + * detect all dummy-Codec second + */ + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + cpu_port = it.node; + cpu_ep = of_get_next_child(cpu_port, NULL); + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + codec_port = of_graph_get_port_parent(codec_ep); + + of_node_put(cpu_port); + of_node_put(cpu_ep); + of_node_put(codec_ep); + of_node_put(codec_port); + + if (codec) { + if (!codec_port) + continue; + + if (codec_port_old == codec_port) + continue; + + codec_port_old = codec_port; + + /* Back-End (= Codec) */ + ret = asoc_graph_card_dai_link_of(codec_ep, priv, daifmt, dai_idx++, 0); + if (ret < 0) + goto parse_of_err; + } else { + /* Front-End (= CPU) */ + ret = asoc_graph_card_dai_link_of(cpu_ep, priv, daifmt, dai_idx++, 1); + if (ret < 0) + goto parse_of_err; + } + } + } + + ret = asoc_simple_card_parse_card_name(card, NULL); + if (ret) + goto parse_of_err; + + ret = 0; + +parse_of_err: + return ret; +} + +static int asoc_graph_get_dais_count(struct device *dev) +{ + struct of_phandle_iterator it; + struct device_node *node = dev->of_node; + struct device_node *cpu_port; + struct device_node *cpu_ep; + struct device_node *codec_ep; + struct device_node *codec_port; + struct device_node *codec_port_old; + int count = 0; + int rc; + + codec_port_old = NULL; + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + cpu_port = it.node; + cpu_ep = of_get_next_child(cpu_port, NULL); + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + codec_port = of_graph_get_port_parent(codec_ep); + + of_node_put(cpu_port); + of_node_put(cpu_ep); + of_node_put(codec_ep); + of_node_put(codec_port); + + if (cpu_ep) + count++; + + if (!codec_port) + continue; + + if (codec_port_old == codec_port) + continue; + + count++; + codec_port_old = codec_port; + } + + return count; +} + +static int asoc_graph_card_probe(struct platform_device *pdev) +{ + struct graph_card_data *priv; + struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dai_props; + struct device *dev = &pdev->dev; + struct snd_soc_card *card; + int num, ret; + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + num = asoc_graph_get_dais_count(dev); + if (num == 0) + return -EINVAL; + + dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); + dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + if (!dai_props || !dai_link) + return -ENOMEM; + + priv->dai_props = dai_props; + priv->dai_link = dai_link; + + /* Init snd_soc_card */ + card = graph_priv_to_card(priv); + card->owner = THIS_MODULE; + card->dev = dev; + card->dai_link = priv->dai_link; + card->num_links = num; + card->codec_conf = &priv->codec_conf; + card->num_configs = 1; + + ret = asoc_graph_card_parse_of(priv); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + goto err; + } + + snd_soc_card_set_drvdata(card, priv); + + ret = devm_snd_soc_register_card(dev, card); + if (ret < 0) + goto err; + + return 0; +err: + asoc_simple_card_clean_reference(card); + + return ret; +} + +static int asoc_graph_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + return asoc_simple_card_clean_reference(card); +} + +static const struct of_device_id asoc_graph_of_match[] = { + { .compatible = "audio-graph-scu-card", }, + {}, +}; +MODULE_DEVICE_TABLE(of, asoc_graph_of_match); + +static struct platform_driver asoc_graph_card = { + .driver = { + .name = "asoc-audio-graph-scu-card", + .of_match_table = asoc_graph_of_match, + }, + .probe = asoc_graph_card_probe, + .remove = asoc_graph_card_remove, +}; +module_platform_driver(asoc_graph_card); + +MODULE_ALIAS("platform:asoc-audio-graph-scu-card"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Audio Graph SCU Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 343b291fc372..26d64fa40c9c 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -10,8 +10,49 @@ #include <linux/clk.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <sound/simple_card_utils.h> +void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (data->convert_rate) + rate->min = + rate->max = data->convert_rate; + + if (data->convert_channels) + channels->min = + channels->max = data->convert_channels; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup); + +void asoc_simple_card_parse_convert(struct device *dev, char *prefix, + struct asoc_simple_card_data *data) +{ + struct device_node *np = dev->of_node; + char prop[128]; + + if (!prefix) + prefix = ""; + + /* sampling rate convert */ + snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate"); + of_property_read_u32(np, prop, &data->convert_rate); + + /* channels transfer */ + snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels"); + of_property_read_u32(np, prop, &data->convert_channels); + + dev_dbg(dev, "convert_rate %d\n", data->convert_rate); + dev_dbg(dev, "convert_channels %d\n", data->convert_channels); +} +EXPORT_SYMBOL_GPL(asoc_simple_card_parse_convert); + int asoc_simple_card_parse_daifmt(struct device *dev, struct device_node *node, struct device_node *codec, @@ -20,14 +61,13 @@ int asoc_simple_card_parse_daifmt(struct device *dev, { struct device_node *bitclkmaster = NULL; struct device_node *framemaster = NULL; - int prefix_len = prefix ? strlen(prefix) : 0; unsigned int daifmt; daifmt = snd_soc_of_parse_daifmt(node, prefix, &bitclkmaster, &framemaster); daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - if (prefix_len && !bitclkmaster && !framemaster) { + if (!bitclkmaster && !framemaster) { /* * No dai-link level and master setting was not found from * sound node level, revert back to legacy DT parsing and @@ -51,6 +91,8 @@ int asoc_simple_card_parse_daifmt(struct device *dev, *retfmt = daifmt; + dev_dbg(dev, "format : %04x\n", daifmt); + return 0; } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt); @@ -72,6 +114,8 @@ int asoc_simple_card_set_dailink_name(struct device *dev, dai_link->name = name; dai_link->stream_name = name; + + dev_dbg(dev, "name : %s\n", name); } return ret; @@ -81,27 +125,54 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name); int asoc_simple_card_parse_card_name(struct snd_soc_card *card, char *prefix) { - char prop[128]; int ret; - snprintf(prop, sizeof(prop), "%sname", prefix); + if (!prefix) + prefix = ""; /* Parse the card name from DT */ - ret = snd_soc_of_parse_card_name(card, prop); - if (ret < 0) - return ret; + ret = snd_soc_of_parse_card_name(card, "label"); + if (ret < 0) { + char prop[128]; + + snprintf(prop, sizeof(prop), "%sname", prefix); + ret = snd_soc_of_parse_card_name(card, prop); + if (ret < 0) + return ret; + } if (!card->name && card->dai_link) card->name = card->dai_link->name; + dev_dbg(card->dev, "Card Name: %s\n", card->name ? card->name : ""); + return 0; } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); +static void asoc_simple_card_clk_register(struct asoc_simple_dai *dai, + struct clk *clk) +{ + dai->clk = clk; +} + +int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai) +{ + return clk_prepare_enable(dai->clk); +} +EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable); + +void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai) +{ + clk_disable_unprepare(dai->clk); +} +EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable); + int asoc_simple_card_parse_clk(struct device *dev, struct device_node *node, struct device_node *dai_of_node, - struct asoc_simple_dai *simple_dai) + struct asoc_simple_dai *simple_dai, + const char *name) { struct clk *clk; u32 val; @@ -115,7 +186,8 @@ int asoc_simple_card_parse_clk(struct device *dev, clk = devm_get_clk_from_child(dev, node, NULL); if (!IS_ERR(clk)) { simple_dai->sysclk = clk_get_rate(clk); - simple_dai->clk = clk; + + asoc_simple_card_clk_register(simple_dai, clk); } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { simple_dai->sysclk = val; } else { @@ -124,6 +196,8 @@ int asoc_simple_card_parse_clk(struct device *dev, simple_dai->sysclk = clk_get_rate(clk); } + dev_dbg(dev, "%s : sysclk = %d\n", name, simple_dai->sysclk); + return 0; } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk); @@ -165,6 +239,71 @@ int asoc_simple_card_parse_dai(struct device_node *node, } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); +static int asoc_simple_card_get_dai_id(struct device_node *ep) +{ + struct device_node *node; + struct device_node *endpoint; + int i, id; + int ret; + + ret = snd_soc_get_dai_id(ep); + if (ret != -ENOTSUPP) + return ret; + + node = of_graph_get_port_parent(ep); + + /* + * Non HDMI sound case, counting port/endpoint on its DT + * is enough. Let's count it. + */ + i = 0; + id = -1; + for_each_endpoint_of_node(node, endpoint) { + if (endpoint == ep) + id = i; + i++; + } + if (id < 0) + return -ENODEV; + + return id; +} + +int asoc_simple_card_parse_graph_dai(struct device_node *ep, + struct device_node **dai_of_node, + const char **dai_name) +{ + struct device_node *node; + struct of_phandle_args args; + int ret; + + if (!ep) + return 0; + if (!dai_name) + return 0; + + /* + * of_graph_get_port_parent() will call + * of_node_put(). So, call of_node_get() here + */ + of_node_get(ep); + node = of_graph_get_port_parent(ep); + + /* Get dai->name */ + args.np = node; + args.args[0] = asoc_simple_card_get_dai_id(ep); + args.args_count = (of_graph_get_endpoint_count(node) > 1); + + ret = snd_soc_get_dai_name(&args, dai_name); + if (ret < 0) + return ret; + + *dai_of_node = node; + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai); + int asoc_simple_card_init_dai(struct snd_soc_dai *dai, struct asoc_simple_dai *simple_dai) { @@ -236,6 +375,47 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card) } EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference); +int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, + char *prefix, + int optional) +{ + struct device_node *node = card->dev->of_node; + char prop[128]; + + if (!prefix) + prefix = ""; + + snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); + + if (!of_property_read_bool(node, prop)) { + if (optional) + return 0; + return -EINVAL; + } + + return snd_soc_of_parse_audio_routing(card, prop); +} +EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_routing); + +int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card, + char *prefix) +{ + struct device_node *node = card->dev->of_node; + char prop[128]; + + if (!prefix) + prefix = ""; + + snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); + + if (of_property_read_bool(node, prop)) + return snd_soc_of_parse_audio_simple_widgets(card, prop); + + /* no widgets is not error */ + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_widgets); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index bc136d2bd7cd..dfaf48ff88b0 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -118,13 +118,13 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) simple_priv_to_props(priv, rtd->num); int ret; - ret = clk_prepare_enable(dai_props->cpu_dai.clk); + ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai); if (ret) return ret; - ret = clk_prepare_enable(dai_props->codec_dai.clk); + ret = asoc_simple_card_clk_enable(&dai_props->codec_dai); if (ret) - clk_disable_unprepare(dai_props->cpu_dai.clk); + asoc_simple_card_clk_disable(&dai_props->cpu_dai); return ret; } @@ -136,9 +136,9 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - clk_disable_unprepare(dai_props->cpu_dai.clk); + asoc_simple_card_clk_disable(&dai_props->cpu_dai); - clk_disable_unprepare(dai_props->codec_dai.clk); + asoc_simple_card_clk_disable(&dai_props->codec_dai); } static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, @@ -233,13 +233,19 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, snprintf(prop, sizeof(prop), "%scpu", prefix); cpu = of_get_child_by_name(node, prop); + if (!cpu) { + ret = -EINVAL; + dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); + goto dai_link_of_err; + } + snprintf(prop, sizeof(prop), "%splat", prefix); plat = of_get_child_by_name(node, prop); snprintf(prop, sizeof(prop), "%scodec", prefix); codec = of_get_child_by_name(node, prop); - if (!cpu || !codec) { + if (!codec) { ret = -EINVAL; dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); goto dai_link_of_err; @@ -265,17 +271,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, if (ret < 0) goto dai_link_of_err; - ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask, - &cpu_dai->rx_slot_mask, - &cpu_dai->slots, - &cpu_dai->slot_width); + ret = asoc_simple_card_of_parse_tdm(cpu, cpu_dai); if (ret < 0) goto dai_link_of_err; - ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask, - &codec_dai->rx_slot_mask, - &codec_dai->slots, - &codec_dai->slot_width); + ret = asoc_simple_card_of_parse_tdm(codec, codec_dai); if (ret < 0) goto dai_link_of_err; @@ -301,15 +301,6 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, dai_link->ops = &asoc_simple_card_ops; dai_link->init = asoc_simple_card_dai_init; - dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); - dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); - dev_dbg(dev, "\tcpu : %s / %d\n", - dai_link->cpu_dai_name, - dai_props->cpu_dai.sysclk); - dev_dbg(dev, "\tcodec : %s / %d\n", - dai_link->codec_dai_name, - dai_props->codec_dai.sysclk); - asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); dai_link_of_err: @@ -350,12 +341,12 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node, return 0; } -static int asoc_simple_card_parse_of(struct device_node *node, - struct simple_card_data *priv) +static int asoc_simple_card_parse_of(struct simple_card_data *priv) { struct device *dev = simple_priv_to_dev(priv); struct snd_soc_card *card = simple_priv_to_card(priv); struct device_node *dai_link; + struct device_node *node = dev->of_node; int ret; if (!node) @@ -363,21 +354,13 @@ static int asoc_simple_card_parse_of(struct device_node *node, dai_link = of_get_child_by_name(node, PREFIX "dai-link"); - /* The off-codec widgets */ - if (of_property_read_bool(node, PREFIX "widgets")) { - ret = snd_soc_of_parse_audio_simple_widgets(card, - PREFIX "widgets"); - if (ret) - goto card_parse_end; - } + ret = asoc_simple_card_of_parse_widgets(card, PREFIX); + if (ret < 0) + goto card_parse_end; - /* DAPM routes */ - if (of_property_read_bool(node, PREFIX "routing")) { - ret = snd_soc_of_parse_audio_routing(card, - PREFIX "routing"); - if (ret) - goto card_parse_end; - } + ret = asoc_simple_card_of_parse_routing(card, PREFIX, 1); + if (ret < 0) + goto card_parse_end; /* Factor to mclk, used in hw_params() */ of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); @@ -454,7 +437,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) if (np && of_device_is_available(np)) { - ret = asoc_simple_card_parse_of(np, priv); + ret = asoc_simple_card_parse_of(priv); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); @@ -497,8 +480,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(dev, card); - if (ret >= 0) - return ret; + if (ret < 0) + goto err; + + return 0; err: asoc_simple_card_clean_reference(card); diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index dcbcab230d1b..a75b385455c4 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -27,8 +27,7 @@ struct simple_card_data { struct snd_soc_codec_conf codec_conf; struct asoc_simple_dai *dai_props; struct snd_soc_dai_link *dai_link; - u32 convert_rate; - u32 convert_channels; + struct asoc_simple_card_data adata; }; #define simple_priv_to_card(priv) (&(priv)->snd_card) @@ -47,7 +46,7 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, rtd->num); - return clk_prepare_enable(dai_props->clk); + return asoc_simple_card_clk_enable(dai_props); } static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) @@ -57,7 +56,7 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, rtd->num); - clk_disable_unprepare(dai_props->clk); + asoc_simple_card_clk_disable(dai_props); } static const struct snd_soc_ops asoc_simple_card_ops = { @@ -86,18 +85,8 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_interval *rate = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - if (priv->convert_rate) - rate->min = - rate->max = priv->convert_rate; - - if (priv->convert_channels) - channels->min = - channels->max = priv->convert_channels; + asoc_simple_card_convert_fixup(&priv->adata, params); return 0; } @@ -171,11 +160,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, PREFIX "prefix"); } - ret = snd_soc_of_parse_tdm_slot(np, - &dai_props->tx_slot_mask, - &dai_props->rx_slot_mask, - &dai_props->slots, - &dai_props->slot_width); + ret = asoc_simple_card_of_parse_tdm(np, dai_props); if (ret) return ret; @@ -189,21 +174,16 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, dai_link->ops = &asoc_simple_card_ops; dai_link->init = asoc_simple_card_dai_init; - dev_dbg(dev, "\t%s / %04x / %d\n", - dai_link->name, - dai_link->dai_fmt, - dai_props->sysclk); - return 0; } -static int asoc_simple_card_parse_of(struct device_node *node, - struct simple_card_data *priv) +static int asoc_simple_card_parse_of(struct simple_card_data *priv) { struct device *dev = simple_priv_to_dev(priv); struct device_node *np; struct snd_soc_card *card = simple_priv_to_card(priv); + struct device_node *node = dev->of_node; unsigned int daifmt = 0; bool is_fe; int ret, i; @@ -211,15 +191,11 @@ static int asoc_simple_card_parse_of(struct device_node *node, if (!node) return -EINVAL; - ret = snd_soc_of_parse_audio_routing(card, PREFIX "routing"); + ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0); if (ret < 0) return ret; - /* sampling rate convert */ - of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate); - - /* channels transfer */ - of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels); + asoc_simple_card_parse_convert(dev, PREFIX, &priv->adata); /* find 1st codec */ np = of_get_child_by_name(node, PREFIX "codec"); @@ -246,11 +222,6 @@ static int asoc_simple_card_parse_of(struct device_node *node, if (ret < 0) return ret; - dev_dbg(dev, "New card: %s\n", - card->name ? card->name : ""); - dev_dbg(dev, "convert_rate %d\n", priv->convert_rate); - dev_dbg(dev, "convert_channels %d\n", priv->convert_channels); - return 0; } @@ -288,7 +259,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) card->codec_conf = &priv->codec_conf; card->num_configs = 1; - ret = asoc_simple_card_parse_of(np, priv); + ret = asoc_simple_card_parse_of(priv); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); @@ -298,8 +269,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(dev, card); - if (ret >= 0) - return ret; + if (ret < 0) + goto err; + + return 0; err: asoc_simple_card_clean_reference(card); diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c index 45163e5202f5..b193d3beb253 100644 --- a/sound/soc/hisilicon/hi6210-i2s.c +++ b/sound/soc/hisilicon/hi6210-i2s.c @@ -97,8 +97,8 @@ static inline u32 hi6210_read_reg(struct hi6210_i2s *i2s, int reg) return readl(i2s->base + reg); } -int hi6210_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) +static int hi6210_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev); int ret, n; @@ -175,8 +175,9 @@ int hi6210_i2s_startup(struct snd_pcm_substream *substream, return 0; } -void hi6210_i2s_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) + +static void hi6210_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev); int n; @@ -524,7 +525,7 @@ static struct snd_soc_dai_ops hi6210_i2s_dai_ops = { .shutdown = hi6210_i2s_shutdown, }; -struct snd_soc_dai_driver hi6210_i2s_dai_init = { +static const struct snd_soc_dai_driver hi6210_i2s_dai_init = { .probe = hi6210_i2s_dai_probe, .playback = { .channels_min = 2, diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 67968ef3bbda..b301bfff1c09 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -214,6 +214,18 @@ config SND_SOC_INTEL_BYT_CHT_DA7213_MACH platforms with DA7212/7213 audio codec. If unsure select "N". +config SND_SOC_INTEL_BYT_CHT_ES8316_MACH + tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with ES8316 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_ES8316 + select SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI + help + This adds support for ASoC machine driver for Intel(R) Baytrail & + Cherrytrail platforms with ES8316 audio codec. + If unsure select "N". + config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" depends on X86_INTEL_LPSS && I2C && ACPI @@ -226,6 +238,36 @@ config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH connector If unsure select "N". +config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH + tristate "ASoC Audio driver for KBL with RT5663 and MAX98927 in I2S Mode" + depends on X86_INTEL_LPSS && I2C + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT5663 + select SND_SOC_MAX98927 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for RT5663 + MAX98927. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH + tristate "ASoC Audio driver for KBL with RT5663, RT5514 and MAX98927 in I2S Mode" + depends on X86_INTEL_LPSS && I2C + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT5663 + select SND_SOC_RT5514 + select SND_SOC_MAX98927 + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for RT5663 + RT5514 + MAX98927. + Say Y if you have such a device. + If unsure select "N". + config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 21cac1c8dd4c..b082b31023d5 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -690,7 +690,7 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) snd_dma_continuous_data(GFP_DMA), SST_MIN_BUFFER, SST_MAX_BUFFER); if (retval) { - dev_err(rtd->dev, "dma buffer allocationf fail\n"); + dev_err(rtd->dev, "dma buffer allocation failure\n"); return retval; } } diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index f9ba71315e33..8afdff457579 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -258,7 +258,7 @@ static ssize_t firmware_version_show(struct device *dev, } -DEVICE_ATTR_RO(firmware_version); +static DEVICE_ATTR_RO(firmware_version); static const struct attribute *sst_fw_version_attrs[] = { &dev_attr_firmware_version.attr, @@ -382,37 +382,6 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) } EXPORT_SYMBOL_GPL(sst_context_cleanup); -static inline void sst_save_shim64(struct intel_sst_drv *ctx, - void __iomem *shim, - struct sst_shim_regs64 *shim_regs) -{ - unsigned long irq_flags; - - spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); - - shim_regs->imrx = sst_shim_read64(shim, SST_IMRX); - shim_regs->csr = sst_shim_read64(shim, SST_CSR); - - - spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); -} - -static inline void sst_restore_shim64(struct intel_sst_drv *ctx, - void __iomem *shim, - struct sst_shim_regs64 *shim_regs) -{ - unsigned long irq_flags; - - /* - * we only need to restore IMRX for this case, rest will be - * initialize by FW or driver when firmware is loaded - */ - spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); - sst_shim_write64(shim, SST_IMRX, shim_regs->imrx); - sst_shim_write64(shim, SST_CSR, shim_regs->csr); - spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); -} - void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY); @@ -432,8 +401,6 @@ void sst_configure_runtime_pm(struct intel_sst_drv *ctx) pm_runtime_set_active(ctx->dev); else pm_runtime_put_noidle(ctx->dev); - - sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); } EXPORT_SYMBOL_GPL(sst_configure_runtime_pm); @@ -457,8 +424,6 @@ static int intel_sst_runtime_suspend(struct device *dev) flush_workqueue(ctx->post_msg_wq); ctx->ops->reset(ctx); - /* save the shim registers because PMC doesn't save state */ - sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); return ret; } @@ -499,23 +464,23 @@ static int intel_sst_suspend(struct device *dev) fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL); if (!fw_save) return -ENOMEM; - fw_save->iram = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL); + fw_save->iram = kvzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL); if (!fw_save->iram) { ret = -ENOMEM; goto iram; } - fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL); + fw_save->dram = kvzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL); if (!fw_save->dram) { ret = -ENOMEM; goto dram; } - fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); + fw_save->sram = kvzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); if (!fw_save->sram) { ret = -ENOMEM; goto sram; } - fw_save->ddr = kzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL); + fw_save->ddr = kvzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL); if (!fw_save->ddr) { ret = -ENOMEM; goto ddr; @@ -530,11 +495,11 @@ static int intel_sst_suspend(struct device *dev) ctx->ops->reset(ctx); return 0; ddr: - kfree(fw_save->sram); + kvfree(fw_save->sram); sram: - kfree(fw_save->dram); + kvfree(fw_save->dram); dram: - kfree(fw_save->iram); + kvfree(fw_save->iram); iram: kfree(fw_save); return ret; @@ -562,10 +527,10 @@ static int intel_sst_resume(struct device *dev) memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE); memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base); - kfree(fw_save->sram); - kfree(fw_save->dram); - kfree(fw_save->iram); - kfree(fw_save->ddr); + kvfree(fw_save->sram); + kvfree(fw_save->dram); + kvfree(fw_save->iram); + kvfree(fw_save->ddr); kfree(fw_save); block = sst_create_block(ctx, 0, FW_DWNL_ID); diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h index 5c9a51cc77aa..e02e2b4cc08f 100644 --- a/sound/soc/intel/atom/sst/sst.h +++ b/sound/soc/intel/atom/sst/sst.h @@ -317,31 +317,11 @@ struct sst_ipc_reg { int ipcd; }; -struct sst_shim_regs64 { - u64 csr; - u64 pisr; - u64 pimr; - u64 isrx; - u64 isrd; - u64 imrx; - u64 imrd; - u64 ipcx; - u64 ipcd; - u64 isrsc; - u64 isrlpesc; - u64 imrsc; - u64 imrlpesc; - u64 ipcsc; - u64 ipclpesc; - u64 clkctl; - u64 csr2; -}; - struct sst_fw_save { - void *iram; - void *dram; - void *sram; - void *ddr; + void *iram; /* allocated via kvmalloc() */ + void *dram; /* allocated via kvmalloc() */ + void *sram; /* allocated via kvmalloc() */ + void *ddr; /* allocated via kvmalloc() */ }; /** @@ -356,7 +336,6 @@ struct sst_fw_save { * @dram : SST DRAM pointer * @pdata : SST info passed as a part of pci platform data * @shim_phy_add : SST shim phy addr - * @shim_regs64: Struct to save shim registers * @ipc_dispatch_list : ipc messages dispatched * @rx_list : to copy the process_reply/process_msg from DSP * @ipc_post_msg_wq : wq to post IPC messages context @@ -398,7 +377,6 @@ struct intel_sst_drv { unsigned int ddr_end; unsigned int ddr_base; unsigned int mailbox_recv_offset; - struct sst_shim_regs64 *shim_regs64; struct list_head block_list; struct list_head ipc_dispatch_list; struct sst_platform_info *pdata; diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index dd250b8b26f2..0e928d54305d 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -303,8 +303,6 @@ static int sst_acpi_probe(struct platform_device *pdev) dev_err(dev, "No matching machine driver found\n"); return -ENODEV; } - if (mach->machine_quirk) - mach = mach->machine_quirk(mach); pdata = mach->pdata; @@ -360,23 +358,9 @@ static int sst_acpi_probe(struct platform_device *pdev) if (ret < 0) return ret; - /* need to save shim registers in BYT */ - ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64), - GFP_KERNEL); - if (!ctx->shim_regs64) { - ret = -ENOMEM; - goto do_sst_cleanup; - } - sst_configure_runtime_pm(ctx); platform_set_drvdata(pdev, ctx); return ret; - -do_sst_cleanup: - sst_context_cleanup(ctx); - platform_set_drvdata(pdev, NULL); - dev_err(ctx->dev, "failed with %d\n", ret); - return ret; } /** @@ -453,12 +437,20 @@ static const struct dmi_system_id cht_table[] = { static struct sst_acpi_mach cht_surface_mach = { - "10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, - &chv_platform_data }; + .id = "10EC5640", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .pdata = &chv_platform_data, +}; static struct sst_acpi_mach byt_thinkpad_10 = { - "10EC5640", "cht-bsw-rt5672", "intel/fw_sst_0f28.bin", "cht-bsw", NULL, - &byt_rvp_platform_data }; + .id = "10EC5640", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .pdata = &byt_rvp_platform_data, +}; static struct sst_acpi_mach *cht_quirk(void *arg) { @@ -486,68 +478,182 @@ static struct sst_acpi_mach *byt_quirk(void *arg) static struct sst_acpi_mach sst_acpi_bytcr[] = { - {"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", byt_quirk, - &byt_rvp_platform_data }, - {"10EC5642", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL, - &byt_rvp_platform_data }, - {"INTCCFFD", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL, - &byt_rvp_platform_data }, - {"10EC5651", "bytcr_rt5651", "intel/fw_sst_0f28.bin", "bytcr_rt5651", NULL, - &byt_rvp_platform_data }, - {"DLGS7212", "bytcht_da7213", "intel/fw_sst_0f28.bin", "bytcht_da7213", NULL, - &byt_rvp_platform_data }, - {"DLGS7213", "bytcht_da7213", "intel/fw_sst_0f28.bin", "bytcht_da7213", NULL, - &byt_rvp_platform_data }, + { + .id = "10EC5640", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .machine_quirk = byt_quirk, + .pdata = &byt_rvp_platform_data, + }, + { + .id = "10EC5642", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .pdata = &byt_rvp_platform_data + }, + { + .id = "INTCCFFD", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .pdata = &byt_rvp_platform_data + }, + { + .id = "10EC5651", + .drv_name = "bytcr_rt5651", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5651", + .pdata = &byt_rvp_platform_data + }, + { + .id = "DLGS7212", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_da7213", + .pdata = &byt_rvp_platform_data + }, + { + .id = "DLGS7213", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_da7213", + .pdata = &byt_rvp_platform_data + }, /* some Baytrail platforms rely on RT5645, use CHT machine driver */ - {"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL, - &byt_rvp_platform_data }, - {"10EC5648", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL, - &byt_rvp_platform_data }, + { + .id = "10EC5645", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .pdata = &byt_rvp_platform_data + }, + { + .id = "10EC5648", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .pdata = &byt_rvp_platform_data + }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) /* * This is always last in the table so that it is selected only when * enabled explicitly and there is no codec-related information in SSDT */ - {"80860F28", "bytcht_nocodec", "intel/fw_sst_0f28.bin", "bytcht_nocodec", NULL, - &byt_rvp_platform_data }, + { + .id = "80860F28", + .drv_name = "bytcht_nocodec", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_nocodec", + .pdata = &byt_rvp_platform_data + }, #endif {}, }; /* Cherryview-based platforms: CherryTrail and Braswell */ static struct sst_acpi_mach sst_acpi_chv[] = { - {"10EC5670", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, - &chv_platform_data }, - {"10EC5672", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, - &chv_platform_data }, - {"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, - &chv_platform_data }, - {"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, - &chv_platform_data }, - {"10EC3270", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, - &chv_platform_data }, - - {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, - &chv_platform_data }, - {"DLGS7212", "bytcht_da7213", "intel/fw_sst_22a8.bin", "bytcht_da7213", NULL, - &chv_platform_data }, - {"DLGS7213", "bytcht_da7213", "intel/fw_sst_22a8.bin", "bytcht_da7213", NULL, - &chv_platform_data }, + { + .id = "10EC5670", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .pdata = &chv_platform_data + }, + { + .id = "10EC5672", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .pdata = &chv_platform_data + }, + { + .id = "10EC5645", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .pdata = &chv_platform_data + }, + { + .id = "10EC5650", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .pdata = &chv_platform_data + }, + { + .id = "10EC3270", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .pdata = &chv_platform_data + }, + + { + .id = "193C9890", + .drv_name = "cht-bsw-max98090", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .pdata = &chv_platform_data + }, + { + .id = "DLGS7212", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_da7213", + .pdata = &chv_platform_data + }, + { + .id = "DLGS7213", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_da7213", + .pdata = &chv_platform_data + }, + { + .id = "ESSX8316", + .drv_name = "bytcht_es8316", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_es8316", + .pdata = &chv_platform_data + }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ - {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk, - &chv_platform_data }, - {"10EC3276", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL, - &chv_platform_data }, + { + .id = "10EC5640", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5640", + .machine_quirk = cht_quirk, + .pdata = &chv_platform_data + }, + { + .id = "10EC3276", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5640", + .pdata = &chv_platform_data + }, /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ - {"10EC5651", "bytcr_rt5651", "intel/fw_sst_22a8.bin", "bytcr_rt5651", NULL, - &chv_platform_data }, + { + .id = "10EC5651", + .drv_name = "bytcr_rt5651", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5651", + .pdata = &chv_platform_data + }, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) /* * This is always last in the table so that it is selected only when * enabled explicitly and there is no codec-related information in SSDT */ - {"808622A8", "bytcht_nocodec", "intel/fw_sst_22a8.bin", "bytcht_nocodec", NULL, - &chv_platform_data }, + { + .id = "808622A8", + .drv_name = "bytcht_nocodec", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_nocodec", + .pdata = &chv_platform_data + }, #endif {}, }; diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 56896e09445d..a5c5bc5732a2 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -11,7 +11,10 @@ snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o +snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o +snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o +snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o @@ -29,7 +32,10 @@ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o +obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o +obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH) += snd-soc-kbl_rt5663_max98927.o +obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt5663_rt5514_max98927.o obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 14d9693c1641..058b8ccedf02 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -16,6 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <linux/acpi.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/gpio/consumer.h> @@ -120,6 +121,26 @@ static struct snd_soc_jack_gpio mic_jack_gpio = { .invert = 1, }; +/* GPIO indexes defined by ACPI */ +enum { + RT5677_GPIO_PLUG_DET = 0, + RT5677_GPIO_MIC_PRESENT_L = 1, + RT5677_GPIO_HOTWORD_DET_L = 2, + RT5677_GPIO_DSP_INT = 3, + RT5677_GPIO_HP_AMP_SHDN_L = 4, +}; + +static const struct acpi_gpio_params plug_det_gpio = { RT5677_GPIO_PLUG_DET, 0, false }; +static const struct acpi_gpio_params mic_present_gpio = { RT5677_GPIO_MIC_PRESENT_L, 0, false }; +static const struct acpi_gpio_params headphone_enable_gpio = { RT5677_GPIO_HP_AMP_SHDN_L, 0, false }; + +static const struct acpi_gpio_mapping bdw_rt5677_gpios[] = { + { "plug-det-gpios", &plug_det_gpio, 1 }, + { "mic-present-gpios", &mic_present_gpio, 1 }, + { "headphone-enable-gpios", &headphone_enable_gpio, 1 }, + { NULL }, +}; + static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -184,6 +205,11 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) snd_soc_card_get_drvdata(rtd->card); struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret; + + ret = devm_acpi_dev_add_driver_gpios(codec->dev, bdw_rt5677_gpios); + if (ret) + dev_warn(codec->dev, "Failed to add driver gpios\n"); /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1. * The ASRC clock source is clk_i2s1_asrc. diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 3a8c4d954a91..ce35ec7884d1 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -89,11 +89,6 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, if (ret) dev_err(card->dev, "failed to stop PLL: %d\n", ret); } else if(SND_SOC_DAPM_EVENT_ON(event)) { - ret = snd_soc_dai_set_sysclk(codec_dai, - DA7219_CLKSRC_MCLK, 19200000, SND_SOC_CLOCK_IN); - if (ret) - dev_err(card->dev, "can't set codec sysclk configuration\n"); - ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM, 0, DA7219_PLL_FREQ_OUT_98304); if (ret) @@ -187,8 +182,17 @@ static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd, static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) { int ret; + struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; + /* Configure sysclk for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, 19200000, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return ret; + } + /* * Headset buttons map to the google Reference headset. * These can be configured by userspace. @@ -238,31 +242,31 @@ static int broxton_da7219_fe_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static unsigned int rates[] = { +static const unsigned int rates[] = { 48000, }; -static struct snd_pcm_hw_constraint_list constraints_rates = { +static const struct snd_pcm_hw_constraint_list constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; -static unsigned int channels[] = { +static const unsigned int channels[] = { DUAL_CHANNEL, }; -static struct snd_pcm_hw_constraint_list constraints_channels = { +static const struct snd_pcm_hw_constraint_list constraints_channels = { .count = ARRAY_SIZE(channels), .list = channels, .mask = 0, }; -static unsigned int channels_quad[] = { +static const unsigned int channels_quad[] = { QUAD_CHANNEL, }; -static struct snd_pcm_hw_constraint_list constraints_channels_quad = { +static const struct snd_pcm_hw_constraint_list constraints_channels_quad = { .count = ARRAY_SIZE(channels_quad), .list = channels_quad, .mask = 0, diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 1a68d043c803..0c3a3cbcb884 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -207,11 +207,11 @@ static const struct snd_soc_ops broxton_rt298_ops = { .hw_params = broxton_rt298_hw_params, }; -static unsigned int rates[] = { +static const unsigned int rates[] = { 48000, }; -static struct snd_pcm_hw_constraint_list constraints_rates = { +static const struct snd_pcm_hw_constraint_list constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, @@ -222,19 +222,16 @@ static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (params_channels(params) == 2) - channels->min = channels->max = 2; - else - channels->min = channels->max = 4; + channels->min = channels->max = 4; return 0; } -static unsigned int channels_dmic[] = { - 2, 4, +static const unsigned int channels_dmic[] = { + 1, 2, 3, 4, }; -static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { +static const struct snd_pcm_hw_constraint_list constraints_dmic_channels = { .count = ARRAY_SIZE(channels_dmic), .list = channels_dmic, .mask = 0, @@ -256,11 +253,11 @@ static const struct snd_soc_ops broxton_dmic_ops = { .startup = broxton_dmic_startup, }; -static unsigned int channels[] = { +static const unsigned int channels[] = { 2, }; -static struct snd_pcm_hw_constraint_list constraints_channels = { +static const struct snd_pcm_hw_constraint_list constraints_channels = { .count = ARRAY_SIZE(channels), .list = channels, .mask = 0, diff --git a/sound/soc/intel/boards/byt-max98090.c b/sound/soc/intel/boards/byt-max98090.c index d9f81b8d915d..047be7fa0ce9 100644 --- a/sound/soc/intel/boards/byt-max98090.c +++ b/sound/soc/intel/boards/byt-max98090.c @@ -67,20 +67,27 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { static struct snd_soc_jack_gpio hs_jack_gpios[] = { { - .name = "hp-gpio", - .idx = 0, + .name = "hp", .report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT, .debounce_time = 200, }, { - .name = "mic-gpio", - .idx = 1, + .name = "mic", .invert = 1, .report = SND_JACK_MICROPHONE, .debounce_time = 200, }, }; +static const struct acpi_gpio_params hp_gpios = { 0, 0, false }; +static const struct acpi_gpio_params mic_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_byt_max98090_gpios[] = { + { "hp-gpios", &hp_gpios, 1 }, + { "mic-gpios", &mic_gpios, 1 }, + {}, +}; + static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime) { int ret; @@ -140,8 +147,9 @@ static struct snd_soc_card byt_max98090_card = { static int byt_max98090_probe(struct platform_device *pdev) { - int ret_val = 0; + struct device *dev = &pdev->dev; struct byt_max98090_private *priv; + int ret_val; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); if (!priv) { @@ -149,6 +157,10 @@ static int byt_max98090_probe(struct platform_device *pdev) return -ENOMEM; } + ret_val = devm_acpi_dev_add_driver_gpios(dev->parent, acpi_byt_max98090_gpios); + if (ret_val) + dev_dbg(dev, "Unable to add GPIO mapping table\n"); + byt_max98090_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&byt_max98090_card, priv); ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_max98090_card); @@ -158,7 +170,7 @@ static int byt_max98090_probe(struct platform_device *pdev) return ret_val; } - return ret_val; + return 0; } static int byt_max98090_remove(struct platform_device *pdev) diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c new file mode 100644 index 000000000000..52635462dac6 --- /dev/null +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -0,0 +1,300 @@ +/* + * bytcht_es8316.c - ASoc Machine driver for Intel Baytrail/Cherrytrail + * platforms with Everest ES8316 SoC + * + * Copyright (C) 2017 Endless Mobile, Inc. + * Authors: David Yang <yangxiaohua@everest-semi.com>, + * Daniel Drake <drake@endlessm.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; version 2 of the License. + * + * 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/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <asm/platform_sst_audio.h> +#include <linux/clk.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../atom/sst-atom-controls.h" +#include "../common/sst-acpi.h" +#include "../common/sst-dsp.h" + +struct byt_cht_es8316_private { + struct clk *mclk; +}; + +#define CODEC_DAI1 "ES8316 HiFi" + +static inline struct snd_soc_dai *get_codec_dai(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strncmp(rtd->codec_dai->name, CODEC_DAI1, + strlen(CODEC_DAI1))) + return rtd->codec_dai; + } + return NULL; +} + +static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + + /* + * The codec supports two analog microphone inputs. I have only + * tested MIC1. A DMIC route could also potentially be added + * if such functionality is found on another platform. + */ + SND_SOC_DAPM_MIC("Microphone 1", NULL), + SND_SOC_DAPM_MIC("Microphone 2", NULL), +}; + +static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = { + {"MIC1", NULL, "Microphone 1"}, + {"MIC2", NULL, "Microphone 2"}, + + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + + {"Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "codec_out0"}, + {"ssp2 Tx", NULL, "codec_out1"}, + {"codec_in0", NULL, "ssp2 Rx" }, + {"codec_in1", NULL, "ssp2 Rx" }, + {"ssp2 Rx", NULL, "Capture"}, +}; + +static const struct snd_kcontrol_new byt_cht_es8316_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Microphone 1"), + SOC_DAPM_PIN_SWITCH("Microphone 2"), +}; + +static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); + int ret; + + card->dapm.idle_bias_off = true; + + /* + * The firmware might enable the clock at boot (this information + * may or may not be reflected in the enable clock register). + * To change the rate we must disable the clock first to cover these + * cases. Due to common clock framework restrictions that do not allow + * to disable a clock that has not been enabled, we need to enable + * the clock first. + */ + ret = clk_prepare_enable(priv->mclk); + if (!ret) + clk_disable_unprepare(priv->mclk); + + ret = clk_set_rate(priv->mclk, 19200000); + if (ret) + dev_err(card->dev, "unable to set MCLK rate\n"); + + ret = clk_prepare_enable(priv->mclk); + if (ret) + dev_err(card->dev, "unable to enable MCLK\n"); + + ret = snd_soc_dai_set_sysclk(runtime->codec_dai, 0, 19200000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "can't set codec clock %d\n", ret); + return ret; + } + + return 0; +} + +static const struct snd_soc_pcm_stream byt_cht_es8316_dai_params = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int ret; + + /* The DSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP2 to 24-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot, override config + * with explicit setting to I2S 2ch 24-bit. The word length is set with + * dai_set_tdm_slot() since there is no other API exposed + */ + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS + ); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); + if (ret < 0) { + dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); + return ret; + } + + return 0; +} + +static int byt_cht_es8316_aif1_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 48000); +} + +static const struct snd_soc_ops byt_cht_es8316_aif1_ops = { + .startup = byt_cht_es8316_aif1_startup, +}; + +static struct snd_soc_dai_link byt_cht_es8316_dais[] = { + [MERR_DPCM_AUDIO] = { + .name = "Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "media-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .nonatomic = true, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &byt_cht_es8316_aif1_ops, + }, + + [MERR_DPCM_DEEP_BUFFER] = { + .name = "Deep-Buffer Audio Port", + .stream_name = "Deep-Buffer Audio", + .cpu_dai_name = "deepbuffer-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .nonatomic = true, + .dynamic = 1, + .dpcm_playback = 1, + .ops = &byt_cht_es8316_aif1_ops, + }, + + [MERR_DPCM_COMPR] = { + .name = "Compressed Port", + .stream_name = "Compress", + .cpu_dai_name = "compress-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + }, + + /* back ends */ + { + /* Only SSP2 has been tested here, so BYT-CR platforms that + * require SSP0 will not work. + */ + .name = "SSP2-Codec", + .id = 1, + .cpu_dai_name = "ssp2-port", + .platform_name = "sst-mfld-platform", + .no_pcm = 1, + .codec_dai_name = "ES8316 HiFi", + .codec_name = "i2c-ESSX8316:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .be_hw_params_fixup = byt_cht_es8316_codec_fixup, + .nonatomic = true, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = byt_cht_es8316_init, + }, +}; + + +/* SoC card */ +static struct snd_soc_card byt_cht_es8316_card = { + .name = "bytcht-es8316", + .owner = THIS_MODULE, + .dai_link = byt_cht_es8316_dais, + .num_links = ARRAY_SIZE(byt_cht_es8316_dais), + .dapm_widgets = byt_cht_es8316_widgets, + .num_dapm_widgets = ARRAY_SIZE(byt_cht_es8316_widgets), + .dapm_routes = byt_cht_es8316_audio_map, + .num_dapm_routes = ARRAY_SIZE(byt_cht_es8316_audio_map), + .controls = byt_cht_es8316_controls, + .num_controls = ARRAY_SIZE(byt_cht_es8316_controls), + .fully_routed = true, +}; + +static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) +{ + int ret = 0; + struct byt_cht_es8316_private *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); + if (!priv) + return -ENOMEM; + + /* register the soc card */ + byt_cht_es8316_card.dev = &pdev->dev; + snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv); + + priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(priv->mclk)) { + ret = PTR_ERR(priv->mclk); + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %d\n", + ret); + return ret; + } + + ret = devm_snd_soc_register_card(&pdev->dev, &byt_cht_es8316_card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); + return ret; + } + platform_set_drvdata(pdev, &byt_cht_es8316_card); + return ret; +} + +static struct platform_driver snd_byt_cht_es8316_mc_driver = { + .driver = { + .name = "bytcht_es8316", + }, + .probe = snd_byt_cht_es8316_mc_probe, +}; + +module_platform_driver(snd_byt_cht_es8316_mc_driver); +MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver"); +MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bytcht_es8316"); diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c index 89853eeaaf9d..1dd9441806fa 100644 --- a/sound/soc/intel/boards/bytcht_nocodec.c +++ b/sound/soc/intel/boards/bytcht_nocodec.c @@ -85,11 +85,11 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static unsigned int rates_48000[] = { +static const unsigned int rates_48000[] = { 48000, }; -static struct snd_pcm_hw_constraint_list constraints_48000 = { +static const struct snd_pcm_hw_constraint_list constraints_48000 = { .count = ARRAY_SIZE(rates_48000), .list = rates_48000, }; diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 8164bec63bf1..4a3516b38c2c 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -203,11 +203,11 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static unsigned int rates_48000[] = { +static const unsigned int rates_48000[] = { 48000, }; -static struct snd_pcm_hw_constraint_list constraints_48000 = { +static const struct snd_pcm_hw_constraint_list constraints_48000 = { .count = ARRAY_SIZE(rates_48000), .list = rates_48000, }; diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 742bc0d4e681..20755ecc7f9e 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -39,18 +39,6 @@ struct cht_mc_private { bool ts3a227e_present; }; -static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, - strlen(CHT_CODEC_DAI))) - return rtd->codec_dai; - } - return NULL; -} - static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index e4d46d4360d7..bc2a52de06a3 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -19,6 +19,8 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/clk.h> +#include <asm/cpu_device_id.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -31,8 +33,11 @@ #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI "rt5670-aif1" -static struct snd_soc_jack cht_bsw_headset; -static char cht_bsw_codec_name[16]; +struct cht_mc_private { + struct snd_soc_jack headset; + char codec_name[16]; + struct clk *mclk; +}; /* Headset jack detection DAPM pins */ static struct snd_soc_jack_pin cht_bsw_headset_pins[] = { @@ -64,6 +69,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct snd_soc_dai *codec_dai; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); int ret; codec_dai = cht_get_codec_dai(card); @@ -73,6 +79,15 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, } if (SND_SOC_DAPM_EVENT_ON(event)) { + if (ctx->mclk) { + ret = clk_prepare_enable(ctx->mclk); + if (ret < 0) { + dev_err(card->dev, + "could not configure MCLK state"); + return ret; + } + } + /* set codec PLL source to the 19.2MHz platform clock (MCLK) */ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK, CHT_PLAT_CLK_3_HZ, 48000 * 512); @@ -96,6 +111,9 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, */ snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, 48000 * 512, SND_SOC_CLOCK_IN); + + if (ctx->mclk) + clk_disable_unprepare(ctx->mclk); } return 0; } @@ -171,6 +189,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) int ret; struct snd_soc_dai *codec_dai = runtime->codec_dai; struct snd_soc_codec *codec = codec_dai->codec; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); @@ -194,13 +213,37 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) RT5670_CLK_SEL_I2S1_ASRC); ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2, &cht_bsw_headset, - cht_bsw_headset_pins, ARRAY_SIZE(cht_bsw_headset_pins)); + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2, + &ctx->headset, + cht_bsw_headset_pins, + ARRAY_SIZE(cht_bsw_headset_pins)); if (ret) return ret; - rt5670_set_jack_detect(codec, &cht_bsw_headset); + rt5670_set_jack_detect(codec, &ctx->headset); + if (ctx->mclk) { + /* + * The firmware might enable the clock at + * boot (this information may or may not + * be reflected in the enable clock register). + * To change the rate we must disable the clock + * first to cover these cases. Due to common + * clock framework restrictions that do not allow + * to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(ctx->mclk); + if (!ret) + clk_disable_unprepare(ctx->mclk); + + ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ); + + if (ret) { + dev_err(runtime->dev, "unable to set MCLK rate\n"); + return ret; + } + } return 0; } @@ -341,34 +384,62 @@ static struct snd_soc_card snd_soc_card_cht = { .resume_post = cht_resume_post, }; +static bool is_valleyview(void) +{ + static const struct x86_cpu_id cpu_ids[] = { + { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ + {} + }; + + if (!x86_match_cpu(cpu_ids)) + return false; + return true; +} + #define RT5672_I2C_DEFAULT "i2c-10EC5670:00" static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; + struct cht_mc_private *drv; struct sst_acpi_mach *mach = pdev->dev.platform_data; const char *i2c_name; int i; - strcpy(cht_bsw_codec_name, RT5672_I2C_DEFAULT); + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); + if (!drv) + return -ENOMEM; + + strcpy(drv->codec_name, RT5672_I2C_DEFAULT); /* fixup codec name based on HID */ if (mach) { i2c_name = sst_acpi_find_name_from_hid(mach->id); if (i2c_name) { - snprintf(cht_bsw_codec_name, sizeof(cht_bsw_codec_name), + snprintf(drv->codec_name, sizeof(drv->codec_name), "i2c-%s", i2c_name); for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) { if (!strcmp(cht_dailink[i].codec_name, RT5672_I2C_DEFAULT)) { cht_dailink[i].codec_name = - cht_bsw_codec_name; + drv->codec_name; break; } } } } + if (is_valleyview()) { + drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(drv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(drv->mclk)); + return PTR_ERR(drv->mclk); + } + } + snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); + /* register the soc card */ snd_soc_card_cht.dev = &pdev->dev; ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c new file mode 100644 index 000000000000..f9ba97788157 --- /dev/null +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -0,0 +1,687 @@ +/* + * Intel Kabylake I2S Machine Driver with MAXIM98927 + * and RT5663 Codecs + * + * Copyright (C) 2017, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Skylake I2S Machine driver + * + * 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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../codecs/rt5663.h" +#include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" + +#define KBL_REALTEK_CODEC_DAI "rt5663-aif" +#define KBL_MAXIM_CODEC_DAI "max98927-aif1" +#define DMIC_CH(p) p->list[p->count-1] +#define MAXIM_DEV0_NAME "i2c-MX98927:00" +#define MAXIM_DEV1_NAME "i2c-MX98927:01" + +static struct snd_soc_card kabylake_audio_card; +static const struct snd_pcm_hw_constraint_list *dmic_constraints; +static struct snd_soc_jack skylake_hdmi[3]; + +struct kbl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct kbl_rt5663_private { + struct snd_soc_jack kabylake_headset; + struct list_head hdmi_pcm_list; +}; + +enum { + KBL_DPCM_AUDIO_PB = 0, + KBL_DPCM_AUDIO_CP, + KBL_DPCM_AUDIO_REF_CP, + KBL_DPCM_AUDIO_DMIC_CP, + KBL_DPCM_AUDIO_HDMI1_PB, + KBL_DPCM_AUDIO_HDMI2_PB, + KBL_DPCM_AUDIO_HDMI3_PB, +}; + +static const struct snd_kcontrol_new kabylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_soc_dapm_widget kabylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("DP", NULL), + SND_SOC_DAPM_SPK("HDMI", NULL), + +}; + +static const struct snd_soc_dapm_route kabylake_map[] = { + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* speaker */ + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, + + /* other jacks */ + { "IN1P", NULL, "Headset Mic" }, + { "IN1N", NULL, "Headset Mic" }, + { "DMic", NULL, "SoC DMIC" }, + + { "HDMI", NULL, "hif5 Output" }, + { "DP", NULL, "hif6 Output" }, + + /* CODEC BE connections */ + { "Left HiFi Playback", NULL, "ssp0 Tx" }, + { "Right HiFi Playback", NULL, "ssp0 Tx" }, + { "ssp0 Tx", NULL, "codec0_out" }, + + { "AIF Playback", NULL, "ssp1 Tx" }, + { "ssp1 Tx", NULL, "codec1_out" }, + + { "codec0_in", NULL, "ssp1 Rx" }, + { "ssp1 Rx", NULL, "AIF Capture" }, + + /* DMIC */ + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, +}; + +static struct snd_soc_codec_conf max98927_codec_conf[] = { + { + .dev_name = MAXIM_DEV0_NAME, + .name_prefix = "Right", + }, + { + .dev_name = MAXIM_DEV1_NAME, + .name_prefix = "Left", + }, +}; + +static struct snd_soc_dai_link_component max98927_codec_components[] = { + { /* Left */ + .name = MAXIM_DEV0_NAME, + .dai_name = KBL_MAXIM_CODEC_DAI, + }, + { /* Right */ + .name = MAXIM_DEV1_NAME, + .dai_name = KBL_MAXIM_CODEC_DAI, + }, +}; + +static int kabylake_rt5663_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + + dapm = snd_soc_component_get_dapm(component); + ret = snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + if (ret) { + dev_err(rtd->dev, "Ref Cap ignore suspend failed %d\n", ret); + return ret; + } + + return ret; +} + +static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct kbl_rt5663_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_codec *codec = rtd->codec; + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(&kabylake_audio_card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, &ctx->kabylake_headset, + NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); + return ret; + } + + rt5663_set_jack_detect(codec, &ctx->kabylake_headset); + ret = snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); + if (ret) { + dev_err(rtd->dev, "SoC DMIC ignore suspend failed %d\n", ret); + return ret; + } + + return ret; +} + +static int kabylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) +{ + struct kbl_rt5663_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct kbl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = KBL_DPCM_AUDIO_HDMI1_PB; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int kabylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) +{ + struct kbl_rt5663_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct kbl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = KBL_DPCM_AUDIO_HDMI2_PB; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int kabylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) +{ + struct kbl_rt5663_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct kbl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = KBL_DPCM_AUDIO_HDMI3_PB; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static unsigned int rates[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static unsigned int channels[] = { + 2, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int kbl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * On this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = 2; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops kabylake_rt5663_fe_ops = { + .startup = kbl_fe_startup, +}; + +static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + /* set SSP1 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int kabylake_rt5663_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_sysclk(codec_dai, + RT5663_SCLK_S_MCLK, 24576000, SND_SOC_CLOCK_IN); + /* use ASRC for internal clocks, as PLL rate isn't multiple of BCLK */ + rt5663_sel_asrc_clk_src(codec_dai->codec, RT5663_DA_STEREO_FILTER, 1); + + if (ret < 0) + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + + return ret; +} + +static struct snd_soc_ops kabylake_rt5663_ops = { + .hw_params = kabylake_rt5663_hw_params, +}; + +static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; + + return 0; +} + +static unsigned int channels_dmic[] = { + 2, 4, +}; + +static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { + .count = ARRAY_SIZE(channels_dmic), + .list = channels_dmic, + .mask = 0, +}; + +static const unsigned int dmic_2ch[] = { + 2, +}; + +static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = { + .count = ARRAY_SIZE(dmic_2ch), + .list = dmic_2ch, + .mask = 0, +}; + +static int kabylake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = DMIC_CH(dmic_constraints); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dmic_constraints); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static struct snd_soc_ops kabylake_dmic_ops = { + .startup = kabylake_dmic_startup, +}; + +static unsigned int rates_16000[] = { + 16000, +}; + +static struct snd_pcm_hw_constraint_list constraints_16000 = { + .count = ARRAY_SIZE(rates_16000), + .list = rates_16000, +}; + +static const unsigned int ch_mono[] = { + 1, +}; + +static const struct snd_pcm_hw_constraint_list constraints_refcap = { + .count = ARRAY_SIZE(ch_mono), + .list = ch_mono, +}; + +static int kabylake_refcap_startup(struct snd_pcm_substream *substream) +{ + substream->runtime->hw.channels_max = 1; + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_refcap); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_16000); +} + +static struct snd_soc_ops skylaye_refcap_ops = { + .startup = kabylake_refcap_startup, +}; + +/* kabylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link kabylake_dais[] = { + /* Front End DAI links */ + [KBL_DPCM_AUDIO_PB] = { + .name = "Kbl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = kabylake_rt5663_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &kabylake_rt5663_fe_ops, + }, + [KBL_DPCM_AUDIO_CP] = { + .name = "Kbl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + .ops = &kabylake_rt5663_fe_ops, + }, + [KBL_DPCM_AUDIO_REF_CP] = { + .name = "Kbl Audio Reference cap", + .stream_name = "Wake on Voice", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &skylaye_refcap_ops, + }, + [KBL_DPCM_AUDIO_DMIC_CP] = { + .name = "Kbl Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &kabylake_dmic_ops, + }, + [KBL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Kbl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Kbl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Kbl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codecs = max98927_codec_components, + .num_codecs = ARRAY_SIZE(max98927_codec_components), + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp_fixup, + .dpcm_playback = 1, + }, + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-10EC5663:00", + .codec_dai_name = KBL_REALTEK_CODEC_DAI, + .init = kabylake_rt5663_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp_fixup, + .ops = &kabylake_rt5663_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 2, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .be_hw_params_fixup = kabylake_dmic_fixup, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = kabylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi3_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +#define NAME_SIZE 32 +static int kabylake_card_late_probe(struct snd_soc_card *card) +{ + struct kbl_rt5663_private *ctx = snd_soc_card_get_drvdata(card); + struct kbl_hdmi_pcm *pcm; + int err, i = 0; + char jack_name[NAME_SIZE]; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &skylake_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &skylake_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + return 0; +} + +/* kabylake audio machine driver for SPT + RT5663 */ +static struct snd_soc_card kabylake_audio_card = { + .name = "kblrt5663max", + .owner = THIS_MODULE, + .dai_link = kabylake_dais, + .num_links = ARRAY_SIZE(kabylake_dais), + .controls = kabylake_controls, + .num_controls = ARRAY_SIZE(kabylake_controls), + .dapm_widgets = kabylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_widgets), + .dapm_routes = kabylake_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_map), + .codec_conf = max98927_codec_conf, + .num_configs = ARRAY_SIZE(max98927_codec_conf), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + +static int kabylake_audio_probe(struct platform_device *pdev) +{ + struct kbl_rt5663_private *ctx; + struct skl_machine_pdata *pdata; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + kabylake_audio_card.dev = &pdev->dev; + snd_soc_card_set_drvdata(&kabylake_audio_card, ctx); + + pdata = dev_get_drvdata(&pdev->dev); + if (pdata) + dmic_constraints = pdata->dmic_num == 2 ? + &constraints_dmic_2ch : &constraints_dmic_channels; + + return devm_snd_soc_register_card(&pdev->dev, &kabylake_audio_card); +} + +static const struct platform_device_id kbl_board_ids[] = { + { .name = "kbl_rt5663_m98927" }, + { } +}; + +static struct platform_driver kabylake_audio = { + .probe = kabylake_audio_probe, + .driver = { + .name = "kbl_rt5663_m98927", + .pm = &snd_soc_pm_ops, + }, + .id_table = kbl_board_ids, +}; + +module_platform_driver(kabylake_audio) + +/* Module information */ +MODULE_DESCRIPTION("Audio Machine driver-RT5663 & MAX98927 in I2S mode"); +MODULE_AUTHOR("Naveen M <naveen.m@intel.com>"); +MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kbl_rt5663_m98927"); diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c new file mode 100644 index 000000000000..3fe4a0807095 --- /dev/null +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -0,0 +1,640 @@ +/* + * Intel Kabylake I2S Machine Driver with MAXIM98927 + * RT5514 and RT5663 Codecs + * + * Copyright (C) 2017, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Kabylake I2S Machine driver supporting MAXIM98927 and + * RT5663 codecs + * + * 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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../codecs/rt5514.h" +#include "../../codecs/rt5663.h" +#include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" + +#define KBL_REALTEK_CODEC_DAI "rt5663-aif" +#define KBL_REALTEK_DMIC_CODEC_DAI "rt5514-aif1" +#define KBL_MAXIM_CODEC_DAI "max98927-aif1" +#define MAXIM_DEV0_NAME "i2c-MX98927:00" +#define MAXIM_DEV1_NAME "i2c-MX98927:01" +#define RT5514_DEV_NAME "i2c-10EC5514:00" +#define RT5663_DEV_NAME "i2c-10EC5663:00" +#define RT5514_AIF1_BCLK_FREQ (48000 * 8 * 16) +#define RT5514_AIF1_SYSCLK_FREQ 12288000 +#define NAME_SIZE 32 + +#define DMIC_CH(p) p->list[p->count-1] + + +static struct snd_soc_card kabylake_audio_card; +static const struct snd_pcm_hw_constraint_list *dmic_constraints; + +struct kbl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct kbl_codec_private { + struct snd_soc_jack kabylake_headset; + struct list_head hdmi_pcm_list; + struct snd_soc_jack kabylake_hdmi[2]; +}; + +enum { + KBL_DPCM_AUDIO_PB = 0, + KBL_DPCM_AUDIO_CP, + KBL_DPCM_AUDIO_DMIC_CP, + KBL_DPCM_AUDIO_HDMI1_PB, + KBL_DPCM_AUDIO_HDMI2_PB, +}; + +static const struct snd_kcontrol_new kabylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), + SOC_DAPM_PIN_SWITCH("DMIC"), +}; + +static const struct snd_soc_dapm_widget kabylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), + SND_SOC_DAPM_SPK("DP", NULL), + SND_SOC_DAPM_SPK("HDMI", NULL), + +}; + +static const struct snd_soc_dapm_route kabylake_map[] = { + /* Headphones */ + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* speaker */ + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, + + /* other jacks */ + { "IN1P", NULL, "Headset Mic" }, + { "IN1N", NULL, "Headset Mic" }, + + { "HDMI", NULL, "hif5 Output" }, + { "DP", NULL, "hif6 Output" }, + + /* CODEC BE connections */ + { "Left HiFi Playback", NULL, "ssp0 Tx" }, + { "Right HiFi Playback", NULL, "ssp0 Tx" }, + { "ssp0 Tx", NULL, "codec0_out" }, + + { "AIF Playback", NULL, "ssp1 Tx" }, + { "ssp1 Tx", NULL, "codec1_out" }, + + { "codec0_in", NULL, "ssp1 Rx" }, + { "ssp1 Rx", NULL, "AIF Capture" }, + + { "codec1_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "AIF1 Capture" }, + + /* DMIC */ + { "DMIC1L", NULL, "DMIC" }, + { "DMIC1R", NULL, "DMIC" }, + { "DMIC2L", NULL, "DMIC" }, + { "DMIC2R", NULL, "DMIC" }, + + { "hifi2", NULL, "iDisp2 Tx" }, + { "iDisp2 Tx", NULL, "iDisp2_out" }, + { "hifi1", NULL, "iDisp1 Tx" }, + { "iDisp1 Tx", NULL, "iDisp1_out" }, +}; + +static struct snd_soc_codec_conf max98927_codec_conf[] = { + { + .dev_name = MAXIM_DEV0_NAME, + .name_prefix = "Right", + }, + { + .dev_name = MAXIM_DEV1_NAME, + .name_prefix = "Left", + }, +}; + +static struct snd_soc_dai_link_component ssp0_codec_components[] = { + { /* Left */ + .name = MAXIM_DEV0_NAME, + .dai_name = KBL_MAXIM_CODEC_DAI, + }, + { /* Right */ + .name = MAXIM_DEV1_NAME, + .dai_name = KBL_MAXIM_CODEC_DAI, + }, + { /*dmic */ + .name = RT5514_DEV_NAME, + .dai_name = KBL_REALTEK_DMIC_CODEC_DAI, + }, +}; + +static int kabylake_rt5663_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + int ret; + + dapm = snd_soc_component_get_dapm(component); + ret = snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + if (ret) + dev_err(rtd->dev, "Ref Cap -Ignore suspend failed = %d\n", ret); + + return ret; +} + +static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_codec *codec = rtd->codec; + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(&kabylake_audio_card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, &ctx->kabylake_headset, + NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); + return ret; + } + + rt5663_set_jack_detect(codec, &ctx->kabylake_headset); + + ret = snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "DMIC"); + if (ret) + dev_err(rtd->dev, "DMIC - Ignore suspend failed = %d\n", ret); + + return ret; +} + +static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device) +{ + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct kbl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = device; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int kabylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI1_PB); +} + +static int kabylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI2_PB); +} + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + 2, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int kbl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * On this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = 2; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops kabylake_rt5663_fe_ops = { + .startup = kbl_fe_startup, +}; + +static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_soc_dpcm *dpcm = container_of( + params, struct snd_soc_dpcm, hw_params); + struct snd_soc_dai_link *fe_dai_link = dpcm->fe->dai_link; + struct snd_soc_dai_link *be_dai_link = dpcm->be->dai_link; + + /* + * The ADSP will convert the FE rate to 48k, stereo, 24 bit + */ + if (!strcmp(fe_dai_link->name, "Kbl Audio Port") || + !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) { + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + } else if (!strcmp(fe_dai_link->name, "Kbl Audio DMIC cap")) { + if (params_channels(params) == 2 || + DMIC_CH(dmic_constraints) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; + } + /* + * The speaker on the SSP0 supports S16_LE and not S24_LE. + * thus changing the mask here + */ + if (!strcmp(be_dai_link->name, "SSP0-Codec")) + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static int kabylake_rt5663_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; + + /* use ASRC for internal clocks, as PLL rate isn't multiple of BCLK */ + rt5663_sel_asrc_clk_src(codec_dai->codec, RT5663_DA_STEREO_FILTER, 1); + + ret = snd_soc_dai_set_sysclk(codec_dai, + RT5663_SCLK_S_MCLK, 24576000, SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + + return ret; +} + +static struct snd_soc_ops kabylake_rt5663_ops = { + .hw_params = kabylake_rt5663_hw_params, +}; + +static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret = 0, j; + + for (j = 0; j < rtd->num_codecs; j++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + + if (!strcmp(codec_dai->component->name, RT5514_DEV_NAME)) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0, 8, 16); + if (ret < 0) { + dev_err(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5514_PLL1_S_BCLK, RT5514_AIF1_BCLK_FREQ, + RT5514_AIF1_SYSCLK_FREQ); + if (ret < 0) { + dev_err(rtd->dev, "set bclk err: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, + RT5514_SCLK_S_PLL1, RT5514_AIF1_SYSCLK_FREQ, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "set sclk err: %d\n", ret); + return ret; + } + } + if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME) || + !strcmp(codec_dai->component->name, MAXIM_DEV1_NAME)) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF0, 3, 8, 16); + if (ret < 0) { + dev_err(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + } + } + return ret; +} + +static struct snd_soc_ops kabylake_ssp0_ops = { + .hw_params = kabylake_ssp0_hw_params, +}; + +static const unsigned int channels_dmic[] = { + 4, +}; + +static const struct snd_pcm_hw_constraint_list constraints_dmic_channels = { + .count = ARRAY_SIZE(channels_dmic), + .list = channels_dmic, + .mask = 0, +}; + +static const unsigned int dmic_2ch[] = { + 4, +}; + +static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = { + .count = ARRAY_SIZE(dmic_2ch), + .list = dmic_2ch, + .mask = 0, +}; + +static int kabylake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = DMIC_CH(dmic_constraints); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dmic_constraints); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static struct snd_soc_ops kabylake_dmic_ops = { + .startup = kabylake_dmic_startup, +}; + +/* kabylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link kabylake_dais[] = { + /* Front End DAI links */ + [KBL_DPCM_AUDIO_PB] = { + .name = "Kbl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = kabylake_rt5663_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &kabylake_rt5663_fe_ops, + }, + [KBL_DPCM_AUDIO_CP] = { + .name = "Kbl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + .ops = &kabylake_rt5663_fe_ops, + }, + [KBL_DPCM_AUDIO_DMIC_CP] = { + .name = "Kbl Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &kabylake_dmic_ops, + }, + [KBL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Kbl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Kbl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + /* Back End DAI links */ + /* single Back end dai for both max speakers and dmic */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codecs = ssp0_codec_components, + .num_codecs = ARRAY_SIZE(ssp0_codec_components), + .dai_fmt = SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp_fixup, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &kabylake_ssp0_ops, + }, + { + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = RT5663_DEV_NAME, + .codec_dai_name = KBL_REALTEK_CODEC_DAI, + .init = kabylake_rt5663_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp_fixup, + .ops = &kabylake_rt5663_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = kabylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int kabylake_card_late_probe(struct snd_soc_card *card) +{ + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(card); + struct kbl_hdmi_pcm *pcm; + int err, i = 0; + char jack_name[NAME_SIZE]; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &ctx->kabylake_hdmi[i], + NULL, 0); + + if (err) + return err; + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &ctx->kabylake_hdmi[i]); + if (err < 0) + return err; + i++; + } + + return 0; +} + +/* + * kabylake audio machine driver for MAX98927 + RT5514 + RT5663 + */ +static struct snd_soc_card kabylake_audio_card = { + .name = "kbl_r5514_5663_max", + .owner = THIS_MODULE, + .dai_link = kabylake_dais, + .num_links = ARRAY_SIZE(kabylake_dais), + .controls = kabylake_controls, + .num_controls = ARRAY_SIZE(kabylake_controls), + .dapm_widgets = kabylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_widgets), + .dapm_routes = kabylake_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_map), + .codec_conf = max98927_codec_conf, + .num_configs = ARRAY_SIZE(max98927_codec_conf), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + +static int kabylake_audio_probe(struct platform_device *pdev) +{ + struct kbl_codec_private *ctx; + struct skl_machine_pdata *pdata; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + kabylake_audio_card.dev = &pdev->dev; + snd_soc_card_set_drvdata(&kabylake_audio_card, ctx); + + pdata = dev_get_drvdata(&pdev->dev); + if (pdata) + dmic_constraints = pdata->dmic_num == 2 ? + &constraints_dmic_2ch : &constraints_dmic_channels; + + return devm_snd_soc_register_card(&pdev->dev, &kabylake_audio_card); +} + +static const struct platform_device_id kbl_board_ids[] = { + { .name = "kbl_r5514_5663_max" }, + { } +}; + +static struct platform_driver kabylake_audio = { + .probe = kabylake_audio_probe, + .driver = { + .name = "kbl_r5514_5663_max", + .pm = &snd_soc_pm_ops, + }, + .id_table = kbl_board_ids, +}; + +module_platform_driver(kabylake_audio) + +/* Module information */ +MODULE_DESCRIPTION("Audio Machine driver-RT5663 RT5514 & MAX98927"); +MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kbl_r5514_5663_max"); diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 3b12bc1fa518..5ed0aa27b467 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -266,21 +266,21 @@ static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static unsigned int rates[] = { +static const unsigned int rates[] = { 48000, }; -static struct snd_pcm_hw_constraint_list constraints_rates = { +static const struct snd_pcm_hw_constraint_list constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; -static unsigned int channels[] = { +static const unsigned int channels[] = { 2, }; -static struct snd_pcm_hw_constraint_list constraints_channels = { +static const struct snd_pcm_hw_constraint_list constraints_channels = { .count = ARRAY_SIZE(channels), .list = channels, .mask = 0, @@ -348,11 +348,11 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static unsigned int channels_dmic[] = { +static const unsigned int channels_dmic[] = { 2, 4, }; -static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { +static const struct snd_pcm_hw_constraint_list constraints_dmic_channels = { .count = ARRAY_SIZE(channels_dmic), .list = channels_dmic, .mask = 0, @@ -384,11 +384,11 @@ static const struct snd_soc_ops skylake_dmic_ops = { .startup = skylake_dmic_startup, }; -static unsigned int rates_16000[] = { +static const unsigned int rates_16000[] = { 16000, }; -static struct snd_pcm_hw_constraint_list constraints_16000 = { +static const struct snd_pcm_hw_constraint_list constraints_16000 = { .count = ARRAY_SIZE(rates_16000), .list = rates_16000, }; diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index eb7751b0599b..01b8b140bb08 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -297,21 +297,21 @@ static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static unsigned int rates[] = { +static const unsigned int rates[] = { 48000, }; -static struct snd_pcm_hw_constraint_list constraints_rates = { +static const struct snd_pcm_hw_constraint_list constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; -static unsigned int channels[] = { +static const unsigned int channels[] = { 2, }; -static struct snd_pcm_hw_constraint_list constraints_channels = { +static const struct snd_pcm_hw_constraint_list constraints_channels = { .count = ARRAY_SIZE(channels), .list = channels, .mask = 0, @@ -397,11 +397,11 @@ static const struct snd_soc_ops skylake_nau8825_ops = { .hw_params = skylake_nau8825_hw_params, }; -static unsigned int channels_dmic[] = { +static const unsigned int channels_dmic[] = { 2, 4, }; -static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { +static const struct snd_pcm_hw_constraint_list constraints_dmic_channels = { .count = ARRAY_SIZE(channels_dmic), .list = channels_dmic, .mask = 0, @@ -433,11 +433,11 @@ static const struct snd_soc_ops skylake_dmic_ops = { .startup = skylake_dmic_startup, }; -static unsigned int rates_16000[] = { +static const unsigned int rates_16000[] = { 16000, }; -static struct snd_pcm_hw_constraint_list constraints_16000 = { +static const struct snd_pcm_hw_constraint_list constraints_16000 = { .count = ARRAY_SIZE(rates_16000), .list = rates_16000, }; diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index f5ab7b8d51d1..2bc4cfca594e 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -43,6 +43,7 @@ struct skl_rt286_private { enum { SKL_DPCM_AUDIO_PB = 0, + SKL_DPCM_AUDIO_DB_PB, SKL_DPCM_AUDIO_CP, SKL_DPCM_AUDIO_REF_CP, SKL_DPCM_AUDIO_DMIC_CP, @@ -165,21 +166,21 @@ static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static unsigned int rates[] = { +static const unsigned int rates[] = { 48000, }; -static struct snd_pcm_hw_constraint_list constraints_rates = { +static const struct snd_pcm_hw_constraint_list constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; -static unsigned int channels[] = { +static const unsigned int channels[] = { 2, }; -static struct snd_pcm_hw_constraint_list constraints_channels = { +static const struct snd_pcm_hw_constraint_list constraints_channels = { .count = ARRAY_SIZE(channels), .list = channels, .mask = 0, @@ -264,11 +265,11 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static unsigned int channels_dmic[] = { +static const unsigned int channels_dmic[] = { 2, 4, }; -static struct snd_pcm_hw_constraint_list constraints_dmic_channels = { +static const struct snd_pcm_hw_constraint_list constraints_dmic_channels = { .count = ARRAY_SIZE(channels_dmic), .list = channels_dmic, .mask = 0, @@ -310,6 +311,23 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dpcm_playback = 1, .ops = &skylake_rt286_fe_ops, }, + [SKL_DPCM_AUDIO_DB_PB] = { + .name = "Skl Deepbuffer Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + .ops = &skylake_rt286_fe_ops, + + }, [SKL_DPCM_AUDIO_CP] = { .name = "Skl Audio Capture Port", .stream_name = "Audio Record", diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h index 214e000667ae..afe9b87b8bd5 100644 --- a/sound/soc/intel/common/sst-acpi.h +++ b/sound/soc/intel/common/sst-acpi.h @@ -43,6 +43,9 @@ static inline bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], /* acpi match */ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); +/* acpi check hid */ +bool sst_acpi_check_hid(const u8 hid[ACPI_ID_LEN]); + /* Descriptor for SST ASoC machine driver */ struct sst_acpi_mach { /* ACPI ID for the matching machine driver. Audio codec for instance */ @@ -55,5 +58,25 @@ struct sst_acpi_mach { /* board name */ const char *board; struct sst_acpi_mach * (*machine_quirk)(void *arg); + const void *quirk_data; void *pdata; }; + +#define SST_ACPI_MAX_CODECS 3 + +/** + * struct sst_codecs: Structure to hold secondary codec information apart from + * the matched one, this data will be passed to the quirk function to match + * with the ACPI detected devices + * + * @num_codecs: number of secondary codecs used in the platform + * @codecs: holds the codec IDs + * + */ +struct sst_codecs { + int num_codecs; + u8 codecs[SST_ACPI_MAX_CODECS][ACPI_ID_LEN]; +}; + +/* check all codecs */ +struct sst_acpi_mach *sst_acpi_codec_list(void *arg); diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index d13c84364c3c..8734040d64d3 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -77,6 +77,10 @@ struct sst_addr { u32 dram_offset; u32 dsp_iram_offset; u32 dsp_dram_offset; + u32 sram0_base; + u32 sram1_base; + u32 w0_stat_sz; + u32 w0_up_sz; void __iomem *lpe; void __iomem *shim; void __iomem *pci_cfg; diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c index 1070f3ad23e5..56d26f36a3cb 100644 --- a/sound/soc/intel/common/sst-match-acpi.c +++ b/sound/soc/intel/common/sst-match-acpi.c @@ -63,16 +63,33 @@ static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, return AE_OK; } +bool sst_acpi_check_hid(const u8 hid[ACPI_ID_LEN]) +{ + acpi_status status; + bool found = false; + + status = acpi_get_devices(hid, sst_acpi_mach_match, &found, NULL); + + if (ACPI_FAILURE(status)) + return false; + + return found; +} +EXPORT_SYMBOL_GPL(sst_acpi_check_hid); + struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) { struct sst_acpi_mach *mach; - bool found = false; - for (mach = machines; mach->id[0]; mach++) - if (ACPI_SUCCESS(acpi_get_devices(mach->id, - sst_acpi_mach_match, - &found, NULL)) && found) - return mach; + for (mach = machines; mach->id[0]; mach++) { + if (sst_acpi_check_hid(mach->id) == true) { + if (mach->machine_quirk == NULL) + return mach; + + if (mach->machine_quirk(mach) != NULL) + return mach; + } + } return NULL; } EXPORT_SYMBOL_GPL(sst_acpi_find_machine); @@ -134,5 +151,23 @@ bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], } EXPORT_SYMBOL_GPL(sst_acpi_find_package_from_hid); +struct sst_acpi_mach *sst_acpi_codec_list(void *arg) +{ + struct sst_acpi_mach *mach = arg; + struct sst_codecs *codec_list = (struct sst_codecs *) mach->quirk_data; + int i; + + if (mach->quirk_data == NULL) + return mach; + + for (i = 0; i < codec_list->num_codecs; i++) { + if (sst_acpi_check_hid(codec_list->codecs[i]) != true) + return NULL; + } + + return mach; +} +EXPORT_SYMBOL_GPL(sst_acpi_codec_list); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 60fbc9bbe473..e7d77722d560 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,6 +1,10 @@ snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \ skl-topology.o +ifdef CONFIG_DEBUG_FS + snd-soc-skl-objs += skl-debug.o +endif + obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o # Skylake IPC Support diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index f5e7dbb1ba39..cf11b84888b9 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -573,6 +573,10 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, sst->fw_ops = bxt_fw_ops; sst->addr.lpe = mmio_base; sst->addr.shim = mmio_base; + sst->addr.sram0_base = BXT_ADSP_SRAM0_BASE; + sst->addr.sram1_base = BXT_ADSP_SRAM1_BASE; + sst->addr.w0_stat_sz = SKL_ADSP_W0_STAT_SZ; + sst->addr.w0_up_sz = SKL_ADSP_W0_UP_SZ; sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c new file mode 100644 index 000000000000..dc20d91f62e6 --- /dev/null +++ b/sound/soc/intel/skylake/skl-debug.c @@ -0,0 +1,261 @@ +/* + * skl-debug.c - Debugfs for skl driver + * + * Copyright (C) 2016-17 Intel Corp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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/pci.h> +#include <linux/debugfs.h> +#include "skl.h" +#include "skl-sst-dsp.h" +#include "skl-sst-ipc.h" +#include "skl-tplg-interface.h" +#include "skl-topology.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" + +#define MOD_BUF PAGE_SIZE +#define FW_REG_BUF PAGE_SIZE +#define FW_REG_SIZE 0x60 + +struct skl_debug { + struct skl *skl; + struct device *dev; + + struct dentry *fs; + struct dentry *modules; + u8 fw_read_buff[FW_REG_BUF]; +}; + +static ssize_t skl_print_pins(struct skl_module_pin *m_pin, char *buf, + int max_pin, ssize_t size, bool direction) +{ + int i; + ssize_t ret = 0; + + for (i = 0; i < max_pin; i++) + ret += snprintf(buf + size, MOD_BUF - size, + "%s %d\n\tModule %d\n\tInstance %d\n\t" + "In-used %s\n\tType %s\n" + "\tState %d\n\tIndex %d\n", + direction ? "Input Pin:" : "Output Pin:", + i, m_pin[i].id.module_id, + m_pin[i].id.instance_id, + m_pin[i].in_use ? "Used" : "Unused", + m_pin[i].is_dynamic ? "Dynamic" : "Static", + m_pin[i].pin_state, i); + return ret; +} + +static ssize_t skl_print_fmt(struct skl_module_fmt *fmt, char *buf, + ssize_t size, bool direction) +{ + return snprintf(buf + size, MOD_BUF - size, + "%s\n\tCh %d\n\tFreq %d\n\tBit depth %d\n\t" + "Valid bit depth %d\n\tCh config %#x\n\tInterleaving %d\n\t" + "Sample Type %d\n\tCh Map %#x\n", + direction ? "Input Format:" : "Output Format:", + fmt->channels, fmt->s_freq, fmt->bit_depth, + fmt->valid_bit_depth, fmt->ch_cfg, + fmt->interleaving_style, fmt->sample_type, + fmt->ch_map); +} + +static ssize_t module_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct skl_module_cfg *mconfig = file->private_data; + char *buf; + ssize_t ret; + + buf = kzalloc(MOD_BUF, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = snprintf(buf, MOD_BUF, "Module:\n\tUUID %pUL\n\tModule id %d\n" + "\tInstance id %d\n\tPvt_id %d\n", mconfig->guid, + mconfig->id.module_id, mconfig->id.instance_id, + mconfig->id.pvt_id); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "Resources:\n\tMCPS %#x\n\tIBS %#x\n\tOBS %#x\t\n", + mconfig->mcps, mconfig->ibs, mconfig->obs); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "Module data:\n\tCore %d\n\tIn queue %d\n\t" + "Out queue %d\n\tType %s\n", + mconfig->core_id, mconfig->max_in_queue, + mconfig->max_out_queue, + mconfig->is_loadable ? "loadable" : "inbuilt"); + + ret += skl_print_fmt(mconfig->in_fmt, buf, ret, true); + ret += skl_print_fmt(mconfig->out_fmt, buf, ret, false); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "Fixup:\n\tParams %#x\n\tConverter %#x\n", + mconfig->params_fixup, mconfig->converter); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "Module Gateway:\n\tType %#x\n\tVbus %#x\n\tHW conn %#x\n\tSlot %#x\n", + mconfig->dev_type, mconfig->vbus_id, + mconfig->hw_conn_type, mconfig->time_slot); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "Pipeline:\n\tID %d\n\tPriority %d\n\tConn Type %d\n\t" + "Pages %#x\n", mconfig->pipe->ppl_id, + mconfig->pipe->pipe_priority, mconfig->pipe->conn_type, + mconfig->pipe->memory_pages); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "\tParams:\n\t\tHost DMA %d\n\t\tLink DMA %d\n", + mconfig->pipe->p_params->host_dma_id, + mconfig->pipe->p_params->link_dma_id); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "\tPCM params:\n\t\tCh %d\n\t\tFreq %d\n\t\tFormat %d\n", + mconfig->pipe->p_params->ch, + mconfig->pipe->p_params->s_freq, + mconfig->pipe->p_params->s_fmt); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "\tLink %#x\n\tStream %#x\n", + mconfig->pipe->p_params->linktype, + mconfig->pipe->p_params->stream); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "\tState %d\n\tPassthru %s\n", + mconfig->pipe->state, + mconfig->pipe->passthru ? "true" : "false"); + + ret += skl_print_pins(mconfig->m_in_pin, buf, + mconfig->max_in_queue, ret, true); + ret += skl_print_pins(mconfig->m_out_pin, buf, + mconfig->max_out_queue, ret, false); + + ret += snprintf(buf + ret, MOD_BUF - ret, + "Other:\n\tDomain %d\n\tHomogenous Input %s\n\t" + "Homogenous Output %s\n\tIn Queue Mask %d\n\t" + "Out Queue Mask %d\n\tDMA ID %d\n\tMem Pages %d\n\t" + "Module Type %d\n\tModule State %d\n", + mconfig->domain, + mconfig->homogenous_inputs ? "true" : "false", + mconfig->homogenous_outputs ? "true" : "false", + mconfig->in_queue_mask, mconfig->out_queue_mask, + mconfig->dma_id, mconfig->mem_pages, mconfig->m_state, + mconfig->m_type); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + kfree(buf); + return ret; +} + +static const struct file_operations mcfg_fops = { + .open = simple_open, + .read = module_read, + .llseek = default_llseek, +}; + + +void skl_debug_init_module(struct skl_debug *d, + struct snd_soc_dapm_widget *w, + struct skl_module_cfg *mconfig) +{ + if (!debugfs_create_file(w->name, 0444, + d->modules, mconfig, + &mcfg_fops)) + dev_err(d->dev, "%s: module debugfs init failed\n", w->name); +} + +static ssize_t fw_softreg_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + struct sst_dsp *sst = d->skl->skl_sst->dsp; + size_t w0_stat_sz = sst->addr.w0_stat_sz; + void __iomem *in_base = sst->mailbox.in_base; + void __iomem *fw_reg_addr; + unsigned int offset; + char *tmp; + ssize_t ret = 0; + + tmp = kzalloc(FW_REG_BUF, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + fw_reg_addr = in_base - w0_stat_sz; + memset(d->fw_read_buff, 0, FW_REG_BUF); + + if (w0_stat_sz > 0) + __iowrite32_copy(d->fw_read_buff, fw_reg_addr, w0_stat_sz >> 2); + + for (offset = 0; offset < FW_REG_SIZE; offset += 16) { + ret += snprintf(tmp + ret, FW_REG_BUF - ret, "%#.4x: ", offset); + hex_dump_to_buffer(d->fw_read_buff + offset, 16, 16, 4, + tmp + ret, FW_REG_BUF - ret, 0); + ret += strlen(tmp + ret); + + /* print newline for each offset */ + if (FW_REG_BUF - ret > 0) + tmp[ret++] = '\n'; + } + + ret = simple_read_from_buffer(user_buf, count, ppos, tmp, ret); + kfree(tmp); + + return ret; +} + +static const struct file_operations soft_regs_ctrl_fops = { + .open = simple_open, + .read = fw_softreg_read, + .llseek = default_llseek, +}; + +struct skl_debug *skl_debugfs_init(struct skl *skl) +{ + struct skl_debug *d; + + d = devm_kzalloc(&skl->pci->dev, sizeof(*d), GFP_KERNEL); + if (!d) + return NULL; + + /* create the debugfs dir with platform component's debugfs as parent */ + d->fs = debugfs_create_dir("dsp", + skl->platform->component.debugfs_root); + if (IS_ERR(d->fs) || !d->fs) { + dev_err(&skl->pci->dev, "debugfs root creation failed\n"); + return NULL; + } + + d->skl = skl; + d->dev = &skl->pci->dev; + + /* now create the module dir */ + d->modules = debugfs_create_dir("modules", d->fs); + if (IS_ERR(d->modules) || !d->modules) { + dev_err(&skl->pci->dev, "modules debugfs create failed\n"); + goto err; + } + + if (!debugfs_create_file("fw_soft_regs_rd", 0444, d->fs, d, + &soft_regs_ctrl_fops)) { + dev_err(d->dev, "fw soft regs control debugfs init failed\n"); + goto err; + } + + return d; + +err: + debugfs_remove_recursive(d->fs); + return NULL; +} diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index ab1adc0c9cc3..eca85827dbd2 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -507,6 +507,8 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_cpr_cfg *cpr_mconfig) { + u32 dma_io_buf; + cpr_mconfig->gtw_cfg.node_id = skl_get_node_id(ctx, mconfig); if (cpr_mconfig->gtw_cfg.node_id == SKL_NON_GATEWAY_CPR_NODE_ID) { @@ -514,10 +516,29 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, return; } - if (SKL_CONN_SOURCE == mconfig->hw_conn_type) - cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs; - else - cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->ibs; + switch (mconfig->hw_conn_type) { + case SKL_CONN_SOURCE: + if (mconfig->dev_type == SKL_DEVICE_HDAHOST) + dma_io_buf = mconfig->ibs; + else + dma_io_buf = mconfig->obs; + break; + + case SKL_CONN_SINK: + if (mconfig->dev_type == SKL_DEVICE_HDAHOST) + dma_io_buf = mconfig->obs; + else + dma_io_buf = mconfig->ibs; + break; + + default: + dev_warn(ctx->dev, "wrong connection type: %d\n", + mconfig->hw_conn_type); + return; + } + + cpr_mconfig->gtw_cfg.dma_buffer_size = + mconfig->dma_buffer_size * dma_io_buf; cpr_mconfig->cpr_feature_mask = 0; cpr_mconfig->gtw_cfg.config_length = 0; @@ -707,6 +728,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, return param_size; case SKL_MODULE_TYPE_BASE_OUTFMT: + case SKL_MODULE_TYPE_MIC_SELECT: case SKL_MODULE_TYPE_KPB: return sizeof(struct skl_base_outfmt_cfg); @@ -761,6 +783,7 @@ static int skl_set_module_format(struct skl_sst *ctx, break; case SKL_MODULE_TYPE_BASE_OUTFMT: + case SKL_MODULE_TYPE_MIC_SELECT: case SKL_MODULE_TYPE_KPB: skl_set_base_outfmt_format(ctx, module_config, *param_data); break; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index e91bbcffc856..0ebea34a4988 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1249,12 +1249,16 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform) pm_runtime_get_sync(platform->dev); if ((ebus_to_hbus(ebus))->ppcap) { + skl->platform = platform; + + /* init debugfs */ + skl->debugfs = skl_debugfs_init(skl); + ret = skl_tplg_init(platform, ebus); if (ret < 0) { dev_err(platform->dev, "Failed to init topology!\n"); return ret; } - skl->platform = platform; /* load the firmwares, since all is set */ ops = skl_get_dsp_ops(skl->pci->device); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 155e456b7a3a..aba9ea11ac74 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -553,6 +553,11 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, sst = skl->dsp; sst->addr.lpe = mmio_base; sst->addr.shim = mmio_base; + sst->addr.sram0_base = SKL_ADSP_SRAM0_BASE; + sst->addr.sram1_base = SKL_ADSP_SRAM1_BASE; + sst->addr.w0_stat_sz = SKL_ADSP_W0_STAT_SZ; + sst->addr.w0_up_sz = SKL_ADSP_W0_UP_SZ; + sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 64a0f8ed33e1..68c3f121efc3 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -36,6 +36,19 @@ #define SKL_IN_DIR_BIT_MASK BIT(0) #define SKL_PIN_COUNT_MASK GENMASK(7, 4) +static const int mic_mono_list[] = { +0, 1, 2, 3, +}; +static const int mic_stereo_list[][SKL_CH_STEREO] = { +{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}, +}; +static const int mic_trio_list[][SKL_CH_TRIO] = { +{0, 1, 2}, {0, 1, 3}, {0, 2, 3}, {1, 2, 3}, +}; +static const int mic_quatro_list[][SKL_CH_QUATRO] = { +{0, 1, 2, 3}, +}; + void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps) { struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3; @@ -1314,6 +1327,111 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol, return 0; } +static int skl_tplg_mic_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + u32 ch_type = *((u32 *)ec->dobj.private); + + if (mconfig->dmic_ch_type == ch_type) + ucontrol->value.enumerated.item[0] = + mconfig->dmic_ch_combo_index; + else + ucontrol->value.enumerated.item[0] = 0; + + return 0; +} + +static int skl_fill_mic_sel_params(struct skl_module_cfg *mconfig, + struct skl_mic_sel_config *mic_cfg, struct device *dev) +{ + struct skl_specific_cfg *sp_cfg = &mconfig->formats_config; + + sp_cfg->caps_size = sizeof(struct skl_mic_sel_config); + sp_cfg->set_params = SKL_PARAM_SET; + sp_cfg->param_id = 0x00; + if (!sp_cfg->caps) { + sp_cfg->caps = devm_kzalloc(dev, sp_cfg->caps_size, GFP_KERNEL); + if (!sp_cfg->caps) + return -ENOMEM; + } + + mic_cfg->mic_switch = SKL_MIC_SEL_SWITCH; + mic_cfg->flags = 0; + memcpy(sp_cfg->caps, mic_cfg, sp_cfg->caps_size); + + return 0; +} + +static int skl_tplg_mic_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct skl_mic_sel_config mic_cfg = {0}; + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + u32 ch_type = *((u32 *)ec->dobj.private); + const int *list; + u8 in_ch, out_ch, index; + + mconfig->dmic_ch_type = ch_type; + mconfig->dmic_ch_combo_index = ucontrol->value.enumerated.item[0]; + + /* enum control index 0 is INVALID, so no channels to be set */ + if (mconfig->dmic_ch_combo_index == 0) + return 0; + + /* No valid channel selection map for index 0, so offset by 1 */ + index = mconfig->dmic_ch_combo_index - 1; + + switch (ch_type) { + case SKL_CH_MONO: + if (mconfig->dmic_ch_combo_index > ARRAY_SIZE(mic_mono_list)) + return -EINVAL; + + list = &mic_mono_list[index]; + break; + + case SKL_CH_STEREO: + if (mconfig->dmic_ch_combo_index > ARRAY_SIZE(mic_stereo_list)) + return -EINVAL; + + list = mic_stereo_list[index]; + break; + + case SKL_CH_TRIO: + if (mconfig->dmic_ch_combo_index > ARRAY_SIZE(mic_trio_list)) + return -EINVAL; + + list = mic_trio_list[index]; + break; + + case SKL_CH_QUATRO: + if (mconfig->dmic_ch_combo_index > ARRAY_SIZE(mic_quatro_list)) + return -EINVAL; + + list = mic_quatro_list[index]; + break; + + default: + dev_err(w->dapm->dev, + "Invalid channel %d for mic_select module\n", + ch_type); + return -EINVAL; + + } + + /* channel type enum map to number of chanels for that type */ + for (out_ch = 0; out_ch < ch_type; out_ch++) { + in_ch = list[out_ch]; + mic_cfg.blob[out_ch][in_ch] = SKL_DEFAULT_MIC_SEL_GAIN; + } + + return skl_fill_mic_sel_params(mconfig, &mic_cfg, w->dapm->dev); +} + /* * Fill the dma id for host and link. In case of passthrough * pipeline, this will both host and link in the same @@ -1666,6 +1784,14 @@ static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = { skl_tplg_tlv_control_set}, }; +static const struct snd_soc_tplg_kcontrol_ops skl_tplg_kcontrol_ops[] = { + { + .id = SKL_CONTROL_TYPE_MIC_SELECT, + .get = skl_tplg_mic_control_get, + .put = skl_tplg_mic_control_set, + }, +}; + static int skl_tplg_fill_pipe_tkn(struct device *dev, struct skl_pipe *pipe, u32 tkn, u32 tkn_val) @@ -1995,7 +2121,7 @@ static int skl_tplg_get_token(struct device *dev, mconfig->converter = tkn_elem->value; break; - case SKL_TKL_U32_D0I3_CAPS: + case SKL_TKN_U32_D0I3_CAPS: mconfig->d0i3_caps = tkn_elem->value; break; @@ -2070,12 +2196,26 @@ static int skl_tplg_get_token(struct device *dev, break; + case SKL_TKN_U32_CAPS_SET_PARAMS: + mconfig->formats_config.set_params = + tkn_elem->value; + break; + + case SKL_TKN_U32_CAPS_PARAMS_ID: + mconfig->formats_config.param_id = + tkn_elem->value; + break; + case SKL_TKN_U32_PROC_DOMAIN: mconfig->domain = tkn_elem->value; break; + case SKL_TKN_U32_DMA_BUF_SIZE: + mconfig->dma_buffer_size = tkn_elem->value; + break; + case SKL_TKN_U8_IN_PIN_TYPE: case SKL_TKN_U8_OUT_PIN_TYPE: case SKL_TKN_U8_CONN_TYPE: @@ -2147,7 +2287,7 @@ static int skl_tplg_get_tokens(struct device *dev, tuple_size += tkn_count * sizeof(*tkn_elem); } - return 0; + return off; } /* @@ -2198,10 +2338,11 @@ static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w, num_blocks = ret; off += array->size; - array = (struct snd_soc_tplg_vendor_array *)(tplg_w->priv.data + off); - /* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */ while (num_blocks > 0) { + array = (struct snd_soc_tplg_vendor_array *) + (tplg_w->priv.data + off); + ret = skl_tplg_get_desc_blocks(dev, array); if (ret < 0) @@ -2237,7 +2378,9 @@ static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w, memcpy(mconfig->formats_config.caps, data, mconfig->formats_config.caps_size); --num_blocks; + ret = mconfig->formats_config.caps_size; } + off += ret; } return 0; @@ -2329,6 +2472,9 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, ret = skl_tplg_get_pvt_data(tplg_w, skl, bus->dev, mconfig); if (ret < 0) return ret; + + skl_debug_init_module(skl->debugfs, w, mconfig); + bind_event: if (tplg_w->event_type == 0) { dev_dbg(bus->dev, "ASoC: No event handler required\n"); @@ -2377,14 +2523,34 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be, return 0; } +static int skl_init_enum_data(struct device *dev, struct soc_enum *se, + struct snd_soc_tplg_enum_control *ec) +{ + + void *data; + + if (ec->priv.size) { + data = devm_kzalloc(dev, sizeof(ec->priv.size), GFP_KERNEL); + if (!data) + return -ENOMEM; + memcpy(data, ec->priv.data, ec->priv.size); + se->dobj.private = data; + } + + return 0; + +} + static int skl_tplg_control_load(struct snd_soc_component *cmpnt, struct snd_kcontrol_new *kctl, struct snd_soc_tplg_ctl_hdr *hdr) { struct soc_bytes_ext *sb; struct snd_soc_tplg_bytes_control *tplg_bc; + struct snd_soc_tplg_enum_control *tplg_ec; struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt); struct hdac_bus *bus = ebus_to_hbus(ebus); + struct soc_enum *se; switch (hdr->ops.info) { case SND_SOC_TPLG_CTL_BYTES: @@ -2398,6 +2564,17 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt, } break; + case SND_SOC_TPLG_CTL_ENUM: + tplg_ec = container_of(hdr, + struct snd_soc_tplg_enum_control, hdr); + if (kctl->access & SNDRV_CTL_ELEM_ACCESS_READWRITE) { + se = (struct soc_enum *)kctl->private_value; + if (tplg_ec->priv.size) + return skl_init_enum_data(bus->dev, se, + tplg_ec); + } + break; + default: dev_warn(bus->dev, "Control load not supported %d:%d:%d\n", hdr->ops.get, hdr->ops.put, hdr->ops.info); @@ -2626,6 +2803,8 @@ static struct snd_soc_tplg_ops skl_tplg_ops = { .control_load = skl_tplg_control_load, .bytes_ext_ops = skl_tlv_ops, .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops), + .io_ops = skl_tplg_kcontrol_ops, + .io_ops_count = ARRAY_SIZE(skl_tplg_kcontrol_ops), .manifest = skl_manifest_load, }; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index cc64d6bdb4f6..c25e8868b84e 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -39,6 +39,11 @@ #define MODULE_MAX_IN_PINS 8 #define MODULE_MAX_OUT_PINS 8 +#define SKL_MIC_CH_SUPPORT 4 +#define SKL_MIC_MAX_CH_SUPPORT 8 +#define SKL_DEFAULT_MIC_SEL_GAIN 0x3FF +#define SKL_MIC_SEL_SWITCH 0x3 + enum skl_channel_index { SKL_CHANNEL_LEFT = 0, SKL_CHANNEL_RIGHT = 1, @@ -309,11 +314,14 @@ struct skl_module_cfg { u8 dev_type; u8 dma_id; u8 time_slot; + u8 dmic_ch_combo_index; + u32 dmic_ch_type; u32 params_fixup; u32 converter; u32 vbus_id; u32 mem_pages; enum d0i3_capability d0i3_caps; + u32 dma_buffer_size; /* in milli seconds */ struct skl_module_pin *m_in_pin; struct skl_module_pin *m_out_pin; enum skl_module_type m_type; @@ -342,6 +350,19 @@ struct skl_module_deferred_bind { struct list_head node; }; +struct skl_mic_sel_config { + u16 mic_switch; + u16 flags; + u16 blob[SKL_MIC_MAX_CH_SUPPORT][SKL_MIC_MAX_CH_SUPPORT]; +} __packed; + +enum skl_channel { + SKL_CH_MONO = 1, + SKL_CH_STEREO = 2, + SKL_CH_TRIO = 3, + SKL_CH_QUATRO = 4, +}; + static inline struct skl *get_skl_ctx(struct device *dev) { struct hdac_ext_bus *ebus = dev_get_drvdata(dev); diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 7a2febf99019..f8d1749a2e0c 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -24,6 +24,7 @@ * SST types start at higher to avoid any overlapping in future */ #define SKL_CONTROL_TYPE_BYTE_TLV 0x100 +#define SKL_CONTROL_TYPE_MIC_SELECT 0x102 #define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ #define MAX_IN_QUEUE 8 @@ -82,6 +83,7 @@ enum skl_module_type { SKL_MODULE_TYPE_ALGO, SKL_MODULE_TYPE_BASE_OUTFMT, SKL_MODULE_TYPE_KPB, + SKL_MODULE_TYPE_MIC_SELECT, }; enum skl_core_affinity { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 4c9b5781282b..334917ee41cf 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -866,6 +866,7 @@ static void skl_remove(struct pci_dev *pci) /* codec removal, invoke bus_device_remove */ snd_hdac_ext_bus_device_remove(ebus); + skl->debugfs = NULL; skl_platform_unregister(&pci->dev); skl_free_dsp(skl); skl_machine_device_unregister(skl); @@ -876,29 +877,120 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } +static struct sst_codecs skl_codecs = { + .num_codecs = 1, + .codecs = {"NAU88L25"} +}; + +static struct sst_codecs kbl_codecs = { + .num_codecs = 1, + .codecs = {"NAU88L25"} +}; + +static struct sst_codecs bxt_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +static struct sst_codecs kbl_poppy_codecs = { + .num_codecs = 1, + .codecs = {"10EC5663"} +}; + +static struct sst_codecs kbl_5663_5514_codecs = { + .num_codecs = 2, + .codecs = {"10EC5663", "10EC5514"} +}; + + static struct sst_acpi_mach sst_skl_devdata[] = { - { "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL }, - { "INT343B", "skl_n88l25_s4567", "intel/dsp_fw_release.bin", - NULL, NULL, &skl_dmic_data }, - { "MX98357A", "skl_n88l25_m98357a", "intel/dsp_fw_release.bin", - NULL, NULL, &skl_dmic_data }, + { + .id = "INT343A", + .drv_name = "skl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_release.bin", + }, + { + .id = "INT343B", + .drv_name = "skl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = sst_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98357A", + .drv_name = "skl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = sst_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data + }, {} }; static struct sst_acpi_mach sst_bxtp_devdata[] = { - { "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL }, - { "DLGS7219", "bxt_da7219_max98357a_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL }, + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, + { + .id = "DLGS7219", + .drv_name = "bxt_da7219_max98357a_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = sst_acpi_codec_list, + .quirk_data = &bxt_codecs, + }, }; static struct sst_acpi_mach sst_kbl_devdata[] = { - { "INT343A", "kbl_alc286s_i2s", "intel/dsp_fw_kbl.bin", NULL, NULL, NULL }, - { "INT343B", "kbl_n88l25_s4567", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data }, - { "MX98357A", "kbl_n88l25_m98357a", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data }, + { + .id = "INT343A", + .drv_name = "kbl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "INT343B", + .drv_name = "kbl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = sst_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98357A", + .drv_name = "kbl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = sst_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98927", + .drv_name = "kbl_r5514_5663_max", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = sst_acpi_codec_list, + .quirk_data = &kbl_5663_5514_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98927", + .drv_name = "kbl_rt5663_m98927", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = sst_acpi_codec_list, + .quirk_data = &kbl_poppy_codecs, + .pdata = &skl_dmic_data + }, + {} }; static struct sst_acpi_mach sst_glk_devdata[] = { - { "INT343A", "glk_alc298s_i2s", "intel/dsp_fw_glk.bin", NULL, NULL, NULL }, + { + .id = "INT343A", + .drv_name = "glk_alc298s_i2s", + .fw_filename = "intel/dsp_fw_glk.bin", + }, }; /* PCI IDs */ diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 2a630fcb7f08..a6b134b4c037 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -23,6 +23,7 @@ #include <sound/hda_register.h> #include <sound/hdaudio_ext.h> +#include <sound/soc.h> #include "skl-nhlt.h" #define SKL_SUSPEND_DELAY 2000 @@ -42,6 +43,8 @@ struct skl_dsp_resource { u32 mem; }; +struct skl_debug; + struct skl { struct hdac_ext_bus ebus; struct pci_dev *pci; @@ -66,6 +69,8 @@ struct skl { int supend_active; struct work_struct probe_work; + + struct skl_debug *debugfs; }; #define skl_to_ebus(s) (&(s)->ebus) @@ -116,4 +121,22 @@ void skl_update_d0i3c(struct device *dev, bool enable); int skl_nhlt_create_sysfs(struct skl *skl); void skl_nhlt_remove_sysfs(struct skl *skl); +struct skl_module_cfg; + +#ifdef CONFIG_DEBUG_FS +struct skl_debug *skl_debugfs_init(struct skl *skl); +void skl_debug_init_module(struct skl_debug *d, + struct snd_soc_dapm_widget *w, + struct skl_module_cfg *mconfig); +#else +static inline struct skl_debug *skl_debugfs_init(struct skl *skl) +{ + return NULL; +} +static inline void skl_debug_init_module(struct skl_debug *d, + struct snd_soc_dapm_widget *w, + struct skl_module_cfg *mconfig) +{} +#endif + #endif /* __SOUND_SOC_SKL_H */ diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c index aa5b31b121e3..70f61d53fe05 100644 --- a/sound/soc/mediatek/mt2701/mt2701-cs42448.c +++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c @@ -107,7 +107,7 @@ static const struct snd_kcontrol_new mt2701_cs42448_controls[] = { static const unsigned int mt2701_cs42448_sampling_rates[] = {48000}; -static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = { +static const struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = { .count = ARRAY_SIZE(mt2701_cs42448_sampling_rates), .list = mt2701_cs42448_sampling_rates, .mask = 0, diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 06fec5699cc8..7a54e3083203 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -835,15 +835,11 @@ static ssize_t dma_op_mode_store(struct device *dev, const char *buf, size_t size) { struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - const char * const *s; - int i = 0; - - for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) - if (sysfs_streq(buf, *s)) - break; + int i; - if (i == ARRAY_SIZE(dma_op_modes)) - return -EINVAL; + i = sysfs_match_string(dma_op_modes, buf); + if (i < 0) + return i; spin_lock_irq(&mcbsp->lock); if (!mcbsp->free) { diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 823b5a236d8d..960744e46edc 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -1,6 +1,6 @@ config SND_PXA2XX_SOC tristate "SoC Audio for the Intel PXA2xx chip" - depends on ARCH_PXA + depends on ARCH_PXA || COMPILE_TEST select SND_PXA2XX_LIB help Say Y or M if you want to add support for codecs attached to diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index e3ca1e973de5..c84487805876 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -15,6 +15,15 @@ config SND_SOC_ROCKCHIP_I2S Rockchip I2S device. The device supports upto maximum of 8 channels each for play and record. +config SND_SOC_ROCKCHIP_PDM + tristate "Rockchip PDM Controller Driver" + depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for PDM driver for + Rockchip PDM Controller. The Controller supports up to maximum of + 8 channels record. + config SND_SOC_ROCKCHIP_SPDIF tristate "Rockchip SPDIF Device Driver" depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 991f91bea9f9..105f0e14a4ab 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,8 +1,10 @@ # ROCKCHIP Platform Support snd-soc-rockchip-i2s-objs := rockchip_i2s.o +snd-soc-rockchip-pdm-objs := rockchip_pdm.o snd-soc-rockchip-spdif-objs := rockchip_spdif.o obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o snd-soc-rockchip-max98090-objs := rockchip_max98090.o diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 974915cb4c4f..199338fdeda0 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -116,6 +116,7 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) I2S_XFER_TXS_STOP | I2S_XFER_RXS_STOP); + udelay(150); regmap_update_bits(i2s->regmap, I2S_CLR, I2S_CLR_TXC | I2S_CLR_RXC, I2S_CLR_TXC | I2S_CLR_RXC); @@ -162,6 +163,7 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) I2S_XFER_TXS_STOP | I2S_XFER_RXS_STOP); + udelay(150); regmap_update_bits(i2s->regmap, I2S_CLR, I2S_CLR_TXC | I2S_CLR_RXC, I2S_CLR_TXC | I2S_CLR_RXC); @@ -204,7 +206,21 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, regmap_update_bits(i2s->regmap, I2S_CKR, mask, val); - mask = I2S_TXCR_IBM_MASK; + mask = I2S_CKR_CKP_MASK; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val = I2S_CKR_CKP_NEG; + break; + case SND_SOC_DAIFMT_IB_NF: + val = I2S_CKR_CKP_POS; + break; + default: + return -EINVAL; + } + + regmap_update_bits(i2s->regmap, I2S_CKR, mask, val); + + mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: val = I2S_TXCR_IBM_RSJM; @@ -215,13 +231,19 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_I2S: val = I2S_TXCR_IBM_NORMAL; break; + case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */ + val = I2S_TXCR_TFS_PCM; + break; + case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */ + val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1); + break; default: return -EINVAL; } regmap_update_bits(i2s->regmap, I2S_TXCR, mask, val); - mask = I2S_RXCR_IBM_MASK; + mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: val = I2S_RXCR_IBM_RSJM; @@ -232,6 +254,12 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_I2S: val = I2S_RXCR_IBM_NORMAL; break; + case SND_SOC_DAIFMT_DSP_A: /* PCM no delay mode */ + val = I2S_RXCR_TFS_PCM; + break; + case SND_SOC_DAIFMT_DSP_B: /* PCM delay 1 mode */ + val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1); + break; default: return -EINVAL; } @@ -615,12 +643,13 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_pm_disable; } - soc_dai = devm_kzalloc(&pdev->dev, + soc_dai = devm_kmemdup(&pdev->dev, &rockchip_i2s_dai, sizeof(*soc_dai), GFP_KERNEL); - if (!soc_dai) - return -ENOMEM; + if (!soc_dai) { + ret = -ENOMEM; + goto err_pm_disable; + } - memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai)); if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) { if (val >= 2 && val <= 8) soc_dai->playback.channels_max = val; diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h index 31f11fd25393..a7b8527d8a73 100644 --- a/sound/soc/rockchip/rockchip_i2s.h +++ b/sound/soc/rockchip/rockchip_i2s.h @@ -41,6 +41,7 @@ #define I2S_TXCR_TFS_SHIFT 5 #define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT) #define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT) +#define I2S_TXCR_TFS_MASK (1 << I2S_TXCR_TFS_SHIFT) #define I2S_TXCR_VDW_SHIFT 0 #define I2S_TXCR_VDW(x) ((x - 1) << I2S_TXCR_VDW_SHIFT) #define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT) @@ -70,6 +71,7 @@ #define I2S_RXCR_TFS_SHIFT 5 #define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT) #define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT) +#define I2S_RXCR_TFS_MASK (1 << I2S_RXCR_TFS_SHIFT) #define I2S_RXCR_VDW_SHIFT 0 #define I2S_RXCR_VDW(x) ((x - 1) << I2S_RXCR_VDW_SHIFT) #define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT) @@ -91,6 +93,7 @@ #define I2S_CKR_CKP_SHIFT 26 #define I2S_CKR_CKP_NEG (0 << I2S_CKR_CKP_SHIFT) #define I2S_CKR_CKP_POS (1 << I2S_CKR_CKP_SHIFT) +#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT) #define I2S_CKR_RLP_SHIFT 25 #define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT) #define I2S_CKR_RLP_OPPSITE (1 << I2S_CKR_RLP_SHIFT) diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c new file mode 100644 index 000000000000..c5ddeed97260 --- /dev/null +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -0,0 +1,516 @@ +/* + * Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/clk.h> +#include <linux/of.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +#include "rockchip_pdm.h" + +#define PDM_DMA_BURST_SIZE (16) /* size * width: 16*4 = 64 bytes */ + +struct rk_pdm_dev { + struct device *dev; + struct clk *clk; + struct clk *hclk; + struct regmap *regmap; + struct snd_dmaengine_dai_dma_data capture_dma_data; +}; + +struct rk_pdm_clkref { + unsigned int sr; + unsigned int clk; +}; + +static struct rk_pdm_clkref clkref[] = { + { 8000, 40960000 }, + { 11025, 56448000 }, + { 12000, 61440000 }, +}; + +static unsigned int get_pdm_clk(unsigned int sr) +{ + unsigned int i, count, clk, div; + + clk = 0; + if (!sr) + return clk; + + count = ARRAY_SIZE(clkref); + for (i = 0; i < count; i++) { + if (sr % clkref[i].sr) + continue; + div = sr / clkref[i].sr; + if ((div & (div - 1)) == 0) { + clk = clkref[i].clk; + break; + } + } + + return clk; +} + +static inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai) +{ + return snd_soc_dai_get_drvdata(dai); +} + +static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on) +{ + if (on) { + regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, + PDM_DMA_RD_MSK, PDM_DMA_RD_EN); + regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, + PDM_RX_MASK, PDM_RX_START); + } else { + regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, + PDM_DMA_RD_MSK, PDM_DMA_RD_DIS); + regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, + PDM_RX_MASK | PDM_RX_CLR_MASK, + PDM_RX_STOP | PDM_RX_CLR_WR); + } +} + +static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rk_pdm_dev *pdm = to_info(dai); + unsigned int val = 0; + unsigned int clk_rate, clk_div, samplerate; + int ret; + + samplerate = params_rate(params); + clk_rate = get_pdm_clk(samplerate); + if (!clk_rate) + return -EINVAL; + + ret = clk_set_rate(pdm->clk, clk_rate); + if (ret) + return -EINVAL; + + clk_div = DIV_ROUND_CLOSEST(clk_rate, samplerate); + + switch (clk_div) { + case 320: + val = PDM_CLK_320FS; + break; + case 640: + val = PDM_CLK_640FS; + break; + case 1280: + val = PDM_CLK_1280FS; + break; + case 2560: + val = PDM_CLK_2560FS; + break; + case 5120: + val = PDM_CLK_5120FS; + break; + default: + dev_err(pdm->dev, "unsupported div: %d\n", clk_div); + return -EINVAL; + } + + regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val); + regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, + PDM_HPF_CF_MSK, PDM_HPF_60HZ); + regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, + PDM_HPF_LE | PDM_HPF_RE, PDM_HPF_LE | PDM_HPF_RE); + regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_EN, PDM_CLK_EN); + + val = 0; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + val |= PDM_VDW(8); + break; + case SNDRV_PCM_FORMAT_S16_LE: + val |= PDM_VDW(16); + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val |= PDM_VDW(20); + break; + case SNDRV_PCM_FORMAT_S24_LE: + val |= PDM_VDW(24); + break; + case SNDRV_PCM_FORMAT_S32_LE: + val |= PDM_VDW(32); + break; + default: + return -EINVAL; + } + + switch (params_channels(params)) { + case 8: + val |= PDM_PATH3_EN; + /* fallthrough */ + case 6: + val |= PDM_PATH2_EN; + /* fallthrough */ + case 4: + val |= PDM_PATH1_EN; + /* fallthrough */ + case 2: + val |= PDM_PATH0_EN; + break; + default: + dev_err(pdm->dev, "invalid channel: %d\n", + params_channels(params)); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + regmap_update_bits(pdm->regmap, PDM_CTRL0, + PDM_PATH_MSK | PDM_VDW_MSK, + val); + regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RDL_MSK, + PDM_DMA_RDL(16)); + regmap_update_bits(pdm->regmap, PDM_SYSCONFIG, + PDM_RX_MASK | PDM_RX_CLR_MASK, + PDM_RX_STOP | PDM_RX_CLR_WR); + } + + return 0; +} + +static int rockchip_pdm_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct rk_pdm_dev *pdm = to_info(cpu_dai); + unsigned int mask = 0, val = 0; + + mask = PDM_CKP_MSK; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val = PDM_CKP_NORMAL; + break; + case SND_SOC_DAIFMT_IB_NF: + val = PDM_CKP_INVERTED; + break; + default: + return -EINVAL; + } + + regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, mask, val); + + return 0; +} + +static int rockchip_pdm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct rk_pdm_dev *pdm = to_info(dai); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rockchip_pdm_rxctrl(pdm, 1); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + rockchip_pdm_rxctrl(pdm, 0); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai) +{ + struct rk_pdm_dev *pdm = to_info(dai); + + dai->capture_dma_data = &pdm->capture_dma_data; + + return 0; +} + +static struct snd_soc_dai_ops rockchip_pdm_dai_ops = { + .set_fmt = rockchip_pdm_set_fmt, + .trigger = rockchip_pdm_trigger, + .hw_params = rockchip_pdm_hw_params, +}; + +#define ROCKCHIP_PDM_RATES SNDRV_PCM_RATE_8000_192000 +#define ROCKCHIP_PDM_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 rockchip_pdm_dai = { + .probe = rockchip_pdm_dai_probe, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = ROCKCHIP_PDM_RATES, + .formats = ROCKCHIP_PDM_FORMATS, + }, + .ops = &rockchip_pdm_dai_ops, + .symmetric_rates = 1, +}; + +static const struct snd_soc_component_driver rockchip_pdm_component = { + .name = "rockchip-pdm", +}; + +static int rockchip_pdm_runtime_suspend(struct device *dev) +{ + struct rk_pdm_dev *pdm = dev_get_drvdata(dev); + + clk_disable_unprepare(pdm->clk); + clk_disable_unprepare(pdm->hclk); + + return 0; +} + +static int rockchip_pdm_runtime_resume(struct device *dev) +{ + struct rk_pdm_dev *pdm = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(pdm->clk); + if (ret) { + dev_err(pdm->dev, "clock enable failed %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(pdm->hclk); + if (ret) { + dev_err(pdm->dev, "hclock enable failed %d\n", ret); + return ret; + } + + return 0; +} + +static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PDM_SYSCONFIG: + case PDM_CTRL0: + case PDM_CTRL1: + case PDM_CLK_CTRL: + case PDM_HPF_CTRL: + case PDM_FIFO_CTRL: + case PDM_DMA_CTRL: + case PDM_INT_EN: + case PDM_INT_CLR: + case PDM_DATA_VALID: + return true; + default: + return false; + } +} + +static bool rockchip_pdm_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PDM_SYSCONFIG: + case PDM_CTRL0: + case PDM_CTRL1: + case PDM_CLK_CTRL: + case PDM_HPF_CTRL: + case PDM_FIFO_CTRL: + case PDM_DMA_CTRL: + case PDM_INT_EN: + case PDM_INT_CLR: + case PDM_INT_ST: + case PDM_DATA_VALID: + case PDM_VERSION: + return true; + default: + return false; + } +} + +static bool rockchip_pdm_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PDM_SYSCONFIG: + case PDM_INT_CLR: + case PDM_INT_ST: + return true; + default: + return false; + } +} + +static const struct regmap_config rockchip_pdm_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = PDM_VERSION, + .writeable_reg = rockchip_pdm_wr_reg, + .readable_reg = rockchip_pdm_rd_reg, + .volatile_reg = rockchip_pdm_volatile_reg, + .cache_type = REGCACHE_FLAT, +}; + +static int rockchip_pdm_probe(struct platform_device *pdev) +{ + struct rk_pdm_dev *pdm; + struct resource *res; + void __iomem *regs; + int ret; + + pdm = devm_kzalloc(&pdev->dev, sizeof(*pdm), GFP_KERNEL); + if (!pdm) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + pdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &rockchip_pdm_regmap_config); + if (IS_ERR(pdm->regmap)) + return PTR_ERR(pdm->regmap); + + pdm->capture_dma_data.addr = res->start + PDM_RXFIFO_DATA; + pdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + pdm->capture_dma_data.maxburst = PDM_DMA_BURST_SIZE; + + pdm->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, pdm); + + pdm->clk = devm_clk_get(&pdev->dev, "pdm_clk"); + if (IS_ERR(pdm->clk)) + return PTR_ERR(pdm->clk); + + pdm->hclk = devm_clk_get(&pdev->dev, "pdm_hclk"); + if (IS_ERR(pdm->hclk)) + return PTR_ERR(pdm->hclk); + + ret = clk_prepare_enable(pdm->hclk); + if (ret) + return ret; + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = rockchip_pdm_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &rockchip_pdm_component, + &rockchip_pdm_dai, 1); + + if (ret) { + dev_err(&pdev->dev, "could not register dai: %d\n", ret); + goto err_suspend; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "could not register pcm: %d\n", ret); + goto err_suspend; + } + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + rockchip_pdm_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + + clk_disable_unprepare(pdm->hclk); + + return ret; +} + +static int rockchip_pdm_remove(struct platform_device *pdev) +{ + struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + rockchip_pdm_runtime_suspend(&pdev->dev); + + clk_disable_unprepare(pdm->clk); + clk_disable_unprepare(pdm->hclk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int rockchip_pdm_suspend(struct device *dev) +{ + struct rk_pdm_dev *pdm = dev_get_drvdata(dev); + + regcache_mark_dirty(pdm->regmap); + + return 0; +} + +static int rockchip_pdm_resume(struct device *dev) +{ + struct rk_pdm_dev *pdm = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) + return ret; + + ret = regcache_sync(pdm->regmap); + + pm_runtime_put(dev); + + return ret; +} +#endif + +static const struct dev_pm_ops rockchip_pdm_pm_ops = { + SET_RUNTIME_PM_OPS(rockchip_pdm_runtime_suspend, + rockchip_pdm_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(rockchip_pdm_suspend, rockchip_pdm_resume) +}; + +static const struct of_device_id rockchip_pdm_match[] = { + { .compatible = "rockchip,pdm", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rockchip_pdm_match); + +static struct platform_driver rockchip_pdm_driver = { + .probe = rockchip_pdm_probe, + .remove = rockchip_pdm_remove, + .driver = { + .name = "rockchip-pdm", + .of_match_table = of_match_ptr(rockchip_pdm_match), + .pm = &rockchip_pdm_pm_ops, + }, +}; + +module_platform_driver(rockchip_pdm_driver); + +MODULE_AUTHOR("Sugar <sugar.zhang@rock-chips.com>"); +MODULE_DESCRIPTION("Rockchip PDM Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/rockchip/rockchip_pdm.h b/sound/soc/rockchip/rockchip_pdm.h new file mode 100644 index 000000000000..886b48d128fd --- /dev/null +++ b/sound/soc/rockchip/rockchip_pdm.h @@ -0,0 +1,83 @@ +/* + * Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 _ROCKCHIP_PDM_H +#define _ROCKCHIP_PDM_H + +/* PDM REGS */ +#define PDM_SYSCONFIG (0x0000) +#define PDM_CTRL0 (0x0004) +#define PDM_CTRL1 (0x0008) +#define PDM_CLK_CTRL (0x000c) +#define PDM_HPF_CTRL (0x0010) +#define PDM_FIFO_CTRL (0x0014) +#define PDM_DMA_CTRL (0x0018) +#define PDM_INT_EN (0x001c) +#define PDM_INT_CLR (0x0020) +#define PDM_INT_ST (0x0024) +#define PDM_RXFIFO_DATA (0x0030) +#define PDM_DATA_VALID (0x0054) +#define PDM_VERSION (0x0058) + +/* PDM_SYSCONFIG */ +#define PDM_RX_MASK (0x1 << 2) +#define PDM_RX_START (0x1 << 2) +#define PDM_RX_STOP (0x0 << 2) +#define PDM_RX_CLR_MASK (0x1 << 0) +#define PDM_RX_CLR_WR (0x1 << 0) +#define PDM_RX_CLR_DONE (0x0 << 0) + +/* PDM CTRL0 */ +#define PDM_PATH_MSK (0xf << 27) +#define PDM_PATH3_EN BIT(30) +#define PDM_PATH2_EN BIT(29) +#define PDM_PATH1_EN BIT(28) +#define PDM_PATH0_EN BIT(27) +#define PDM_HWT_EN BIT(26) +#define PDM_VDW_MSK (0x1f << 0) +#define PDM_VDW(X) ((X - 1) << 0) + +/* PDM CLK CTRL */ +#define PDM_CLK_MSK BIT(5) +#define PDM_CLK_EN BIT(5) +#define PDM_CLK_DIS (0x0 << 5) +#define PDM_CKP_MSK BIT(3) +#define PDM_CKP_NORMAL (0x0 << 3) +#define PDM_CKP_INVERTED BIT(3) +#define PDM_DS_RATIO_MSK (0x7 << 0) +#define PDM_CLK_320FS (0x0 << 0) +#define PDM_CLK_640FS (0x1 << 0) +#define PDM_CLK_1280FS (0x2 << 0) +#define PDM_CLK_2560FS (0x3 << 0) +#define PDM_CLK_5120FS (0x4 << 0) + +/* PDM HPF CTRL */ +#define PDM_HPF_LE BIT(3) +#define PDM_HPF_RE BIT(2) +#define PDM_HPF_CF_MSK (0x3 << 0) +#define PDM_HPF_3P79HZ (0x0 << 0) +#define PDM_HPF_60HZ (0x1 << 0) +#define PDM_HPF_243HZ (0x2 << 0) +#define PDM_HPF_493HZ (0x3 << 0) + +/* PDM DMA CTRL */ +#define PDM_DMA_RD_MSK BIT(8) +#define PDM_DMA_RD_EN BIT(8) +#define PDM_DMA_RD_DIS (0x0 << 8) +#define PDM_DMA_RDL_MSK (0x7f << 0) +#define PDM_DMA_RDL(X) ((X - 1) << 0) + +#endif /* _ROCKCHIP_PDM_H */ diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index fa8101d1e16f..ee5055d47d13 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -49,8 +49,12 @@ static const struct of_device_id rk_spdif_match[] = { .data = (void *)RK_SPDIF_RK3066 }, { .compatible = "rockchip,rk3188-spdif", .data = (void *)RK_SPDIF_RK3188 }, + { .compatible = "rockchip,rk3228-spdif", + .data = (void *)RK_SPDIF_RK3366 }, { .compatible = "rockchip,rk3288-spdif", .data = (void *)RK_SPDIF_RK3288 }, + { .compatible = "rockchip,rk3328-spdif", + .data = (void *)RK_SPDIF_RK3366 }, { .compatible = "rockchip,rk3366-spdif", .data = (void *)RK_SPDIF_RK3366 }, { .compatible = "rockchip,rk3368-spdif", diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index 81a78940967c..55538e333cc8 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -44,7 +44,7 @@ struct s3c24xx_uda134x { static unsigned int rates[33 * 2]; #ifdef ENFORCE_RATES -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { +static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 147ebecfed94..1aa5cd77ca24 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -38,7 +38,7 @@ config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" depends on COMMON_CLK depends on OF || COMPILE_TEST - select SND_SIMPLE_CARD + select SND_SIMPLE_CARD_UTILS select REGMAP_MMIO help This option enables R-Car SRU/SCU/SSIU/SSI sound support diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index ead520182e26..7c4bdd82bb95 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -301,7 +301,12 @@ struct fsi_master { spinlock_t lock; }; -static int fsi_stream_is_play(struct fsi_priv *fsi, struct fsi_stream *io); +static inline int fsi_stream_is_play(struct fsi_priv *fsi, + struct fsi_stream *io) +{ + return &fsi->playback == io; +} + /* * basic read write function @@ -489,12 +494,6 @@ static void fsi_count_fifo_err(struct fsi_priv *fsi) /* * fsi_stream_xx() function */ -static inline int fsi_stream_is_play(struct fsi_priv *fsi, - struct fsi_stream *io) -{ - return &fsi->playback == io; -} - static inline struct fsi_stream *fsi_stream_get(struct fsi_priv *fsi, struct snd_pcm_substream *substream) { diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d3b0dc145a56..197cb3ec075f 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -308,23 +308,12 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) } } -int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) -{ - rsnd_adg_set_ssi_clk(ssi_mod, 0); - - return 0; -} - -int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) +int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) { - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct clk *clk; int i; - u32 data; - u32 ckr = 0; int sel_table[] = { [CLKA] = 0x1, [CLKB] = 0x2, @@ -338,30 +327,42 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int 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; - } + if (rate == clk_get_rate(clk)) + return sel_table[i]; } /* * find divided clock from BRGA/BRGB */ - if (rate == adg->rbga_rate_for_441khz) { - data = 0x10; - goto found_clock; - } + if (rate == adg->rbga_rate_for_441khz) + return 0x10; - if (rate == adg->rbgb_rate_for_48khz) { - data = 0x20; - goto found_clock; - } + if (rate == adg->rbgb_rate_for_48khz) + return 0x20; return -EIO; +} -found_clock: +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod) +{ + rsnd_adg_set_ssi_clk(ssi_mod, 0); + + return 0; +} + +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + int data; + u32 ckr = 0; + + data = rsnd_adg_clk_query(priv, rate); + if (data < 0) + return data; rsnd_adg_set_ssi_clk(ssi_mod, data); @@ -480,6 +481,9 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, if (req_rate[0] % 48000 == 0) adg->flags = AUDIO_OUT_48; + if (of_get_property(np, "clkout-lr-asynchronous", NULL)) + adg->flags = LRCLK_ASYNC; + /* * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC * have 44.1kHz or 48kHz base clocks for now. @@ -555,7 +559,6 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, clk = clk_register_fixed_rate(dev, clkout_name[i], parent_clk_name, 0, req_rate[0]); - adg->clkout[i] = ERR_PTR(-ENOENT); if (!IS_ERR(clk)) adg->clkout[i] = clk; } @@ -580,7 +583,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv) { struct rsnd_adg *adg; struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np = dev->of_node; int ret; adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); @@ -597,9 +599,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv) rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); - if (of_get_property(np, "clkout-lr-asynchronous", NULL)) - adg->flags = LRCLK_ASYNC; - priv->adg = adg; rsnd_adg_clk_enable(priv); diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index d879c010cf03..f1d4fb566892 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -31,7 +31,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); struct device *dev = rsnd_priv_to_dev(priv); u32 data; - u32 path[] = { + static const u32 path[] = { [1] = 1 << 0, [5] = 1 << 8, [6] = 1 << 12, @@ -71,7 +71,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, } else { struct rsnd_mod *src = rsnd_io_to_mod_src(io); - u8 cmd_case[] = { + static const u8 cmd_case[] = { [0] = 0x3, [1] = 0x3, [2] = 0x4, @@ -82,6 +82,9 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, [9] = 0x2, }; + if (unlikely(!src)) + return -EIO; + data = path[rsnd_mod_id(src)] | cmd_case[rsnd_mod_id(src)] << 16; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 8c1f4e2e0c4f..3f2ced26ed37 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -203,27 +203,6 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io) return !!io->substream; } -void rsnd_set_slot(struct rsnd_dai *rdai, - int slots, int num) -{ - rdai->slots = slots; - rdai->slots_num = num; -} - -int rsnd_get_slot(struct rsnd_dai_stream *io) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - - return rdai->slots; -} - -int rsnd_get_slot_num(struct rsnd_dai_stream *io) -{ - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - - return rdai->slots_num; -} - int rsnd_runtime_channel_original(struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); @@ -248,13 +227,14 @@ int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io) int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) { + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); int chan = rsnd_io_is_play(io) ? rsnd_runtime_channel_after_ctu(io) : rsnd_runtime_channel_original(io); /* Use Multi SSI */ if (rsnd_runtime_is_ssi_multi(io)) - chan /= rsnd_get_slot_num(io); + chan /= rsnd_rdai_ssi_lane_get(rdai); /* TDM Extend Mode needs 8ch */ if (chan == 6) @@ -265,12 +245,13 @@ int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) { - int slots = rsnd_get_slot_num(io); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + int lane = rsnd_rdai_ssi_lane_get(rdai); int chan = rsnd_io_is_play(io) ? rsnd_runtime_channel_after_ctu(io) : rsnd_runtime_channel_original(io); - return (chan >= 6) && (slots > 1); + return (chan > 2) && (lane > 1); } int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io) @@ -310,6 +291,24 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u32 val = 0x76543210; u32 mask = ~0; + /* + * *Hardware* L/R and *Software* L/R are inverted. + * We need to care about inversion timing to control + * Playback/Capture correctly. + * The point is [DVC] needs *Hardware* L/R, [MEM] needs *Software* L/R + * + * sL/R : software L/R + * hL/R : hardware L/R + * (*) : conversion timing + * + * Playback + * sL/R (*) hL/R hL/R hL/R hL/R hL/R + * [MEM] -> [SRC] -> [DVC] -> [CMD] -> [SSIU] -> [SSI] -> codec + * + * Capture + * hL/R hL/R hL/R hL/R hL/R (*) sL/R + * codec -> [SSI] -> [SSIU] -> [SRC] -> [DVC] -> [CMD] -> [MEM] + */ if (rsnd_io_is_play(io)) { struct rsnd_mod *src = rsnd_io_to_mod_src(io); @@ -470,8 +469,7 @@ static int rsnd_status_update(u32 *status, #define rsnd_dai_call(fn, io, param...) \ ({ \ - struct rsnd_priv *priv = rsnd_io_to_priv(io); \ - struct device *dev = rsnd_priv_to_dev(priv); \ + struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); \ struct rsnd_mod *mod; \ int is_play = rsnd_io_is_play(io); \ int ret = 0, i; \ @@ -532,6 +530,24 @@ static void rsnd_dai_disconnect(struct rsnd_mod *mod, io->mod[type] = NULL; } +int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai, + int max_channels) +{ + if (max_channels > 0) + rdai->max_channels = max_channels; + + return rdai->max_channels; +} + +int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai, + int ssi_lane) +{ + if (ssi_lane > 0) + rdai->ssi_lane = ssi_lane; + + return rdai->ssi_lane; +} + struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) { if ((id < 0) || (id >= rsnd_rdai_nr(priv))) @@ -551,40 +567,6 @@ static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) /* * 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; -} - -bool 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; - } - - return true; - } - - return false; -} - void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io) { struct snd_pcm_substream *substream = io->substream; @@ -602,15 +584,7 @@ void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io) static void rsnd_dai_stream_init(struct rsnd_dai_stream *io, struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - 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; } static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io) @@ -749,9 +723,13 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, struct device *dev = rsnd_priv_to_dev(priv); switch (slots) { + case 2: case 6: + case 8: + case 16: /* TDM Extend Mode */ - rsnd_set_slot(rdai, slots, 1); + rsnd_rdai_channels_set(rdai, slots); + rsnd_rdai_ssi_lane_set(rdai, 1); break; default: dev_err(dev, "unsupported TDM slots (%d)\n", slots); @@ -761,22 +739,177 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static unsigned int rsnd_soc_hw_channels_list[] = { + 2, 6, 8, 16, +}; + +static unsigned int rsnd_soc_hw_rate_list[] = { + 8000, + 11025, + 16000, + 22050, + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 176400, + 192000, +}; + +static int rsnd_soc_hw_rule(struct rsnd_priv *priv, + unsigned int *list, int list_num, + struct snd_interval *baseline, struct snd_interval *iv) +{ + struct snd_interval p; + unsigned int rate; + int i; + + snd_interval_any(&p); + p.min = UINT_MAX; + p.max = 0; + + for (i = 0; i < list_num; i++) { + + if (!snd_interval_test(iv, list[i])) + continue; + + rate = rsnd_ssi_clk_query(priv, + baseline->min, list[i], NULL); + if (rate > 0) { + p.min = min(p.min, list[i]); + p.max = max(p.max, list[i]); + } + + rate = rsnd_ssi_clk_query(priv, + baseline->max, list[i], NULL); + if (rate > 0) { + p.min = min(p.min, list[i]); + p.max = max(p.max, list[i]); + } + } + + return snd_interval_refine(iv, &p); +} + +static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval ic; + struct snd_soc_dai *dai = rule->private; + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + + /* + * possible sampling rate limitation is same as + * 2ch if it supports multi ssi + */ + ic = *ic_; + if (1 < rsnd_rdai_ssi_lane_get(rdai)) { + ic.min = 2; + ic.max = 2; + } + + return rsnd_soc_hw_rule(priv, rsnd_soc_hw_rate_list, + ARRAY_SIZE(rsnd_soc_hw_rate_list), + &ic, ir); +} + + +static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval ic; + struct snd_soc_dai *dai = rule->private; + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + + /* + * possible sampling rate limitation is same as + * 2ch if it supports multi ssi + */ + ic = *ic_; + if (1 < rsnd_rdai_ssi_lane_get(rdai)) { + ic.min = 2; + ic.max = 2; + } + + return rsnd_soc_hw_rule(priv, rsnd_soc_hw_channels_list, + ARRAY_SIZE(rsnd_soc_hw_channels_list), + ir, &ic); +} + +static void rsnd_soc_hw_constraint(struct snd_pcm_runtime *runtime, + struct snd_soc_dai *dai) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct snd_pcm_hw_constraint_list *constraint = &rdai->constraint; + unsigned int max_channels = rsnd_rdai_channels_get(rdai); + int i; + + /* + * Channel Limitation + * It depends on Platform design + */ + constraint->list = rsnd_soc_hw_channels_list; + constraint->count = 0; + constraint->mask = 0; + + for (i = 0; i < ARRAY_SIZE(rsnd_soc_hw_channels_list); i++) { + if (rsnd_soc_hw_channels_list[i] > max_channels) + break; + constraint->count = i + 1; + } + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, constraint); + + /* + * Sampling Rate / Channel Limitation + * It depends on Clock Master Mode + */ + if (!rsnd_rdai_is_clk_master(rdai)) + return; + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + rsnd_soc_hw_rule_rate, dai, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + rsnd_soc_hw_rule_channels, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + int ret; + + /* rsnd_io_to_runtime() is not yet enabled here */ + rsnd_soc_hw_constraint(substream->runtime, dai); /* * call rsnd_dai_call without spinlock */ - return rsnd_dai_call(nolock_start, io, priv); + ret = rsnd_dai_call(nolock_start, io, priv); + if (ret < 0) + rsnd_dai_call(nolock_stop, io, priv); + + return ret; } static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); /* @@ -820,32 +953,132 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, of_node_put(node); } -static int rsnd_dai_probe(struct rsnd_priv *priv) +static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, + int *is_graph) { + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; struct device_node *dai_node; - struct device_node *dai_np; + struct device_node *ret; + + *is_graph = 0; + + /* + * parse both previous dai (= rcar_sound,dai), and + * graph dai (= ports/port) + */ + dai_node = of_get_child_by_name(np, RSND_NODE_DAI); + if (dai_node) { + ret = dai_node; + goto of_node_compatible; + } + + ret = np; + + dai_node = of_graph_get_next_endpoint(np, NULL); + if (dai_node) + goto of_node_graph; + + return NULL; + +of_node_graph: + *is_graph = 1; +of_node_compatible: + of_node_put(dai_node); + + return ret; +} + +static void __rsnd_dai_probe(struct rsnd_priv *priv, + struct device_node *dai_np, + int dai_i, int is_graph) +{ struct device_node *playback, *capture; struct rsnd_dai_stream *io_playback; struct rsnd_dai_stream *io_capture; - struct snd_soc_dai_driver *rdrv, *drv; + struct snd_soc_dai_driver *drv; struct rsnd_dai *rdai; struct device *dev = rsnd_priv_to_dev(priv); - int nr, dai_i, io_i; - int ret; + int io_i; + + rdai = rsnd_rdai_get(priv, dai_i); + drv = priv->daidrv + dai_i; + io_playback = &rdai->playback; + io_capture = &rdai->capture; + + snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); + + rdai->priv = priv; + drv->name = rdai->name; + drv->ops = &rsnd_soc_dai_ops; + + snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE, + "DAI%d Playback", dai_i); + drv->playback.rates = RSND_RATES; + drv->playback.formats = RSND_FMTS; + drv->playback.channels_min = 2; + drv->playback.channels_max = 16; + drv->playback.stream_name = rdai->playback.name; + + snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE, + "DAI%d Capture", dai_i); + drv->capture.rates = RSND_RATES; + drv->capture.formats = RSND_FMTS; + drv->capture.channels_min = 2; + drv->capture.channels_max = 16; + drv->capture.stream_name = rdai->capture.name; + + rdai->playback.rdai = rdai; + rdai->capture.rdai = rdai; + rsnd_rdai_channels_set(rdai, 2); /* default 2ch */ + rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */ + + for (io_i = 0;; io_i++) { + playback = of_parse_phandle(dai_np, "playback", io_i); + capture = of_parse_phandle(dai_np, "capture", io_i); + + if (!playback && !capture) + break; - dai_node = rsnd_dai_of_node(priv); - nr = of_get_child_count(dai_node); - if (!nr) { - ret = -EINVAL; - goto rsnd_dai_probe_done; + rsnd_parse_connect_ssi(rdai, playback, capture); + rsnd_parse_connect_src(rdai, playback, capture); + rsnd_parse_connect_ctu(rdai, playback, capture); + rsnd_parse_connect_mix(rdai, playback, capture); + rsnd_parse_connect_dvc(rdai, playback, capture); + + of_node_put(playback); + of_node_put(capture); } + dev_dbg(dev, "%s (%s/%s)\n", rdai->name, + rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", + rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); +} + +static int rsnd_dai_probe(struct rsnd_priv *priv) +{ + struct device_node *dai_node; + struct device_node *dai_np; + struct snd_soc_dai_driver *rdrv; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai *rdai; + int nr; + int is_graph; + int dai_i; + + dai_node = rsnd_dai_of_node(priv, &is_graph); + if (is_graph) + nr = of_graph_get_endpoint_count(dai_node); + else + nr = of_get_child_count(dai_node); + + if (!nr) + return -EINVAL; + rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL); rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL); - if (!rdrv || !rdai) { - ret = -ENOMEM; - goto rsnd_dai_probe_done; - } + if (!rdrv || !rdai) + return -ENOMEM; priv->rdai_nr = nr; priv->daidrv = rdrv; @@ -855,68 +1088,18 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) * parse all dai */ dai_i = 0; - for_each_child_of_node(dai_node, dai_np) { - rdai = rsnd_rdai_get(priv, dai_i); - drv = rdrv + dai_i; - io_playback = &rdai->playback; - io_capture = &rdai->capture; - - snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); - - rdai->priv = priv; - drv->name = rdai->name; - drv->ops = &rsnd_soc_dai_ops; - - snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE, - "DAI%d Playback", dai_i); - drv->playback.rates = RSND_RATES; - drv->playback.formats = RSND_FMTS; - drv->playback.channels_min = 2; - drv->playback.channels_max = 6; - drv->playback.stream_name = rdai->playback.name; - - snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE, - "DAI%d Capture", dai_i); - drv->capture.rates = RSND_RATES; - drv->capture.formats = RSND_FMTS; - drv->capture.channels_min = 2; - drv->capture.channels_max = 6; - drv->capture.stream_name = rdai->capture.name; - - rdai->playback.rdai = rdai; - rdai->capture.rdai = rdai; - rsnd_set_slot(rdai, 2, 1); /* default */ - - for (io_i = 0;; io_i++) { - playback = of_parse_phandle(dai_np, "playback", io_i); - capture = of_parse_phandle(dai_np, "capture", io_i); - - if (!playback && !capture) - break; - - rsnd_parse_connect_ssi(rdai, playback, capture); - rsnd_parse_connect_src(rdai, playback, capture); - rsnd_parse_connect_ctu(rdai, playback, capture); - rsnd_parse_connect_mix(rdai, playback, capture); - rsnd_parse_connect_dvc(rdai, playback, capture); - - of_node_put(playback); - of_node_put(capture); + if (is_graph) { + for_each_endpoint_of_node(dai_node, dai_np) { + __rsnd_dai_probe(priv, dai_np, dai_i, is_graph); + rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i); + dai_i++; } - - dai_i++; - - dev_dbg(dev, "%s (%s/%s)\n", rdai->name, - rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", - rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); + } else { + for_each_child_of_node(dai_node, dai_np) + __rsnd_dai_probe(priv, dai_np, dai_i++, is_graph); } - ret = 0; - -rsnd_dai_probe_done: - of_node_put(dai_node); - - return ret; + return 0; } /* @@ -965,12 +1148,14 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream, 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); + snd_pcm_uframes_t pointer = 0; + + rsnd_dai_call(pointer, io, &pointer); - return bytes_to_frames(runtime, io->byte_pos); + return pointer; } static struct snd_pcm_ops rsnd_pcm_ops = { @@ -1033,6 +1218,9 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl); int i, change = 0; + if (!cfg->accept(cfg->io)) + return 0; + for (i = 0; i < cfg->size; i++) { if (cfg->texts) { change |= (uc->value.enumerated.item[i] != cfg->val[i]); @@ -1049,6 +1237,18 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, return change; } +int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io) +{ + return 1; +} + +int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + return !!runtime; +} + struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg) { cfg->cfg.val = cfg->val; @@ -1067,6 +1267,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, + int (*accept)(struct rsnd_dai_stream *io), void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod), struct rsnd_kctrl_cfg *cfg, @@ -1101,6 +1302,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, cfg->texts = texts; cfg->max = max; cfg->size = size; + cfg->accept = accept; cfg->update = update; cfg->card = card; cfg->kctrl = kctrl; @@ -1332,7 +1534,7 @@ static int rsnd_resume(struct device *dev) return 0; } -static struct dev_pm_ops rsnd_pm_ops = { +static const struct dev_pm_ops rsnd_pm_ops = { .suspend = rsnd_suspend, .resume = rsnd_resume, }; diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 9dcc1f9db026..4ba8f2fe7a4c 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -279,12 +279,14 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* CTU Pass */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", + rsnd_kctrl_accept_anytime, NULL, &ctu->pass, RSND_MAX_CHANNELS, 0xC); /* ROW0 */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", + rsnd_kctrl_accept_anytime, NULL, &ctu->sv0, RSND_MAX_CHANNELS, 0x00FFFFFF); @@ -293,6 +295,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* ROW1 */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", + rsnd_kctrl_accept_anytime, NULL, &ctu->sv1, RSND_MAX_CHANNELS, 0x00FFFFFF); @@ -301,6 +304,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* ROW2 */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", + rsnd_kctrl_accept_anytime, NULL, &ctu->sv2, RSND_MAX_CHANNELS, 0x00FFFFFF); @@ -309,6 +313,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* ROW3 */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", + rsnd_kctrl_accept_anytime, NULL, &ctu->sv3, RSND_MAX_CHANNELS, 0x00FFFFFF); @@ -317,6 +322,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, /* Reset */ ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset", + rsnd_kctrl_accept_anytime, rsnd_ctu_value_reset, &ctu->reset, 1); diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 241cb3b08a07..60aa5e96a49f 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -25,6 +25,7 @@ struct rsnd_dmaen { struct dma_chan *chan; + dma_cookie_t cookie; dma_addr_t dma_buf; unsigned int dma_len; unsigned int dma_period; @@ -103,10 +104,6 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod, * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. * But, Audio-DMAC-peri-peri doesn't have interrupt, * and this driver is assuming that here. - * - * If Audio-DMAC-peri-peri has interrpt, - * rsnd_dai_pointer_update() will be called twice, - * ant it will breaks io->byte_pos */ spin_lock_irqsave(&priv->lock, flags); @@ -121,7 +118,7 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod, */ rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2); - elapsed = rsnd_dai_pointer_update(io, io->byte_per_period); + elapsed = true; dmaen->dma_cnt++; } @@ -292,7 +289,8 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, for (i = 0; i < 2; i++) rsnd_dmaen_sync(dmaen, io, i); - if (dmaengine_submit(desc) < 0) { + dmaen->cookie = dmaengine_submit(desc); + if (dmaen->cookie < 0) { dev_err(dev, "dmaengine_submit() fail\n"); return -EIO; } @@ -348,12 +346,34 @@ static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, return 0; } +static int rsnd_dmaen_pointer(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + snd_pcm_uframes_t *pointer) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_dma *dma = rsnd_mod_to_dma(mod); + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct dma_tx_state state; + enum dma_status status; + unsigned int pos = 0; + + status = dmaengine_tx_status(dmaen->chan, dmaen->cookie, &state); + if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { + if (state.residue > 0 && state.residue <= dmaen->dma_len) + pos = dmaen->dma_len - state.residue; + } + *pointer = bytes_to_frames(runtime, pos); + + return 0; +} + static struct rsnd_mod_ops rsnd_dmaen_ops = { .name = "audmac", .nolock_start = rsnd_dmaen_nolock_start, .nolock_stop = rsnd_dmaen_nolock_stop, .start = rsnd_dmaen_start, .stop = rsnd_dmaen_stop, + .pointer= rsnd_dmaen_pointer, }; /* diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 463de8360985..99d2d9459e75 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -249,16 +249,18 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, struct snd_soc_pcm_runtime *rtd) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); int is_play = rsnd_io_is_play(io); - int slots = rsnd_get_slot(io); + int channels = rsnd_rdai_channels_get(rdai); int ret; /* Volume */ ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Playback Volume" : "DVC In Capture Volume", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, - &dvc->volume, slots, + &dvc->volume, channels, 0x00800000 - 1); if (ret < 0) return ret; @@ -267,8 +269,9 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Mute Switch" : "DVC In Mute Switch", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, - &dvc->mute, slots, + &dvc->mute, channels, 1); if (ret < 0) return ret; @@ -277,6 +280,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_s(mod, io, rtd, is_play ? "DVC Out Ramp Switch" : "DVC In Ramp Switch", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->ren, 1); if (ret < 0) @@ -285,6 +289,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->rup, dvc_ramp_rate); @@ -294,6 +299,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", + rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->rdown, dvc_ramp_rate); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 4b0980728e13..ee00e3516911 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -219,6 +219,8 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884), RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888), RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c), + RSND_GEN_S_REG(HDMI0_SEL, 0x9e0), + RSND_GEN_S_REG(HDMI1_SEL, 0x9e4), /* FIXME: it needs SSI_MODE2/3 in the future */ RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 323af41ecfcb..99c57611df88 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -18,6 +18,7 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/of_graph.h> #include <linux/of_irq.h> #include <linux/sh_dma.h> #include <linux/workqueue.h> @@ -170,6 +171,8 @@ enum rsnd_reg { RSND_REG_SSI_SYS_STATUS5, RSND_REG_SSI_SYS_STATUS6, RSND_REG_SSI_SYS_STATUS7, + RSND_REG_HDMI0_SEL, + RSND_REG_HDMI1_SEL, /* SSI */ RSND_REG_SSICR, @@ -268,6 +271,9 @@ struct rsnd_mod_ops { struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params); + int (*pointer)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + snd_pcm_uframes_t *pointer); int (*fallback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv); @@ -305,6 +311,7 @@ struct rsnd_mod { * H 0: pcm_new * H 0: fallback * H 0: hw_params + * H 0: pointer */ #define __rsnd_mod_shift_nolock_start 0 #define __rsnd_mod_shift_nolock_stop 0 @@ -318,6 +325,7 @@ struct rsnd_mod { #define __rsnd_mod_shift_pcm_new 28 /* always called */ #define __rsnd_mod_shift_fallback 28 /* always called */ #define __rsnd_mod_shift_hw_params 28 /* always called */ +#define __rsnd_mod_shift_pointer 28 /* always called */ #define __rsnd_mod_add_probe 0 #define __rsnd_mod_add_remove 0 @@ -331,6 +339,7 @@ struct rsnd_mod { #define __rsnd_mod_add_pcm_new 0 #define __rsnd_mod_add_fallback 0 #define __rsnd_mod_add_hw_params 0 +#define __rsnd_mod_add_pointer 0 #define __rsnd_mod_call_probe 0 #define __rsnd_mod_call_remove 0 @@ -342,6 +351,7 @@ struct rsnd_mod { #define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 +#define __rsnd_mod_call_pointer 0 #define __rsnd_mod_call_nolock_start 0 #define __rsnd_mod_call_nolock_stop 1 @@ -389,11 +399,6 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct device_node *playback, struct device_node *capture); -void rsnd_set_slot(struct rsnd_dai *rdai, - int slots, int slots_total); -int rsnd_get_slot(struct rsnd_dai_stream *io); -int rsnd_get_slot_num(struct rsnd_dai_stream *io); - int rsnd_runtime_channel_original(struct rsnd_dai_stream *io); int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io); int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io); @@ -420,13 +425,8 @@ struct rsnd_dai_stream { char name[RSND_DAI_NAME_SIZE]; struct snd_pcm_substream *substream; struct rsnd_mod *mod[RSND_MOD_MAX]; - struct rsnd_dai_path_info *info; /* rcar_snd.h */ struct rsnd_dai *rdai; u32 parent_ssi_status; - int byte_pos; - int period_pos; - int byte_per_period; - int next_period_byte; }; #define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) @@ -449,9 +449,10 @@ struct rsnd_dai { struct rsnd_dai_stream playback; struct rsnd_dai_stream capture; struct rsnd_priv *priv; + struct snd_pcm_hw_constraint_list constraint; - int slots; - int slots_num; + int max_channels; /* 2ch - 16ch */ + int ssi_lane; /* 1lane - 4lane */ unsigned int clk_master:1; unsigned int bit_clk_inv:1; @@ -471,13 +472,24 @@ struct rsnd_dai { struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); -bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +#define rsnd_rdai_channels_set(rdai, max_channels) \ + rsnd_rdai_channels_ctrl(rdai, max_channels) +#define rsnd_rdai_channels_get(rdai) \ + rsnd_rdai_channels_ctrl(rdai, 0) +int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai, + int max_channels); + +#define rsnd_rdai_ssi_lane_set(rdai, ssi_lane) \ + rsnd_rdai_ssi_lane_ctrl(rdai, ssi_lane) +#define rsnd_rdai_ssi_lane_get(rdai) \ + rsnd_rdai_ssi_lane_ctrl(rdai, 0) +int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai, + int ssi_lane); + void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io); -int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_dai_stream *io, enum rsnd_mod_type type); -#define rsnd_dai_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DAI) /* * R-Car Gen1/Gen2 @@ -491,6 +503,7 @@ phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); /* * R-Car ADG */ +int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate); 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 rsnd_priv *priv); @@ -596,6 +609,7 @@ struct rsnd_kctrl_cfg { unsigned int size; u32 *val; const char * const *texts; + int (*accept)(struct rsnd_dai_stream *io); void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod); struct rsnd_dai_stream *io; struct snd_card *card; @@ -613,12 +627,15 @@ struct rsnd_kctrl_cfg_s { u32 val; }; +int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io); +int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io); struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg); struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg); int rsnd_kctrl_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, + int (*accept)(struct rsnd_dai_stream *io), void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod), struct rsnd_kctrl_cfg *cfg, @@ -626,16 +643,16 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, int size, u32 max); -#define rsnd_kctrl_new_m(mod, io, rtd, name, update, cfg, size, max) \ - rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_m(cfg), \ +#define rsnd_kctrl_new_m(mod, io, rtd, name, accept, update, cfg, size, max) \ + rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_m(cfg), \ NULL, size, max) -#define rsnd_kctrl_new_s(mod, io, rtd, name, update, cfg, max) \ - rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \ +#define rsnd_kctrl_new_s(mod, io, rtd, name, accept, update, cfg, max) \ + rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ NULL, 1, max) -#define rsnd_kctrl_new_e(mod, io, rtd, name, update, cfg, texts) \ - rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \ +#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts) \ + rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ texts, 1, ARRAY_SIZE(texts)) /* @@ -648,6 +665,13 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); +#define RSND_SSI_HDMI_PORT0 0xf0 +#define RSND_SSI_HDMI_PORT1 0xf1 +int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io); +void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, + struct device_node *endpoint, + int dai_i); + #define rsnd_ssi_is_pin_sharing(io) \ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); @@ -656,6 +680,8 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, struct device_node *playback, struct device_node *capture); +unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv, + int param1, int param2, int *idx); /* * R-Car SSIU diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 76a477a3ccb5..7aa239e28491 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -12,10 +12,6 @@ #define SRC_NAME "src" -/* SRCx_STATUS */ -#define OUF_SRCO ((1 << 12) | (1 << 13)) -#define OUF_SRCI ((1 << 9) | (1 << 8)) - /* SCU_SYSTEM_STATUS0/1 */ #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) @@ -55,20 +51,6 @@ struct rsnd_src { * */ -/* - * src.c is caring... - * - * Gen1 - * - * [mem] -> [SRU] -> [SSI] - * |--------| - * - * Gen2 - * - * [mem] -> [SRC] -> [SSIU] -> [SSI] - * |-----------------| - */ - static void rsnd_src_activation(struct rsnd_mod *mod) { rsnd_mod_write(mod, SRC_SWRSR, 0); @@ -515,6 +497,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, rsnd_io_is_play(io) ? "SRC Out Rate Switch" : "SRC In Rate Switch", + rsnd_kctrl_accept_anytime, rsnd_src_set_convert_rate, &src->sen, 1); if (ret < 0) @@ -524,6 +507,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, rsnd_io_is_play(io) ? "SRC Out Rate" : "SRC In Rate", + rsnd_kctrl_accept_runtime, rsnd_src_set_convert_rate, &src->sync, 192000); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 91e5c07911b4..46feddd78ee2 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -11,6 +11,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <sound/simple_card_utils.h> #include <linux/delay.h> #include "rsnd.h" #define RSND_SSI_NAME_SIZE 16 @@ -76,11 +77,18 @@ struct rsnd_ssi { int rate; int irq; unsigned int usrcnt; + + int byte_pos; + int period_pos; + int byte_per_period; + int next_period_byte; }; /* flags */ #define RSND_SSI_CLK_PIN_SHARE (1 << 0) #define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ +#define RSND_SSI_HDMI0 (1 << 2) /* for HDMI0 */ +#define RSND_SSI_HDMI1 (1 << 3) /* for HDMI1 */ #define for_each_rsnd_ssi(pos, priv, i) \ for (i = 0; \ @@ -99,6 +107,20 @@ struct rsnd_ssi { #define rsnd_ssi_is_run_mods(mod, io) \ (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) +int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI0) + return RSND_SSI_HDMI_PORT0; + + if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI1) + return RSND_SSI_HDMI_PORT1; + + return 0; +} + int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); @@ -186,6 +208,46 @@ u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) return 0; } +unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv, + int param1, int param2, int *idx) +{ + int ssi_clk_mul_table[] = { + 1, 2, 4, 8, 16, 6, 12, + }; + int j, ret; + unsigned int main_rate; + + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * It will set SSIWSR.CONT here, but SSICR.CKDV = 000 + * with it is not allowed. (SSIWSR.WS_MODE with + * SSICR.CKDV = 000 is not allowed either). + * Skip it. See SSICR.CKDV + */ + if (j == 0) + continue; + + /* + * this driver is assuming that + * system word is 32bit x chan + * see rsnd_ssi_init() + */ + main_rate = 32 * param1 * param2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_clk_query(priv, main_rate); + if (ret < 0) + continue; + + if (idx) + *idx = j; + + return main_rate; + } + + return 0; +} + static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { @@ -195,10 +257,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); int chan = rsnd_runtime_channel_for_ssi(io); - int j, ret; - int ssi_clk_mul_table[] = { - 1, 2, 4, 8, 16, 6, 12, - }; + int idx, ret; unsigned int main_rate; unsigned int rate = rsnd_io_is_play(io) ? rsnd_src_get_out_rate(priv, io) : @@ -222,45 +281,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, return 0; } - /* - * Find best clock, and try to start ADG - */ - for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { - - /* - * It will set SSIWSR.CONT here, but SSICR.CKDV = 000 - * with it is not allowed. (SSIWSR.WS_MODE with - * SSICR.CKDV = 000 is not allowed either). - * Skip it. See SSICR.CKDV - */ - if (j == 0) - continue; - - /* - * this driver is assuming that - * system word is 32bit x chan - * see rsnd_ssi_init() - */ - main_rate = rate * 32 * chan * ssi_clk_mul_table[j]; - - ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); - if (0 == ret) { - ssi->cr_clk = FORCE | SWL_32 | - SCKD | SWSD | CKDV(j); - ssi->wsr = CONT; + main_rate = rsnd_ssi_clk_query(priv, rate, chan, &idx); + if (!main_rate) { + dev_err(dev, "unsupported clock rate\n"); + return -EIO; + } - ssi->rate = rate; + ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); + if (ret < 0) + return ret; - dev_dbg(dev, "%s[%d] outputs %u Hz\n", - rsnd_mod_name(mod), - rsnd_mod_id(mod), rate); + ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(idx); + ssi->wsr = CONT; + ssi->rate = rate; - return 0; - } - } + dev_dbg(dev, "%s[%d] outputs %u Hz\n", + rsnd_mod_name(mod), + rsnd_mod_id(mod), rate); - dev_err(dev, "unsupported clock rate\n"); - return -EIO; + return 0; } static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, @@ -357,6 +396,59 @@ static void rsnd_ssi_register_setup(struct rsnd_mod *mod) ssi->cr_mode); /* without EN */ } +static void rsnd_ssi_pointer_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + ssi->byte_pos = 0; + ssi->period_pos = 0; + ssi->byte_per_period = runtime->period_size * + runtime->channels * + samples_to_bytes(runtime, 1); + ssi->next_period_byte = ssi->byte_per_period; +} + +static int rsnd_ssi_pointer_offset(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + int additional) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + int pos = ssi->byte_pos + additional; + + pos %= (runtime->periods * ssi->byte_per_period); + + return pos; +} + +static bool rsnd_ssi_pointer_update(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + int byte) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + ssi->byte_pos += byte; + + if (ssi->byte_pos >= ssi->next_period_byte) { + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + ssi->period_pos++; + ssi->next_period_byte += ssi->byte_per_period; + + if (ssi->period_pos >= runtime->periods) { + ssi->byte_pos = 0; + ssi->period_pos = 0; + ssi->next_period_byte = ssi->byte_per_period; + } + + return true; + } + + return false; +} + /* * SSI mod common functions */ @@ -370,6 +462,8 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, if (!rsnd_ssi_is_run_mods(mod, io)) return 0; + rsnd_ssi_pointer_init(mod, io); + ssi->usrcnt++; rsnd_mod_power_on(mod); @@ -549,7 +643,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, if (!is_dma && (status & DIRQ)) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 *buf = (u32 *)(runtime->dma_area + - rsnd_dai_pointer_offset(io, 0)); + rsnd_ssi_pointer_offset(mod, io, 0)); int shift = 0; switch (runtime->sample_bits) { @@ -568,7 +662,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, else *buf = (rsnd_mod_read(mod, SSIRDR) >> shift); - elapsed = rsnd_dai_pointer_update(io, sizeof(*buf)); + elapsed = rsnd_ssi_pointer_update(mod, io, sizeof(*buf)); } /* DMA only */ @@ -675,6 +769,18 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, return ret; } +static int rsnd_ssi_pointer(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + snd_pcm_uframes_t *pointer) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + *pointer = bytes_to_frames(runtime, ssi->byte_pos); + + return 0; +} + static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .name = SSI_NAME, .probe = rsnd_ssi_common_probe, @@ -683,6 +789,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, .irq = rsnd_ssi_irq, + .pointer= rsnd_ssi_pointer, .pcm_new = rsnd_ssi_pcm_new, .hw_params = rsnd_ssi_hw_params, }; @@ -787,13 +894,6 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) /* - * Non SSI - */ -static struct rsnd_mod_ops rsnd_ssi_non_ops = { - .name = SSI_NAME, -}; - -/* * ssi mod function */ static void rsnd_ssi_connect(struct rsnd_mod *mod, @@ -814,7 +914,8 @@ static void rsnd_ssi_connect(struct rsnd_mod *mod, type = types[i]; if (!rsnd_io_to_mod(io, type)) { rsnd_dai_connect(mod, io, type); - rsnd_set_slot(rdai, 2 * (i + 1), (i + 1)); + rsnd_rdai_channels_set(rdai, (i + 1) * 2); + rsnd_rdai_ssi_lane_set(rdai, (i + 1)); return; } } @@ -847,6 +948,47 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, of_node_put(node); } +static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct device_node *remote_ep) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); + struct rsnd_ssi *ssi; + + if (!mod) + return; + + ssi = rsnd_mod_to_ssi(mod); + + if (strstr(remote_ep->full_name, "hdmi0")) { + ssi->flags |= RSND_SSI_HDMI0; + dev_dbg(dev, "%s[%d] connected to HDMI0\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } + + if (strstr(remote_ep->full_name, "hdmi1")) { + ssi->flags |= RSND_SSI_HDMI1; + dev_dbg(dev, "%s[%d] connected to HDMI1\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } +} + +void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, + struct device_node *endpoint, + int dai_i) +{ + struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); + struct device_node *remote_ep; + + remote_ep = of_graph_get_remote_endpoint(endpoint); + if (!remote_ep) + return; + + __rsnd_ssi_parse_hdmi_connection(priv, &rdai->playback, remote_ep); + __rsnd_ssi_parse_hdmi_connection(priv, &rdai->capture, remote_ep); +} + struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) @@ -952,7 +1094,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) goto rsnd_ssi_probe_done; } - ops = &rsnd_ssi_non_ops; if (of_property_read_bool(np, "pio-transfer")) ops = &rsnd_ssi_pio_ops; else diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 512d238b79e2..bed2c9c0004b 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -123,6 +123,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + int hdmi = rsnd_ssi_hdmi_port(io); int ret; ret = rsnd_ssiu_init(mod, io, priv); @@ -150,6 +151,42 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, rsnd_get_dalign(mod, io)); } + if (hdmi) { + enum rsnd_mod_type rsnd_ssi_array[] = { + RSND_MOD_SSIM1, + RSND_MOD_SSIM2, + RSND_MOD_SSIM3, + }; + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *pos; + u32 val; + int i, shift; + + i = rsnd_mod_id(ssi_mod); + + /* output all same SSI as default */ + val = i << 16 | + i << 20 | + i << 24 | + i << 28 | + i; + + for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) { + shift = (i * 4) + 16; + val = (val & ~(0xF << shift)) | + rsnd_mod_id(pos) << shift; + } + + switch (hdmi) { + case RSND_SSI_HDMI_PORT0: + rsnd_mod_write(mod, HDMI0_SEL, val); + break; + case RSND_SSI_HDMI_PORT1: + rsnd_mod_write(mod, HDMI1_SEL, val); + break; + } + } + return 0; } diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index 76b2ab8c2b4a..4a22aadac294 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -441,7 +441,7 @@ static int siu_dai_put_volume(struct snd_kcontrol *kctrl, return 0; } -static struct snd_kcontrol_new playback_controls = { +static const struct snd_kcontrol_new playback_controls = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .index = 0, @@ -451,7 +451,7 @@ static struct snd_kcontrol_new playback_controls = { .private_value = VOLUME_PLAYBACK, }; -static struct snd_kcontrol_new capture_controls = { +static const struct snd_kcontrol_new capture_controls = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Capture Volume", .index = 0, diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index bfd71b873ca2..206f36bf43e8 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -81,7 +81,8 @@ out: static int soc_compr_open_fe(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *fe = cstream->private_data; - struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; + struct snd_pcm_substream *fe_substream = + fe->pcm->streams[cstream->direction].substream; struct snd_soc_platform *platform = fe->platform; struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; @@ -467,7 +468,8 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, struct snd_compr_params *params) { struct snd_soc_pcm_runtime *fe = cstream->private_data; - struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; + struct snd_pcm_substream *fe_substream = + fe->pcm->streams[cstream->direction].substream; struct snd_soc_platform *platform = fe->platform; struct snd_soc_dai *cpu_dai = fe->cpu_dai; int ret = 0, stream; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 754e3ef8d7ae..921622a01944 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -34,6 +34,7 @@ #include <linux/ctype.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/dmi.h> #include <sound/core.h> #include <sound/jack.h> @@ -68,6 +69,20 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); +/* If a DMI filed contain strings in this blacklist (e.g. + * "Type2 - Board Manufacturer" or "Type1 - TBD by OEM"), it will be taken + * as invalid and dropped when setting the card long name from DMI info. + */ +static const char * const dmi_blacklist[] = { + "To be filled by OEM", + "TBD by OEM", + "Default String", + "Board Manufacturer", + "Board Vendor Name", + "Board Product Name", + NULL, /* terminator */ +}; + /* returns the minimum number of bytes needed to represent * a particular given value */ static int min_bytes_needed(unsigned long val) @@ -1933,6 +1948,22 @@ static void cleanup_dmi_name(char *name) name[j] = '\0'; } +/* Check if a DMI field is valid, i.e. not containing any string + * in the black list. + */ +static int is_dmi_valid(const char *field) +{ + int i = 0; + + while (dmi_blacklist[i]) { + if (strstr(field, dmi_blacklist[i])) + return 0; + i++; + } + + return 1; +} + /** * snd_soc_set_dmi_name() - Register DMI names to card * @card: The card to register DMI names @@ -1975,17 +2006,18 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) /* make up dmi long name as: vendor.product.version.board */ vendor = dmi_get_system_info(DMI_BOARD_VENDOR); - if (!vendor) { + if (!vendor || !is_dmi_valid(vendor)) { dev_warn(card->dev, "ASoC: no DMI vendor name!\n"); return 0; } + snprintf(card->dmi_longname, sizeof(card->snd_card->longname), "%s", vendor); cleanup_dmi_name(card->dmi_longname); product = dmi_get_system_info(DMI_PRODUCT_NAME); - if (product) { + if (product && is_dmi_valid(product)) { len = strlen(card->dmi_longname); snprintf(card->dmi_longname + len, longname_buf_size - len, @@ -1999,7 +2031,7 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) * name in the product version field */ product_version = dmi_get_system_info(DMI_PRODUCT_VERSION); - if (product_version) { + if (product_version && is_dmi_valid(product_version)) { len = strlen(card->dmi_longname); snprintf(card->dmi_longname + len, longname_buf_size - len, @@ -2012,7 +2044,7 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) } board = dmi_get_system_info(DMI_BOARD_NAME); - if (board) { + if (board && is_dmi_valid(board)) { len = strlen(card->dmi_longname); snprintf(card->dmi_longname + len, longname_buf_size - len, @@ -3961,11 +3993,15 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, prefix = ""; /* - * check "[prefix]format = xxx" + * check "dai-format = xxx" + * or "[prefix]format = xxx" * SND_SOC_DAIFMT_FORMAT_MASK area */ - snprintf(prop, sizeof(prop), "%sformat", prefix); - ret = of_property_read_string(np, prop, &str); + ret = of_property_read_string(np, "dai-format", &str); + if (ret < 0) { + snprintf(prop, sizeof(prop), "%sformat", prefix); + ret = of_property_read_string(np, prop, &str); + } if (ret == 0) { for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { if (strcmp(str, of_fmt_table[i].name) == 0) { @@ -4045,6 +4081,42 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); +int snd_soc_get_dai_id(struct device_node *ep) +{ + struct snd_soc_component *pos; + struct device_node *node; + int ret; + + node = of_graph_get_port_parent(ep); + + /* + * For example HDMI case, HDMI has video/sound port, + * but ALSA SoC needs sound port number only. + * Thus counting HDMI DT port/endpoint doesn't work. + * Then, it should have .of_xlate_dai_id + */ + ret = -ENOTSUPP; + mutex_lock(&client_mutex); + list_for_each_entry(pos, &component_list, list) { + struct device_node *component_of_node = pos->dev->of_node; + + if (!component_of_node && pos->dev->parent) + component_of_node = pos->dev->parent->of_node; + + if (component_of_node != node) + continue; + + if (pos->driver->of_xlate_dai_id) + ret = pos->driver->of_xlate_dai_id(pos, ep); + + break; + } + mutex_unlock(&client_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_get_dai_id); + int snd_soc_get_dai_name(struct of_phandle_args *args, const char **dai_name) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index efc5831f205d..dcc5ece08668 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2743,8 +2743,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (platform->driver->ops) { rtd->ops.ack = platform->driver->ops->ack; - rtd->ops.copy = platform->driver->ops->copy; - rtd->ops.silence = platform->driver->ops->silence; + rtd->ops.copy_user = platform->driver->ops->copy_user; + rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; + rtd->ops.fill_silence = platform->driver->ops->fill_silence; rtd->ops.page = platform->driver->ops->page; rtd->ops.mmap = platform->driver->ops->mmap; } diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 002772e3ba2c..dd471d2c0266 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -242,6 +242,14 @@ static const struct soc_tplg_map dapm_map[] = { {SND_SOC_TPLG_DAPM_DAI_IN, snd_soc_dapm_dai_in}, {SND_SOC_TPLG_DAPM_DAI_OUT, snd_soc_dapm_dai_out}, {SND_SOC_TPLG_DAPM_DAI_LINK, snd_soc_dapm_dai_link}, + {SND_SOC_TPLG_DAPM_BUFFER, snd_soc_dapm_buffer}, + {SND_SOC_TPLG_DAPM_SCHEDULER, snd_soc_dapm_scheduler}, + {SND_SOC_TPLG_DAPM_EFFECT, snd_soc_dapm_effect}, + {SND_SOC_TPLG_DAPM_SIGGEN, snd_soc_dapm_siggen}, + {SND_SOC_TPLG_DAPM_SRC, snd_soc_dapm_src}, + {SND_SOC_TPLG_DAPM_ASRC, snd_soc_dapm_asrc}, + {SND_SOC_TPLG_DAPM_ENCODER, snd_soc_dapm_encoder}, + {SND_SOC_TPLG_DAPM_DECODER, snd_soc_dapm_decoder}, }; static int tplc_chan_get_reg(struct soc_tplg *tplg, @@ -344,6 +352,17 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg, return 0; } +/* optionally pass new dynamic widget to component driver. This is mainly for + * external widgets where we can assign private data/ops */ +static int soc_tplg_widget_ready(struct soc_tplg *tplg, + struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) +{ + if (tplg->comp && tplg->ops && tplg->ops->widget_ready) + return tplg->ops->widget_ready(tplg->comp, w, tplg_w); + + return 0; +} + /* pass DAI configurations to component driver for extra initialization */ static int soc_tplg_dai_load(struct soc_tplg *tplg, struct snd_soc_dai_driver *dai_drv) @@ -1152,7 +1171,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, return -EINVAL; } - dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes\n", count); + dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count, + hdr->index); for (i = 0; i < count; i++) { elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos; @@ -1465,6 +1485,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, if (template.id < 0) return template.id; + /* strings are allocated here, but used and freed by the widget */ template.name = kstrdup(w->name, GFP_KERNEL); if (!template.name) return -ENOMEM; @@ -1577,11 +1598,17 @@ widget: widget->dobj.widget.kcontrol_type = kcontrol_type; widget->dobj.ops = tplg->ops; widget->dobj.index = tplg->index; - kfree(template.sname); - kfree(template.name); list_add(&widget->dobj.list, &tplg->comp->dobj_list); + + ret = soc_tplg_widget_ready(tplg, widget, w); + if (ret < 0) + goto ready_err; + return 0; +ready_err: + snd_soc_tplg_widget_remove(widget); + snd_soc_dapm_free_widget(widget); hdr_err: kfree(template.sname); err: @@ -1628,7 +1655,7 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) */ if (!card || !card->instantiated) { dev_warn(tplg->dev, "ASoC: Parent card not yet available," - "Do not add new widgets now\n"); + " widget card binding deferred\n"); return 0; } @@ -2363,7 +2390,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, /* check for matching ID */ if (hdr->index != tplg->req_index && - hdr->index != SND_SOC_TPLG_INDEX_ALL) + tplg->req_index != SND_SOC_TPLG_INDEX_ALL) return 0; tplg->index = hdr->index; diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index 972970f0890a..3398e6c57f37 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -1,8 +1,31 @@ -menuconfig SND_SOC_STM32 - tristate "STMicroelectronics STM32 SOC audio support" +menu "STMicroelectronics STM32 SOC audio support" + +config SND_SOC_STM32_SAI + tristate "STM32 SAI interface (Serial Audio Interface) support" depends on ARCH_STM32 || COMPILE_TEST depends on SND_SOC select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO help - Say Y if you want to enable ASoC-support for STM32 + Say Y if you want to enable SAI for STM32 + +config SND_SOC_STM32_I2S + tristate "STM32 I2S interface (SPI/I2S block) support" + depends on ARCH_STM32 || COMPILE_TEST + depends on SND_SOC + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Say Y if you want to enable I2S for STM32 + +config SND_SOC_STM32_SPDIFRX + tristate "STM32 S/PDIF receiver (SPDIFRX) support" + depends on ARCH_STM32 || COMPILE_TEST + depends on SND_SOC + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + select SND_SOC_SPDIF + help + Say Y if you want to enable S/PDIF capture for STM32 + +endmenu diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile index e466a4759698..4ed22e648a9a 100644 --- a/sound/soc/stm/Makefile +++ b/sound/soc/stm/Makefile @@ -1,6 +1,14 @@ # SAI snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o -obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai-sub.o +obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai-sub.o snd-soc-stm32-sai-objs := stm32_sai.o -obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai.o +obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai.o + +# I2S +snd-soc-stm32-i2s-objs := stm32_i2s.o +obj-$(CONFIG_SND_SOC_STM32_I2S) += snd-soc-stm32-i2s.o + +# SPDIFRX +snd-soc-stm32-spdifrx-objs := stm32_spdifrx.o +obj-$(CONFIG_SND_SOC_STM32_SPDIFRX) += snd-soc-stm32-spdifrx.o diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c new file mode 100644 index 000000000000..8052629a89df --- /dev/null +++ b/sound/soc/stm/stm32_i2s.c @@ -0,0 +1,946 @@ +/* + * STM32 ALSA SoC Digital Audio Interface (I2S) driver. + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. + * + * License terms: GPL V2.0. + * + * 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. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/spinlock.h> + +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +#define STM32_I2S_CR1_REG 0x0 +#define STM32_I2S_CFG1_REG 0x08 +#define STM32_I2S_CFG2_REG 0x0C +#define STM32_I2S_IER_REG 0x10 +#define STM32_I2S_SR_REG 0x14 +#define STM32_I2S_IFCR_REG 0x18 +#define STM32_I2S_TXDR_REG 0X20 +#define STM32_I2S_RXDR_REG 0x30 +#define STM32_I2S_CGFR_REG 0X50 + +/* Bit definition for SPI2S_CR1 register */ +#define I2S_CR1_SPE BIT(0) +#define I2S_CR1_CSTART BIT(9) +#define I2S_CR1_CSUSP BIT(10) +#define I2S_CR1_HDDIR BIT(11) +#define I2S_CR1_SSI BIT(12) +#define I2S_CR1_CRC33_17 BIT(13) +#define I2S_CR1_RCRCI BIT(14) +#define I2S_CR1_TCRCI BIT(15) + +/* Bit definition for SPI_CFG2 register */ +#define I2S_CFG2_IOSWP_SHIFT 15 +#define I2S_CFG2_IOSWP BIT(I2S_CFG2_IOSWP_SHIFT) +#define I2S_CFG2_LSBFRST BIT(23) +#define I2S_CFG2_AFCNTR BIT(31) + +/* Bit definition for SPI_CFG1 register */ +#define I2S_CFG1_FTHVL_SHIFT 5 +#define I2S_CFG1_FTHVL_MASK GENMASK(8, I2S_CFG1_FTHVL_SHIFT) +#define I2S_CFG1_FTHVL_SET(x) ((x) << I2S_CFG1_FTHVL_SHIFT) + +#define I2S_CFG1_TXDMAEN BIT(15) +#define I2S_CFG1_RXDMAEN BIT(14) + +/* Bit definition for SPI2S_IER register */ +#define I2S_IER_RXPIE BIT(0) +#define I2S_IER_TXPIE BIT(1) +#define I2S_IER_DPXPIE BIT(2) +#define I2S_IER_EOTIE BIT(3) +#define I2S_IER_TXTFIE BIT(4) +#define I2S_IER_UDRIE BIT(5) +#define I2S_IER_OVRIE BIT(6) +#define I2S_IER_CRCEIE BIT(7) +#define I2S_IER_TIFREIE BIT(8) +#define I2S_IER_MODFIE BIT(9) +#define I2S_IER_TSERFIE BIT(10) + +/* Bit definition for SPI2S_SR register */ +#define I2S_SR_RXP BIT(0) +#define I2S_SR_TXP BIT(1) +#define I2S_SR_DPXP BIT(2) +#define I2S_SR_EOT BIT(3) +#define I2S_SR_TXTF BIT(4) +#define I2S_SR_UDR BIT(5) +#define I2S_SR_OVR BIT(6) +#define I2S_SR_CRCERR BIT(7) +#define I2S_SR_TIFRE BIT(8) +#define I2S_SR_MODF BIT(9) +#define I2S_SR_TSERF BIT(10) +#define I2S_SR_SUSP BIT(11) +#define I2S_SR_TXC BIT(12) +#define I2S_SR_RXPLVL GENMASK(14, 13) +#define I2S_SR_RXWNE BIT(15) + +#define I2S_SR_MASK GENMASK(15, 0) + +/* Bit definition for SPI_IFCR register */ +#define I2S_IFCR_EOTC BIT(3) +#define I2S_IFCR_TXTFC BIT(4) +#define I2S_IFCR_UDRC BIT(5) +#define I2S_IFCR_OVRC BIT(6) +#define I2S_IFCR_CRCEC BIT(7) +#define I2S_IFCR_TIFREC BIT(8) +#define I2S_IFCR_MODFC BIT(9) +#define I2S_IFCR_TSERFC BIT(10) +#define I2S_IFCR_SUSPC BIT(11) + +#define I2S_IFCR_MASK GENMASK(11, 3) + +/* Bit definition for SPI_I2SCGFR register */ +#define I2S_CGFR_I2SMOD BIT(0) + +#define I2S_CGFR_I2SCFG_SHIFT 1 +#define I2S_CGFR_I2SCFG_MASK GENMASK(3, I2S_CGFR_I2SCFG_SHIFT) +#define I2S_CGFR_I2SCFG_SET(x) ((x) << I2S_CGFR_I2SCFG_SHIFT) + +#define I2S_CGFR_I2SSTD_SHIFT 4 +#define I2S_CGFR_I2SSTD_MASK GENMASK(5, I2S_CGFR_I2SSTD_SHIFT) +#define I2S_CGFR_I2SSTD_SET(x) ((x) << I2S_CGFR_I2SSTD_SHIFT) + +#define I2S_CGFR_PCMSYNC BIT(7) + +#define I2S_CGFR_DATLEN_SHIFT 8 +#define I2S_CGFR_DATLEN_MASK GENMASK(9, I2S_CGFR_DATLEN_SHIFT) +#define I2S_CGFR_DATLEN_SET(x) ((x) << I2S_CGFR_DATLEN_SHIFT) + +#define I2S_CGFR_CHLEN_SHIFT 10 +#define I2S_CGFR_CHLEN BIT(I2S_CGFR_CHLEN_SHIFT) +#define I2S_CGFR_CKPOL BIT(11) +#define I2S_CGFR_FIXCH BIT(12) +#define I2S_CGFR_WSINV BIT(13) +#define I2S_CGFR_DATFMT BIT(14) + +#define I2S_CGFR_I2SDIV_SHIFT 16 +#define I2S_CGFR_I2SDIV_BIT_H 23 +#define I2S_CGFR_I2SDIV_MASK GENMASK(I2S_CGFR_I2SDIV_BIT_H,\ + I2S_CGFR_I2SDIV_SHIFT) +#define I2S_CGFR_I2SDIV_SET(x) ((x) << I2S_CGFR_I2SDIV_SHIFT) +#define I2S_CGFR_I2SDIV_MAX ((1 << (I2S_CGFR_I2SDIV_BIT_H -\ + I2S_CGFR_I2SDIV_SHIFT)) - 1) + +#define I2S_CGFR_ODD_SHIFT 24 +#define I2S_CGFR_ODD BIT(I2S_CGFR_ODD_SHIFT) +#define I2S_CGFR_MCKOE BIT(25) + +enum i2s_master_mode { + I2S_MS_NOT_SET, + I2S_MS_MASTER, + I2S_MS_SLAVE, +}; + +enum i2s_mode { + I2S_I2SMOD_TX_SLAVE, + I2S_I2SMOD_RX_SLAVE, + I2S_I2SMOD_TX_MASTER, + I2S_I2SMOD_RX_MASTER, + I2S_I2SMOD_FD_SLAVE, + I2S_I2SMOD_FD_MASTER, +}; + +enum i2s_fifo_th { + I2S_FIFO_TH_NONE, + I2S_FIFO_TH_ONE_QUARTER, + I2S_FIFO_TH_HALF, + I2S_FIFO_TH_THREE_QUARTER, + I2S_FIFO_TH_FULL, +}; + +enum i2s_std { + I2S_STD_I2S, + I2S_STD_LEFT_J, + I2S_STD_RIGHT_J, + I2S_STD_DSP, +}; + +enum i2s_datlen { + I2S_I2SMOD_DATLEN_16, + I2S_I2SMOD_DATLEN_24, + I2S_I2SMOD_DATLEN_32, +}; + +#define STM32_I2S_DAI_NAME_SIZE 20 +#define STM32_I2S_FIFO_SIZE 16 + +#define STM32_I2S_IS_MASTER(x) ((x)->ms_flg == I2S_MS_MASTER) +#define STM32_I2S_IS_SLAVE(x) ((x)->ms_flg == I2S_MS_SLAVE) + +/** + * @regmap_conf: I2S register map configuration pointer + * @egmap: I2S register map pointer + * @pdev: device data pointer + * @dai_drv: DAI driver pointer + * @dma_data_tx: dma configuration data for tx channel + * @dma_data_rx: dma configuration data for tx channel + * @substream: PCM substream data pointer + * @i2sclk: kernel clock feeding the I2S clock generator + * @pclk: peripheral clock driving bus interface + * @x8kclk: I2S parent clock for sampling frequencies multiple of 8kHz + * @x11kclk: I2S parent clock for sampling frequencies multiple of 11kHz + * @base: mmio register base virtual address + * @phys_addr: I2S registers physical base address + * @lock_fd: lock to manage race conditions in full duplex mode + * @dais_name: DAI name + * @mclk_rate: master clock frequency (Hz) + * @fmt: DAI protocol + * @refcount: keep count of opened streams on I2S + * @ms_flg: master mode flag. + */ +struct stm32_i2s_data { + const struct regmap_config *regmap_conf; + struct regmap *regmap; + struct platform_device *pdev; + struct snd_soc_dai_driver *dai_drv; + struct snd_dmaengine_dai_dma_data dma_data_tx; + struct snd_dmaengine_dai_dma_data dma_data_rx; + struct snd_pcm_substream *substream; + struct clk *i2sclk; + struct clk *pclk; + struct clk *x8kclk; + struct clk *x11kclk; + void __iomem *base; + dma_addr_t phys_addr; + spinlock_t lock_fd; /* Manage race conditions for full duplex */ + char dais_name[STM32_I2S_DAI_NAME_SIZE]; + unsigned int mclk_rate; + unsigned int fmt; + int refcount; + int ms_flg; +}; + +static irqreturn_t stm32_i2s_isr(int irq, void *devid) +{ + struct stm32_i2s_data *i2s = (struct stm32_i2s_data *)devid; + struct platform_device *pdev = i2s->pdev; + u32 sr, ier; + unsigned long flags; + int err = 0; + + regmap_read(i2s->regmap, STM32_I2S_SR_REG, &sr); + regmap_read(i2s->regmap, STM32_I2S_IER_REG, &ier); + + flags = sr & ier; + if (!flags) { + dev_dbg(&pdev->dev, "Spurious IRQ sr=0x%08x, ier=0x%08x\n", + sr, ier); + return IRQ_NONE; + } + + regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, flags); + + if (flags & I2S_SR_OVR) { + dev_dbg(&pdev->dev, "Overrun\n"); + err = 1; + } + + if (flags & I2S_SR_UDR) { + dev_dbg(&pdev->dev, "Underrun\n"); + err = 1; + } + + if (flags & I2S_SR_TIFRE) + dev_dbg(&pdev->dev, "Frame error\n"); + + if (err) + snd_pcm_stop_xrun(i2s->substream); + + return IRQ_HANDLED; +} + +static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case STM32_I2S_CR1_REG: + case STM32_I2S_CFG1_REG: + case STM32_I2S_CFG2_REG: + case STM32_I2S_IER_REG: + case STM32_I2S_SR_REG: + case STM32_I2S_IFCR_REG: + case STM32_I2S_TXDR_REG: + case STM32_I2S_RXDR_REG: + case STM32_I2S_CGFR_REG: + return true; + default: + return false; + } +} + +static bool stm32_i2s_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case STM32_I2S_TXDR_REG: + case STM32_I2S_RXDR_REG: + return true; + default: + return false; + } +} + +static bool stm32_i2s_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case STM32_I2S_CR1_REG: + case STM32_I2S_CFG1_REG: + case STM32_I2S_CFG2_REG: + case STM32_I2S_IER_REG: + case STM32_I2S_IFCR_REG: + case STM32_I2S_TXDR_REG: + case STM32_I2S_CGFR_REG: + return true; + default: + return false; + } +} + +static int stm32_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + u32 cgfr; + u32 cgfr_mask = I2S_CGFR_I2SSTD_MASK | I2S_CGFR_CKPOL | + I2S_CGFR_WSINV | I2S_CGFR_I2SCFG_MASK; + + dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); + + /* + * winv = 0 : default behavior (high/low) for all standards + * ckpol = 0 for all standards. + */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_I2S); + break; + case SND_SOC_DAIFMT_MSB: + cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_LEFT_J); + break; + case SND_SOC_DAIFMT_LSB: + cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_RIGHT_J); + break; + case SND_SOC_DAIFMT_DSP_A: + cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_DSP); + break; + /* DSP_B not mapped on I2S PCM long format. 1 bit offset does not fit */ + default: + dev_err(cpu_dai->dev, "Unsupported protocol %#x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + /* DAI clock strobing */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + cgfr |= I2S_CGFR_CKPOL; + break; + case SND_SOC_DAIFMT_NB_IF: + cgfr |= I2S_CGFR_WSINV; + break; + case SND_SOC_DAIFMT_IB_IF: + cgfr |= I2S_CGFR_CKPOL; + cgfr |= I2S_CGFR_WSINV; + break; + default: + dev_err(cpu_dai->dev, "Unsupported strobing %#x\n", + fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + /* DAI clock master masks */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + i2s->ms_flg = I2S_MS_SLAVE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + i2s->ms_flg = I2S_MS_MASTER; + break; + default: + dev_err(cpu_dai->dev, "Unsupported mode %#x\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + i2s->fmt = fmt; + return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + cgfr_mask, cgfr); +} + +static int stm32_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + + dev_dbg(cpu_dai->dev, "I2S MCLK frequency is %uHz\n", freq); + + if ((dir == SND_SOC_CLOCK_OUT) && STM32_I2S_IS_MASTER(i2s)) { + i2s->mclk_rate = freq; + + /* Enable master clock if master mode and mclk-fs are set */ + return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + I2S_CGFR_MCKOE, I2S_CGFR_MCKOE); + } + + return 0; +} + +static int stm32_i2s_configure_clock(struct snd_soc_dai *cpu_dai, + struct snd_pcm_hw_params *params) +{ + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long i2s_clock_rate; + unsigned int tmp, div, real_div, nb_bits, frame_len; + unsigned int rate = params_rate(params); + int ret; + u32 cgfr, cgfr_mask; + bool odd; + + if (!(rate % 11025)) + clk_set_parent(i2s->i2sclk, i2s->x11kclk); + else + clk_set_parent(i2s->i2sclk, i2s->x8kclk); + i2s_clock_rate = clk_get_rate(i2s->i2sclk); + + /* + * mckl = mclk_ratio x ws + * i2s mode : mclk_ratio = 256 + * dsp mode : mclk_ratio = 128 + * + * mclk on + * i2s mode : div = i2s_clk / (mclk_ratio * ws) + * dsp mode : div = i2s_clk / (mclk_ratio * ws) + * mclk off + * i2s mode : div = i2s_clk / (nb_bits x ws) + * dsp mode : div = i2s_clk / (nb_bits x ws) + */ + if (i2s->mclk_rate) { + tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, i2s->mclk_rate); + } else { + frame_len = 32; + if ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == + SND_SOC_DAIFMT_DSP_A) + frame_len = 16; + + /* master clock not enabled */ + ret = regmap_read(i2s->regmap, STM32_I2S_CGFR_REG, &cgfr); + if (ret < 0) + return ret; + + nb_bits = frame_len * ((cgfr & I2S_CGFR_CHLEN) + 1); + tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, (nb_bits * rate)); + } + + /* Check the parity of the divider */ + odd = tmp & 0x1; + + /* Compute the div prescaler */ + div = tmp >> 1; + + cgfr = I2S_CGFR_I2SDIV_SET(div) | (odd << I2S_CGFR_ODD_SHIFT); + cgfr_mask = I2S_CGFR_I2SDIV_MASK | I2S_CGFR_ODD; + + real_div = ((2 * div) + odd); + dev_dbg(cpu_dai->dev, "I2S clk: %ld, SCLK: %d\n", + i2s_clock_rate, rate); + dev_dbg(cpu_dai->dev, "Divider: 2*%d(div)+%d(odd) = %d\n", + div, odd, real_div); + + if (((div == 1) && odd) || (div > I2S_CGFR_I2SDIV_MAX)) { + dev_err(cpu_dai->dev, "Wrong divider setting\n"); + return -EINVAL; + } + + if (!div && !odd) + dev_warn(cpu_dai->dev, "real divider forced to 1\n"); + + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + cgfr_mask, cgfr); + if (ret < 0) + return ret; + + /* Set bitclock and frameclock to their inactive state */ + return regmap_update_bits(i2s->regmap, STM32_I2S_CFG2_REG, + I2S_CFG2_AFCNTR, I2S_CFG2_AFCNTR); +} + +static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, + struct snd_pcm_hw_params *params, + struct snd_pcm_substream *substream) +{ + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + int format = params_width(params); + u32 cfgr, cfgr_mask, cfg1, cfg1_mask; + unsigned int fthlv; + int ret; + + if ((params_channels(params) == 1) && + ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A)) { + dev_err(cpu_dai->dev, "Mono mode supported only by DSP_A\n"); + return -EINVAL; + } + + switch (format) { + case 16: + cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16); + cfgr_mask = I2S_CGFR_DATLEN_MASK; + break; + case 32: + cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) | + I2S_CGFR_CHLEN; + cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN; + break; + default: + dev_err(cpu_dai->dev, "Unexpected format %d", format); + return -EINVAL; + } + + if (STM32_I2S_IS_SLAVE(i2s)) { + cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_SLAVE); + + /* As data length is either 16 or 32 bits, fixch always set */ + cfgr |= I2S_CGFR_FIXCH; + cfgr_mask |= I2S_CGFR_FIXCH; + } else { + cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_MASTER); + } + cfgr_mask |= I2S_CGFR_I2SCFG_MASK; + + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + cfgr_mask, cfgr); + if (ret < 0) + return ret; + + cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; + cfg1_mask = cfg1; + + fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4; + cfg1 |= I2S_CFG1_FTHVL_SET(fthlv - 1); + cfg1_mask |= I2S_CFG1_FTHVL_MASK; + + return regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, + cfg1_mask, cfg1); +} + +static int stm32_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + + i2s->substream = substream; + + spin_lock(&i2s->lock_fd); + i2s->refcount++; + spin_unlock(&i2s->lock_fd); + + return regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, I2S_IFCR_MASK); +} + +static int stm32_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + int ret; + + ret = stm32_i2s_configure(cpu_dai, params, substream); + if (ret < 0) { + dev_err(cpu_dai->dev, "Configuration returned error %d\n", ret); + return ret; + } + + if (STM32_I2S_IS_MASTER(i2s)) + ret = stm32_i2s_configure_clock(cpu_dai, params); + + return ret; +} + +static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + bool playback_flg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + u32 cfg1_mask, ier; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* Enable i2s */ + dev_dbg(cpu_dai->dev, "start I2S\n"); + + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, + I2S_CR1_SPE, I2S_CR1_SPE); + if (ret < 0) { + dev_err(cpu_dai->dev, "Error %d enabling I2S\n", ret); + return ret; + } + + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, + I2S_CR1_CSTART, I2S_CR1_CSTART); + if (ret < 0) { + dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret); + return ret; + } + + regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, + I2S_IFCR_MASK, I2S_IFCR_MASK); + + if (playback_flg) { + ier = I2S_IER_UDRIE; + } else { + ier = I2S_IER_OVRIE; + + spin_lock(&i2s->lock_fd); + if (i2s->refcount == 1) + /* dummy write to trigger capture */ + regmap_write(i2s->regmap, + STM32_I2S_TXDR_REG, 0); + spin_unlock(&i2s->lock_fd); + } + + if (STM32_I2S_IS_SLAVE(i2s)) + ier |= I2S_IER_TIFREIE; + + regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, ier, ier); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (playback_flg) + regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, + I2S_IER_UDRIE, + (unsigned int)~I2S_IER_UDRIE); + else + regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, + I2S_IER_OVRIE, + (unsigned int)~I2S_IER_OVRIE); + + spin_lock(&i2s->lock_fd); + i2s->refcount--; + if (i2s->refcount) { + spin_unlock(&i2s->lock_fd); + break; + } + spin_unlock(&i2s->lock_fd); + + dev_dbg(cpu_dai->dev, "stop I2S\n"); + + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, + I2S_CR1_SPE, 0); + if (ret < 0) { + dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret); + return ret; + } + + cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; + regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, + cfg1_mask, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void stm32_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + + i2s->substream = NULL; + + regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE); +} + +static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct stm32_i2s_data *i2s = dev_get_drvdata(cpu_dai->dev); + struct snd_dmaengine_dai_dma_data *dma_data_tx = &i2s->dma_data_tx; + struct snd_dmaengine_dai_dma_data *dma_data_rx = &i2s->dma_data_rx; + + /* Buswidth will be set by framework */ + dma_data_tx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; + dma_data_tx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_TXDR_REG; + dma_data_tx->maxburst = 1; + dma_data_rx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; + dma_data_rx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_RXDR_REG; + dma_data_rx->maxburst = 1; + + snd_soc_dai_init_dma_data(cpu_dai, dma_data_tx, dma_data_rx); + + return 0; +} + +static const struct regmap_config stm32_h7_i2s_regmap_conf = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = STM32_I2S_CGFR_REG, + .readable_reg = stm32_i2s_readable_reg, + .volatile_reg = stm32_i2s_volatile_reg, + .writeable_reg = stm32_i2s_writeable_reg, + .fast_io = true, +}; + +static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { + .set_sysclk = stm32_i2s_set_sysclk, + .set_fmt = stm32_i2s_set_dai_fmt, + .startup = stm32_i2s_startup, + .hw_params = stm32_i2s_hw_params, + .trigger = stm32_i2s_trigger, + .shutdown = stm32_i2s_shutdown, +}; + +static const struct snd_pcm_hardware stm32_i2s_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, + .buffer_bytes_max = 8 * PAGE_SIZE, + .period_bytes_max = 2048, + .periods_min = 2, + .periods_max = 8, +}; + +static const struct snd_dmaengine_pcm_config stm32_i2s_pcm_config = { + .pcm_hardware = &stm32_i2s_pcm_hw, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .prealloc_buffer_size = PAGE_SIZE * 8, +}; + +static const struct snd_soc_component_driver stm32_i2s_component = { + .name = "stm32-i2s", +}; + +static void stm32_i2s_dai_init(struct snd_soc_pcm_stream *stream, + char *stream_name) +{ + stream->stream_name = stream_name; + stream->channels_min = 1; + stream->channels_max = 2; + stream->rates = SNDRV_PCM_RATE_8000_192000; + stream->formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE; +} + +static int stm32_i2s_dais_init(struct platform_device *pdev, + struct stm32_i2s_data *i2s) +{ + struct snd_soc_dai_driver *dai_ptr; + + dai_ptr = devm_kzalloc(&pdev->dev, sizeof(struct snd_soc_dai_driver), + GFP_KERNEL); + if (!dai_ptr) + return -ENOMEM; + + snprintf(i2s->dais_name, STM32_I2S_DAI_NAME_SIZE, + "%s", dev_name(&pdev->dev)); + + dai_ptr->probe = stm32_i2s_dai_probe; + dai_ptr->ops = &stm32_i2s_pcm_dai_ops; + dai_ptr->name = i2s->dais_name; + dai_ptr->id = 1; + stm32_i2s_dai_init(&dai_ptr->playback, "playback"); + stm32_i2s_dai_init(&dai_ptr->capture, "capture"); + i2s->dai_drv = dai_ptr; + + return 0; +} + +static const struct of_device_id stm32_i2s_ids[] = { + { + .compatible = "st,stm32h7-i2s", + .data = &stm32_h7_i2s_regmap_conf + }, + {}, +}; + +static int stm32_i2s_parse_dt(struct platform_device *pdev, + struct stm32_i2s_data *i2s) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id; + struct reset_control *rst; + struct resource *res; + int irq, ret; + + if (!np) + return -ENODEV; + + of_id = of_match_device(stm32_i2s_ids, &pdev->dev); + if (of_id) + i2s->regmap_conf = (const struct regmap_config *)of_id->data; + else + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2s->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2s->base)) + return PTR_ERR(i2s->base); + + i2s->phys_addr = res->start; + + /* Get clocks */ + i2s->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(i2s->pclk)) { + dev_err(&pdev->dev, "Could not get pclk\n"); + return PTR_ERR(i2s->pclk); + } + + i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk"); + if (IS_ERR(i2s->i2sclk)) { + dev_err(&pdev->dev, "Could not get i2sclk\n"); + return PTR_ERR(i2s->i2sclk); + } + + i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k"); + if (IS_ERR(i2s->x8kclk)) { + dev_err(&pdev->dev, "missing x8k parent clock\n"); + return PTR_ERR(i2s->x8kclk); + } + + i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k"); + if (IS_ERR(i2s->x11kclk)) { + dev_err(&pdev->dev, "missing x11k parent clock\n"); + return PTR_ERR(i2s->x11kclk); + } + + /* Get irqs */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); + return -ENOENT; + } + + ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, IRQF_ONESHOT, + dev_name(&pdev->dev), i2s); + if (ret) { + dev_err(&pdev->dev, "irq request returned %d\n", ret); + return ret; + } + + /* Reset */ + rst = devm_reset_control_get(&pdev->dev, NULL); + if (!IS_ERR(rst)) { + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); + } + + return 0; +} + +static int stm32_i2s_probe(struct platform_device *pdev) +{ + struct stm32_i2s_data *i2s; + int ret; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + ret = stm32_i2s_parse_dt(pdev, i2s); + if (ret) + return ret; + + i2s->pdev = pdev; + i2s->ms_flg = I2S_MS_NOT_SET; + spin_lock_init(&i2s->lock_fd); + platform_set_drvdata(pdev, i2s); + + ret = stm32_i2s_dais_init(pdev, i2s); + if (ret) + return ret; + + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->base, + i2s->regmap_conf); + if (IS_ERR(i2s->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(i2s->regmap); + } + + ret = clk_prepare_enable(i2s->pclk); + if (ret) { + dev_err(&pdev->dev, "Enable pclk failed: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(i2s->i2sclk); + if (ret) { + dev_err(&pdev->dev, "Enable i2sclk failed: %d\n", ret); + goto err_pclk_disable; + } + + ret = devm_snd_soc_register_component(&pdev->dev, &stm32_i2s_component, + i2s->dai_drv, 1); + if (ret) + goto err_clocks_disable; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, + &stm32_i2s_pcm_config, 0); + if (ret) + goto err_clocks_disable; + + /* Set SPI/I2S in i2s mode */ + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); + if (ret) + goto err_clocks_disable; + + return ret; + +err_clocks_disable: + clk_disable_unprepare(i2s->i2sclk); +err_pclk_disable: + clk_disable_unprepare(i2s->pclk); + + return ret; +} + +static int stm32_i2s_remove(struct platform_device *pdev) +{ + struct stm32_i2s_data *i2s = platform_get_drvdata(pdev); + + clk_disable_unprepare(i2s->i2sclk); + clk_disable_unprepare(i2s->pclk); + + return 0; +} + +MODULE_DEVICE_TABLE(of, stm32_i2s_ids); + +static struct platform_driver stm32_i2s_driver = { + .driver = { + .name = "st,stm32-i2s", + .of_match_table = stm32_i2s_ids, + }, + .probe = stm32_i2s_probe, + .remove = stm32_i2s_remove, +}; + +module_platform_driver(stm32_i2s_driver); + +MODULE_DESCRIPTION("STM32 Soc i2s Interface"); +MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>"); +MODULE_ALIAS("platform:stm32-i2s"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 2a27a26bf7a1..f7713314913b 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -27,8 +27,17 @@ #include "stm32_sai.h" +static const struct stm32_sai_conf stm32_sai_conf_f4 = { + .version = SAI_STM32F4, +}; + +static const struct stm32_sai_conf stm32_sai_conf_h7 = { + .version = SAI_STM32H7, +}; + static const struct of_device_id stm32_sai_ids[] = { - { .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 }, + { .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 }, + { .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 }, {} }; @@ -52,7 +61,7 @@ static int stm32_sai_probe(struct platform_device *pdev) of_id = of_match_device(stm32_sai_ids, &pdev->dev); if (of_id) - sai->version = (enum stm32_sai_version)of_id->data; + sai->conf = (struct stm32_sai_conf *)of_id->data; else return -EINVAL; @@ -110,6 +119,6 @@ static struct platform_driver stm32_sai_driver = { module_platform_driver(stm32_sai_driver); MODULE_DESCRIPTION("STM32 Soc SAI Interface"); -MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>"); +MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>"); MODULE_ALIAS("platform:st,stm32-sai"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index a801fda5066f..889974dc62d9 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -31,6 +31,10 @@ #define STM_SAI_CLRFR_REGX 0x18 #define STM_SAI_DR_REGX 0x1C +/* Sub-block A registers, relative to sub-block A address */ +#define STM_SAI_PDMCR_REGX 0x40 +#define STM_SAI_PDMLY_REGX 0x44 + /******************** Bit definition for SAI_GCR register *******************/ #define SAI_GCR_SYNCIN_SHIFT 0 #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) @@ -75,10 +79,11 @@ #define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT) #define SAI_XCR1_MCKDIV_SHIFT 20 -#define SAI_XCR1_MCKDIV_WIDTH 4 -#define SAI_XCR1_MCKDIV_MASK GENMASK(24, SAI_XCR1_MCKDIV_SHIFT) +#define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == SAI_STM32F4) ? 4 : 6) +#define SAI_XCR1_MCKDIV_MASK(x) GENMASK((SAI_XCR1_MCKDIV_SHIFT + (x) - 1),\ + SAI_XCR1_MCKDIV_SHIFT) #define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT) -#define SAI_XCR1_MCKDIV_MAX ((1 << SAI_XCR1_MCKDIV_WIDTH) - 1) +#define SAI_XCR1_MCKDIV_MAX(x) ((1 << SAI_XCR1_MCKDIV_WIDTH(x)) - 1) #define SAI_XCR1_OSR_SHIFT 26 #define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT) @@ -125,7 +130,6 @@ #define SAI_XFRCR_FSOFF BIT(SAI_XFRCR_FSOFF_SHIFT) /****************** Bit definition for SAI_XSLOTR register ******************/ - #define SAI_XSLOTR_FBOFF_SHIFT 0 #define SAI_XSLOTR_FBOFF_MASK GENMASK(4, SAI_XSLOTR_FBOFF_SHIFT) #define SAI_XSLOTR_FBOFF_SET(x) ((x) << SAI_XSLOTR_FBOFF_SHIFT) @@ -179,8 +183,65 @@ #define SAI_XCLRFR_SHIFT 0 #define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT) +/****************** Bit definition for SAI_PDMCR register ******************/ +#define SAI_PDMCR_PDMEN BIT(0) + +#define SAI_PDMCR_MICNBR_SHIFT 4 +#define SAI_PDMCR_MICNBR_MASK GENMASK(5, SAI_PDMCR_MICNBR_SHIFT) +#define SAI_PDMCR_MICNBR_SET(x) ((x) << SAI_PDMCR_MICNBR_SHIFT) + +#define SAI_PDMCR_CKEN1 BIT(8) +#define SAI_PDMCR_CKEN2 BIT(9) +#define SAI_PDMCR_CKEN3 BIT(10) +#define SAI_PDMCR_CKEN4 BIT(11) + +/****************** Bit definition for (SAI_PDMDLY register ****************/ +#define SAI_PDMDLY_1L_SHIFT 0 +#define SAI_PDMDLY_1L_MASK GENMASK(2, SAI_PDMDLY_1L_SHIFT) +#define SAI_PDMDLY_1L_WIDTH 3 + +#define SAI_PDMDLY_1R_SHIFT 4 +#define SAI_PDMDLY_1R_MASK GENMASK(6, SAI_PDMDLY_1R_SHIFT) +#define SAI_PDMDLY_1R_WIDTH 3 + +#define SAI_PDMDLY_2L_SHIFT 8 +#define SAI_PDMDLY_2L_MASK GENMASK(10, SAI_PDMDLY_2L_SHIFT) +#define SAI_PDMDLY_2L_WIDTH 3 + +#define SAI_PDMDLY_2R_SHIFT 12 +#define SAI_PDMDLY_2R_MASK GENMASK(14, SAI_PDMDLY_2R_SHIFT) +#define SAI_PDMDLY_2R_WIDTH 3 + +#define SAI_PDMDLY_3L_SHIFT 16 +#define SAI_PDMDLY_3L_MASK GENMASK(18, SAI_PDMDLY_3L_SHIFT) +#define SAI_PDMDLY_3L_WIDTH 3 + +#define SAI_PDMDLY_3R_SHIFT 20 +#define SAI_PDMDLY_3R_MASK GENMASK(22, SAI_PDMDLY_3R_SHIFT) +#define SAI_PDMDLY_3R_WIDTH 3 + +#define SAI_PDMDLY_4L_SHIFT 24 +#define SAI_PDMDLY_4L_MASK GENMASK(26, SAI_PDMDLY_4L_SHIFT) +#define SAI_PDMDLY_4L_WIDTH 3 + +#define SAI_PDMDLY_4R_SHIFT 28 +#define SAI_PDMDLY_4R_MASK GENMASK(30, SAI_PDMDLY_4R_SHIFT) +#define SAI_PDMDLY_4R_WIDTH 3 + +#define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) +#define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) + enum stm32_sai_version { - SAI_STM32F4 + SAI_STM32F4, + SAI_STM32H7 +}; + +/** + * struct stm32_sai_conf - SAI configuration + * @version: SAI version + */ +struct stm32_sai_conf { + int version; }; /** @@ -195,6 +256,6 @@ struct stm32_sai_data { struct platform_device *pdev; struct clk *clk_x8k; struct clk *clk_x11k; - int version; + struct stm32_sai_conf *conf; int irq; }; diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index ae4706ca265b..90d439613899 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -51,12 +51,15 @@ #define STM_SAI_A_ID 0x0 #define STM_SAI_B_ID 0x1 +#define STM_SAI_IS_SUB_A(x) ((x)->id == STM_SAI_A_ID) +#define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) * @pdev: device data pointer * @regmap: SAI register map pointer + * @regmap_config: SAI sub block register map configuration pointer * @dma_params: dma configuration data for rx or tx channel * @cpu_dai_drv: DAI driver data pointer * @cpu_dai: DAI runtime data pointer @@ -79,6 +82,7 @@ struct stm32_sai_sub_data { struct platform_device *pdev; struct regmap *regmap; + const struct regmap_config *regmap_config; struct snd_dmaengine_dai_dma_data dma_params; struct snd_soc_dai_driver *cpu_dai_drv; struct snd_soc_dai *cpu_dai; @@ -118,6 +122,8 @@ static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg) case STM_SAI_SR_REGX: case STM_SAI_CLRFR_REGX: case STM_SAI_DR_REGX: + case STM_SAI_PDMCR_REGX: + case STM_SAI_PDMLY_REGX: return true; default: return false; @@ -145,13 +151,15 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) case STM_SAI_SR_REGX: case STM_SAI_CLRFR_REGX: case STM_SAI_DR_REGX: + case STM_SAI_PDMCR_REGX: + case STM_SAI_PDMLY_REGX: return true; default: return false; } } -static const struct regmap_config stm32_sai_sub_regmap_config = { +static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, @@ -162,6 +170,17 @@ static const struct regmap_config stm32_sai_sub_regmap_config = { .fast_io = true, }; +static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = STM_SAI_PDMLY_REGX, + .readable_reg = stm32_sai_sub_readable_reg, + .volatile_reg = stm32_sai_sub_volatile_reg, + .writeable_reg = stm32_sai_sub_writeable_reg, + .fast_io = true, +}; + static irqreturn_t stm32_sai_isr(int irq, void *devid) { struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; @@ -181,29 +200,29 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) SAI_XCLRFR_MASK); if (flags & SAI_XIMR_OVRUDRIE) { - dev_err(&pdev->dev, "IT %s\n", + dev_err(&pdev->dev, "IRQ %s\n", STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); status = SNDRV_PCM_STATE_XRUN; } if (flags & SAI_XIMR_MUTEDETIE) - dev_dbg(&pdev->dev, "IT mute detected\n"); + dev_dbg(&pdev->dev, "IRQ mute detected\n"); if (flags & SAI_XIMR_WCKCFGIE) { - dev_err(&pdev->dev, "IT wrong clock configuration\n"); + dev_err(&pdev->dev, "IRQ wrong clock configuration\n"); status = SNDRV_PCM_STATE_DISCONNECTED; } if (flags & SAI_XIMR_CNRDYIE) - dev_warn(&pdev->dev, "IT Codec not ready\n"); + dev_err(&pdev->dev, "IRQ Codec not ready\n"); if (flags & SAI_XIMR_AFSDETIE) { - dev_warn(&pdev->dev, "IT Anticipated frame synchro\n"); + dev_err(&pdev->dev, "IRQ Anticipated frame synchro\n"); status = SNDRV_PCM_STATE_XRUN; } if (flags & SAI_XIMR_LFSDETIE) { - dev_warn(&pdev->dev, "IT Late frame synchro\n"); + dev_err(&pdev->dev, "IRQ Late frame synchro\n"); status = SNDRV_PCM_STATE_XRUN; } @@ -220,8 +239,15 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); + int ret; if ((dir == SND_SOC_CLOCK_OUT) && sai->master) { + ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, + SAI_XCR1_NODIV, + (unsigned int)~SAI_XCR1_NODIV); + if (ret < 0) + return ret; + sai->mclk_rate = freq; dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); } @@ -235,7 +261,7 @@ static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int slotr, slotr_mask, slot_size; - dev_dbg(cpu_dai->dev, "masks tx/rx:%#x/%#x, slots:%d, width:%d\n", + dev_dbg(cpu_dai->dev, "Masks tx/rx:%#x/%#x, slots:%d, width:%d\n", tx_mask, rx_mask, slots, slot_width); switch (slot_width) { @@ -356,6 +382,10 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) } cr1_mask |= SAI_XCR1_SLAVE; + /* do not generate master by default */ + cr1 |= SAI_XCR1_NODIV; + cr1_mask |= SAI_XCR1_NODIV; + ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); if (ret < 0) { dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); @@ -377,7 +407,7 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, ret = clk_prepare_enable(sai->sai_ck); if (ret < 0) { - dev_err(cpu_dai->dev, "failed to enable clock: %d\n", ret); + dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret); return ret; } @@ -497,7 +527,7 @@ static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai) SAI_XSLOTR_SLOTEN_SET(sai->slot_mask)); } - dev_dbg(cpu_dai->dev, "slots %d, slot width %d\n", + dev_dbg(cpu_dai->dev, "Slots %d, slot width %d\n", sai->slots, sai->slot_width); return 0; @@ -521,7 +551,7 @@ static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai) frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1)); frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK; - dev_dbg(cpu_dai->dev, "frame length %d, frame active %d\n", + dev_dbg(cpu_dai->dev, "Frame length %d, frame active %d\n", sai->fs_length, fs_active); regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr); @@ -540,7 +570,8 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int cr1, mask, div = 0; - int sai_clk_rate, ret; + int sai_clk_rate, mclk_ratio, den, ret; + int version = sai->pdata->conf->version; if (!sai->mclk_rate) { dev_err(cpu_dai->dev, "Mclk rate is null\n"); @@ -553,21 +584,53 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); sai_clk_rate = clk_get_rate(sai->sai_ck); - /* - * mclk_rate = 256 * fs - * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate - * MCKDIV = sai_ck / (2 * mclk_rate) otherwise - */ - if (2 * sai_clk_rate >= 3 * sai->mclk_rate) - div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate); - - if (div > SAI_XCR1_MCKDIV_MAX) { + if (STM_SAI_IS_F4(sai->pdata)) { + /* + * mclk_rate = 256 * fs + * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate + * MCKDIV = sai_ck / (2 * mclk_rate) otherwise + */ + if (2 * sai_clk_rate >= 3 * sai->mclk_rate) + div = DIV_ROUND_CLOSEST(sai_clk_rate, + 2 * sai->mclk_rate); + } else { + /* + * TDM mode : + * mclk on + * MCKDIV = sai_ck / (ws x 256) (NOMCK=0. OSR=0) + * MCKDIV = sai_ck / (ws x 512) (NOMCK=0. OSR=1) + * mclk off + * MCKDIV = sai_ck / (frl x ws) (NOMCK=1) + * Note: NOMCK/NODIV correspond to same bit. + */ + if (sai->mclk_rate) { + mclk_ratio = sai->mclk_rate / params_rate(params); + if (mclk_ratio != 256) { + if (mclk_ratio == 512) { + mask = SAI_XCR1_OSR; + cr1 = SAI_XCR1_OSR; + } else { + dev_err(cpu_dai->dev, + "Wrong mclk ratio %d\n", + mclk_ratio); + return -EINVAL; + } + } + div = DIV_ROUND_CLOSEST(sai_clk_rate, sai->mclk_rate); + } else { + /* mclk-fs not set, master clock not active. NOMCK=1 */ + den = sai->fs_length * params_rate(params); + div = DIV_ROUND_CLOSEST(sai_clk_rate, den); + } + } + + if (div > SAI_XCR1_MCKDIV_MAX(version)) { dev_err(cpu_dai->dev, "Divider %d out of range\n", div); return -EINVAL; } dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div); - mask = SAI_XCR1_MCKDIV_MASK; + mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version)); cr1 = SAI_XCR1_MCKDIV_SET(div); ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); if (ret < 0) { @@ -629,12 +692,12 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n"); regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, - SAI_XCR1_DMAEN, - (unsigned int)~SAI_XCR1_DMAEN); + SAI_XCR1_SAIEN, + (unsigned int)~SAI_XCR1_SAIEN); ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, - SAI_XCR1_SAIEN, - (unsigned int)~SAI_XCR1_SAIEN); + SAI_XCR1_DMAEN, + (unsigned int)~SAI_XCR1_DMAEN); if (ret < 0) dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); break; @@ -652,6 +715,9 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); + regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, + SAI_XCR1_NODIV); + clk_disable_unprepare(sai->sai_ck); sai->substream = NULL; } @@ -761,16 +827,23 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, return -ENODEV; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - dev_err(&pdev->dev, "res %pr\n", res); - base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); sai->phys_addr = res->start; - sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, - &stm32_sai_sub_regmap_config); + + sai->regmap_config = &stm32_sai_sub_regmap_config_f4; + /* Note: PDM registers not available for H7 sub-block B */ + if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai)) + sai->regmap_config = &stm32_sai_sub_regmap_config_h7; + + sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck", + base, sai->regmap_config); + if (IS_ERR(sai->regmap)) { + dev_err(&pdev->dev, "Failed to initialize MMIO\n"); + return PTR_ERR(sai->regmap); + } /* Get direction property */ if (of_property_match_string(np, "dma-names", "tx") >= 0) { @@ -784,7 +857,7 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); if (IS_ERR(sai->sai_ck)) { - dev_err(&pdev->dev, "missing kernel clock sai_ck\n"); + dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); return PTR_ERR(sai->sai_ck); } @@ -849,7 +922,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, IRQF_SHARED, dev_name(&pdev->dev), sai); if (ret) { - dev_err(&pdev->dev, "irq request returned %d\n", ret); + dev_err(&pdev->dev, "IRQ request returned %d\n", ret); return ret; } @@ -861,7 +934,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &stm32_sai_pcm_config, 0); if (ret) { - dev_err(&pdev->dev, "could not register pcm dma\n"); + dev_err(&pdev->dev, "Could not register pcm dma\n"); return ret; } @@ -879,6 +952,6 @@ static struct platform_driver stm32_sai_sub_driver = { module_platform_driver(stm32_sai_sub_driver); MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface"); -MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>"); +MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>"); MODULE_ALIAS("platform:st,stm32-sai-sub"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c new file mode 100644 index 000000000000..4e4250bdb75a --- /dev/null +++ b/sound/soc/stm/stm32_spdifrx.c @@ -0,0 +1,998 @@ +/* + * STM32 ALSA SoC Digital Audio Interface (SPDIF-rx) driver. + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. + * + * License terms: GPL V2.0. + * + * 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. + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +/* SPDIF-rx Register Map */ +#define STM32_SPDIFRX_CR 0x00 +#define STM32_SPDIFRX_IMR 0x04 +#define STM32_SPDIFRX_SR 0x08 +#define STM32_SPDIFRX_IFCR 0x0C +#define STM32_SPDIFRX_DR 0x10 +#define STM32_SPDIFRX_CSR 0x14 +#define STM32_SPDIFRX_DIR 0x18 + +/* Bit definition for SPDIF_CR register */ +#define SPDIFRX_CR_SPDIFEN_SHIFT 0 +#define SPDIFRX_CR_SPDIFEN_MASK GENMASK(1, SPDIFRX_CR_SPDIFEN_SHIFT) +#define SPDIFRX_CR_SPDIFENSET(x) ((x) << SPDIFRX_CR_SPDIFEN_SHIFT) + +#define SPDIFRX_CR_RXDMAEN BIT(2) +#define SPDIFRX_CR_RXSTEO BIT(3) + +#define SPDIFRX_CR_DRFMT_SHIFT 4 +#define SPDIFRX_CR_DRFMT_MASK GENMASK(5, SPDIFRX_CR_DRFMT_SHIFT) +#define SPDIFRX_CR_DRFMTSET(x) ((x) << SPDIFRX_CR_DRFMT_SHIFT) + +#define SPDIFRX_CR_PMSK BIT(6) +#define SPDIFRX_CR_VMSK BIT(7) +#define SPDIFRX_CR_CUMSK BIT(8) +#define SPDIFRX_CR_PTMSK BIT(9) +#define SPDIFRX_CR_CBDMAEN BIT(10) +#define SPDIFRX_CR_CHSEL_SHIFT 11 +#define SPDIFRX_CR_CHSEL BIT(SPDIFRX_CR_CHSEL_SHIFT) + +#define SPDIFRX_CR_NBTR_SHIFT 12 +#define SPDIFRX_CR_NBTR_MASK GENMASK(13, SPDIFRX_CR_NBTR_SHIFT) +#define SPDIFRX_CR_NBTRSET(x) ((x) << SPDIFRX_CR_NBTR_SHIFT) + +#define SPDIFRX_CR_WFA BIT(14) + +#define SPDIFRX_CR_INSEL_SHIFT 16 +#define SPDIFRX_CR_INSEL_MASK GENMASK(18, PDIFRX_CR_INSEL_SHIFT) +#define SPDIFRX_CR_INSELSET(x) ((x) << SPDIFRX_CR_INSEL_SHIFT) + +#define SPDIFRX_CR_CKSEN_SHIFT 20 +#define SPDIFRX_CR_CKSEN BIT(20) +#define SPDIFRX_CR_CKSBKPEN BIT(21) + +/* Bit definition for SPDIFRX_IMR register */ +#define SPDIFRX_IMR_RXNEI BIT(0) +#define SPDIFRX_IMR_CSRNEIE BIT(1) +#define SPDIFRX_IMR_PERRIE BIT(2) +#define SPDIFRX_IMR_OVRIE BIT(3) +#define SPDIFRX_IMR_SBLKIE BIT(4) +#define SPDIFRX_IMR_SYNCDIE BIT(5) +#define SPDIFRX_IMR_IFEIE BIT(6) + +#define SPDIFRX_XIMR_MASK GENMASK(6, 0) + +/* Bit definition for SPDIFRX_SR register */ +#define SPDIFRX_SR_RXNE BIT(0) +#define SPDIFRX_SR_CSRNE BIT(1) +#define SPDIFRX_SR_PERR BIT(2) +#define SPDIFRX_SR_OVR BIT(3) +#define SPDIFRX_SR_SBD BIT(4) +#define SPDIFRX_SR_SYNCD BIT(5) +#define SPDIFRX_SR_FERR BIT(6) +#define SPDIFRX_SR_SERR BIT(7) +#define SPDIFRX_SR_TERR BIT(8) + +#define SPDIFRX_SR_WIDTH5_SHIFT 16 +#define SPDIFRX_SR_WIDTH5_MASK GENMASK(30, PDIFRX_SR_WIDTH5_SHIFT) +#define SPDIFRX_SR_WIDTH5SET(x) ((x) << SPDIFRX_SR_WIDTH5_SHIFT) + +/* Bit definition for SPDIFRX_IFCR register */ +#define SPDIFRX_IFCR_PERRCF BIT(2) +#define SPDIFRX_IFCR_OVRCF BIT(3) +#define SPDIFRX_IFCR_SBDCF BIT(4) +#define SPDIFRX_IFCR_SYNCDCF BIT(5) + +#define SPDIFRX_XIFCR_MASK GENMASK(5, 2) + +/* Bit definition for SPDIFRX_DR register (DRFMT = 0b00) */ +#define SPDIFRX_DR0_DR_SHIFT 0 +#define SPDIFRX_DR0_DR_MASK GENMASK(23, SPDIFRX_DR0_DR_SHIFT) +#define SPDIFRX_DR0_DRSET(x) ((x) << SPDIFRX_DR0_DR_SHIFT) + +#define SPDIFRX_DR0_PE BIT(24) + +#define SPDIFRX_DR0_V BIT(25) +#define SPDIFRX_DR0_U BIT(26) +#define SPDIFRX_DR0_C BIT(27) + +#define SPDIFRX_DR0_PT_SHIFT 28 +#define SPDIFRX_DR0_PT_MASK GENMASK(29, SPDIFRX_DR0_PT_SHIFT) +#define SPDIFRX_DR0_PTSET(x) ((x) << SPDIFRX_DR0_PT_SHIFT) + +/* Bit definition for SPDIFRX_DR register (DRFMT = 0b01) */ +#define SPDIFRX_DR1_PE BIT(0) +#define SPDIFRX_DR1_V BIT(1) +#define SPDIFRX_DR1_U BIT(2) +#define SPDIFRX_DR1_C BIT(3) + +#define SPDIFRX_DR1_PT_SHIFT 4 +#define SPDIFRX_DR1_PT_MASK GENMASK(5, SPDIFRX_DR1_PT_SHIFT) +#define SPDIFRX_DR1_PTSET(x) ((x) << SPDIFRX_DR1_PT_SHIFT) + +#define SPDIFRX_DR1_DR_SHIFT 8 +#define SPDIFRX_DR1_DR_MASK GENMASK(31, SPDIFRX_DR1_DR_SHIFT) +#define SPDIFRX_DR1_DRSET(x) ((x) << SPDIFRX_DR1_DR_SHIFT) + +/* Bit definition for SPDIFRX_DR register (DRFMT = 0b10) */ +#define SPDIFRX_DR1_DRNL1_SHIFT 0 +#define SPDIFRX_DR1_DRNL1_MASK GENMASK(15, SPDIFRX_DR1_DRNL1_SHIFT) +#define SPDIFRX_DR1_DRNL1SET(x) ((x) << SPDIFRX_DR1_DRNL1_SHIFT) + +#define SPDIFRX_DR1_DRNL2_SHIFT 16 +#define SPDIFRX_DR1_DRNL2_MASK GENMASK(31, SPDIFRX_DR1_DRNL2_SHIFT) +#define SPDIFRX_DR1_DRNL2SET(x) ((x) << SPDIFRX_DR1_DRNL2_SHIFT) + +/* Bit definition for SPDIFRX_CSR register */ +#define SPDIFRX_CSR_USR_SHIFT 0 +#define SPDIFRX_CSR_USR_MASK GENMASK(15, SPDIFRX_CSR_USR_SHIFT) +#define SPDIFRX_CSR_USRGET(x) (((x) & SPDIFRX_CSR_USR_MASK)\ + >> SPDIFRX_CSR_USR_SHIFT) + +#define SPDIFRX_CSR_CS_SHIFT 16 +#define SPDIFRX_CSR_CS_MASK GENMASK(23, SPDIFRX_CSR_CS_SHIFT) +#define SPDIFRX_CSR_CSGET(x) (((x) & SPDIFRX_CSR_CS_MASK)\ + >> SPDIFRX_CSR_CS_SHIFT) + +#define SPDIFRX_CSR_SOB BIT(24) + +/* Bit definition for SPDIFRX_DIR register */ +#define SPDIFRX_DIR_THI_SHIFT 0 +#define SPDIFRX_DIR_THI_MASK GENMASK(12, SPDIFRX_DIR_THI_SHIFT) +#define SPDIFRX_DIR_THI_SET(x) ((x) << SPDIFRX_DIR_THI_SHIFT) + +#define SPDIFRX_DIR_TLO_SHIFT 16 +#define SPDIFRX_DIR_TLO_MASK GENMASK(28, SPDIFRX_DIR_TLO_SHIFT) +#define SPDIFRX_DIR_TLO_SET(x) ((x) << SPDIFRX_DIR_TLO_SHIFT) + +#define SPDIFRX_SPDIFEN_DISABLE 0x0 +#define SPDIFRX_SPDIFEN_SYNC 0x1 +#define SPDIFRX_SPDIFEN_ENABLE 0x3 + +#define SPDIFRX_IN1 0x1 +#define SPDIFRX_IN2 0x2 +#define SPDIFRX_IN3 0x3 +#define SPDIFRX_IN4 0x4 +#define SPDIFRX_IN5 0x5 +#define SPDIFRX_IN6 0x6 +#define SPDIFRX_IN7 0x7 +#define SPDIFRX_IN8 0x8 + +#define SPDIFRX_NBTR_NONE 0x0 +#define SPDIFRX_NBTR_3 0x1 +#define SPDIFRX_NBTR_15 0x2 +#define SPDIFRX_NBTR_63 0x3 + +#define SPDIFRX_DRFMT_RIGHT 0x0 +#define SPDIFRX_DRFMT_LEFT 0x1 +#define SPDIFRX_DRFMT_PACKED 0x2 + +/* 192 CS bits in S/PDIF frame. i.e 24 CS bytes */ +#define SPDIFRX_CS_BYTES_NB 24 +#define SPDIFRX_UB_BYTES_NB 48 + +/* + * CSR register is retrieved as a 32 bits word + * It contains 1 channel status byte and 2 user data bytes + * 2 S/PDIF frames are acquired to get all CS/UB bits + */ +#define SPDIFRX_CSR_BUF_LENGTH (SPDIFRX_CS_BYTES_NB * 4 * 2) + +/** + * struct stm32_spdifrx_data - private data of SPDIFRX + * @pdev: device data pointer + * @base: mmio register base virtual address + * @regmap: SPDIFRX register map pointer + * @regmap_conf: SPDIFRX register map configuration pointer + * @cs_completion: channel status retrieving completion + * @kclk: kernel clock feeding the SPDIFRX clock generator + * @dma_params: dma configuration data for rx channel + * @substream: PCM substream data pointer + * @dmab: dma buffer info pointer + * @ctrl_chan: dma channel for S/PDIF control bits + * @desc:dma async transaction descriptor + * @slave_config: dma slave channel runtime config pointer + * @phys_addr: SPDIFRX registers physical base address + * @lock: synchronization enabling lock + * @cs: channel status buffer + * @ub: user data buffer + * @irq: SPDIFRX interrupt line + * @refcount: keep count of opened DMA channels + */ +struct stm32_spdifrx_data { + struct platform_device *pdev; + void __iomem *base; + struct regmap *regmap; + const struct regmap_config *regmap_conf; + struct completion cs_completion; + struct clk *kclk; + struct snd_dmaengine_dai_dma_data dma_params; + struct snd_pcm_substream *substream; + struct snd_dma_buffer *dmab; + struct dma_chan *ctrl_chan; + struct dma_async_tx_descriptor *desc; + struct dma_slave_config slave_config; + dma_addr_t phys_addr; + spinlock_t lock; /* Sync enabling lock */ + unsigned char cs[SPDIFRX_CS_BYTES_NB]; + unsigned char ub[SPDIFRX_UB_BYTES_NB]; + int irq; + int refcount; +}; + +static void stm32_spdifrx_dma_complete(void *data) +{ + struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)data; + struct platform_device *pdev = spdifrx->pdev; + u32 *p_start = (u32 *)spdifrx->dmab->area; + u32 *p_end = p_start + (2 * SPDIFRX_CS_BYTES_NB) - 1; + u32 *ptr = p_start; + u16 *ub_ptr = (short *)spdifrx->ub; + int i = 0; + + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + SPDIFRX_CR_CBDMAEN, + (unsigned int)~SPDIFRX_CR_CBDMAEN); + + if (!spdifrx->dmab->area) + return; + + while (ptr <= p_end) { + if (*ptr & SPDIFRX_CSR_SOB) + break; + ptr++; + } + + if (ptr > p_end) { + dev_err(&pdev->dev, "Start of S/PDIF block not found\n"); + return; + } + + while (i < SPDIFRX_CS_BYTES_NB) { + spdifrx->cs[i] = (unsigned char)SPDIFRX_CSR_CSGET(*ptr); + *ub_ptr++ = SPDIFRX_CSR_USRGET(*ptr++); + if (ptr > p_end) { + dev_err(&pdev->dev, "Failed to get channel status\n"); + return; + } + i++; + } + + complete(&spdifrx->cs_completion); +} + +static int stm32_spdifrx_dma_ctrl_start(struct stm32_spdifrx_data *spdifrx) +{ + dma_cookie_t cookie; + int err; + + spdifrx->desc = dmaengine_prep_slave_single(spdifrx->ctrl_chan, + spdifrx->dmab->addr, + SPDIFRX_CSR_BUF_LENGTH, + DMA_DEV_TO_MEM, + DMA_CTRL_ACK); + if (!spdifrx->desc) + return -EINVAL; + + spdifrx->desc->callback = stm32_spdifrx_dma_complete; + spdifrx->desc->callback_param = spdifrx; + cookie = dmaengine_submit(spdifrx->desc); + err = dma_submit_error(cookie); + if (err) + return -EINVAL; + + dma_async_issue_pending(spdifrx->ctrl_chan); + + return 0; +} + +static void stm32_spdifrx_dma_ctrl_stop(struct stm32_spdifrx_data *spdifrx) +{ + dmaengine_terminate_async(spdifrx->ctrl_chan); +} + +static int stm32_spdifrx_start_sync(struct stm32_spdifrx_data *spdifrx) +{ + int cr, cr_mask, imr, ret; + + /* Enable IRQs */ + imr = SPDIFRX_IMR_IFEIE | SPDIFRX_IMR_SYNCDIE | SPDIFRX_IMR_PERRIE; + ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IMR, imr, imr); + if (ret) + return ret; + + spin_lock(&spdifrx->lock); + + spdifrx->refcount++; + + regmap_read(spdifrx->regmap, STM32_SPDIFRX_CR, &cr); + + if (!(cr & SPDIFRX_CR_SPDIFEN_MASK)) { + /* + * Start sync if SPDIFRX is still in idle state. + * SPDIFRX reception enabled when sync done + */ + dev_dbg(&spdifrx->pdev->dev, "start synchronization\n"); + + /* + * SPDIFRX configuration: + * Wait for activity before starting sync process. This avoid + * to issue sync errors when spdif signal is missing on input. + * Preamble, CS, user, validity and parity error bits not copied + * to DR register. + */ + cr = SPDIFRX_CR_WFA | SPDIFRX_CR_PMSK | SPDIFRX_CR_VMSK | + SPDIFRX_CR_CUMSK | SPDIFRX_CR_PTMSK | SPDIFRX_CR_RXSTEO; + cr_mask = cr; + + cr |= SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_SYNC); + cr_mask |= SPDIFRX_CR_SPDIFEN_MASK; + ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + cr_mask, cr); + if (ret < 0) + dev_err(&spdifrx->pdev->dev, + "Failed to start synchronization\n"); + } + + spin_unlock(&spdifrx->lock); + + return ret; +} + +static void stm32_spdifrx_stop(struct stm32_spdifrx_data *spdifrx) +{ + int cr, cr_mask, reg; + + spin_lock(&spdifrx->lock); + + if (--spdifrx->refcount) { + spin_unlock(&spdifrx->lock); + return; + } + + cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_DISABLE); + cr_mask = SPDIFRX_CR_SPDIFEN_MASK | SPDIFRX_CR_RXDMAEN; + + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, cr_mask, cr); + + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IMR, + SPDIFRX_XIMR_MASK, 0); + + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IFCR, + SPDIFRX_XIFCR_MASK, SPDIFRX_XIFCR_MASK); + + /* dummy read to clear CSRNE and RXNE in status register */ + regmap_read(spdifrx->regmap, STM32_SPDIFRX_DR, ®); + regmap_read(spdifrx->regmap, STM32_SPDIFRX_CSR, ®); + + spin_unlock(&spdifrx->lock); +} + +static int stm32_spdifrx_dma_ctrl_register(struct device *dev, + struct stm32_spdifrx_data *spdifrx) +{ + int ret; + + spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer), + GFP_KERNEL); + if (!spdifrx->dmab) + return -ENOMEM; + + spdifrx->dmab->dev.type = SNDRV_DMA_TYPE_DEV_IRAM; + spdifrx->dmab->dev.dev = dev; + ret = snd_dma_alloc_pages(spdifrx->dmab->dev.type, dev, + SPDIFRX_CSR_BUF_LENGTH, spdifrx->dmab); + if (ret < 0) { + dev_err(dev, "snd_dma_alloc_pages returned error %d\n", ret); + return ret; + } + + spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); + if (!spdifrx->ctrl_chan) { + dev_err(dev, "dma_request_slave_channel failed\n"); + return -EINVAL; + } + + spdifrx->slave_config.direction = DMA_DEV_TO_MEM; + spdifrx->slave_config.src_addr = (dma_addr_t)(spdifrx->phys_addr + + STM32_SPDIFRX_CSR); + spdifrx->slave_config.dst_addr = spdifrx->dmab->addr; + spdifrx->slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + spdifrx->slave_config.src_maxburst = 1; + + ret = dmaengine_slave_config(spdifrx->ctrl_chan, + &spdifrx->slave_config); + if (ret < 0) { + dev_err(dev, "dmaengine_slave_config returned error %d\n", ret); + dma_release_channel(spdifrx->ctrl_chan); + spdifrx->ctrl_chan = NULL; + } + + return ret; +}; + +static const char * const spdifrx_enum_input[] = { + "in0", "in1", "in2", "in3" +}; + +/* By default CS bits are retrieved from channel A */ +static const char * const spdifrx_enum_cs_channel[] = { + "A", "B" +}; + +static SOC_ENUM_SINGLE_DECL(ctrl_enum_input, + STM32_SPDIFRX_CR, SPDIFRX_CR_INSEL_SHIFT, + spdifrx_enum_input); + +static SOC_ENUM_SINGLE_DECL(ctrl_enum_cs_channel, + STM32_SPDIFRX_CR, SPDIFRX_CR_CHSEL_SHIFT, + spdifrx_enum_cs_channel); + +static int stm32_spdifrx_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 stm32_spdifrx_ub_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 stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx) +{ + int ret = 0; + + memset(spdifrx->cs, 0, SPDIFRX_CS_BYTES_NB); + memset(spdifrx->ub, 0, SPDIFRX_UB_BYTES_NB); + + ret = stm32_spdifrx_dma_ctrl_start(spdifrx); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(spdifrx->kclk); + if (ret) { + dev_err(&spdifrx->pdev->dev, "Enable kclk failed: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + SPDIFRX_CR_CBDMAEN, SPDIFRX_CR_CBDMAEN); + if (ret < 0) + goto end; + + ret = stm32_spdifrx_start_sync(spdifrx); + if (ret < 0) + goto end; + + if (wait_for_completion_interruptible_timeout(&spdifrx->cs_completion, + msecs_to_jiffies(100)) + <= 0) { + dev_err(&spdifrx->pdev->dev, "Failed to get control data\n"); + ret = -EAGAIN; + } + + stm32_spdifrx_stop(spdifrx); + stm32_spdifrx_dma_ctrl_stop(spdifrx); + +end: + clk_disable_unprepare(spdifrx->kclk); + + return ret; +} + +static int stm32_spdifrx_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + + stm32_spdifrx_get_ctrl_data(spdifrx); + + ucontrol->value.iec958.status[0] = spdifrx->cs[0]; + ucontrol->value.iec958.status[1] = spdifrx->cs[1]; + ucontrol->value.iec958.status[2] = spdifrx->cs[2]; + ucontrol->value.iec958.status[3] = spdifrx->cs[3]; + ucontrol->value.iec958.status[4] = spdifrx->cs[4]; + + return 0; +} + +static int stm32_spdif_user_bits_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + + stm32_spdifrx_get_ctrl_data(spdifrx); + + ucontrol->value.iec958.status[0] = spdifrx->ub[0]; + ucontrol->value.iec958.status[1] = spdifrx->ub[1]; + ucontrol->value.iec958.status[2] = spdifrx->ub[2]; + ucontrol->value.iec958.status[3] = spdifrx->ub[3]; + ucontrol->value.iec958.status[4] = spdifrx->ub[4]; + + return 0; +} + +static struct snd_kcontrol_new stm32_spdifrx_iec_ctrls[] = { + /* Channel status control */ + { + .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 = stm32_spdifrx_info, + .get = stm32_spdifrx_capture_get, + }, + /* User bits control */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 User Bit Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = stm32_spdifrx_ub_info, + .get = stm32_spdif_user_bits_get, + }, +}; + +static struct snd_kcontrol_new stm32_spdifrx_ctrls[] = { + SOC_ENUM("SPDIFRX input", ctrl_enum_input), + SOC_ENUM("SPDIFRX CS channel", ctrl_enum_cs_channel), +}; + +static int stm32_spdifrx_dai_register_ctrls(struct snd_soc_dai *cpu_dai) +{ + int ret; + + ret = snd_soc_add_dai_controls(cpu_dai, stm32_spdifrx_iec_ctrls, + ARRAY_SIZE(stm32_spdifrx_iec_ctrls)); + if (ret < 0) + return ret; + + return snd_soc_add_component_controls(cpu_dai->component, + stm32_spdifrx_ctrls, + ARRAY_SIZE(stm32_spdifrx_ctrls)); +} + +static int stm32_spdifrx_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct stm32_spdifrx_data *spdifrx = dev_get_drvdata(cpu_dai->dev); + + spdifrx->dma_params.addr = (dma_addr_t)(spdifrx->phys_addr + + STM32_SPDIFRX_DR); + spdifrx->dma_params.maxburst = 1; + + snd_soc_dai_init_dma_data(cpu_dai, NULL, &spdifrx->dma_params); + + return stm32_spdifrx_dai_register_ctrls(cpu_dai); +} + +static bool stm32_spdifrx_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case STM32_SPDIFRX_CR: + case STM32_SPDIFRX_IMR: + case STM32_SPDIFRX_SR: + case STM32_SPDIFRX_IFCR: + case STM32_SPDIFRX_DR: + case STM32_SPDIFRX_CSR: + case STM32_SPDIFRX_DIR: + return true; + default: + return false; + } +} + +static bool stm32_spdifrx_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg == STM32_SPDIFRX_DR) + return true; + + return false; +} + +static bool stm32_spdifrx_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case STM32_SPDIFRX_CR: + case STM32_SPDIFRX_IMR: + case STM32_SPDIFRX_IFCR: + return true; + default: + return false; + } +} + +static const struct regmap_config stm32_h7_spdifrx_regmap_conf = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = STM32_SPDIFRX_DIR, + .readable_reg = stm32_spdifrx_readable_reg, + .volatile_reg = stm32_spdifrx_volatile_reg, + .writeable_reg = stm32_spdifrx_writeable_reg, + .fast_io = true, +}; + +static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) +{ + struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)devid; + struct snd_pcm_substream *substream = spdifrx->substream; + struct platform_device *pdev = spdifrx->pdev; + unsigned int cr, mask, sr, imr; + unsigned int flags; + int err = 0, err_xrun = 0; + + regmap_read(spdifrx->regmap, STM32_SPDIFRX_SR, &sr); + regmap_read(spdifrx->regmap, STM32_SPDIFRX_IMR, &imr); + + mask = imr & SPDIFRX_XIMR_MASK; + /* SERR, TERR, FERR IRQs are generated if IFEIE is set */ + if (mask & SPDIFRX_IMR_IFEIE) + mask |= (SPDIFRX_IMR_IFEIE << 1) | (SPDIFRX_IMR_IFEIE << 2); + + flags = sr & mask; + if (!flags) { + dev_err(&pdev->dev, "Unexpected IRQ. rflags=%#x, imr=%#x\n", + sr, imr); + return IRQ_NONE; + } + + /* Clear IRQs */ + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IFCR, + SPDIFRX_XIFCR_MASK, flags); + + if (flags & SPDIFRX_SR_PERR) { + dev_dbg(&pdev->dev, "Parity error\n"); + err_xrun = 1; + } + + if (flags & SPDIFRX_SR_OVR) { + dev_dbg(&pdev->dev, "Overrun error\n"); + err_xrun = 1; + } + + if (flags & SPDIFRX_SR_SBD) + dev_dbg(&pdev->dev, "Synchronization block detected\n"); + + if (flags & SPDIFRX_SR_SYNCD) { + dev_dbg(&pdev->dev, "Synchronization done\n"); + + /* Enable spdifrx */ + cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_ENABLE); + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + SPDIFRX_CR_SPDIFEN_MASK, cr); + } + + if (flags & SPDIFRX_SR_FERR) { + dev_dbg(&pdev->dev, "Frame error\n"); + err = 1; + } + + if (flags & SPDIFRX_SR_SERR) { + dev_dbg(&pdev->dev, "Synchronization error\n"); + err = 1; + } + + if (flags & SPDIFRX_SR_TERR) { + dev_dbg(&pdev->dev, "Timeout error\n"); + err = 1; + } + + if (err) { + /* SPDIFRX in STATE_STOP. Disable SPDIFRX to clear errors */ + cr = SPDIFRX_CR_SPDIFENSET(SPDIFRX_SPDIFEN_DISABLE); + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + SPDIFRX_CR_SPDIFEN_MASK, cr); + + if (substream) + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + + return IRQ_HANDLED; + } + + if (err_xrun && substream) + snd_pcm_stop_xrun(substream); + + return IRQ_HANDLED; +} + +static int stm32_spdifrx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + int ret; + + spdifrx->substream = substream; + + ret = clk_prepare_enable(spdifrx->kclk); + if (ret) + dev_err(&spdifrx->pdev->dev, "Enable kclk failed: %d\n", ret); + + return ret; +} + +static int stm32_spdifrx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + int data_size = params_width(params); + int fmt; + + switch (data_size) { + case 16: + fmt = SPDIFRX_DRFMT_PACKED; + spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 32: + fmt = SPDIFRX_DRFMT_LEFT; + spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + dev_err(&spdifrx->pdev->dev, "Unexpected data format\n"); + return -EINVAL; + } + + snd_soc_dai_init_dma_data(cpu_dai, NULL, &spdifrx->dma_params); + + return regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + SPDIFRX_CR_DRFMT_MASK, + SPDIFRX_CR_DRFMTSET(fmt)); +} + +static int stm32_spdifrx_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_IMR, + SPDIFRX_IMR_OVRIE, SPDIFRX_IMR_OVRIE); + + regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, + SPDIFRX_CR_RXDMAEN, SPDIFRX_CR_RXDMAEN); + + ret = stm32_spdifrx_start_sync(spdifrx); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + stm32_spdifrx_stop(spdifrx); + break; + default: + return -EINVAL; + } + + return ret; +} + +static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); + + spdifrx->substream = NULL; + clk_disable_unprepare(spdifrx->kclk); +} + +static const struct snd_soc_dai_ops stm32_spdifrx_pcm_dai_ops = { + .startup = stm32_spdifrx_startup, + .hw_params = stm32_spdifrx_hw_params, + .trigger = stm32_spdifrx_trigger, + .shutdown = stm32_spdifrx_shutdown, +}; + +static struct snd_soc_dai_driver stm32_spdifrx_dai[] = { + { + .name = "spdifrx-capture-cpu-dai", + .probe = stm32_spdifrx_dai_probe, + .capture = { + .stream_name = "CPU-Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &stm32_spdifrx_pcm_dai_ops, + } +}; + +static const struct snd_pcm_hardware stm32_spdifrx_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, + .buffer_bytes_max = 8 * PAGE_SIZE, + .period_bytes_max = 2048, /* MDMA constraint */ + .periods_min = 2, + .periods_max = 8, +}; + +static const struct snd_soc_component_driver stm32_spdifrx_component = { + .name = "stm32-spdifrx", +}; + +static const struct snd_dmaengine_pcm_config stm32_spdifrx_pcm_config = { + .pcm_hardware = &stm32_spdifrx_pcm_hw, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + +static const struct of_device_id stm32_spdifrx_ids[] = { + { + .compatible = "st,stm32h7-spdifrx", + .data = &stm32_h7_spdifrx_regmap_conf + }, + {} +}; + +static int stm_spdifrx_parse_of(struct platform_device *pdev, + struct stm32_spdifrx_data *spdifrx) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id; + struct resource *res; + + if (!np) + return -ENODEV; + + of_id = of_match_device(stm32_spdifrx_ids, &pdev->dev); + if (of_id) + spdifrx->regmap_conf = + (const struct regmap_config *)of_id->data; + else + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spdifrx->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spdifrx->base)) + return PTR_ERR(spdifrx->base); + + spdifrx->phys_addr = res->start; + + spdifrx->kclk = devm_clk_get(&pdev->dev, "kclk"); + if (IS_ERR(spdifrx->kclk)) { + dev_err(&pdev->dev, "Could not get kclk\n"); + return PTR_ERR(spdifrx->kclk); + } + + spdifrx->irq = platform_get_irq(pdev, 0); + if (spdifrx->irq < 0) { + dev_err(&pdev->dev, "No irq for node %s\n", pdev->name); + return spdifrx->irq; + } + + return 0; +} + +static int stm32_spdifrx_probe(struct platform_device *pdev) +{ + struct stm32_spdifrx_data *spdifrx; + struct reset_control *rst; + const struct snd_dmaengine_pcm_config *pcm_config = NULL; + int ret; + + spdifrx = devm_kzalloc(&pdev->dev, sizeof(*spdifrx), GFP_KERNEL); + if (!spdifrx) + return -ENOMEM; + + spdifrx->pdev = pdev; + init_completion(&spdifrx->cs_completion); + spin_lock_init(&spdifrx->lock); + + platform_set_drvdata(pdev, spdifrx); + + ret = stm_spdifrx_parse_of(pdev, spdifrx); + if (ret) + return ret; + + spdifrx->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "kclk", + spdifrx->base, + spdifrx->regmap_conf); + if (IS_ERR(spdifrx->regmap)) { + dev_err(&pdev->dev, "Regmap init failed\n"); + return PTR_ERR(spdifrx->regmap); + } + + ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, + dev_name(&pdev->dev), spdifrx); + if (ret) { + dev_err(&pdev->dev, "IRQ request returned %d\n", ret); + return ret; + } + + rst = devm_reset_control_get(&pdev->dev, NULL); + if (!IS_ERR(rst)) { + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &stm32_spdifrx_component, + stm32_spdifrx_dai, + ARRAY_SIZE(stm32_spdifrx_dai)); + if (ret) + return ret; + + ret = stm32_spdifrx_dma_ctrl_register(&pdev->dev, spdifrx); + if (ret) + goto error; + + pcm_config = &stm32_spdifrx_pcm_config; + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); + if (ret) { + dev_err(&pdev->dev, "PCM DMA register returned %d\n", ret); + goto error; + } + + return 0; + +error: + if (spdifrx->ctrl_chan) + dma_release_channel(spdifrx->ctrl_chan); + if (spdifrx->dmab) + snd_dma_free_pages(spdifrx->dmab); + + return ret; +} + +static int stm32_spdifrx_remove(struct platform_device *pdev) +{ + struct stm32_spdifrx_data *spdifrx = platform_get_drvdata(pdev); + + if (spdifrx->ctrl_chan) + dma_release_channel(spdifrx->ctrl_chan); + + if (spdifrx->dmab) + snd_dma_free_pages(spdifrx->dmab); + + return 0; +} + +MODULE_DEVICE_TABLE(of, stm32_spdifrx_ids); + +static struct platform_driver stm32_spdifrx_driver = { + .driver = { + .name = "st,stm32-spdifrx", + .of_match_table = stm32_spdifrx_ids, + }, + .probe = stm32_spdifrx_probe, + .remove = stm32_spdifrx_remove, +}; + +module_platform_driver(stm32_spdifrx_driver); + +MODULE_DESCRIPTION("STM32 Soc spdifrx Interface"); +MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>"); +MODULE_ALIAS("platform:stm32-spdifrx"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index c3aab10fa085..150069987c0c 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1339,6 +1339,44 @@ static struct snd_soc_card *sun8i_h3_codec_create_card(struct device *dev) return card; }; +static struct snd_soc_card *sun8i_v3s_codec_create_card(struct device *dev) +{ + struct snd_soc_card *card; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return ERR_PTR(-ENOMEM); + + aux_dev.codec_of_node = of_parse_phandle(dev->of_node, + "allwinner,codec-analog-controls", + 0); + if (!aux_dev.codec_of_node) { + dev_err(dev, "Can't find analog controls for codec.\n"); + return ERR_PTR(-EINVAL); + }; + + card->dai_link = sun4i_codec_create_link(dev, &card->num_links); + if (!card->dai_link) + return ERR_PTR(-ENOMEM); + + card->dev = dev; + card->name = "V3s Audio Codec"; + card->dapm_widgets = sun6i_codec_card_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets); + card->dapm_routes = sun8i_codec_card_routes; + card->num_dapm_routes = ARRAY_SIZE(sun8i_codec_card_routes); + card->aux_dev = &aux_dev; + card->num_aux_devs = 1; + card->fully_routed = true; + + ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); + if (ret) + dev_warn(dev, "failed to parse audio-routing: %d\n", ret); + + return card; +}; + static const struct regmap_config sun4i_codec_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -1374,6 +1412,13 @@ static const struct regmap_config sun8i_h3_codec_regmap_config = { .max_register = SUN8I_H3_CODEC_ADC_DBG, }; +static const struct regmap_config sun8i_v3s_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN8I_H3_CODEC_ADC_DBG, +}; + struct sun4i_codec_quirks { const struct regmap_config *regmap_config; const struct snd_soc_codec_driver *codec; @@ -1437,6 +1482,20 @@ static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = { .has_reset = true, }; +static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = { + .regmap_config = &sun8i_v3s_codec_regmap_config, + /* + * TODO The codec structure should be split out, like + * H3, when adding digital audio processing support. + */ + .codec = &sun8i_a23_codec_codec, + .create_card = sun8i_v3s_codec_create_card, + .reg_adc_fifoc = REG_FIELD(SUN6I_CODEC_ADC_FIFOC, 0, 31), + .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, + .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, + .has_reset = true, +}; + static const struct of_device_id sun4i_codec_of_match[] = { { .compatible = "allwinner,sun4i-a10-codec", @@ -1458,6 +1517,10 @@ static const struct of_device_id sun4i_codec_of_match[] = { .compatible = "allwinner,sun8i-h3-codec", .data = &sun8i_h3_codec_quirks, }, + { + .compatible = "allwinner,sun8i-v3s-codec", + .data = &sun8i_v3s_codec_quirks, + }, {} }; MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c index 6c17c99c2c8d..485e79f292c4 100644 --- a/sound/soc/sunxi/sun8i-codec-analog.c +++ b/sound/soc/sunxi/sun8i-codec-analog.c @@ -219,6 +219,22 @@ static const struct snd_kcontrol_new sun8i_codec_mixer_controls[] = { SUN8I_ADDA_LOMIXSC_MIC2, 1, 0), }; +/* mixer controls */ +static const struct snd_kcontrol_new sun8i_v3s_codec_mixer_controls[] = { + SOC_DAPM_DOUBLE_R("DAC Playback Switch", + SUN8I_ADDA_LOMIXSC, + SUN8I_ADDA_ROMIXSC, + SUN8I_ADDA_LOMIXSC_DACL, 1, 0), + SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch", + SUN8I_ADDA_LOMIXSC, + SUN8I_ADDA_ROMIXSC, + SUN8I_ADDA_LOMIXSC_DACR, 1, 0), + SOC_DAPM_DOUBLE_R("Mic1 Playback Switch", + SUN8I_ADDA_LOMIXSC, + SUN8I_ADDA_ROMIXSC, + SUN8I_ADDA_LOMIXSC_MIC1, 1, 0), +}; + /* ADC mixer controls */ static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = { SOC_DAPM_DOUBLE_R("Mixer Capture Switch", @@ -243,6 +259,22 @@ static const struct snd_kcontrol_new sun8i_codec_adc_mixer_controls[] = { SUN8I_ADDA_LADCMIXSC_MIC2, 1, 0), }; +/* ADC mixer controls */ +static const struct snd_kcontrol_new sun8i_v3s_codec_adc_mixer_controls[] = { + SOC_DAPM_DOUBLE_R("Mixer Capture Switch", + SUN8I_ADDA_LADCMIXSC, + SUN8I_ADDA_RADCMIXSC, + SUN8I_ADDA_LADCMIXSC_OMIXRL, 1, 0), + SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", + SUN8I_ADDA_LADCMIXSC, + SUN8I_ADDA_RADCMIXSC, + SUN8I_ADDA_LADCMIXSC_OMIXRR, 1, 0), + SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", + SUN8I_ADDA_LADCMIXSC, + SUN8I_ADDA_RADCMIXSC, + SUN8I_ADDA_LADCMIXSC_MIC1, 1, 0), +}; + /* volume / mute controls */ static const DECLARE_TLV_DB_SCALE(sun8i_codec_out_mixer_pregain_scale, -450, 150, 0); @@ -289,16 +321,12 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = { /* Microphone input */ SND_SOC_DAPM_INPUT("MIC1"), - /* Microphone Bias */ - SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, - SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN, - 0, NULL, 0), - /* Mic input path */ SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0), +}; - /* Mixers */ +static const struct snd_soc_dapm_widget sun8i_codec_mixer_widgets[] = { SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC, SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0, sun8i_codec_mixer_controls, @@ -317,10 +345,31 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = { ARRAY_SIZE(sun8i_codec_adc_mixer_controls)), }; +static const struct snd_soc_dapm_widget sun8i_v3s_codec_mixer_widgets[] = { + SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_LMIXEN, 0, + sun8i_v3s_codec_mixer_controls, + ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SUN8I_ADDA_DAC_PA_SRC, + SUN8I_ADDA_DAC_PA_SRC_RMIXEN, 0, + sun8i_v3s_codec_mixer_controls, + ARRAY_SIZE(sun8i_v3s_codec_mixer_controls)), + SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCLEN, 0, + sun8i_v3s_codec_adc_mixer_controls, + ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)), + SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN8I_ADDA_ADC_AP_EN, + SUN8I_ADDA_ADC_AP_EN_ADCREN, 0, + sun8i_v3s_codec_adc_mixer_controls, + ARRAY_SIZE(sun8i_v3s_codec_adc_mixer_controls)), +}; + static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = { /* Microphone Routes */ { "Mic1 Amplifier", NULL, "MIC1"}, +}; +static const struct snd_soc_dapm_route sun8i_codec_mixer_routes[] = { /* Left Mixer Routes */ { "Left Mixer", "DAC Playback Switch", "Left DAC" }, { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, @@ -453,6 +502,27 @@ static int sun8i_codec_add_headphone(struct snd_soc_component *cmpnt) return 0; } +/* mbias specific widget */ +static const struct snd_soc_dapm_widget sun8i_codec_mbias_widgets[] = { + SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, + SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MMICBIASEN, + 0, NULL, 0), +}; + +static int sun8i_codec_add_mbias(struct snd_soc_component *cmpnt) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); + struct device *dev = cmpnt->dev; + int ret; + + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mbias_widgets, + ARRAY_SIZE(sun8i_codec_mbias_widgets)); + if (ret) + dev_err(dev, "Failed to add MBIAS DAPM widgets: %d\n", ret); + + return ret; +} + /* hmic specific widget */ static const struct snd_soc_dapm_widget sun8i_codec_hmic_widgets[] = { SND_SOC_DAPM_SUPPLY("HBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL, @@ -679,6 +749,7 @@ struct sun8i_codec_analog_quirks { bool has_hmic; bool has_linein; bool has_lineout; + bool has_mbias; bool has_mic2; }; @@ -686,15 +757,64 @@ static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = { .has_headphone = true, .has_hmic = true, .has_linein = true, + .has_mbias = true, .has_mic2 = true, }; static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = { .has_linein = true, .has_lineout = true, + .has_mbias = true, .has_mic2 = true, }; +static int sun8i_codec_analog_add_mixer(struct snd_soc_component *cmpnt, + const struct sun8i_codec_analog_quirks *quirks) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); + struct device *dev = cmpnt->dev; + int ret; + + if (!quirks->has_mic2 && !quirks->has_linein) { + /* + * Apply the special widget set which has uses a control + * without MIC2 and Line In, for SoCs without these. + * TODO: not all special cases are supported now, this case + * is present because it's the case of V3s. + */ + ret = snd_soc_dapm_new_controls(dapm, + sun8i_v3s_codec_mixer_widgets, + ARRAY_SIZE(sun8i_v3s_codec_mixer_widgets)); + if (ret) { + dev_err(dev, "Failed to add V3s Mixer DAPM widgets: %d\n", ret); + return ret; + } + } else { + /* Apply the generic mixer widget set. */ + ret = snd_soc_dapm_new_controls(dapm, + sun8i_codec_mixer_widgets, + ARRAY_SIZE(sun8i_codec_mixer_widgets)); + if (ret) { + dev_err(dev, "Failed to add Mixer DAPM widgets: %d\n", ret); + return ret; + } + } + + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mixer_routes, + ARRAY_SIZE(sun8i_codec_mixer_routes)); + if (ret) { + dev_err(dev, "Failed to add Mixer DAPM routes: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct sun8i_codec_analog_quirks sun8i_v3s_quirks = { + .has_headphone = true, + .has_hmic = true, +}; + static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) { struct device *dev = cmpnt->dev; @@ -709,6 +829,9 @@ static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) quirks = of_device_get_match_data(dev); /* Add controls, widgets, and routes for individual features */ + ret = sun8i_codec_analog_add_mixer(cmpnt, quirks); + if (ret) + return ret; if (quirks->has_headphone) { ret = sun8i_codec_add_headphone(cmpnt); @@ -734,6 +857,12 @@ static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt) return ret; } + if (quirks->has_mbias) { + ret = sun8i_codec_add_mbias(cmpnt); + if (ret) + return ret; + } + if (quirks->has_mic2) { ret = sun8i_codec_add_mic2(cmpnt); if (ret) @@ -762,6 +891,10 @@ static const struct of_device_id sun8i_codec_analog_of_match[] = { .compatible = "allwinner,sun8i-h3-codec-analog", .data = &sun8i_h3_quirks, }, + { + .compatible = "allwinner,sun8i-v3s-codec-analog", + .data = &sun8i_v3s_quirks, + }, {} }; MODULE_DEVICE_TABLE(of, sun8i_codec_analog_of_match); diff --git a/sound/soc/zte/zx-i2s.c b/sound/soc/zte/zx-i2s.c index a865f37c2a56..8bbad1d72bc5 100644 --- a/sound/soc/zte/zx-i2s.c +++ b/sound/soc/zte/zx-i2s.c @@ -203,13 +203,15 @@ static int zx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - i2s->master = 1; - val |= ZX_I2S_TIMING_MAST; - break; - case SND_SOC_DAIFMT_CBS_CFS: + /* Codec is master, and I2S is slave. */ i2s->master = 0; val |= ZX_I2S_TIMING_SLAVE; break; + case SND_SOC_DAIFMT_CBS_CFS: + /* Codec is slave, and I2S is master. */ + i2s->master = 1; + val |= ZX_I2S_TIMING_MAST; + break; default: dev_err(cpu_dai->dev, "Unknown master/slave format\n"); return -EINVAL; @@ -226,11 +228,12 @@ static int zx_i2s_hw_params(struct snd_pcm_substream *substream, struct zx_i2s_info *i2s = snd_soc_dai_get_drvdata(socdai); struct snd_dmaengine_dai_dma_data *dma_data; unsigned int lane, ch_num, len, ret = 0; + unsigned int ts_width = 32; unsigned long val; unsigned long chn_cfg; dma_data = snd_soc_dai_get_dma_data(socdai, substream); - dma_data->addr_width = params_width(params) >> 3; + dma_data->addr_width = ts_width >> 3; val = readl_relaxed(i2s->reg_base + ZX_I2S_TIMING_CTRL); val &= ~(ZX_I2S_TIMING_TS_WIDTH_MASK | ZX_I2S_TIMING_DATA_SIZE_MASK | @@ -251,7 +254,7 @@ static int zx_i2s_hw_params(struct snd_pcm_substream *substream, dev_err(socdai->dev, "Unknown data format\n"); return -EINVAL; } - val |= ZX_I2S_TIMING_TS_WIDTH(len) | ZX_I2S_TIMING_DATA_SIZE(len); + val |= ZX_I2S_TIMING_TS_WIDTH(ts_width) | ZX_I2S_TIMING_DATA_SIZE(len); ch_num = params_channels(params); switch (ch_num) { diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 30bdc971883b..3d7d425fbd24 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -200,12 +200,12 @@ static unsigned char freq_bits[14] = { /* 48000 */ 0x0C | CS4231_XTAL1 }; -static unsigned int rates[14] = { +static const unsigned int rates[14] = { 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, 27042, 32000, 33075, 37800, 44100, 48000 }; -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { +static const struct snd_pcm_hw_constraint_list hw_constraints_rates = { .count = ARRAY_SIZE(rates), .list = rates, }; diff --git a/sound/synth/Kconfig b/sound/synth/Kconfig new file mode 100644 index 000000000000..dfe8950e0556 --- /dev/null +++ b/sound/synth/Kconfig @@ -0,0 +1,2 @@ +config SND_SYNTH_EMUX + tristate diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile index fb761c2c2b50..d1bac923eb1b 100644 --- a/sound/synth/emux/Makefile +++ b/sound/synth/emux/Makefile @@ -6,8 +6,8 @@ snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \ emux_effect.o emux_hwdep.o soundfont.o snd-emux-synth-$(CONFIG_SND_PROC_FS) += emux_proc.o -snd-emux-synth-$(CONFIG_SND_SEQUENCER_OSS) += emux_oss.o +ifneq ($(CONFIG_SND_SEQUENCER_OSS),) +snd-emux-synth-y += emux_oss.o +endif -# Toplevel Module Dependencies -obj-$(CONFIG_SND_SBAWE_SEQ) += snd-emux-synth.o -obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emux-synth.o +obj-$(CONFIG_SND_SYNTH_EMUX) += snd-emux-synth.o diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index 9312cd8a6fdd..b9981e8c0027 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -47,7 +47,7 @@ int snd_emux_new(struct snd_emux **remu) mutex_init(&emu->register_mutex); emu->client = -1; -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) emu->oss_synth = NULL; #endif emu->max_voices = 0; @@ -123,7 +123,7 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch snd_emux_init_voices(emu); snd_emux_init_seq(emu, card, index); -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) snd_emux_init_seq_oss(emu); #endif snd_emux_init_virmidi(emu, card); @@ -150,7 +150,7 @@ int snd_emux_free(struct snd_emux *emu) snd_emux_proc_free(emu); snd_emux_delete_virmidi(emu); -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) snd_emux_detach_seq_oss(emu); #endif snd_emux_detach_seq(emu); diff --git a/sound/synth/emux/emux_effect.c b/sound/synth/emux/emux_effect.c index a447218b6160..9ac0bf531b4b 100644 --- a/sound/synth/emux/emux_effect.c +++ b/sound/synth/emux/emux_effect.c @@ -150,7 +150,7 @@ effect_get_offset(struct snd_midi_channel *chan, int lo, int hi, int mode) return addr; } -#ifdef CONFIG_SND_SEQUENCER_OSS +#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) /* change effects - for OSS sequencer compatibility */ void snd_emux_send_effect_oss(struct snd_emux_port *port, diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c index 850fab4a8f3b..de19e108974a 100644 --- a/sound/synth/emux/emux_oss.c +++ b/sound/synth/emux/emux_oss.c @@ -23,8 +23,6 @@ */ -#ifdef CONFIG_SND_SEQUENCER_OSS - #include <linux/export.h> #include <linux/uaccess.h> #include <sound/core.h> @@ -505,5 +503,3 @@ fake_event(struct snd_emux *emu, struct snd_emux_port *port, int ch, int param, ev.data.control.value = val; snd_emux_event_input(&ev, 0, port, atomic, hop); } - -#endif /* CONFIG_SND_SEQUENCER_OSS */ diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index a452ad7cec40..f61b5662bb89 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -91,7 +91,7 @@ config SND_USB_CAIAQ_INPUT config SND_USB_US122L tristate "Tascam US-122L USB driver" - depends on X86 + depends on X86 || COMPILE_TEST select SND_HWDEP select SND_RAWMIDI help diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h index a5c2e9ae5f17..dc97895547be 100644 --- a/sound/usb/line6/driver.h +++ b/sound/usb/line6/driver.h @@ -117,6 +117,8 @@ enum { LINE6_CAP_IN_NEEDS_OUT = 1 << 3, /* device uses raw MIDI via USB (data endpoints) */ LINE6_CAP_CONTROL_MIDI = 1 << 4, + /* device provides low-level information */ + LINE6_CAP_CONTROL_INFO = 1 << 5, }; /* diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index 6ab23e5aee71..956f847a96e4 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -3,6 +3,7 @@ * * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com> * Copyright (C) 2015 Andrej Krutak <dev@andree.sk> + * Copyright (C) 2017 Hans P. Moller <hmoller@uc.cl> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -37,7 +38,8 @@ enum { LINE6_PODHD500_0, LINE6_PODHD500_1, LINE6_PODX3, - LINE6_PODX3LIVE + LINE6_PODX3LIVE, + LINE6_PODHD500X }; struct usb_line6_podhd { @@ -291,7 +293,7 @@ static void podhd_disconnect(struct usb_line6 *line6) { struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6; - if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { + if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO) { struct usb_interface *intf; del_timer_sync(&pod->startup_timer); @@ -331,7 +333,9 @@ static int podhd_init(struct usb_line6 *line6, pod->line6.properties->ctrl_if, err); return err; } + } + if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO) { /* create sysfs entries: */ err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group); if (err < 0) @@ -348,7 +352,7 @@ static int podhd_init(struct usb_line6 *line6, return err; } - if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) { + if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO)) { /* register USB audio system directly */ return podhd_startup_finalize(pod); } @@ -372,6 +376,7 @@ static const struct usb_device_id podhd_id_table[] = { { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 }, { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE }, + { LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X }, {} }; @@ -425,7 +430,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODX3] = { .id = "PODX3", .name = "POD X3", - .capabilities = LINE6_CAP_CONTROL + .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_CONTROL_INFO | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT, .altsetting = 1, .ep_ctrl_r = 0x81, @@ -437,7 +442,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODX3LIVE] = { .id = "PODX3LIVE", .name = "POD X3 LIVE", - .capabilities = LINE6_CAP_CONTROL + .capabilities = LINE6_CAP_CONTROL | LINE6_CAP_CONTROL_INFO | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT, .altsetting = 1, .ep_ctrl_r = 0x81, @@ -446,6 +451,18 @@ static const struct line6_properties podhd_properties_table[] = { .ep_audio_r = 0x86, .ep_audio_w = 0x02, }, + [LINE6_PODHD500X] = { + .id = "PODHD500X", + .name = "POD HD500X", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ctrl_if = 1, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, }; /* diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 4fa0053a40af..e3d1dec48ee4 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -362,7 +362,7 @@ static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list) } /* name and private_value are set dynamically */ -static struct snd_kcontrol_new snd_audigy2nx_control = { +static const struct snd_kcontrol_new snd_audigy2nx_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .info = snd_audigy2nx_led_info, .get = snd_audigy2nx_led_get, diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index e118bdca983d..a33e31b2fc2f 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -46,8 +46,10 @@ MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS"."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS"."); -static int snd_us122l_card_used[SNDRV_CARDS]; +/* driver_info flags */ +#define US122L_FLAG_US144 BIT(0) +static int snd_us122l_card_used[SNDRV_CARDS]; static int us122l_create_usbmidi(struct snd_card *card) { @@ -198,8 +200,7 @@ static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file) if (!us122l->first) us122l->first = file; - if (us122l->dev->descriptor.idProduct == USB_ID_US144 || - us122l->dev->descriptor.idProduct == USB_ID_US144MKII) { + if (us122l->is_us144) { iface = usb_ifnum_to_if(us122l->dev, 0); usb_autopm_get_interface(iface); } @@ -214,8 +215,7 @@ static int usb_stream_hwdep_release(struct snd_hwdep *hw, struct file *file) struct usb_interface *iface; snd_printdd(KERN_DEBUG "%p %p\n", hw, file); - if (us122l->dev->descriptor.idProduct == USB_ID_US144 || - us122l->dev->descriptor.idProduct == USB_ID_US144MKII) { + if (us122l->is_us144) { iface = usb_ifnum_to_if(us122l->dev, 0); usb_autopm_put_interface(iface); } @@ -483,8 +483,7 @@ static bool us122l_create_card(struct snd_card *card) int err; struct us122l *us122l = US122L(card); - if (us122l->dev->descriptor.idProduct == USB_ID_US144 || - us122l->dev->descriptor.idProduct == USB_ID_US144MKII) { + if (us122l->is_us144) { err = usb_set_interface(us122l->dev, 0, 1); if (err) { snd_printk(KERN_ERR "usb_set_interface error \n"); @@ -503,8 +502,7 @@ static bool us122l_create_card(struct snd_card *card) if (!us122l_start(us122l, 44100, 256)) return false; - if (us122l->dev->descriptor.idProduct == USB_ID_US144 || - us122l->dev->descriptor.idProduct == USB_ID_US144MKII) + if (us122l->is_us144) err = us144_create_usbmidi(card); else err = us122l_create_usbmidi(card); @@ -536,7 +534,8 @@ static void snd_us122l_free(struct snd_card *card) static int usx2y_create_card(struct usb_device *device, struct usb_interface *intf, - struct snd_card **cardp) + struct snd_card **cardp, + unsigned long flags) { int dev; struct snd_card *card; @@ -556,6 +555,7 @@ static int usx2y_create_card(struct usb_device *device, US122L(card)->dev = device; mutex_init(&US122L(card)->mutex); init_waitqueue_head(&US122L(card)->sk.sleep); + US122L(card)->is_us144 = flags & US122L_FLAG_US144; INIT_LIST_HEAD(&US122L(card)->midi_list); strcpy(card->driver, "USB "NAME_ALLCAPS""); sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); @@ -579,7 +579,7 @@ static int us122l_usb_probe(struct usb_interface *intf, struct snd_card *card; int err; - err = usx2y_create_card(device, intf, &card); + err = usx2y_create_card(device, intf, &card, device_id->driver_info); if (err < 0) return err; @@ -607,9 +607,8 @@ static int snd_us122l_probe(struct usb_interface *intf, struct snd_card *card; int err; - if ((device->descriptor.idProduct == USB_ID_US144 || - device->descriptor.idProduct == USB_ID_US144MKII) - && device->speed == USB_SPEED_HIGH) { + if (id->driver_info & US122L_FLAG_US144 && + device->speed == USB_SPEED_HIGH) { snd_printk(KERN_ERR "disable ehci-hcd to run US-144 \n"); return -ENODEV; } @@ -703,8 +702,7 @@ static int snd_us122l_resume(struct usb_interface *intf) mutex_lock(&us122l->mutex); /* needed, doesn't restart without: */ - if (us122l->dev->descriptor.idProduct == USB_ID_US144 || - us122l->dev->descriptor.idProduct == USB_ID_US144MKII) { + if (us122l->is_us144) { err = usb_set_interface(us122l->dev, 0, 1); if (err) { snd_printk(KERN_ERR "usb_set_interface error \n"); @@ -747,7 +745,8 @@ static struct usb_device_id snd_us122l_usb_id_table[] = { { /* US-144 only works at USB1.1! Disable module ehci-hcd. */ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x0644, - .idProduct = USB_ID_US144 + .idProduct = USB_ID_US144, + .driver_info = US122L_FLAG_US144 }, { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, @@ -757,7 +756,8 @@ static struct usb_device_id snd_us122l_usb_id_table[] = { { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x0644, - .idProduct = USB_ID_US144MKII + .idProduct = USB_ID_US144MKII, + .driver_info = US122L_FLAG_US144 }, { /* terminator */ } }; diff --git a/sound/usb/usx2y/us122l.h b/sound/usb/usx2y/us122l.h index f263b3f96c86..3e2a2d0041ee 100644 --- a/sound/usb/usx2y/us122l.h +++ b/sound/usb/usx2y/us122l.h @@ -16,6 +16,8 @@ struct us122l { struct list_head midi_list; atomic_t mmap_count; + + bool is_us144; }; diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index b11d3920b9a5..37f06ffdf1e6 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1699,8 +1699,8 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) /* get resources */ irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "Could not get irq resource\n"); - return -ENODEV; + dev_err(&pdev->dev, "Could not get irq resource: %d\n", irq); + return irq; } res_mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); |