mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-17 17:53:56 +08:00
ASoC: Updates for v5.5
Some big changes in the core but more about cleanps and refactorings than new features, plus a collection of new drivers and lots of small fixes and improvements to existing ones. - Lots more cleanups from Morimoto-san. Now that everything is a component this is mostly about refactorings to clarify and simplify the core, a combination of things that are no longer required due to refactorings and spotting similarities. - Many fixes to the Sound Open Firmware code. - Wake on voice support for Chromebooks. - SPI support for RT5677. - New drivers for Analog Devices ADAU7118, Intel Cannonlake systems with RT1011 and RT5682, Texas Instruments TAS2562 and TAS2770. -----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAl3EFLYTHGJyb29uaWVA a2VybmVsLm9yZwAKCRAk1otyXVSH0JCuB/40P/KPRGlEFBSJAwTfCkjQxzvQUGBy Y3w9QeMq3ONhCJt5BusmeuFqdkanzYqnl+NveGYKdKNTAwh6vEMGiMbGSB8dgrR5 R7PLNBDRJi7ZUDdOZle7VrdUiZWyieaZk/ecWxfTPKfqzoBjnM3XYSa30i55hvbQ A2MTimFsO7nf0caLNBLqEqBjy68IHQ3tuHH27kA0MIpVNsYVrjaWfM400ot3odbg 0vMpNTM+PDcQGkWcq3sKJBOVVjmGg2Xs1yM5hv6Mu+q1zXLCtCKj+Pv+ZXC3BT6e Yyxv/arpgvtjIU79Tv9RamVRC4jN6ZJRkThP9UW6JrX7tPCjvD+ygzPn =4FMp -----END PGP SIGNATURE----- Merge tag 'asoc-v5.5' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next ASoC: Updates for v5.5 Some big changes in the core but more about cleanps and refactorings than new features, plus a collection of new drivers and lots of small fixes and improvements to existing ones. - Lots more cleanups from Morimoto-san. Now that everything is a component this is mostly about refactorings to clarify and simplify the core, a combination of things that are no longer required due to refactorings and spotting similarities. - Many fixes to the Sound Open Firmware code. - Wake on voice support for Chromebooks. - SPI support for RT5677. - New drivers for Analog Devices ADAU7118, Intel Cannonlake systems with RT1011 and RT5682, Texas Instruments TAS2562 and TAS2770.
This commit is contained in:
commit
9ff7759731
85
Documentation/devicetree/bindings/sound/adi,adau7118.yaml
Normal file
85
Documentation/devicetree/bindings/sound/adi,adau7118.yaml
Normal file
@ -0,0 +1,85 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/adi,adau7118.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
|
||||
title: Analog Devices ADAU7118 8 Channel PDM to I2S/TDM Converter
|
||||
|
||||
maintainers:
|
||||
- Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices ADAU7118 8 Channel PDM to I2S/TDM Converter over I2C or HW
|
||||
standalone mode.
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ADAU7118.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adau7118
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#sound-dai-cells":
|
||||
const: 0
|
||||
|
||||
iovdd-supply:
|
||||
description: Digital Input/Output Power Supply.
|
||||
|
||||
dvdd-supply:
|
||||
description: Internal Core Digital Power Supply.
|
||||
|
||||
adi,decimation-ratio:
|
||||
description: |
|
||||
This property set's the decimation ratio of PDM to PCM audio data.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [64, 32, 16]
|
||||
default: 64
|
||||
|
||||
adi,pdm-clk-map:
|
||||
description: |
|
||||
The ADAU7118 has two PDM clocks for the four Inputs. Each input must be
|
||||
assigned to one of these two clocks. This property set's the mapping
|
||||
between the clocks and the inputs.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
- minItems: 4
|
||||
maxItems: 4
|
||||
items:
|
||||
maximum: 1
|
||||
default: [0, 0, 1, 1]
|
||||
|
||||
required:
|
||||
- "#sound-dai-cells"
|
||||
- compatible
|
||||
- iovdd-supply
|
||||
- dvdd-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
/* example with i2c support */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
adau7118_codec: audio-codec@14 {
|
||||
compatible = "adi,adau7118";
|
||||
reg = <0x14>;
|
||||
#sound-dai-cells = <0>;
|
||||
iovdd-supply = <&supply>;
|
||||
dvdd-supply = <&supply>;
|
||||
adi,pdm-clk-map = <1 1 0 0>;
|
||||
adi,decimation-ratio = <16>;
|
||||
};
|
||||
};
|
||||
|
||||
/* example with hw standalone mode */
|
||||
adau7118_codec_hw: adau7118-codec-hw {
|
||||
compatible = "adi,adau7118";
|
||||
#sound-dai-cells = <0>;
|
||||
iovdd-supply = <&supply>;
|
||||
dvdd-supply = <&supply>;
|
||||
};
|
@ -0,0 +1,267 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/allwinner,sun4i-a10-codec.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A10 Codec Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
"#sound-dai-cells":
|
||||
const: 0
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun4i-a10-codec
|
||||
- allwinner,sun6i-a31-codec
|
||||
- allwinner,sun7i-a20-codec
|
||||
- allwinner,sun8i-a23-codec
|
||||
- allwinner,sun8i-h3-codec
|
||||
- allwinner,sun8i-v3s-codec
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: apb
|
||||
- const: codec
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: RX DMA Channel
|
||||
- description: TX DMA Channel
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
allwinner,audio-routing:
|
||||
description: |-
|
||||
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.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#definitions/non-unique-string-array
|
||||
- minItems: 2
|
||||
maxItems: 18
|
||||
items:
|
||||
enum:
|
||||
# Audio Pins on the SoC
|
||||
- HP
|
||||
- HPCOM
|
||||
- LINEIN
|
||||
- LINEOUT
|
||||
- MIC1
|
||||
- MIC2
|
||||
- MIC3
|
||||
|
||||
# Microphone Biases from the SoC
|
||||
- HBIAS
|
||||
- MBIAS
|
||||
|
||||
# Board Connectors
|
||||
- Headphone
|
||||
- Headset Mic
|
||||
- Line In
|
||||
- Line Out
|
||||
- Mic
|
||||
- Speaker
|
||||
|
||||
allwinner,codec-analog-controls:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: Phandle to the codec analog controls in the PRCM
|
||||
|
||||
allwinner,pa-gpios:
|
||||
description: GPIO to enable the external amplifier
|
||||
|
||||
required:
|
||||
- "#sound-dai-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- dmas
|
||||
- dma-names
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun6i-a31-codec
|
||||
- allwinner,sun8i-a23-codec
|
||||
- allwinner,sun8i-h3-codec
|
||||
- allwinner,sun8i-v3s-codec
|
||||
|
||||
then:
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
const: allwinner,sun6i-a31-codec
|
||||
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
- allwinner,audio-routing
|
||||
|
||||
else:
|
||||
required:
|
||||
- resets
|
||||
- allwinner,audio-routing
|
||||
- allwinner,codec-analog-controls
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun6i-a31-codec
|
||||
|
||||
then:
|
||||
properties:
|
||||
allwinner,audio-routing:
|
||||
items:
|
||||
enum:
|
||||
- HP
|
||||
- HPCOM
|
||||
- LINEIN
|
||||
- LINEOUT
|
||||
- MIC1
|
||||
- MIC2
|
||||
- MIC3
|
||||
- HBIAS
|
||||
- MBIAS
|
||||
- Headphone
|
||||
- Headset Mic
|
||||
- Line In
|
||||
- Line Out
|
||||
- Mic
|
||||
- Speaker
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun8i-a23-codec
|
||||
|
||||
then:
|
||||
properties:
|
||||
allwinner,audio-routing:
|
||||
items:
|
||||
enum:
|
||||
- HP
|
||||
- HPCOM
|
||||
- LINEIN
|
||||
- MIC1
|
||||
- MIC2
|
||||
- HBIAS
|
||||
- MBIAS
|
||||
- Headphone
|
||||
- Headset Mic
|
||||
- Line In
|
||||
- Line Out
|
||||
- Mic
|
||||
- Speaker
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun8i-h3-codec
|
||||
|
||||
then:
|
||||
properties:
|
||||
allwinner,audio-routing:
|
||||
items:
|
||||
enum:
|
||||
- HP
|
||||
- HPCOM
|
||||
- LINEIN
|
||||
- LINEOUT
|
||||
- MIC1
|
||||
- MIC2
|
||||
- HBIAS
|
||||
- MBIAS
|
||||
- Headphone
|
||||
- Headset Mic
|
||||
- Line In
|
||||
- Line Out
|
||||
- Mic
|
||||
- Speaker
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun8i-v3s-codec
|
||||
|
||||
then:
|
||||
properties:
|
||||
allwinner,audio-routing:
|
||||
items:
|
||||
enum:
|
||||
- HP
|
||||
- HPCOM
|
||||
- MIC1
|
||||
- HBIAS
|
||||
- Headphone
|
||||
- Headset Mic
|
||||
- Line In
|
||||
- Line Out
|
||||
- Mic
|
||||
- Speaker
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
codec@1c22c00 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "allwinner,sun7i-a20-codec";
|
||||
reg = <0x01c22c00 0x40>;
|
||||
interrupts = <0 30 4>;
|
||||
clocks = <&apb0_gates 0>, <&codec_clk>;
|
||||
clock-names = "apb", "codec";
|
||||
dmas = <&dma 0 19>, <&dma 0 19>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
|
||||
- |
|
||||
codec@1c22c00 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "allwinner,sun6i-a31-codec";
|
||||
reg = <0x01c22c00 0x98>;
|
||||
interrupts = <0 29 4>;
|
||||
clocks = <&ccu 61>, <&ccu 135>;
|
||||
clock-names = "apb", "codec";
|
||||
resets = <&ccu 42>;
|
||||
dmas = <&dma 15>, <&dma 15>;
|
||||
dma-names = "rx", "tx";
|
||||
allwinner,audio-routing =
|
||||
"Headphone", "HP",
|
||||
"Speaker", "LINEOUT",
|
||||
"LINEIN", "Line In",
|
||||
"MIC1", "MBIAS",
|
||||
"MIC1", "Mic",
|
||||
"MIC2", "HBIAS",
|
||||
"MIC2", "Headset Mic";
|
||||
};
|
||||
|
||||
...
|
@ -0,0 +1,38 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/allwinner,sun8i-a23-codec-analog.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A23 Analog Codec Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
# FIXME: This is documented in the PRCM binding, but needs to be
|
||||
# migrated here at some point
|
||||
# - allwinner,sun8i-a23-codec-analog
|
||||
- allwinner,sun8i-h3-codec-analog
|
||||
- allwinner,sun8i-v3s-codec-analog
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
codec_analog: codec-analog@1f015c0 {
|
||||
compatible = "allwinner,sun8i-h3-codec-analog";
|
||||
reg = <0x01f015c0 0x4>;
|
||||
};
|
||||
|
||||
...
|
@ -1,8 +1,9 @@
|
||||
Audio Binding for Arndale boards
|
||||
|
||||
Required properties:
|
||||
- compatible : Can be the following,
|
||||
"samsung,arndale-rt5631"
|
||||
- compatible : Can be one of the following:
|
||||
"samsung,arndale-rt5631",
|
||||
"samsung,arndale-wm1811"
|
||||
|
||||
- samsung,audio-cpu: The phandle of the Samsung I2S controller
|
||||
- samsung,audio-codec: The phandle of the audio codec
|
||||
|
36
Documentation/devicetree/bindings/sound/fsl,mqs.txt
Normal file
36
Documentation/devicetree/bindings/sound/fsl,mqs.txt
Normal file
@ -0,0 +1,36 @@
|
||||
fsl,mqs audio CODEC
|
||||
|
||||
Required properties:
|
||||
- compatible : Must contain one of "fsl,imx6sx-mqs", "fsl,codec-mqs"
|
||||
"fsl,imx8qm-mqs", "fsl,imx8qxp-mqs".
|
||||
- clocks : A list of phandles + clock-specifiers, one for each entry in
|
||||
clock-names
|
||||
- clock-names : "mclk" - must required.
|
||||
"core" - required if compatible is "fsl,imx8qm-mqs", it
|
||||
is for register access.
|
||||
- gpr : A phandle of General Purpose Registers in IOMUX Controller.
|
||||
Required if compatible is "fsl,imx6sx-mqs".
|
||||
|
||||
Required if compatible is "fsl,imx8qm-mqs":
|
||||
- power-domains: A phandle of PM domain provider node.
|
||||
- reg: Offset and length of the register set for the device.
|
||||
|
||||
Example:
|
||||
|
||||
mqs: mqs {
|
||||
compatible = "fsl,imx6sx-mqs";
|
||||
gpr = <&gpr>;
|
||||
clocks = <&clks IMX6SX_CLK_SAI1>;
|
||||
clock-names = "mclk";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
mqs: mqs@59850000 {
|
||||
compatible = "fsl,imx8qm-mqs";
|
||||
reg = <0x59850000 0x10000>;
|
||||
clocks = <&clk IMX8QM_AUD_MQS_IPG>,
|
||||
<&clk IMX8QM_AUD_MQS_HMCLK>;
|
||||
clock-names = "core", "mclk";
|
||||
power-domains = <&pd_mqs0>;
|
||||
status = "disabled";
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
* Audio codec controlled by ChromeOS EC
|
||||
Audio codec controlled by ChromeOS EC
|
||||
|
||||
Google's ChromeOS EC codec is a digital mic codec provided by the
|
||||
Embedded Controller (EC) and is controlled via a host-command interface.
|
||||
@ -9,10 +9,27 @@ Documentation/devicetree/bindings/mfd/cros-ec.txt).
|
||||
Required properties:
|
||||
- compatible: Must contain "google,cros-ec-codec"
|
||||
- #sound-dai-cells: Should be 1. The cell specifies number of DAIs.
|
||||
- max-dmic-gain: A number for maximum gain in dB on digital microphone.
|
||||
|
||||
Optional properties:
|
||||
- reg: Pysical base address and length of shared memory region from EC.
|
||||
It contains 3 unsigned 32-bit integer. The first 2 integers
|
||||
combine to become an unsigned 64-bit physical address. The last
|
||||
one integer is length of the shared memory.
|
||||
- memory-region: Shared memory region to EC. A "shared-dma-pool". See
|
||||
../reserved-memory/reserved-memory.txt for details.
|
||||
|
||||
Example:
|
||||
|
||||
{
|
||||
...
|
||||
|
||||
reserved_mem: reserved_mem {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0 0x52800000 0 0x100000>;
|
||||
no-map;
|
||||
};
|
||||
}
|
||||
|
||||
cros-ec@0 {
|
||||
compatible = "google,cros-ec-spi";
|
||||
|
||||
@ -21,6 +38,7 @@ cros-ec@0 {
|
||||
cros_ec_codec: ec-codec {
|
||||
compatible = "google,cros-ec-codec";
|
||||
#sound-dai-cells = <1>;
|
||||
max-dmic-gain = <43>;
|
||||
reg = <0x0 0x10500000 0x80000>;
|
||||
memory-region = <&reserved_mem>;
|
||||
};
|
||||
};
|
||||
|
@ -4,6 +4,10 @@ Required properties:
|
||||
- compatible = "mediatek,mt68183-audio";
|
||||
- reg: register location and size
|
||||
- interrupts: should contain AFE interrupt
|
||||
- resets: Must contain an entry for each entry in reset-names
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names: should have these reset names:
|
||||
"audiosys";
|
||||
- power-domains: should define the power domain
|
||||
- clocks: Must contain an entry for each entry in clock-names
|
||||
- clock-names: should have these clock names:
|
||||
@ -20,6 +24,8 @@ Example:
|
||||
compatible = "mediatek,mt8183-audio";
|
||||
reg = <0 0x11220000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_LOW>;
|
||||
resets = <&watchdog MT8183_TOPRGU_AUDIO_SW_RST>;
|
||||
reset-names = "audiosys";
|
||||
power-domains = <&scpsys MT8183_POWER_DOMAIN_AUDIO>;
|
||||
clocks = <&infrasys CLK_INFRA_AUDIO>,
|
||||
<&infrasys CLK_INFRA_AUDIO_26M_BCLK>,
|
||||
|
@ -2,14 +2,19 @@ MT8183 with MT6358, TS3A227 and MAX98357 CODECS
|
||||
|
||||
Required properties:
|
||||
- compatible : "mediatek,mt8183_mt6358_ts3a227_max98357"
|
||||
- mediatek,headset-codec: the phandles of ts3a227 codecs
|
||||
- mediatek,platform: the phandle of MT8183 ASoC platform
|
||||
|
||||
Optional properties:
|
||||
- mediatek,headset-codec: the phandles of ts3a227 codecs
|
||||
- mediatek,ec-codec: the phandle of EC codecs.
|
||||
See google,cros-ec-codec.txt for more details.
|
||||
|
||||
Example:
|
||||
|
||||
sound {
|
||||
compatible = "mediatek,mt8183_mt6358_ts3a227_max98357";
|
||||
mediatek,headset-codec = <&ts3a227>;
|
||||
mediatek,ec-codec = <&ec_codec>;
|
||||
mediatek,platform = <&afe>;
|
||||
};
|
||||
|
||||
|
@ -268,6 +268,7 @@ Required properties:
|
||||
- "renesas,rcar_sound-r8a7745" (RZ/G1E)
|
||||
- "renesas,rcar_sound-r8a77470" (RZ/G1C)
|
||||
- "renesas,rcar_sound-r8a774a1" (RZ/G2M)
|
||||
- "renesas,rcar_sound-r8a774b1" (RZ/G2N)
|
||||
- "renesas,rcar_sound-r8a774c0" (RZ/G2E)
|
||||
- "renesas,rcar_sound-r8a7778" (R-Car M1A)
|
||||
- "renesas,rcar_sound-r8a7779" (R-Car H1)
|
||||
|
@ -5,11 +5,16 @@ Required properties:
|
||||
- rockchip,model: The user-visible name of this sound complex
|
||||
- rockchip,i2s-controller: The phandle of the Rockchip I2S controller that's
|
||||
connected to the CODEC
|
||||
- rockchip,audio-codec: The phandle of the MAX98090 audio codec
|
||||
- rockchip,headset-codec: The phandle of Ext chip for jack detection
|
||||
|
||||
Optional properties:
|
||||
- rockchip,audio-codec: The phandle of the MAX98090 audio codec.
|
||||
- rockchip,headset-codec: The phandle of Ext chip for jack detection. This is
|
||||
required if there is rockchip,audio-codec.
|
||||
- rockchip,hdmi-codec: The phandle of HDMI device for HDMI codec.
|
||||
|
||||
Example:
|
||||
|
||||
/* For max98090-only board. */
|
||||
sound {
|
||||
compatible = "rockchip,rockchip-audio-max98090";
|
||||
rockchip,model = "ROCKCHIP-I2S";
|
||||
@ -17,3 +22,21 @@ sound {
|
||||
rockchip,audio-codec = <&max98090>;
|
||||
rockchip,headset-codec = <&headsetcodec>;
|
||||
};
|
||||
|
||||
/* For HDMI-only board. */
|
||||
sound {
|
||||
compatible = "rockchip,rockchip-audio-max98090";
|
||||
rockchip,model = "ROCKCHIP-I2S";
|
||||
rockchip,i2s-controller = <&i2s>;
|
||||
rockchip,hdmi-codec = <&hdmi>;
|
||||
};
|
||||
|
||||
/* For max98090 plus HDMI board. */
|
||||
sound {
|
||||
compatible = "rockchip,rockchip-audio-max98090";
|
||||
rockchip,model = "ROCKCHIP-I2S";
|
||||
rockchip,i2s-controller = <&i2s>;
|
||||
rockchip,audio-codec = <&max98090>;
|
||||
rockchip,headset-codec = <&headsetcodec>;
|
||||
rockchip,hdmi-codec = <&hdmi>;
|
||||
};
|
||||
|
@ -20,6 +20,14 @@ Required properties:
|
||||
| 1 | 1 | 0x3b |
|
||||
-------------------------------------
|
||||
|
||||
Optional properties:
|
||||
|
||||
- realtek,temperature_calib
|
||||
u32. The temperature was measured while doing the calibration. Units: Celsius degree
|
||||
|
||||
- realtek,r0_calib
|
||||
u32. This is r0 calibration data which was measured in factory mode.
|
||||
|
||||
Pins on the device (for linking into audio routes) for RT1011:
|
||||
|
||||
* SPO
|
||||
@ -29,4 +37,6 @@ Example:
|
||||
rt1011: codec@38 {
|
||||
compatible = "realtek,rt1011";
|
||||
reg = <0x38>;
|
||||
realtek,temperature_calib = <25>;
|
||||
realtek,r0_calib = <0x224050>;
|
||||
};
|
||||
|
@ -27,6 +27,11 @@ Optional properties:
|
||||
|
||||
- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
|
||||
|
||||
- realtek,btndet-delay
|
||||
The debounce delay for push button.
|
||||
The delay time is realtek,btndet-delay value multiple of 8.192 ms.
|
||||
If absent, the default is 16.
|
||||
|
||||
Pins on the device (for linking into audio routes) for RT5682:
|
||||
|
||||
* DMIC L1
|
||||
@ -47,4 +52,5 @@ rt5682 {
|
||||
realtek,dmic1-data-pin = <1>;
|
||||
realtek,dmic1-clk-pin = <1>;
|
||||
realtek,jd-src = <1>;
|
||||
realtek,btndet-delay = <16>;
|
||||
};
|
||||
|
@ -1,54 +0,0 @@
|
||||
Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible - "hardkernel,odroid-xu3-audio" - for Odroid XU3 board,
|
||||
"hardkernel,odroid-xu4-audio" - for Odroid XU4 board (deprecated),
|
||||
"samsung,odroid-xu3-audio" - for Odroid XU3 board (deprecated),
|
||||
"samsung,odroid-xu4-audio" - for Odroid XU4 board (deprecated)
|
||||
- model - the user-visible name of this sound complex
|
||||
- clocks - should contain entries matching clock names in the clock-names
|
||||
property
|
||||
- samsung,audio-widgets - this property specifies off-codec audio elements
|
||||
like headphones or speakers, for details see widgets.txt
|
||||
- samsung,audio-routing - a list of the connections between audio
|
||||
components; each entry is a pair of strings, the first being the
|
||||
connection's sink, the second being the connection's source;
|
||||
valid names for sources and sinks are the MAX98090's pins (as
|
||||
documented in its binding), and the jacks on the board
|
||||
|
||||
For Odroid X2:
|
||||
"Headphone Jack", "Mic Jack", "DMIC"
|
||||
|
||||
For Odroid U3, XU3:
|
||||
"Headphone Jack", "Speakers"
|
||||
|
||||
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 = "hardkernel,odroid-xu3-audio";
|
||||
model = "Odroid-XU3";
|
||||
samsung,audio-routing =
|
||||
"Headphone Jack", "HPL",
|
||||
"Headphone Jack", "HPR",
|
||||
"IN1", "Mic Jack",
|
||||
"Mic Jack", "MICBIAS";
|
||||
|
||||
cpu {
|
||||
sound-dai = <&i2s0 0>;
|
||||
};
|
||||
codec {
|
||||
sound-dai = <&hdmi>, <&max98090>;
|
||||
};
|
||||
};
|
91
Documentation/devicetree/bindings/sound/samsung,odroid.yaml
Normal file
91
Documentation/devicetree/bindings/sound/samsung,odroid.yaml
Normal file
@ -0,0 +1,91 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/samsung,odroid.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
- Sylwester Nawrocki <s.nawrocki@samsung.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: hardkernel,odroid-xu3-audio
|
||||
|
||||
- const: hardkernel,odroid-xu4-audio
|
||||
deprecated: true
|
||||
|
||||
- const: samsung,odroid-xu3-audio
|
||||
deprecated: true
|
||||
|
||||
- const: samsung,odroid-xu4-audio
|
||||
deprecated: true
|
||||
|
||||
model:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: The user-visible name of this sound complex.
|
||||
|
||||
cpu:
|
||||
type: object
|
||||
properties:
|
||||
sound-dai:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description: phandles to the I2S controllers
|
||||
|
||||
codec:
|
||||
type: object
|
||||
properties:
|
||||
sound-dai:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description: |
|
||||
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.
|
||||
|
||||
samsung,audio-routing:
|
||||
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
|
||||
description: |
|
||||
List of the connections between audio
|
||||
components; each entry is a pair of strings, the first being the
|
||||
connection's sink, the second being the connection's source;
|
||||
valid names for sources and sinks are the MAX98090's pins (as
|
||||
documented in its binding), and the jacks on the board.
|
||||
For Odroid X2: "Headphone Jack", "Mic Jack", "DMIC"
|
||||
For Odroid U3, XU3: "Headphone Jack", "Speakers"
|
||||
For Odroid XU4: no entries
|
||||
|
||||
samsung,audio-widgets:
|
||||
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
|
||||
description: |
|
||||
This property specifies off-codec audio elements
|
||||
like headphones or speakers, for details see widgets.txt
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- model
|
||||
- cpu
|
||||
- codec
|
||||
|
||||
examples:
|
||||
- |
|
||||
sound {
|
||||
compatible = "hardkernel,odroid-xu3-audio";
|
||||
model = "Odroid-XU3";
|
||||
samsung,audio-routing =
|
||||
"Headphone Jack", "HPL",
|
||||
"Headphone Jack", "HPR",
|
||||
"IN1", "Mic Jack",
|
||||
"Mic Jack", "MICBIAS";
|
||||
|
||||
cpu {
|
||||
sound-dai = <&i2s0 0>;
|
||||
};
|
||||
|
||||
codec {
|
||||
sound-dai = <&hdmi>, <&max98090>;
|
||||
};
|
||||
};
|
||||
|
@ -1,84 +0,0 @@
|
||||
* Samsung I2S controller
|
||||
|
||||
Required SoC Specific Properties:
|
||||
|
||||
- compatible : should be one of the following.
|
||||
- samsung,s3c6410-i2s: for 8/16/24bit stereo I2S.
|
||||
- samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with
|
||||
secondary fifo, s/w reset control and internal mux for root clk src.
|
||||
- samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for
|
||||
playback, stereo channel capture, secondary fifo using internal
|
||||
or external dma, s/w reset control, internal mux for root clk src
|
||||
and 7.1 channel TDM support for playback. TDM (Time division multiplexing)
|
||||
is to allow transfer of multiple channel audio data on single data line.
|
||||
- samsung,exynos7-i2s: with all the available features of exynos5 i2s,
|
||||
exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo
|
||||
with only external dma and more no.of root clk sampling frequencies.
|
||||
- samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports
|
||||
stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with
|
||||
slightly modified bit offsets.
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- dmas: list of DMA controller phandle and DMA request line ordered pairs.
|
||||
- dma-names: identifier string for each DMA request line in the dmas property.
|
||||
These strings correspond 1:1 with the ordered pairs in dmas.
|
||||
- clocks: Handle to iis clock and RCLK source clk.
|
||||
- clock-names:
|
||||
i2s0 uses some base clocks from CMU and some are from audio subsystem internal
|
||||
clock controller. The clock names for i2s0 should be "iis", "i2s_opclk0" and
|
||||
"i2s_opclk1" as shown in the example below.
|
||||
i2s1 and i2s2 uses clocks from CMU. The clock names for i2s1 and i2s2 should
|
||||
be "iis" and "i2s_opclk0".
|
||||
"iis" is the i2s bus clock and i2s_opclk0, i2s_opclk1 are sources of the root
|
||||
clk. i2s0 has internal mux to select the source of root clk and i2s1 and i2s2
|
||||
doesn't have any such mux.
|
||||
- #clock-cells: should be 1, this property must be present if the I2S device
|
||||
is a clock provider in terms of the common clock bindings, described in
|
||||
../clock/clock-bindings.txt.
|
||||
- clock-output-names (deprecated): from the common clock bindings, names of
|
||||
the CDCLK I2S output clocks, suggested values are "i2s_cdclk0", "i2s_cdclk1",
|
||||
"i2s_cdclk3" for the I2S0, I2S1, I2S2 devices respectively.
|
||||
|
||||
There are following clocks available at the I2S device nodes:
|
||||
CLK_I2S_CDCLK - the CDCLK (CODECLKO) gate clock,
|
||||
CLK_I2S_RCLK_PSR - the RCLK prescaler divider clock (corresponding to the
|
||||
IISPSR register),
|
||||
CLK_I2S_RCLK_SRC - the RCLKSRC mux clock (corresponding to RCLKSRC bit in
|
||||
IISMOD register).
|
||||
|
||||
Refer to the SoC datasheet for availability of the above clocks.
|
||||
The CLK_I2S_RCLK_PSR and CLK_I2S_RCLK_SRC clocks are usually only available
|
||||
in the IIS Multi Audio Interface.
|
||||
|
||||
Note: Old DTs may not have the #clock-cells property and then not use the I2S
|
||||
node as a clock supplier.
|
||||
|
||||
Optional SoC Specific Properties:
|
||||
|
||||
- samsung,idma-addr: Internal DMA register base address of the audio
|
||||
sub system(used in secondary sound source).
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
- #sound-dai-cells: should be 1.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
i2s0: i2s@3830000 {
|
||||
compatible = "samsung,s5pv210-i2s";
|
||||
reg = <0x03830000 0x100>;
|
||||
dmas = <&pdma0 10
|
||||
&pdma0 9
|
||||
&pdma0 8>;
|
||||
dma-names = "tx", "rx", "tx-sec";
|
||||
clocks = <&clock_audss EXYNOS_I2S_BUS>,
|
||||
<&clock_audss EXYNOS_I2S_BUS>,
|
||||
<&clock_audss EXYNOS_SCLK_I2S>;
|
||||
clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
|
||||
#clock-cells = <1>;
|
||||
samsung,idma-addr = <0x03000000>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2s0_bus>;
|
||||
#sound-dai-cells = <1>;
|
||||
};
|
138
Documentation/devicetree/bindings/sound/samsung-i2s.yaml
Normal file
138
Documentation/devicetree/bindings/sound/samsung-i2s.yaml
Normal file
@ -0,0 +1,138 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/samsung-i2s.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung SoC I2S controller
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
- Sylwester Nawrocki <s.nawrocki@samsung.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
description: |
|
||||
samsung,s3c6410-i2s: for 8/16/24bit stereo I2S.
|
||||
|
||||
samsung,s5pv210-i2s: for 8/16/24bit multichannel (5.1) I2S with
|
||||
secondary FIFO, s/w reset control and internal mux for root clock
|
||||
source.
|
||||
|
||||
samsung,exynos5420-i2s: for 8/16/24bit multichannel (5.1) I2S for
|
||||
playback, stereo channel capture, secondary FIFO using internal
|
||||
or external DMA, s/w reset control, internal mux for root clock
|
||||
source and 7.1 channel TDM support for playback; TDM (Time division
|
||||
multiplexing) is to allow transfer of multiple channel audio data on
|
||||
single data line.
|
||||
|
||||
samsung,exynos7-i2s: with all the available features of Exynos5 I2S.
|
||||
Exynos7 I2S has 7.1 channel TDM support for capture, secondary FIFO
|
||||
with only external DMA and more number of root clock sampling
|
||||
frequencies.
|
||||
|
||||
samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports
|
||||
stereo channels. Exynos7 I2S1 upgraded to 5.1 multichannel with
|
||||
slightly modified bit offsets.
|
||||
enum:
|
||||
- samsung,s3c6410-i2s
|
||||
- samsung,s5pv210-i2s
|
||||
- samsung,exynos5420-i2s
|
||||
- samsung,exynos7-i2s
|
||||
- samsung,exynos7-i2s1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
dma-names:
|
||||
oneOf:
|
||||
- items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
- items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
- const: tx-sec
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
oneOf:
|
||||
- items:
|
||||
- const: iis
|
||||
- items: # for I2S0
|
||||
- const: iis
|
||||
- const: i2s_opclk0
|
||||
- const: i2s_opclk1
|
||||
- items: # for I2S1 and I2S2
|
||||
- const: iis
|
||||
- const: i2s_opclk0
|
||||
description: |
|
||||
"iis" is the I2S bus clock and i2s_opclk0, i2s_opclk1 are sources
|
||||
of the root clock. I2S0 has internal mux to select the source
|
||||
of root clock and I2S1 and I2S2 doesn't have any such mux.
|
||||
|
||||
"#clock-cells":
|
||||
const: 1
|
||||
|
||||
clock-output-names:
|
||||
deprecated: true
|
||||
oneOf:
|
||||
- items: # for I2S0
|
||||
- const: i2s_cdclk0
|
||||
- items: # for I2S1
|
||||
- const: i2s_cdclk1
|
||||
- items: # for I2S2
|
||||
- const: i2s_cdclk2
|
||||
description: Names of the CDCLK I2S output clocks.
|
||||
|
||||
samsung,idma-addr:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Internal DMA register base address of the audio
|
||||
subsystem (used in secondary sound source).
|
||||
|
||||
pinctrl-0:
|
||||
description: Should specify pin control groups used for this controller.
|
||||
|
||||
pinctrl-names:
|
||||
const: default
|
||||
|
||||
"#sound-dai-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- dmas
|
||||
- dma-names
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos-audss-clk.h>
|
||||
|
||||
i2s0: i2s@3830000 {
|
||||
compatible = "samsung,s5pv210-i2s";
|
||||
reg = <0x03830000 0x100>;
|
||||
dmas = <&pdma0 10>,
|
||||
<&pdma0 9>,
|
||||
<&pdma0 8>;
|
||||
dma-names = "tx", "rx", "tx-sec";
|
||||
clocks = <&clock_audss EXYNOS_I2S_BUS>,
|
||||
<&clock_audss EXYNOS_I2S_BUS>,
|
||||
<&clock_audss EXYNOS_SCLK_I2S>;
|
||||
clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
|
||||
#clock-cells = <1>;
|
||||
samsung,idma-addr = <0x03000000>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2s0_bus>;
|
||||
#sound-dai-cells = <1>;
|
||||
};
|
@ -1,94 +0,0 @@
|
||||
* Allwinner A10 Codec
|
||||
|
||||
Required properties:
|
||||
- compatible: must be one of the following compatibles:
|
||||
- "allwinner,sun4i-a10-codec"
|
||||
- "allwinner,sun6i-a31-codec"
|
||||
- "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,
|
||||
Documentation/devicetree/bindings/dma/dma.txt
|
||||
- dma-names: should include "tx" and "rx".
|
||||
- clocks: a list of phandle + clock-specifer pairs, one for each entry
|
||||
in clock-names.
|
||||
- clock-names: should contain the following:
|
||||
- "apb": the parent APB clock for this controller
|
||||
- "codec": the parent module clock
|
||||
|
||||
Optional properties:
|
||||
- allwinner,pa-gpios: gpio to enable external amplifier
|
||||
|
||||
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
|
||||
connection's sink, the second being the connection's
|
||||
source. Valid names include:
|
||||
|
||||
Audio pins on the SoC:
|
||||
"HP"
|
||||
"HPCOM"
|
||||
"LINEIN" (not on sun8i-v3s)
|
||||
"LINEOUT" (not on sun8i-a23 or sun8i-v3s)
|
||||
"MIC1"
|
||||
"MIC2" (not on sun8i-v3s)
|
||||
"MIC3" (sun6i-a31 only)
|
||||
|
||||
Microphone biases from the SoC:
|
||||
"HBIAS"
|
||||
"MBIAS" (not on sun8i-v3s)
|
||||
|
||||
Board connectors:
|
||||
"Headphone"
|
||||
"Headset Mic"
|
||||
"Line In"
|
||||
"Line Out"
|
||||
"Mic"
|
||||
"Speaker"
|
||||
|
||||
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.
|
||||
|
||||
Example:
|
||||
codec: codec@1c22c00 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "allwinner,sun7i-a20-codec";
|
||||
reg = <0x01c22c00 0x40>;
|
||||
interrupts = <0 30 4>;
|
||||
clocks = <&apb0_gates 0>, <&codec_clk>;
|
||||
clock-names = "apb", "codec";
|
||||
dmas = <&dma 0 19>, <&dma 0 19>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
|
||||
codec: codec@1c22c00 {
|
||||
#sound-dai-cells = <0>;
|
||||
compatible = "allwinner,sun6i-a31-codec";
|
||||
reg = <0x01c22c00 0x98>;
|
||||
interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&ccu CLK_APB1_CODEC>, <&ccu CLK_CODEC>;
|
||||
clock-names = "apb", "codec";
|
||||
resets = <&ccu RST_APB1_CODEC>;
|
||||
dmas = <&dma 15>, <&dma 15>;
|
||||
dma-names = "rx", "tx";
|
||||
allwinner,audio-routing =
|
||||
"Headphone", "HP",
|
||||
"Speaker", "LINEOUT",
|
||||
"LINEIN", "Line In",
|
||||
"MIC1", "MBIAS",
|
||||
"MIC1", "Mic",
|
||||
"MIC2", "HBIAS",
|
||||
"MIC2", "Headset Mic";
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
* Allwinner Codec Analog Controls
|
||||
|
||||
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
|
||||
|
||||
Example:
|
||||
prcm: prcm@1f01400 {
|
||||
codec_analog: codec-analog {
|
||||
compatible = "allwinner,sun8i-a23-codec-analog";
|
||||
};
|
||||
};
|
34
Documentation/devicetree/bindings/sound/tas2562.txt
Normal file
34
Documentation/devicetree/bindings/sound/tas2562.txt
Normal file
@ -0,0 +1,34 @@
|
||||
Texas Instruments TAS2562 Smart PA
|
||||
|
||||
The TAS2562 is a mono, digital input Class-D audio amplifier optimized for
|
||||
efficiently driving high peak power into small loudspeakers.
|
||||
Integrated speaker voltage and current sense provides for
|
||||
real time monitoring of loudspeaker behavior.
|
||||
|
||||
Required properties:
|
||||
- #address-cells - Should be <1>.
|
||||
- #size-cells - Should be <0>.
|
||||
- compatible: - Should contain "ti,tas2562".
|
||||
- reg: - The i2c address. Should be 0x4c, 0x4d, 0x4e or 0x4f.
|
||||
- ti,imon-slot-no:- TDM TX current sense time slot.
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent: phandle to the interrupt controller which provides
|
||||
the interrupt.
|
||||
- interrupts: (GPIO) interrupt to which the chip is connected.
|
||||
- shut-down: GPIO used to control the state of the device.
|
||||
|
||||
Examples:
|
||||
tas2562@4c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "ti,tas2562";
|
||||
reg = <0x4c>;
|
||||
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <14>;
|
||||
|
||||
shut-down = <&gpio1 15 0>;
|
||||
ti,imon-slot-no = <0>;
|
||||
};
|
||||
|
37
Documentation/devicetree/bindings/sound/tas2770.txt
Normal file
37
Documentation/devicetree/bindings/sound/tas2770.txt
Normal file
@ -0,0 +1,37 @@
|
||||
Texas Instruments TAS2770 Smart PA
|
||||
|
||||
The TAS2770 is a mono, digital input Class-D audio amplifier optimized for
|
||||
efficiently driving high peak power into small loudspeakers.
|
||||
Integrated speaker voltage and current sense provides for
|
||||
real time monitoring of loudspeaker behavior.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: - Should contain "ti,tas2770".
|
||||
- reg: - The i2c address. Should contain <0x4c>, <0x4d>,<0x4e>, or <0x4f>.
|
||||
- #address-cells - Should be <1>.
|
||||
- #size-cells - Should be <0>.
|
||||
- ti,asi-format: - Sets TDM RX capture edge. 0->Rising; 1->Falling.
|
||||
- ti,imon-slot-no:- TDM TX current sense time slot.
|
||||
- ti,vmon-slot-no:- TDM TX voltage sense time slot.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupt-parent: the phandle to the interrupt controller which provides
|
||||
the interrupt.
|
||||
- interrupts: interrupt specification for data-ready.
|
||||
|
||||
Examples:
|
||||
|
||||
tas2770@4c {
|
||||
compatible = "ti,tas2770";
|
||||
reg = <0x4c>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupt-parent = <&msm_gpio>;
|
||||
interrupts = <97 0>;
|
||||
ti,asi-format = <0>;
|
||||
ti,imon-slot-no = <0>;
|
||||
ti,vmon-slot-no = <2>;
|
||||
};
|
||||
|
@ -1002,6 +1002,7 @@ F: drivers/media/i2c/adv7842*
|
||||
|
||||
ANALOG DEVICES INC ASOC CODEC DRIVERS
|
||||
M: Lars-Peter Clausen <lars@metafoo.de>
|
||||
M: Nuno Sá <nuno.sa@analog.com>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
W: http://wiki.analog.com/
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
|
@ -151,11 +151,22 @@ static int dw_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dw_hdmi_i2s_hook_plugged_cb(struct device *dev, void *data,
|
||||
hdmi_codec_plugged_cb fn,
|
||||
struct device *codec_dev)
|
||||
{
|
||||
struct dw_hdmi_i2s_audio_data *audio = data;
|
||||
struct dw_hdmi *hdmi = audio->hdmi;
|
||||
|
||||
return dw_hdmi_set_plugged_cb(hdmi, fn, codec_dev);
|
||||
}
|
||||
|
||||
static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
|
||||
.hw_params = dw_hdmi_i2s_hw_params,
|
||||
.audio_shutdown = dw_hdmi_i2s_audio_shutdown,
|
||||
.get_eld = dw_hdmi_i2s_get_eld,
|
||||
.get_dai_id = dw_hdmi_i2s_get_dai_id,
|
||||
.hook_plugged_cb = dw_hdmi_i2s_hook_plugged_cb,
|
||||
};
|
||||
|
||||
static int snd_dw_hdmi_probe(struct platform_device *pdev)
|
||||
|
@ -191,6 +191,10 @@ struct dw_hdmi {
|
||||
|
||||
struct mutex cec_notifier_mutex;
|
||||
struct cec_notifier *cec_notifier;
|
||||
|
||||
hdmi_codec_plugged_cb plugged_cb;
|
||||
struct device *codec_dev;
|
||||
enum drm_connector_status last_connector_result;
|
||||
};
|
||||
|
||||
#define HDMI_IH_PHY_STAT0_RX_SENSE \
|
||||
@ -215,6 +219,28 @@ static inline u8 hdmi_readb(struct dw_hdmi *hdmi, int offset)
|
||||
return val;
|
||||
}
|
||||
|
||||
static void handle_plugged_change(struct dw_hdmi *hdmi, bool plugged)
|
||||
{
|
||||
if (hdmi->plugged_cb && hdmi->codec_dev)
|
||||
hdmi->plugged_cb(hdmi->codec_dev, plugged);
|
||||
}
|
||||
|
||||
int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
|
||||
struct device *codec_dev)
|
||||
{
|
||||
bool plugged;
|
||||
|
||||
mutex_lock(&hdmi->mutex);
|
||||
hdmi->plugged_cb = fn;
|
||||
hdmi->codec_dev = codec_dev;
|
||||
plugged = hdmi->last_connector_result == connector_status_connected;
|
||||
handle_plugged_change(hdmi, plugged);
|
||||
mutex_unlock(&hdmi->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_hdmi_set_plugged_cb);
|
||||
|
||||
static void hdmi_modb(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
|
||||
{
|
||||
regmap_update_bits(hdmi->regm, reg << hdmi->reg_shift, mask, data);
|
||||
@ -2161,6 +2187,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
|
||||
connector);
|
||||
enum drm_connector_status result;
|
||||
|
||||
mutex_lock(&hdmi->mutex);
|
||||
hdmi->force = DRM_FORCE_UNSPECIFIED;
|
||||
@ -2168,7 +2195,18 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
dw_hdmi_update_phy_mask(hdmi);
|
||||
mutex_unlock(&hdmi->mutex);
|
||||
|
||||
return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
|
||||
result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
|
||||
|
||||
mutex_lock(&hdmi->mutex);
|
||||
if (result != hdmi->last_connector_result) {
|
||||
dev_dbg(hdmi->dev, "read_hpd result: %d", result);
|
||||
handle_plugged_change(hdmi,
|
||||
result == connector_status_connected);
|
||||
hdmi->last_connector_result = result;
|
||||
}
|
||||
mutex_unlock(&hdmi->mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
@ -2619,6 +2657,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
|
||||
hdmi->rxsense = true;
|
||||
hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
|
||||
hdmi->mc_clkdis = 0x7f;
|
||||
hdmi->last_connector_result = connector_status_disconnected;
|
||||
|
||||
mutex_init(&hdmi->mutex);
|
||||
mutex_init(&hdmi->audio_mutex);
|
||||
|
@ -98,7 +98,10 @@
|
||||
TRACE_SYMBOL(EC_CMD_SB_READ_BLOCK), \
|
||||
TRACE_SYMBOL(EC_CMD_SB_WRITE_BLOCK), \
|
||||
TRACE_SYMBOL(EC_CMD_BATTERY_VENDOR_PARAM), \
|
||||
TRACE_SYMBOL(EC_CMD_CODEC_I2S), \
|
||||
TRACE_SYMBOL(EC_CMD_EC_CODEC), \
|
||||
TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \
|
||||
TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \
|
||||
TRACE_SYMBOL(EC_CMD_EC_CODEC_WOV), \
|
||||
TRACE_SYMBOL(EC_CMD_REBOOT_EC), \
|
||||
TRACE_SYMBOL(EC_CMD_GET_PANIC_INFO), \
|
||||
TRACE_SYMBOL(EC_CMD_ACPI_READ), \
|
||||
|
@ -6,6 +6,8 @@
|
||||
#ifndef __DW_HDMI__
|
||||
#define __DW_HDMI__
|
||||
|
||||
#include <sound/hdmi-codec.h>
|
||||
|
||||
struct drm_connector;
|
||||
struct drm_display_mode;
|
||||
struct drm_encoder;
|
||||
@ -154,6 +156,8 @@ void dw_hdmi_resume(struct dw_hdmi *hdmi);
|
||||
|
||||
void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);
|
||||
|
||||
int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
|
||||
struct device *codec_dev);
|
||||
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate);
|
||||
void dw_hdmi_set_channel_count(struct dw_hdmi *hdmi, unsigned int cnt);
|
||||
void dw_hdmi_set_channel_allocation(struct dw_hdmi *hdmi, unsigned int ca);
|
||||
|
@ -2,8 +2,14 @@
|
||||
#ifndef _DT_BINDINGS_SAMSUNG_I2S_H
|
||||
#define _DT_BINDINGS_SAMSUNG_I2S_H
|
||||
|
||||
#define CLK_I2S_CDCLK 0
|
||||
#define CLK_I2S_RCLK_SRC 1
|
||||
#define CLK_I2S_RCLK_PSR 2
|
||||
#define CLK_I2S_CDCLK 0 /* the CDCLK (CODECLKO) gate clock */
|
||||
|
||||
#define CLK_I2S_RCLK_SRC 1 /* the RCLKSRC mux clock (corresponding to
|
||||
* RCLKSRC bit in IISMOD register)
|
||||
*/
|
||||
|
||||
#define CLK_I2S_RCLK_PSR 2 /* the RCLK prescaler divider clock
|
||||
* (corresponding to the IISPSR register)
|
||||
*/
|
||||
|
||||
#endif /* _DT_BINDINGS_SAMSUNG_I2S_H */
|
||||
|
@ -556,6 +556,9 @@ enum host_event_code {
|
||||
/* Keyboard recovery combo with hardware reinitialization */
|
||||
EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT = 30,
|
||||
|
||||
/* WoV */
|
||||
EC_HOST_EVENT_WOV = 31,
|
||||
|
||||
/*
|
||||
* The high bit of the event mask is not used as a host event code. If
|
||||
* it reads back as set, then the entire event mask should be
|
||||
@ -1277,8 +1280,6 @@ enum ec_feature_code {
|
||||
* MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE.
|
||||
*/
|
||||
EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37,
|
||||
/* EC supports audio codec. */
|
||||
EC_FEATURE_AUDIO_CODEC = 38,
|
||||
/* The MCU is a System Companion Processor (SCP). */
|
||||
EC_FEATURE_SCP = 39,
|
||||
/* The MCU is an Integrated Sensor Hub */
|
||||
@ -4468,92 +4469,246 @@ enum mkbp_cec_event {
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Commands for I2S recording on audio codec. */
|
||||
/* Commands for audio codec. */
|
||||
#define EC_CMD_EC_CODEC 0x00BC
|
||||
|
||||
#define EC_CMD_CODEC_I2S 0x00BC
|
||||
#define EC_WOV_I2S_SAMPLE_RATE 48000
|
||||
|
||||
enum ec_codec_i2s_subcmd {
|
||||
EC_CODEC_SET_SAMPLE_DEPTH = 0x0,
|
||||
EC_CODEC_SET_GAIN = 0x1,
|
||||
EC_CODEC_GET_GAIN = 0x2,
|
||||
EC_CODEC_I2S_ENABLE = 0x3,
|
||||
EC_CODEC_I2S_SET_CONFIG = 0x4,
|
||||
EC_CODEC_I2S_SET_TDM_CONFIG = 0x5,
|
||||
EC_CODEC_I2S_SET_BCLK = 0x6,
|
||||
EC_CODEC_I2S_SUBCMD_COUNT = 0x7,
|
||||
enum ec_codec_subcmd {
|
||||
EC_CODEC_GET_CAPABILITIES = 0x0,
|
||||
EC_CODEC_GET_SHM_ADDR = 0x1,
|
||||
EC_CODEC_SET_SHM_ADDR = 0x2,
|
||||
EC_CODEC_SUBCMD_COUNT,
|
||||
};
|
||||
|
||||
enum ec_sample_depth_value {
|
||||
EC_CODEC_SAMPLE_DEPTH_16 = 0,
|
||||
EC_CODEC_SAMPLE_DEPTH_24 = 1,
|
||||
enum ec_codec_cap {
|
||||
EC_CODEC_CAP_WOV_AUDIO_SHM = 0,
|
||||
EC_CODEC_CAP_WOV_LANG_SHM = 1,
|
||||
EC_CODEC_CAP_LAST = 32,
|
||||
};
|
||||
|
||||
enum ec_i2s_config {
|
||||
EC_DAI_FMT_I2S = 0,
|
||||
EC_DAI_FMT_RIGHT_J = 1,
|
||||
EC_DAI_FMT_LEFT_J = 2,
|
||||
EC_DAI_FMT_PCM_A = 3,
|
||||
EC_DAI_FMT_PCM_B = 4,
|
||||
EC_DAI_FMT_PCM_TDM = 5,
|
||||
enum ec_codec_shm_id {
|
||||
EC_CODEC_SHM_ID_WOV_AUDIO = 0x0,
|
||||
EC_CODEC_SHM_ID_WOV_LANG = 0x1,
|
||||
EC_CODEC_SHM_ID_LAST,
|
||||
};
|
||||
|
||||
/*
|
||||
* For subcommand EC_CODEC_GET_GAIN.
|
||||
*/
|
||||
struct __ec_align1 ec_codec_i2s_gain {
|
||||
uint8_t left;
|
||||
uint8_t right;
|
||||
enum ec_codec_shm_type {
|
||||
EC_CODEC_SHM_TYPE_EC_RAM = 0x0,
|
||||
EC_CODEC_SHM_TYPE_SYSTEM_RAM = 0x1,
|
||||
};
|
||||
|
||||
struct __ec_todo_unpacked ec_param_codec_i2s_tdm {
|
||||
int16_t ch0_delay; /* 0 to 496 */
|
||||
int16_t ch1_delay; /* -1 to 496 */
|
||||
uint8_t adjacent_to_ch0;
|
||||
uint8_t adjacent_to_ch1;
|
||||
struct __ec_align1 ec_param_ec_codec_get_shm_addr {
|
||||
uint8_t shm_id;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
|
||||
struct __ec_todo_packed ec_param_codec_i2s {
|
||||
/* enum ec_codec_i2s_subcmd */
|
||||
uint8_t cmd;
|
||||
struct __ec_align4 ec_param_ec_codec_set_shm_addr {
|
||||
uint64_t phys_addr;
|
||||
uint32_t len;
|
||||
uint8_t shm_id;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_param_ec_codec {
|
||||
uint8_t cmd; /* enum ec_codec_subcmd */
|
||||
uint8_t reserved[3];
|
||||
|
||||
union {
|
||||
/*
|
||||
* EC_CODEC_SET_SAMPLE_DEPTH
|
||||
* Value should be one of ec_sample_depth_value.
|
||||
*/
|
||||
uint8_t depth;
|
||||
|
||||
/*
|
||||
* EC_CODEC_SET_GAIN
|
||||
* Value should be 0~43 for both channels.
|
||||
*/
|
||||
struct ec_codec_i2s_gain gain;
|
||||
|
||||
/*
|
||||
* EC_CODEC_I2S_ENABLE
|
||||
* 1 to enable, 0 to disable.
|
||||
*/
|
||||
uint8_t i2s_enable;
|
||||
|
||||
/*
|
||||
* EC_CODEC_I2S_SET_CONFIG
|
||||
* Value should be one of ec_i2s_config.
|
||||
*/
|
||||
uint8_t i2s_config;
|
||||
|
||||
/*
|
||||
* EC_CODEC_I2S_SET_TDM_CONFIG
|
||||
* Value should be one of ec_i2s_config.
|
||||
*/
|
||||
struct ec_param_codec_i2s_tdm tdm_param;
|
||||
|
||||
/*
|
||||
* EC_CODEC_I2S_SET_BCLK
|
||||
*/
|
||||
uint32_t bclk;
|
||||
struct ec_param_ec_codec_get_shm_addr
|
||||
get_shm_addr_param;
|
||||
struct ec_param_ec_codec_set_shm_addr
|
||||
set_shm_addr_param;
|
||||
};
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_response_ec_codec_get_capabilities {
|
||||
uint32_t capabilities;
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_response_ec_codec_get_shm_addr {
|
||||
uint64_t phys_addr;
|
||||
uint32_t len;
|
||||
uint8_t type;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Commands for DMIC on audio codec. */
|
||||
#define EC_CMD_EC_CODEC_DMIC 0x00BD
|
||||
|
||||
enum ec_codec_dmic_subcmd {
|
||||
EC_CODEC_DMIC_GET_MAX_GAIN = 0x0,
|
||||
EC_CODEC_DMIC_SET_GAIN_IDX = 0x1,
|
||||
EC_CODEC_DMIC_GET_GAIN_IDX = 0x2,
|
||||
EC_CODEC_DMIC_SUBCMD_COUNT,
|
||||
};
|
||||
|
||||
enum ec_codec_dmic_channel {
|
||||
EC_CODEC_DMIC_CHANNEL_0 = 0x0,
|
||||
EC_CODEC_DMIC_CHANNEL_1 = 0x1,
|
||||
EC_CODEC_DMIC_CHANNEL_2 = 0x2,
|
||||
EC_CODEC_DMIC_CHANNEL_3 = 0x3,
|
||||
EC_CODEC_DMIC_CHANNEL_4 = 0x4,
|
||||
EC_CODEC_DMIC_CHANNEL_5 = 0x5,
|
||||
EC_CODEC_DMIC_CHANNEL_6 = 0x6,
|
||||
EC_CODEC_DMIC_CHANNEL_7 = 0x7,
|
||||
EC_CODEC_DMIC_CHANNEL_COUNT,
|
||||
};
|
||||
|
||||
struct __ec_align1 ec_param_ec_codec_dmic_set_gain_idx {
|
||||
uint8_t channel; /* enum ec_codec_dmic_channel */
|
||||
uint8_t gain;
|
||||
uint8_t reserved[2];
|
||||
};
|
||||
|
||||
struct __ec_align1 ec_param_ec_codec_dmic_get_gain_idx {
|
||||
uint8_t channel; /* enum ec_codec_dmic_channel */
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_param_ec_codec_dmic {
|
||||
uint8_t cmd; /* enum ec_codec_dmic_subcmd */
|
||||
uint8_t reserved[3];
|
||||
|
||||
union {
|
||||
struct ec_param_ec_codec_dmic_set_gain_idx
|
||||
set_gain_idx_param;
|
||||
struct ec_param_ec_codec_dmic_get_gain_idx
|
||||
get_gain_idx_param;
|
||||
};
|
||||
};
|
||||
|
||||
struct __ec_align1 ec_response_ec_codec_dmic_get_max_gain {
|
||||
uint8_t max_gain;
|
||||
};
|
||||
|
||||
struct __ec_align1 ec_response_ec_codec_dmic_get_gain_idx {
|
||||
uint8_t gain;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Commands for I2S RX on audio codec. */
|
||||
|
||||
#define EC_CMD_EC_CODEC_I2S_RX 0x00BE
|
||||
|
||||
enum ec_codec_i2s_rx_subcmd {
|
||||
EC_CODEC_I2S_RX_ENABLE = 0x0,
|
||||
EC_CODEC_I2S_RX_DISABLE = 0x1,
|
||||
EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH = 0x2,
|
||||
EC_CODEC_I2S_RX_SET_DAIFMT = 0x3,
|
||||
EC_CODEC_I2S_RX_SET_BCLK = 0x4,
|
||||
EC_CODEC_I2S_RX_SUBCMD_COUNT,
|
||||
};
|
||||
|
||||
enum ec_codec_i2s_rx_sample_depth {
|
||||
EC_CODEC_I2S_RX_SAMPLE_DEPTH_16 = 0x0,
|
||||
EC_CODEC_I2S_RX_SAMPLE_DEPTH_24 = 0x1,
|
||||
EC_CODEC_I2S_RX_SAMPLE_DEPTH_COUNT,
|
||||
};
|
||||
|
||||
enum ec_codec_i2s_rx_daifmt {
|
||||
EC_CODEC_I2S_RX_DAIFMT_I2S = 0x0,
|
||||
EC_CODEC_I2S_RX_DAIFMT_RIGHT_J = 0x1,
|
||||
EC_CODEC_I2S_RX_DAIFMT_LEFT_J = 0x2,
|
||||
EC_CODEC_I2S_RX_DAIFMT_COUNT,
|
||||
};
|
||||
|
||||
struct __ec_align1 ec_param_ec_codec_i2s_rx_set_sample_depth {
|
||||
uint8_t depth;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
|
||||
struct __ec_align1 ec_param_ec_codec_i2s_rx_set_gain {
|
||||
uint8_t left;
|
||||
uint8_t right;
|
||||
uint8_t reserved[2];
|
||||
};
|
||||
|
||||
struct __ec_align1 ec_param_ec_codec_i2s_rx_set_daifmt {
|
||||
uint8_t daifmt;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_param_ec_codec_i2s_rx_set_bclk {
|
||||
uint32_t bclk;
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_param_ec_codec_i2s_rx {
|
||||
uint8_t cmd; /* enum ec_codec_i2s_rx_subcmd */
|
||||
uint8_t reserved[3];
|
||||
|
||||
union {
|
||||
struct ec_param_ec_codec_i2s_rx_set_sample_depth
|
||||
set_sample_depth_param;
|
||||
struct ec_param_ec_codec_i2s_rx_set_daifmt
|
||||
set_daifmt_param;
|
||||
struct ec_param_ec_codec_i2s_rx_set_bclk
|
||||
set_bclk_param;
|
||||
};
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Commands for WoV on audio codec. */
|
||||
|
||||
#define EC_CMD_EC_CODEC_WOV 0x00BF
|
||||
|
||||
enum ec_codec_wov_subcmd {
|
||||
EC_CODEC_WOV_SET_LANG = 0x0,
|
||||
EC_CODEC_WOV_SET_LANG_SHM = 0x1,
|
||||
EC_CODEC_WOV_GET_LANG = 0x2,
|
||||
EC_CODEC_WOV_ENABLE = 0x3,
|
||||
EC_CODEC_WOV_DISABLE = 0x4,
|
||||
EC_CODEC_WOV_READ_AUDIO = 0x5,
|
||||
EC_CODEC_WOV_READ_AUDIO_SHM = 0x6,
|
||||
EC_CODEC_WOV_SUBCMD_COUNT,
|
||||
};
|
||||
|
||||
/*
|
||||
* @hash is SHA256 of the whole language model.
|
||||
* @total_len indicates the length of whole language model.
|
||||
* @offset is the cursor from the beginning of the model.
|
||||
* @buf is the packet buffer.
|
||||
* @len denotes how many bytes in the buf.
|
||||
*/
|
||||
struct __ec_align4 ec_param_ec_codec_wov_set_lang {
|
||||
uint8_t hash[32];
|
||||
uint32_t total_len;
|
||||
uint32_t offset;
|
||||
uint8_t buf[128];
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_param_ec_codec_wov_set_lang_shm {
|
||||
uint8_t hash[32];
|
||||
uint32_t total_len;
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_param_ec_codec_wov {
|
||||
uint8_t cmd; /* enum ec_codec_wov_subcmd */
|
||||
uint8_t reserved[3];
|
||||
|
||||
union {
|
||||
struct ec_param_ec_codec_wov_set_lang
|
||||
set_lang_param;
|
||||
struct ec_param_ec_codec_wov_set_lang_shm
|
||||
set_lang_shm_param;
|
||||
};
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_response_ec_codec_wov_get_lang {
|
||||
uint8_t hash[32];
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_response_ec_codec_wov_read_audio {
|
||||
uint8_t buf[128];
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
struct __ec_align4 ec_response_ec_codec_wov_read_audio_shm {
|
||||
uint32_t offset;
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* System commands */
|
||||
|
@ -83,6 +83,11 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
|
||||
const struct snd_dmaengine_dai_dma_data *dma_data,
|
||||
struct dma_slave_config *config);
|
||||
|
||||
int snd_dmaengine_pcm_refine_runtime_hwparams(
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_dmaengine_dai_dma_data *dma_data,
|
||||
struct snd_pcm_hardware *hw,
|
||||
struct dma_chan *chan);
|
||||
|
||||
/*
|
||||
* Try to request the DMA channel using compat_request_channel or
|
||||
|
@ -254,6 +254,7 @@ struct hda_codec {
|
||||
unsigned int force_pin_prefix:1; /* Add location prefix */
|
||||
unsigned int link_down_at_suspend:1; /* link down at runtime suspend */
|
||||
unsigned int relaxed_resume:1; /* don't resume forcibly for jack */
|
||||
unsigned int mst_no_extra_pcms:1; /* no backup PCMs for DP-MST */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
unsigned long power_on_acct;
|
||||
|
@ -10,6 +10,7 @@ struct snd_pcm_substream;
|
||||
struct snd_pcm_hw_params;
|
||||
struct snd_soc_pcm_runtime;
|
||||
struct snd_pcm;
|
||||
struct snd_soc_component;
|
||||
|
||||
extern int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
@ -23,8 +24,29 @@ extern int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma);
|
||||
extern int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream);
|
||||
extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm);
|
||||
extern int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd);
|
||||
extern const struct snd_pcm_ops pxa2xx_pcm_ops;
|
||||
extern void pxa2xx_soc_pcm_free(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm);
|
||||
extern int pxa2xx_soc_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd);
|
||||
extern int pxa2xx_soc_pcm_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
extern int pxa2xx_soc_pcm_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
extern int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
extern int pxa2xx_soc_pcm_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
extern int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
extern int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd);
|
||||
extern snd_pcm_uframes_t
|
||||
pxa2xx_soc_pcm_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
extern int pxa2xx_soc_pcm_mmap(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma);
|
||||
|
||||
/* AC97 */
|
||||
|
||||
|
@ -31,6 +31,7 @@ struct rt5682_platform_data {
|
||||
enum rt5682_dmic1_data_pin dmic1_data_pin;
|
||||
enum rt5682_dmic1_clk_pin dmic1_clk_pin;
|
||||
enum rt5682_jd_src jd_src;
|
||||
unsigned int btndet_delay;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -8,6 +8,7 @@
|
||||
#ifndef __SIMPLE_CARD_UTILS_H
|
||||
#define __SIMPLE_CARD_UTILS_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define asoc_simple_init_hp(card, sjack, prefix) \
|
||||
|
@ -27,6 +27,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[];
|
||||
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[];
|
||||
|
||||
/*
|
||||
* generic table used for HDA codec-based platforms, possibly with
|
||||
|
@ -60,12 +60,14 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg)
|
||||
* @acpi_ipc_irq_index: used for BYT-CR detection
|
||||
* @platform: string used for HDaudio codec support
|
||||
* @codec_mask: used for HDAudio support
|
||||
* @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver
|
||||
*/
|
||||
struct snd_soc_acpi_mach_params {
|
||||
u32 acpi_ipc_irq_index;
|
||||
const char *platform;
|
||||
u32 codec_mask;
|
||||
u32 dmic_num;
|
||||
bool common_hdmi_codec_drv;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -75,6 +77,7 @@ struct snd_soc_acpi_mach_params {
|
||||
* all firmware/topology related fields.
|
||||
*
|
||||
* @id: ACPI ID (usually the codec's) used to find a matching machine driver.
|
||||
* @link_mask: describes required board layout, e.g. for SoundWire.
|
||||
* @drv_name: machine driver name
|
||||
* @fw_filename: firmware file name. Used when SOF is not enabled.
|
||||
* @board: board name
|
||||
@ -90,6 +93,7 @@ struct snd_soc_acpi_mach_params {
|
||||
/* Descriptor for SST ASoC machine driver */
|
||||
struct snd_soc_acpi_mach {
|
||||
const u8 id[ACPI_ID_LEN];
|
||||
const u32 link_mask;
|
||||
const char *drv_name;
|
||||
const char *fw_filename;
|
||||
const char *board;
|
||||
|
@ -3,10 +3,6 @@
|
||||
* soc-component.h
|
||||
*
|
||||
* Copyright (c) 2019 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __SOC_COMPONENT_H
|
||||
#define __SOC_COMPONENT_H
|
||||
@ -51,8 +47,10 @@ struct snd_soc_component_driver {
|
||||
unsigned int reg, unsigned int val);
|
||||
|
||||
/* pcm creation and destruction */
|
||||
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd);
|
||||
void (*pcm_free)(struct snd_pcm *pcm);
|
||||
int (*pcm_construct)(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd);
|
||||
void (*pcm_destruct)(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm);
|
||||
|
||||
/* component wide operations */
|
||||
int (*set_sysclk)(struct snd_soc_component *component,
|
||||
@ -74,7 +72,40 @@ struct snd_soc_component_driver {
|
||||
int (*set_bias_level)(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level);
|
||||
|
||||
const struct snd_pcm_ops *ops;
|
||||
int (*open)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
int (*close)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
int (*ioctl)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg);
|
||||
int (*hw_params)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params);
|
||||
int (*hw_free)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
int (*prepare)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
int (*trigger)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd);
|
||||
snd_pcm_uframes_t (*pointer)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream);
|
||||
int (*get_time_info)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, 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_user)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int channel,
|
||||
unsigned long pos, void __user *buf,
|
||||
unsigned long bytes);
|
||||
struct page *(*page)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
unsigned long offset);
|
||||
int (*mmap)(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma);
|
||||
|
||||
const struct snd_compr_ops *compr_ops;
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
|
@ -103,15 +103,15 @@ struct snd_soc_dpcm_runtime {
|
||||
int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
|
||||
};
|
||||
|
||||
#define for_each_dpcm_fe(be, stream, dpcm) \
|
||||
list_for_each_entry(dpcm, &(be)->dpcm[stream].fe_clients, list_fe)
|
||||
#define for_each_dpcm_fe(be, stream, _dpcm) \
|
||||
list_for_each_entry(_dpcm, &(be)->dpcm[stream].fe_clients, list_fe)
|
||||
|
||||
#define for_each_dpcm_be(fe, stream, dpcm) \
|
||||
list_for_each_entry(dpcm, &(fe)->dpcm[stream].be_clients, list_be)
|
||||
#define for_each_dpcm_be_safe(fe, stream, dpcm, _dpcm) \
|
||||
list_for_each_entry_safe(dpcm, _dpcm, &(fe)->dpcm[stream].be_clients, list_be)
|
||||
#define for_each_dpcm_be_rollback(fe, stream, dpcm) \
|
||||
list_for_each_entry_continue_reverse(dpcm, &(fe)->dpcm[stream].be_clients, list_be)
|
||||
#define for_each_dpcm_be(fe, stream, _dpcm) \
|
||||
list_for_each_entry(_dpcm, &(fe)->dpcm[stream].be_clients, list_be)
|
||||
#define for_each_dpcm_be_safe(fe, stream, _dpcm, __dpcm) \
|
||||
list_for_each_entry_safe(_dpcm, __dpcm, &(fe)->dpcm[stream].be_clients, list_be)
|
||||
#define for_each_dpcm_be_rollback(fe, stream, _dpcm) \
|
||||
list_for_each_entry_continue_reverse(_dpcm, &(fe)->dpcm[stream].be_clients, list_be)
|
||||
|
||||
/* can this BE stop and free */
|
||||
int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
|
||||
|
@ -739,10 +739,12 @@ struct snd_soc_rtdcom_list {
|
||||
struct snd_soc_component*
|
||||
snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
|
||||
const char *driver_name);
|
||||
#define for_each_rtdcom(rtd, rtdcom) \
|
||||
list_for_each_entry(rtdcom, &(rtd)->component_list, list)
|
||||
#define for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) \
|
||||
list_for_each_entry_safe(rtdcom1, rtdcom2, &(rtd)->component_list, list)
|
||||
#define for_each_rtd_components(rtd, rtdcom, _component) \
|
||||
for (rtdcom = list_first_entry(&(rtd)->component_list, \
|
||||
typeof(*rtdcom), list); \
|
||||
(&rtdcom->list != &(rtd)->component_list) && \
|
||||
(_component = rtdcom->component); \
|
||||
rtdcom = list_next_entry(rtdcom, list))
|
||||
|
||||
struct snd_soc_dai_link_component {
|
||||
const char *name;
|
||||
@ -845,7 +847,9 @@ struct snd_soc_dai_link {
|
||||
unsigned int ignore:1;
|
||||
|
||||
struct list_head list; /* DAI link list of the soc card */
|
||||
#ifdef CONFIG_SND_SOC_TOPOLOGY
|
||||
struct snd_soc_dobj dobj; /* For topology */
|
||||
#endif
|
||||
};
|
||||
#define for_each_link_codecs(link, i, codec) \
|
||||
for ((i) = 0; \
|
||||
@ -1148,7 +1152,6 @@ struct snd_soc_pcm_runtime {
|
||||
struct list_head component_list; /* list of connected components */
|
||||
|
||||
/* bit field */
|
||||
unsigned int dev_registered:1;
|
||||
unsigned int pop_wait:1;
|
||||
unsigned int fe_compr:1; /* for Dynamic PCM */
|
||||
};
|
||||
@ -1168,7 +1171,9 @@ struct soc_mixer_control {
|
||||
unsigned int sign_bit;
|
||||
unsigned int invert:1;
|
||||
unsigned int autodisable:1;
|
||||
#ifdef CONFIG_SND_SOC_TOPOLOGY
|
||||
struct snd_soc_dobj dobj;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct soc_bytes {
|
||||
@ -1179,8 +1184,9 @@ struct soc_bytes {
|
||||
|
||||
struct soc_bytes_ext {
|
||||
int max;
|
||||
#ifdef CONFIG_SND_SOC_TOPOLOGY
|
||||
struct snd_soc_dobj dobj;
|
||||
|
||||
#endif
|
||||
/* used for TLV byte control */
|
||||
int (*get)(struct snd_kcontrol *kcontrol, unsigned int __user *bytes,
|
||||
unsigned int size);
|
||||
@ -1204,7 +1210,9 @@ struct soc_enum {
|
||||
const char * const *texts;
|
||||
const unsigned int *values;
|
||||
unsigned int autodisable:1;
|
||||
#ifdef CONFIG_SND_SOC_TOPOLOGY
|
||||
struct snd_soc_dobj dobj;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* device driver data */
|
||||
@ -1325,8 +1333,10 @@ struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
|
||||
int id, const char *name,
|
||||
const char *stream_name);
|
||||
|
||||
int snd_soc_register_dai(struct snd_soc_component *component,
|
||||
struct snd_soc_dai_driver *dai_drv);
|
||||
struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
|
||||
struct snd_soc_dai_driver *dai_drv,
|
||||
bool legacy_dai_naming);
|
||||
void snd_soc_unregister_dai(struct snd_soc_dai *dai);
|
||||
|
||||
struct snd_soc_dai *snd_soc_find_dai(
|
||||
const struct snd_soc_dai_link_component *dlc);
|
||||
@ -1391,6 +1401,11 @@ static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm)
|
||||
mutex_unlock(&dapm->card->dapm_mutex);
|
||||
}
|
||||
|
||||
/* bypass */
|
||||
int snd_soc_pcm_lib_ioctl(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg);
|
||||
|
||||
#include <sound/soc-component.h>
|
||||
|
||||
#endif
|
||||
|
@ -61,6 +61,9 @@ struct sof_dev_desc {
|
||||
/* list of machines using this configuration */
|
||||
struct snd_soc_acpi_mach *machines;
|
||||
|
||||
/* alternate list of machines using this configuration */
|
||||
struct snd_soc_acpi_mach *alt_machines;
|
||||
|
||||
/* Platform resource indexes in BAR / ACPI resources. */
|
||||
/* Must set to -1 if not used - add new items to end */
|
||||
int resindex_lpe_base;
|
||||
|
34
include/sound/sof/dai-imx.h
Normal file
34
include/sound/sof/dai-imx.h
Normal file
@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
*
|
||||
* Author: Daniel Baluta <daniel.baluta@nxp.com>
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_DAI_IMX_H__
|
||||
#define __INCLUDE_SOUND_SOF_DAI_IMX_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/* ESAI Configuration Request - SOF_IPC_DAI_ESAI_CONFIG */
|
||||
struct sof_ipc_dai_esai_params {
|
||||
struct sof_ipc_hdr hdr;
|
||||
|
||||
/* MCLK */
|
||||
uint16_t reserved1;
|
||||
uint16_t mclk_id;
|
||||
uint32_t mclk_direction;
|
||||
|
||||
uint32_t mclk_rate; /* MCLK frequency in Hz */
|
||||
uint32_t fsync_rate; /* FSYNC frequency in Hz */
|
||||
uint32_t bclk_rate; /* BCLK frequency in Hz */
|
||||
|
||||
/* TDM */
|
||||
uint32_t tdm_slots;
|
||||
uint32_t rx_slots;
|
||||
uint32_t tx_slots;
|
||||
uint16_t tdm_slot_width;
|
||||
uint16_t reserved2; /* alignment */
|
||||
} __packed;
|
||||
|
||||
#endif
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
#include <sound/sof/dai-intel.h>
|
||||
#include <sound/sof/dai-imx.h>
|
||||
|
||||
/*
|
||||
* DAI Configuration.
|
||||
@ -73,6 +74,7 @@ struct sof_ipc_dai_config {
|
||||
struct sof_ipc_dai_dmic_params dmic;
|
||||
struct sof_ipc_dai_hda_params hda;
|
||||
struct sof_ipc_dai_alh_params alh;
|
||||
struct sof_ipc_dai_esai_params esai;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef __INCLUDE_SOUND_SOF_HEADER_H__
|
||||
#define __INCLUDE_SOUND_SOF_HEADER_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <uapi/sound/sof/abi.h>
|
||||
|
||||
/** \addtogroup sof_uapi uAPI
|
||||
@ -74,6 +75,7 @@
|
||||
#define SOF_IPC_PM_CLK_GET SOF_CMD_TYPE(0x005)
|
||||
#define SOF_IPC_PM_CLK_REQ SOF_CMD_TYPE(0x006)
|
||||
#define SOF_IPC_PM_CORE_ENABLE SOF_CMD_TYPE(0x007)
|
||||
#define SOF_IPC_PM_GATE SOF_CMD_TYPE(0x008)
|
||||
|
||||
/* component runtime config - multiple different types */
|
||||
#define SOF_IPC_COMP_SET_VALUE SOF_CMD_TYPE(0x001)
|
||||
|
@ -45,4 +45,12 @@ struct sof_ipc_pm_core_config {
|
||||
uint32_t enable_mask;
|
||||
} __packed;
|
||||
|
||||
struct sof_ipc_pm_gate {
|
||||
struct sof_ipc_cmd_hdr hdr;
|
||||
uint32_t flags; /* platform specific */
|
||||
|
||||
/* reserved for future use */
|
||||
uint32_t reserved[5];
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
@ -83,10 +83,10 @@ struct sof_ipc_stream_params {
|
||||
uint16_t sample_valid_bytes;
|
||||
uint16_t sample_container_bytes;
|
||||
|
||||
/* for notifying host period has completed - 0 means no period IRQ */
|
||||
uint32_t host_period_bytes;
|
||||
uint16_t no_stream_position; /**< 1 means don't send stream position */
|
||||
|
||||
uint32_t reserved[2];
|
||||
uint16_t reserved[3];
|
||||
uint16_t chmap[SOF_IPC_MAX_CHANNELS]; /**< channel map - SOF_CHMAP_ */
|
||||
} __packed;
|
||||
|
||||
|
@ -120,7 +120,7 @@
|
||||
* DRC configurations are specified with a label and a set of register
|
||||
* values to write (the enable bits will be ignored). At runtime an
|
||||
* enumerated control will be presented for each DRC block allowing
|
||||
* the user to choose the configration to use.
|
||||
* the user to choose the configuration to use.
|
||||
*
|
||||
* Configurations may be generated by hand or by using the DRC control
|
||||
* panel provided by the WISCE - see http://www.wolfsonmicro.com/wisce/
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
/* SOF ABI version major, minor and patch numbers */
|
||||
#define SOF_ABI_MAJOR 3
|
||||
#define SOF_ABI_MINOR 10
|
||||
#define SOF_ABI_MINOR 11
|
||||
#define SOF_ABI_PATCH 0
|
||||
|
||||
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
|
||||
|
@ -111,7 +111,14 @@
|
||||
/* TODO: Add SAI tokens */
|
||||
|
||||
/* ESAI */
|
||||
#define SOF_TKN_IMX_ESAI_FIRST_TOKEN 1100
|
||||
/* TODO: Add ESAI tokens */
|
||||
#define SOF_TKN_IMX_ESAI_MCLK_ID 1100
|
||||
|
||||
/* Stream */
|
||||
#define SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3 1200
|
||||
#define SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3 1201
|
||||
|
||||
/* Led control for mute switches */
|
||||
#define SOF_TKN_MUTE_LED_USE 1300
|
||||
#define SOF_TKN_MUTE_LED_DIRECTION 1301
|
||||
|
||||
#endif
|
||||
|
@ -175,7 +175,15 @@ void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers);
|
||||
|
||||
int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
void pxa2xx_soc_pcm_free(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
pxa2xx_pcm_free_dma_buffers(pcm);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_free);
|
||||
|
||||
int pxa2xx_soc_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
@ -203,18 +211,64 @@ int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_new);
|
||||
|
||||
const struct snd_pcm_ops pxa2xx_pcm_ops = {
|
||||
.open = pxa2xx_pcm_open,
|
||||
.close = pxa2xx_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = pxa2xx_pcm_hw_params,
|
||||
.hw_free = pxa2xx_pcm_hw_free,
|
||||
.prepare = pxa2xx_pcm_prepare,
|
||||
.trigger = pxa2xx_pcm_trigger,
|
||||
.pointer = pxa2xx_pcm_pointer,
|
||||
.mmap = pxa2xx_pcm_mmap,
|
||||
};
|
||||
EXPORT_SYMBOL(pxa2xx_pcm_ops);
|
||||
int pxa2xx_soc_pcm_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return pxa2xx_pcm_open(substream);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_open);
|
||||
|
||||
int pxa2xx_soc_pcm_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return pxa2xx_pcm_close(substream);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_close);
|
||||
|
||||
int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
return pxa2xx_pcm_hw_params(substream, params);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_params);
|
||||
|
||||
int pxa2xx_soc_pcm_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return pxa2xx_pcm_hw_free(substream);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_free);
|
||||
|
||||
int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return pxa2xx_pcm_prepare(substream);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_prepare);
|
||||
|
||||
int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
return pxa2xx_pcm_trigger(substream, cmd);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_trigger);
|
||||
|
||||
snd_pcm_uframes_t
|
||||
pxa2xx_soc_pcm_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return pxa2xx_pcm_pointer(substream);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_pointer);
|
||||
|
||||
int pxa2xx_soc_pcm_mmap(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return pxa2xx_pcm_mmap(substream, vma);
|
||||
}
|
||||
EXPORT_SYMBOL(pxa2xx_soc_pcm_mmap);
|
||||
|
||||
MODULE_AUTHOR("Nicolas Pitre");
|
||||
MODULE_DESCRIPTION("Intel PXA2xx sound library");
|
||||
|
@ -528,7 +528,7 @@ static int snd_compress_check_input(struct snd_compr_params *params)
|
||||
{
|
||||
/* first let's check the buffer parameter's */
|
||||
if (params->buffer.fragment_size == 0 ||
|
||||
params->buffer.fragments > INT_MAX / params->buffer.fragment_size ||
|
||||
params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||
|
||||
params->buffer.fragments == 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -369,4 +369,87 @@ int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
|
||||
|
||||
/**
|
||||
* snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params
|
||||
* @substream: PCM substream
|
||||
* @dma_data: DAI DMA data
|
||||
* @hw: PCM hw params
|
||||
* @chan: DMA channel to use for data transfers
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*
|
||||
* This function will query DMA capability, then refine the pcm hardware
|
||||
* parameters.
|
||||
*/
|
||||
int snd_dmaengine_pcm_refine_runtime_hwparams(
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_dmaengine_dai_dma_data *dma_data,
|
||||
struct snd_pcm_hardware *hw,
|
||||
struct dma_chan *chan)
|
||||
{
|
||||
struct dma_slave_caps dma_caps;
|
||||
u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
|
||||
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
|
||||
snd_pcm_format_t i;
|
||||
int ret = 0;
|
||||
|
||||
if (!hw || !chan || !dma_data)
|
||||
return -EINVAL;
|
||||
|
||||
ret = dma_get_slave_caps(chan, &dma_caps);
|
||||
if (ret == 0) {
|
||||
if (dma_caps.cmd_pause && dma_caps.cmd_resume)
|
||||
hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
|
||||
if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
|
||||
hw->info |= SNDRV_PCM_INFO_BATCH;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
addr_widths = dma_caps.dst_addr_widths;
|
||||
else
|
||||
addr_widths = dma_caps.src_addr_widths;
|
||||
}
|
||||
|
||||
/*
|
||||
* If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
|
||||
* hw.formats set to 0, meaning no restrictions are in place.
|
||||
* In this case it's the responsibility of the DAI driver to
|
||||
* provide the supported format information.
|
||||
*/
|
||||
if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
|
||||
/*
|
||||
* Prepare formats mask for valid/allowed sample types. If the
|
||||
* dma does not have support for the given physical word size,
|
||||
* it needs to be masked out so user space can not use the
|
||||
* format which produces corrupted audio.
|
||||
* In case the dma driver does not implement the slave_caps the
|
||||
* default assumption is that it supports 1, 2 and 4 bytes
|
||||
* widths.
|
||||
*/
|
||||
for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) {
|
||||
int bits = snd_pcm_format_physical_width(i);
|
||||
|
||||
/*
|
||||
* Enable only samples with DMA supported physical
|
||||
* widths
|
||||
*/
|
||||
switch (bits) {
|
||||
case 8:
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
case 64:
|
||||
if (addr_widths & (1 << (bits / 8)))
|
||||
hw->formats |= pcm_format_to_bits(i);
|
||||
break;
|
||||
default:
|
||||
/* Unsupported types */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -2072,15 +2072,24 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
|
||||
static int generic_hdmi_build_pcms(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int idx;
|
||||
int idx, pcm_num;
|
||||
|
||||
/*
|
||||
* for non-mst mode, pcm number is the same as before
|
||||
* for DP MST mode, pcm number is (nid number + dev_num - 1)
|
||||
* dev_num is the device entry number in a pin
|
||||
*
|
||||
* for DP MST mode without extra PCM, pcm number is same
|
||||
* for DP MST mode with extra PCMs, pcm number is
|
||||
* (nid number + dev_num - 1)
|
||||
* dev_num is the device entry number in a pin
|
||||
*/
|
||||
for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) {
|
||||
|
||||
if (codec->mst_no_extra_pcms)
|
||||
pcm_num = spec->num_nids;
|
||||
else
|
||||
pcm_num = spec->num_nids + spec->dev_num - 1;
|
||||
|
||||
codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
|
||||
|
||||
for (idx = 0; idx < pcm_num; idx++) {
|
||||
struct hda_pcm *info;
|
||||
struct hda_pcm_stream *pstr;
|
||||
|
||||
|
@ -759,14 +759,12 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int acp_dma_open(struct snd_pcm_substream *substream)
|
||||
static int acp_dma_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
u16 bank;
|
||||
int ret = 0;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *prtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
|
||||
DRV_NAME);
|
||||
struct audio_drv_data *intr_data = dev_get_drvdata(component->dev);
|
||||
struct audio_substream_data *adata =
|
||||
kzalloc(sizeof(struct audio_substream_data), GFP_KERNEL);
|
||||
@ -834,7 +832,8 @@ static int acp_dma_open(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
static int acp_dma_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
int status;
|
||||
@ -843,8 +842,6 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct audio_substream_data *rtd;
|
||||
struct snd_soc_pcm_runtime *prtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
|
||||
DRV_NAME);
|
||||
struct audio_drv_data *adata = dev_get_drvdata(component->dev);
|
||||
struct snd_soc_card *card = prtd->card;
|
||||
struct acp_platform_info *pinfo = snd_soc_card_get_drvdata(card);
|
||||
@ -995,7 +992,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
return status;
|
||||
}
|
||||
|
||||
static int acp_dma_hw_free(struct snd_pcm_substream *substream)
|
||||
static int acp_dma_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
@ -1011,7 +1009,8 @@ static u64 acp_get_byte_count(struct audio_substream_data *rtd)
|
||||
return byte_count.bytescount;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream)
|
||||
static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
u32 buffersize;
|
||||
u32 pos = 0;
|
||||
@ -1053,13 +1052,15 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream)
|
||||
return bytes_to_frames(runtime, pos);
|
||||
}
|
||||
|
||||
static int acp_dma_mmap(struct snd_pcm_substream *substream,
|
||||
static int acp_dma_mmap(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return snd_pcm_lib_default_mmap(substream, vma);
|
||||
}
|
||||
|
||||
static int acp_dma_prepare(struct snd_pcm_substream *substream)
|
||||
static int acp_dma_prepare(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_substream_data *rtd = runtime->private_data;
|
||||
@ -1086,7 +1087,8 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int acp_dma_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -1132,10 +1134,9 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int acp_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int acp_dma_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd,
|
||||
DRV_NAME);
|
||||
struct audio_drv_data *adata = dev_get_drvdata(component->dev);
|
||||
struct device *parent = component->dev->parent;
|
||||
|
||||
@ -1158,14 +1159,12 @@ static int acp_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp_dma_close(struct snd_pcm_substream *substream)
|
||||
static int acp_dma_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
u16 bank;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_substream_data *rtd = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *prtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
|
||||
DRV_NAME);
|
||||
struct audio_drv_data *adata = dev_get_drvdata(component->dev);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
@ -1216,22 +1215,18 @@ static int acp_dma_close(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops acp_dma_ops = {
|
||||
.open = acp_dma_open,
|
||||
.close = acp_dma_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = acp_dma_hw_params,
|
||||
.hw_free = acp_dma_hw_free,
|
||||
.trigger = acp_dma_trigger,
|
||||
.pointer = acp_dma_pointer,
|
||||
.mmap = acp_dma_mmap,
|
||||
.prepare = acp_dma_prepare,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver acp_asoc_platform = {
|
||||
.name = DRV_NAME,
|
||||
.ops = &acp_dma_ops,
|
||||
.pcm_new = acp_dma_new,
|
||||
.name = DRV_NAME,
|
||||
.open = acp_dma_open,
|
||||
.close = acp_dma_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = acp_dma_hw_params,
|
||||
.hw_free = acp_dma_hw_free,
|
||||
.trigger = acp_dma_trigger,
|
||||
.pointer = acp_dma_pointer,
|
||||
.mmap = acp_dma_mmap,
|
||||
.prepare = acp_dma_prepare,
|
||||
.pcm_construct = acp_dma_new,
|
||||
};
|
||||
|
||||
static int acp_audio_probe(struct platform_device *pdev)
|
||||
|
@ -275,16 +275,12 @@ static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction)
|
||||
rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL);
|
||||
}
|
||||
|
||||
static int acp3x_dma_open(struct snd_pcm_substream *substream)
|
||||
static int acp3x_dma_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *prtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
|
||||
DRV_NAME);
|
||||
struct i2s_dev_data *adata = dev_get_drvdata(component->dev);
|
||||
|
||||
struct i2s_stream_instance *i2s_data = kzalloc(sizeof(struct i2s_stream_instance),
|
||||
GFP_KERNEL);
|
||||
if (!i2s_data)
|
||||
@ -334,7 +330,8 @@ static u64 acp_get_byte_count(struct i2s_stream_instance *rtd, int direction)
|
||||
return byte_count;
|
||||
}
|
||||
|
||||
static int acp3x_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
static int acp3x_dma_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
int status;
|
||||
@ -362,7 +359,8 @@ static int acp3x_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
return status;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream)
|
||||
static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
u32 pos = 0;
|
||||
u32 buffersize = 0;
|
||||
@ -379,33 +377,32 @@ static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream)
|
||||
return bytes_to_frames(substream->runtime, pos);
|
||||
}
|
||||
|
||||
static int acp3x_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int acp3x_dma_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd,
|
||||
DRV_NAME);
|
||||
struct device *parent = component->dev->parent;
|
||||
snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
|
||||
parent, MIN_BUFFER, MAX_BUFFER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp3x_dma_hw_free(struct snd_pcm_substream *substream)
|
||||
static int acp3x_dma_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int acp3x_dma_mmap(struct snd_pcm_substream *substream,
|
||||
static int acp3x_dma_mmap(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return snd_pcm_lib_default_mmap(substream, vma);
|
||||
}
|
||||
|
||||
static int acp3x_dma_close(struct snd_pcm_substream *substream)
|
||||
static int acp3x_dma_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *prtd = substream->private_data;
|
||||
struct i2s_stream_instance *rtd = substream->runtime->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd,
|
||||
DRV_NAME);
|
||||
struct i2s_dev_data *adata = dev_get_drvdata(component->dev);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
@ -422,17 +419,6 @@ static int acp3x_dma_close(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops acp3x_dma_ops = {
|
||||
.open = acp3x_dma_open,
|
||||
.close = acp3x_dma_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = acp3x_dma_hw_params,
|
||||
.hw_free = acp3x_dma_hw_free,
|
||||
.pointer = acp3x_dma_pointer,
|
||||
.mmap = acp3x_dma_mmap,
|
||||
};
|
||||
|
||||
|
||||
static int acp3x_dai_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
||||
{
|
||||
|
||||
@ -610,9 +596,15 @@ static struct snd_soc_dai_driver acp3x_i2s_dai_driver = {
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver acp3x_i2s_component = {
|
||||
.name = DRV_NAME,
|
||||
.ops = &acp3x_dma_ops,
|
||||
.pcm_new = acp3x_dma_new,
|
||||
.name = DRV_NAME,
|
||||
.open = acp3x_dma_open,
|
||||
.close = acp3x_dma_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = acp3x_dma_hw_params,
|
||||
.hw_free = acp3x_dma_hw_free,
|
||||
.pointer = acp3x_dma_pointer,
|
||||
.mmap = acp3x_dma_mmap,
|
||||
.pcm_construct = acp3x_dma_new,
|
||||
};
|
||||
|
||||
static int acp3x_audio_probe(struct platform_device *pdev)
|
||||
@ -631,7 +623,7 @@ static int acp3x_audio_probe(struct platform_device *pdev)
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
|
||||
return -ENODEV;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
|
||||
|
@ -56,15 +56,17 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
static int atmel_pcm_mmap(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return remap_pfn_range(vma, vma->vm_start,
|
||||
substream->dma_buffer.addr >> PAGE_SHIFT,
|
||||
vma->vm_end - vma->vm_start, vma->vm_page_prot);
|
||||
}
|
||||
|
||||
static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int atmel_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
@ -93,7 +95,8 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void atmel_pcm_free(struct snd_pcm *pcm)
|
||||
static void atmel_pcm_free(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
@ -196,8 +199,9 @@ static void atmel_pcm_dma_irq(u32 ssc_sr,
|
||||
/*--------------------------------------------------------------------------*\
|
||||
* PCM operations
|
||||
\*--------------------------------------------------------------------------*/
|
||||
static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
static int atmel_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct atmel_runtime_data *prtd = runtime->private_data;
|
||||
@ -225,7 +229,8 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
static int atmel_pcm_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct atmel_pcm_dma_params *params = prtd->params;
|
||||
@ -239,7 +244,8 @@ static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
static int atmel_pcm_prepare(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct atmel_pcm_dma_params *params = prtd->params;
|
||||
@ -251,8 +257,8 @@ static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
static int atmel_pcm_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *rtd = substream->runtime;
|
||||
struct atmel_runtime_data *prtd = rtd->private_data;
|
||||
@ -317,8 +323,8 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t atmel_pcm_pointer(
|
||||
struct snd_pcm_substream *substream)
|
||||
static snd_pcm_uframes_t atmel_pcm_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct atmel_runtime_data *prtd = runtime->private_data;
|
||||
@ -335,7 +341,8 @@ static snd_pcm_uframes_t atmel_pcm_pointer(
|
||||
return x;
|
||||
}
|
||||
|
||||
static int atmel_pcm_open(struct snd_pcm_substream *substream)
|
||||
static int atmel_pcm_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct atmel_runtime_data *prtd;
|
||||
@ -360,7 +367,8 @@ static int atmel_pcm_open(struct snd_pcm_substream *substream)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_pcm_close(struct snd_pcm_substream *substream)
|
||||
static int atmel_pcm_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||
|
||||
@ -368,22 +376,18 @@ static int atmel_pcm_close(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops atmel_pcm_ops = {
|
||||
static const struct snd_soc_component_driver atmel_soc_platform = {
|
||||
.open = atmel_pcm_open,
|
||||
.close = atmel_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = atmel_pcm_hw_params,
|
||||
.hw_free = atmel_pcm_hw_free,
|
||||
.prepare = atmel_pcm_prepare,
|
||||
.trigger = atmel_pcm_trigger,
|
||||
.pointer = atmel_pcm_pointer,
|
||||
.mmap = atmel_pcm_mmap,
|
||||
};
|
||||
|
||||
static struct snd_soc_component_driver atmel_soc_platform = {
|
||||
.ops = &atmel_pcm_ops,
|
||||
.pcm_new = atmel_pcm_new,
|
||||
.pcm_free = atmel_pcm_free,
|
||||
.pcm_construct = atmel_pcm_new,
|
||||
.pcm_destruct = atmel_pcm_free,
|
||||
};
|
||||
|
||||
int atmel_pcm_pdc_platform_register(struct device *dev)
|
||||
|
@ -182,15 +182,15 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss)
|
||||
static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss,
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = ss->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct au1xpsc_audio_dmadata *pcd = snd_soc_component_get_drvdata(component);
|
||||
return &pcd[ss->stream];
|
||||
}
|
||||
|
||||
static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
static int au1xpsc_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
@ -202,7 +202,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
goto out;
|
||||
|
||||
stype = substream->stream;
|
||||
pcd = to_dmadata(substream);
|
||||
pcd = to_dmadata(substream, component);
|
||||
|
||||
DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
|
||||
"runtime->min_align %lu\n",
|
||||
@ -232,15 +232,17 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
static int au1xpsc_pcm_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_pcm_lib_free_pages(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
static int au1xpsc_pcm_prepare(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
|
||||
struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
|
||||
|
||||
au1xxx_dbdma_reset(pcd->ddma_chan);
|
||||
|
||||
@ -255,9 +257,10 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int au1xpsc_pcm_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
u32 c = to_dmadata(substream)->ddma_chan;
|
||||
u32 c = to_dmadata(substream, component)->ddma_chan;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
@ -275,14 +278,17 @@ static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
au1xpsc_pcm_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return bytes_to_frames(substream->runtime, to_dmadata(substream)->pos);
|
||||
return bytes_to_frames(substream->runtime,
|
||||
to_dmadata(substream, component)->pos);
|
||||
}
|
||||
|
||||
static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
|
||||
static int au1xpsc_pcm_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
|
||||
struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
int stype = substream->stream, *dmaids;
|
||||
|
||||
@ -296,24 +302,15 @@ static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
|
||||
static int au1xpsc_pcm_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
au1x_pcm_dbdma_free(to_dmadata(substream));
|
||||
au1x_pcm_dbdma_free(to_dmadata(substream, component));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops au1xpsc_pcm_ops = {
|
||||
.open = au1xpsc_pcm_open,
|
||||
.close = au1xpsc_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = au1xpsc_pcm_hw_params,
|
||||
.hw_free = au1xpsc_pcm_hw_free,
|
||||
.prepare = au1xpsc_pcm_prepare,
|
||||
.trigger = au1xpsc_pcm_trigger,
|
||||
.pointer = au1xpsc_pcm_pointer,
|
||||
};
|
||||
|
||||
static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int au1xpsc_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
@ -327,8 +324,15 @@ static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
/* au1xpsc audio platform */
|
||||
static struct snd_soc_component_driver au1xpsc_soc_component = {
|
||||
.name = DRV_NAME,
|
||||
.ops = &au1xpsc_pcm_ops,
|
||||
.pcm_new = au1xpsc_pcm_new,
|
||||
.open = au1xpsc_pcm_open,
|
||||
.close = au1xpsc_pcm_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = au1xpsc_pcm_hw_params,
|
||||
.hw_free = au1xpsc_pcm_hw_free,
|
||||
.prepare = au1xpsc_pcm_prepare,
|
||||
.trigger = au1xpsc_pcm_trigger,
|
||||
.pointer = au1xpsc_pcm_pointer,
|
||||
.pcm_construct = au1xpsc_pcm_new,
|
||||
};
|
||||
|
||||
static int au1xpsc_pcm_drvprobe(struct platform_device *pdev)
|
||||
|
@ -174,22 +174,23 @@ static const struct snd_pcm_hardware alchemy_pcm_hardware = {
|
||||
.fifo_size = 16,
|
||||
};
|
||||
|
||||
static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss)
|
||||
static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss,
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = ss->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
return snd_soc_component_get_drvdata(component);
|
||||
}
|
||||
|
||||
static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss)
|
||||
static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss,
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss);
|
||||
struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss, component);
|
||||
return &(ctx->stream[ss->stream]);
|
||||
}
|
||||
|
||||
static int alchemy_pcm_open(struct snd_pcm_substream *substream)
|
||||
static int alchemy_pcm_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
|
||||
struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
int *dmaids, s = substream->stream;
|
||||
char *name;
|
||||
@ -213,9 +214,10 @@ static int alchemy_pcm_open(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alchemy_pcm_close(struct snd_pcm_substream *substream)
|
||||
static int alchemy_pcm_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
|
||||
struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream, component);
|
||||
int stype = substream->stream;
|
||||
|
||||
ctx->stream[stype].substream = NULL;
|
||||
@ -224,10 +226,11 @@ static int alchemy_pcm_close(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
static int alchemy_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(substream);
|
||||
struct audio_stream *stream = ss_to_as(substream, component);
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
@ -243,16 +246,18 @@ static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
static int alchemy_pcm_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(substream);
|
||||
struct audio_stream *stream = ss_to_as(substream, component);
|
||||
au1000_release_dma_link(stream);
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int alchemy_pcm_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(substream);
|
||||
struct audio_stream *stream = ss_to_as(substream, component);
|
||||
int err = 0;
|
||||
|
||||
switch (cmd) {
|
||||
@ -269,9 +274,10 @@ static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
return err;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
|
||||
static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *ss)
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(ss);
|
||||
struct audio_stream *stream = ss_to_as(ss, component);
|
||||
long location;
|
||||
|
||||
location = get_dma_residue(stream->dma);
|
||||
@ -281,17 +287,8 @@ static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
|
||||
return bytes_to_frames(ss->runtime, location);
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops alchemy_pcm_ops = {
|
||||
.open = alchemy_pcm_open,
|
||||
.close = alchemy_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = alchemy_pcm_hw_params,
|
||||
.hw_free = alchemy_pcm_hw_free,
|
||||
.trigger = alchemy_pcm_trigger,
|
||||
.pointer = alchemy_pcm_pointer,
|
||||
};
|
||||
|
||||
static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int alchemy_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
@ -303,8 +300,14 @@ static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
|
||||
static struct snd_soc_component_driver alchemy_pcm_soc_component = {
|
||||
.name = DRV_NAME,
|
||||
.ops = &alchemy_pcm_ops,
|
||||
.pcm_new = alchemy_pcm_new,
|
||||
.open = alchemy_pcm_open,
|
||||
.close = alchemy_pcm_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = alchemy_pcm_hw_params,
|
||||
.hw_free = alchemy_pcm_hw_free,
|
||||
.trigger = alchemy_pcm_trigger,
|
||||
.pointer = alchemy_pcm_pointer,
|
||||
.pcm_construct = alchemy_pcm_new,
|
||||
};
|
||||
|
||||
static int alchemy_pcm_drvprobe(struct platform_device *pdev)
|
||||
|
@ -376,7 +376,8 @@ static void disable_intr(struct snd_pcm_substream *substream)
|
||||
|
||||
}
|
||||
|
||||
static int cygnus_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int cygnus_pcm_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@ -577,7 +578,8 @@ static irqreturn_t cygnus_dma_irq(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cygnus_pcm_open(struct snd_pcm_substream *substream)
|
||||
static int cygnus_pcm_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
@ -613,7 +615,8 @@ static int cygnus_pcm_open(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cygnus_pcm_close(struct snd_pcm_substream *substream)
|
||||
static int cygnus_pcm_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct cygnus_aio_port *aio;
|
||||
@ -633,8 +636,9 @@ static int cygnus_pcm_close(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
static int cygnus_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
@ -649,7 +653,8 @@ static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
static int cygnus_pcm_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct cygnus_aio_port *aio;
|
||||
@ -661,7 +666,8 @@ static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
static int cygnus_pcm_prepare(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
@ -694,7 +700,8 @@ static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct cygnus_aio_port *aio;
|
||||
unsigned int res = 0, cur = 0, base = 0;
|
||||
@ -750,19 +757,8 @@ static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct snd_pcm_ops cygnus_pcm_ops = {
|
||||
.open = cygnus_pcm_open,
|
||||
.close = cygnus_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = cygnus_pcm_hw_params,
|
||||
.hw_free = cygnus_pcm_hw_free,
|
||||
.prepare = cygnus_pcm_prepare,
|
||||
.trigger = cygnus_pcm_trigger,
|
||||
.pointer = cygnus_pcm_pointer,
|
||||
};
|
||||
|
||||
static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
|
||||
static void cygnus_dma_free_dma_buffers(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_dma_buffer *buf;
|
||||
@ -788,7 +784,8 @@ static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
|
||||
}
|
||||
}
|
||||
|
||||
static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int cygnus_dma_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
@ -810,7 +807,7 @@ static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
ret = cygnus_pcm_preallocate_dma_buffer(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret) {
|
||||
cygnus_dma_free_dma_buffers(pcm);
|
||||
cygnus_dma_free_dma_buffers(component, pcm);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -819,9 +816,16 @@ static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
}
|
||||
|
||||
static struct snd_soc_component_driver cygnus_soc_platform = {
|
||||
.ops = &cygnus_pcm_ops,
|
||||
.pcm_new = cygnus_dma_new,
|
||||
.pcm_free = cygnus_dma_free_dma_buffers,
|
||||
.open = cygnus_pcm_open,
|
||||
.close = cygnus_pcm_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = cygnus_pcm_hw_params,
|
||||
.hw_free = cygnus_pcm_hw_free,
|
||||
.prepare = cygnus_pcm_prepare,
|
||||
.trigger = cygnus_pcm_trigger,
|
||||
.pointer = cygnus_pcm_pointer,
|
||||
.pcm_construct = cygnus_dma_new,
|
||||
.pcm_destruct = cygnus_dma_free_dma_buffers,
|
||||
};
|
||||
|
||||
int cygnus_soc_platform_register(struct device *dev,
|
||||
|
@ -34,6 +34,8 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_ADAU1977_I2C if I2C
|
||||
select SND_SOC_ADAU1701 if I2C
|
||||
select SND_SOC_ADAU7002
|
||||
select SND_SOC_ADAU7118_I2C if I2C
|
||||
select SND_SOC_ADAU7118_HW
|
||||
select SND_SOC_ADS117X
|
||||
select SND_SOC_AK4104 if SPI_MASTER
|
||||
select SND_SOC_AK4118 if I2C
|
||||
@ -179,6 +181,8 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_STI_SAS
|
||||
select SND_SOC_TAS2552 if I2C
|
||||
select SND_SOC_TAS2562 if I2C
|
||||
select SND_SOC_TAS2770 if I2C
|
||||
select SND_SOC_TAS5086 if I2C
|
||||
select SND_SOC_TAS571X if I2C
|
||||
select SND_SOC_TAS5720 if I2C
|
||||
@ -395,6 +399,33 @@ config SND_SOC_ADAU1977_I2C
|
||||
config SND_SOC_ADAU7002
|
||||
tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter"
|
||||
|
||||
config SND_SOC_ADAU7118
|
||||
tristate
|
||||
|
||||
config SND_SOC_ADAU7118_HW
|
||||
tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - HW Mode"
|
||||
select SND_SOC_ADAU7118
|
||||
help
|
||||
Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
|
||||
Converter. In this mode, the device works in standalone mode which
|
||||
means that there is no bus to comunicate with it. Stereo mode is not
|
||||
supported in this mode.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-soc-adau7118-hw.
|
||||
|
||||
config SND_SOC_ADAU7118_I2C
|
||||
tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - I2C"
|
||||
depends on I2C
|
||||
select SND_SOC_ADAU7118
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
|
||||
Converter over I2C. This gives full support over the device.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-soc-adau7118-i2c.
|
||||
|
||||
config SND_SOC_ADAV80X
|
||||
tristate
|
||||
|
||||
@ -478,6 +509,8 @@ config SND_SOC_CQ0093VC
|
||||
config SND_SOC_CROS_EC_CODEC
|
||||
tristate "codec driver for ChromeOS EC"
|
||||
depends on CROS_EC
|
||||
select CRYPTO
|
||||
select CRYPTO_SHA256
|
||||
help
|
||||
If you say yes here you will get support for the
|
||||
ChromeOS Embedded Controller's Audio Codec.
|
||||
@ -1104,6 +1137,14 @@ config SND_SOC_TAS2552
|
||||
tristate "Texas Instruments TAS2552 Mono Audio amplifier"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_TAS2562
|
||||
tristate "Texas Instruments TAS2562 Mono Audio amplifier"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_TAS2770
|
||||
tristate "Texas Instruments TAS2770 speaker amplifier"
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_TAS5086
|
||||
tristate "Texas Instruments TAS5086 speaker amplifier"
|
||||
depends on I2C
|
||||
|
@ -22,6 +22,9 @@ snd-soc-adau1977-objs := adau1977.o
|
||||
snd-soc-adau1977-spi-objs := adau1977-spi.o
|
||||
snd-soc-adau1977-i2c-objs := adau1977-i2c.o
|
||||
snd-soc-adau7002-objs := adau7002.o
|
||||
snd-soc-adau7118-objs := adau7118.o
|
||||
snd-soc-adau7118-i2c-objs := adau7118-i2c.o
|
||||
snd-soc-adau7118-hw-objs := adau7118-hw.o
|
||||
snd-soc-adav80x-objs := adav80x.o
|
||||
snd-soc-adav801-objs := adav801.o
|
||||
snd-soc-adav803-objs := adav803.o
|
||||
@ -196,6 +199,7 @@ snd-soc-tas571x-objs := tas571x.o
|
||||
snd-soc-tas5720-objs := tas5720.o
|
||||
snd-soc-tas6424-objs := tas6424.o
|
||||
snd-soc-tda7419-objs := tda7419.o
|
||||
snd-soc-tas2770-objs := tas2770.o
|
||||
snd-soc-tfa9879-objs := tfa9879.o
|
||||
snd-soc-tlv320aic23-objs := tlv320aic23.o
|
||||
snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
|
||||
@ -280,6 +284,7 @@ snd-soc-max98504-objs := max98504.o
|
||||
snd-soc-simple-amplifier-objs := simple-amplifier.o
|
||||
snd-soc-tpa6130a2-objs := tpa6130a2.o
|
||||
snd-soc-tas2552-objs := tas2552.o
|
||||
snd-soc-tas2562-objs := tas2562.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
|
||||
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
|
||||
@ -304,6 +309,9 @@ obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o
|
||||
obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o
|
||||
obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o
|
||||
obj-$(CONFIG_SND_SOC_ADAU7118) += snd-soc-adau7118.o
|
||||
obj-$(CONFIG_SND_SOC_ADAU7118_I2C) += snd-soc-adau7118-i2c.o
|
||||
obj-$(CONFIG_SND_SOC_ADAU7118_HW) += snd-soc-adau7118-hw.o
|
||||
obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
|
||||
obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o
|
||||
obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
|
||||
@ -474,11 +482,13 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
|
||||
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
|
||||
obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
|
||||
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
|
||||
obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
|
||||
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
|
||||
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
|
||||
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
|
||||
obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o
|
||||
obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o
|
||||
obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
|
||||
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
|
||||
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
|
||||
|
@ -28,6 +28,10 @@
|
||||
#define ADAU1761_REC_MIXER_RIGHT1 0x400d
|
||||
#define ADAU1761_LEFT_DIFF_INPUT_VOL 0x400e
|
||||
#define ADAU1761_RIGHT_DIFF_INPUT_VOL 0x400f
|
||||
#define ADAU1761_ALC_CTRL0 0x4011
|
||||
#define ADAU1761_ALC_CTRL1 0x4012
|
||||
#define ADAU1761_ALC_CTRL2 0x4013
|
||||
#define ADAU1761_ALC_CTRL3 0x4014
|
||||
#define ADAU1761_PLAY_LR_MIXER_LEFT 0x4020
|
||||
#define ADAU1761_PLAY_MIXER_LEFT0 0x401c
|
||||
#define ADAU1761_PLAY_MIXER_LEFT1 0x401d
|
||||
@ -71,6 +75,10 @@ static const struct reg_default adau1761_reg_defaults[] = {
|
||||
{ ADAU1761_REC_MIXER_RIGHT0, 0x00 },
|
||||
{ ADAU1761_REC_MIXER_RIGHT1, 0x00 },
|
||||
{ ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 },
|
||||
{ ADAU1761_ALC_CTRL0, 0x00 },
|
||||
{ ADAU1761_ALC_CTRL1, 0x00 },
|
||||
{ ADAU1761_ALC_CTRL2, 0x00 },
|
||||
{ ADAU1761_ALC_CTRL3, 0x00 },
|
||||
{ ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 },
|
||||
{ ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 },
|
||||
{ ADAU1761_PLAY_MIXER_LEFT0, 0x00 },
|
||||
@ -121,6 +129,10 @@ static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1);
|
||||
static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1);
|
||||
static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1);
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(adau1761_alc_max_gain_tlv, -1200, 600, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(adau1761_alc_target_tlv, -2850, 150, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(adau1761_alc_ng_threshold_tlv, -7650, 150, 0);
|
||||
|
||||
static const unsigned int adau1761_bias_select_values[] = {
|
||||
0, 2, 3,
|
||||
};
|
||||
@ -147,6 +159,103 @@ static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum,
|
||||
ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text,
|
||||
adau1761_bias_select_values);
|
||||
|
||||
static const unsigned int adau1761_pga_slew_time_values[] = {
|
||||
3, 0, 1, 2,
|
||||
};
|
||||
|
||||
static const char * const adau1761_pga_slew_time_text[] = {
|
||||
"Off",
|
||||
"24 ms",
|
||||
"48 ms",
|
||||
"96 ms",
|
||||
};
|
||||
|
||||
static const char * const adau1761_alc_function_text[] = {
|
||||
"Off",
|
||||
"Right",
|
||||
"Left",
|
||||
"Stereo",
|
||||
"DSP control",
|
||||
};
|
||||
|
||||
static const char * const adau1761_alc_hold_time_text[] = {
|
||||
"2.67 ms",
|
||||
"5.34 ms",
|
||||
"10.68 ms",
|
||||
"21.36 ms",
|
||||
"42.72 ms",
|
||||
"85.44 ms",
|
||||
"170.88 ms",
|
||||
"341.76 ms",
|
||||
"683.52 ms",
|
||||
"1367 ms",
|
||||
"2734.1 ms",
|
||||
"5468.2 ms",
|
||||
"10936 ms",
|
||||
"21873 ms",
|
||||
"43745 ms",
|
||||
"87491 ms",
|
||||
};
|
||||
|
||||
static const char * const adau1761_alc_attack_time_text[] = {
|
||||
"6 ms",
|
||||
"12 ms",
|
||||
"24 ms",
|
||||
"48 ms",
|
||||
"96 ms",
|
||||
"192 ms",
|
||||
"384 ms",
|
||||
"768 ms",
|
||||
"1540 ms",
|
||||
"3070 ms",
|
||||
"6140 ms",
|
||||
"12290 ms",
|
||||
"24580 ms",
|
||||
"49150 ms",
|
||||
"98300 ms",
|
||||
"196610 ms",
|
||||
};
|
||||
|
||||
static const char * const adau1761_alc_decay_time_text[] = {
|
||||
"24 ms",
|
||||
"48 ms",
|
||||
"96 ms",
|
||||
"192 ms",
|
||||
"384 ms",
|
||||
"768 ms",
|
||||
"15400 ms",
|
||||
"30700 ms",
|
||||
"61400 ms",
|
||||
"12290 ms",
|
||||
"24580 ms",
|
||||
"49150 ms",
|
||||
"98300 ms",
|
||||
"196610 ms",
|
||||
"393220 ms",
|
||||
"786430 ms",
|
||||
};
|
||||
|
||||
static const char * const adau1761_alc_ng_type_text[] = {
|
||||
"Hold",
|
||||
"Mute",
|
||||
"Fade",
|
||||
"Fade + Mute",
|
||||
};
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_pga_slew_time_enum,
|
||||
ADAU1761_ALC_CTRL0, 6, 0x3, adau1761_pga_slew_time_text,
|
||||
adau1761_pga_slew_time_values);
|
||||
static SOC_ENUM_SINGLE_DECL(adau1761_alc_function_enum,
|
||||
ADAU1761_ALC_CTRL0, 0, adau1761_alc_function_text);
|
||||
static SOC_ENUM_SINGLE_DECL(adau1761_alc_hold_time_enum,
|
||||
ADAU1761_ALC_CTRL1, 4, adau1761_alc_hold_time_text);
|
||||
static SOC_ENUM_SINGLE_DECL(adau1761_alc_attack_time_enum,
|
||||
ADAU1761_ALC_CTRL2, 4, adau1761_alc_attack_time_text);
|
||||
static SOC_ENUM_SINGLE_DECL(adau1761_alc_decay_time_enum,
|
||||
ADAU1761_ALC_CTRL2, 0, adau1761_alc_decay_time_text);
|
||||
static SOC_ENUM_SINGLE_DECL(adau1761_alc_ng_type_enum,
|
||||
ADAU1761_ALC_CTRL3, 6, adau1761_alc_ng_type_text);
|
||||
|
||||
static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = {
|
||||
SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT,
|
||||
4, 1, 0),
|
||||
@ -161,6 +270,22 @@ static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
|
||||
|
||||
SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1,
|
||||
ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv),
|
||||
|
||||
SOC_ENUM("PGA Capture Slew Time", adau1761_pga_slew_time_enum),
|
||||
|
||||
SOC_SINGLE_TLV("ALC Capture Max Gain Volume", ADAU1761_ALC_CTRL0,
|
||||
3, 7, 0, adau1761_alc_max_gain_tlv),
|
||||
SOC_ENUM("ALC Capture Function", adau1761_alc_function_enum),
|
||||
SOC_ENUM("ALC Capture Hold Time", adau1761_alc_hold_time_enum),
|
||||
SOC_SINGLE_TLV("ALC Capture Target Volume", ADAU1761_ALC_CTRL1,
|
||||
0, 15, 0, adau1761_alc_target_tlv),
|
||||
SOC_ENUM("ALC Capture Attack Time", adau1761_alc_decay_time_enum),
|
||||
SOC_ENUM("ALC Capture Decay Time", adau1761_alc_attack_time_enum),
|
||||
SOC_ENUM("ALC Capture Noise Gate Type", adau1761_alc_ng_type_enum),
|
||||
SOC_SINGLE("ALC Capture Noise Gate Switch",
|
||||
ADAU1761_ALC_CTRL3, 5, 1, 0),
|
||||
SOC_SINGLE_TLV("ALC Capture Noise Gate Threshold Volume",
|
||||
ADAU1761_ALC_CTRL3, 0, 31, 0, adau1761_alc_ng_threshold_tlv),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new adau1761_single_mode_controls[] = {
|
||||
@ -632,6 +757,10 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
|
||||
case ADAU1761_DEJITTER:
|
||||
case ADAU1761_CLK_ENABLE0:
|
||||
case ADAU1761_CLK_ENABLE1:
|
||||
case ADAU1761_ALC_CTRL0:
|
||||
case ADAU1761_ALC_CTRL1:
|
||||
case ADAU1761_ALC_CTRL2:
|
||||
case ADAU1761_ALC_CTRL3:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
|
43
sound/soc/codecs/adau7118-hw.c
Normal file
43
sound/soc/codecs/adau7118-hw.c
Normal file
@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter Standalone Hw
|
||||
// driver
|
||||
//
|
||||
// Copyright 2019 Analog Devices Inc.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "adau7118.h"
|
||||
|
||||
static int adau7118_probe_hw(struct platform_device *pdev)
|
||||
{
|
||||
return adau7118_probe(&pdev->dev, NULL, true);
|
||||
}
|
||||
|
||||
static const struct of_device_id adau7118_of_match[] = {
|
||||
{ .compatible = "adi,adau7118" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adau7118_of_match);
|
||||
|
||||
static const struct platform_device_id adau7118_id[] = {
|
||||
{ .name = "adau7118" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, adau7118_id);
|
||||
|
||||
static struct platform_driver adau7118_driver_hw = {
|
||||
.driver = {
|
||||
.name = "adau7118",
|
||||
.of_match_table = adau7118_of_match,
|
||||
},
|
||||
.probe = adau7118_probe_hw,
|
||||
.id_table = adau7118_id,
|
||||
};
|
||||
module_platform_driver(adau7118_driver_hw);
|
||||
|
||||
MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
|
||||
MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver for standalone hw mode");
|
||||
MODULE_LICENSE("GPL");
|
82
sound/soc/codecs/adau7118-i2c.c
Normal file
82
sound/soc/codecs/adau7118-i2c.c
Normal file
@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C
|
||||
//
|
||||
// Copyright 2019 Analog Devices Inc.
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "adau7118.h"
|
||||
|
||||
static const struct reg_default adau7118_reg_defaults[] = {
|
||||
{ ADAU7118_REG_VENDOR_ID, 0x41 },
|
||||
{ ADAU7118_REG_DEVICE_ID1, 0x71 },
|
||||
{ ADAU7118_REG_DEVICE_ID2, 0x18 },
|
||||
{ ADAU7118_REG_REVISION_ID, 0x00 },
|
||||
{ ADAU7118_REG_ENABLES, 0x3F },
|
||||
{ ADAU7118_REG_DEC_RATIO_CLK_MAP, 0xC0 },
|
||||
{ ADAU7118_REG_HPF_CONTROL, 0xD0 },
|
||||
{ ADAU7118_REG_SPT_CTRL1, 0x41 },
|
||||
{ ADAU7118_REG_SPT_CTRL2, 0x00 },
|
||||
{ ADAU7118_REG_SPT_CX(0), 0x01 },
|
||||
{ ADAU7118_REG_SPT_CX(1), 0x11 },
|
||||
{ ADAU7118_REG_SPT_CX(2), 0x21 },
|
||||
{ ADAU7118_REG_SPT_CX(3), 0x31 },
|
||||
{ ADAU7118_REG_SPT_CX(4), 0x41 },
|
||||
{ ADAU7118_REG_SPT_CX(5), 0x51 },
|
||||
{ ADAU7118_REG_SPT_CX(6), 0x61 },
|
||||
{ ADAU7118_REG_SPT_CX(7), 0x71 },
|
||||
{ ADAU7118_REG_DRIVE_STRENGTH, 0x2a },
|
||||
{ ADAU7118_REG_RESET, 0x00 },
|
||||
};
|
||||
|
||||
static const struct regmap_config adau7118_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.reg_defaults = adau7118_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(adau7118_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.max_register = ADAU7118_REG_RESET,
|
||||
};
|
||||
|
||||
static int adau7118_probe_i2c(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *map;
|
||||
|
||||
map = devm_regmap_init_i2c(i2c, &adau7118_regmap_config);
|
||||
if (IS_ERR(map)) {
|
||||
dev_err(&i2c->dev, "Failed to init regmap %ld\n", PTR_ERR(map));
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
return adau7118_probe(&i2c->dev, map, false);
|
||||
}
|
||||
|
||||
static const struct of_device_id adau7118_of_match[] = {
|
||||
{ .compatible = "adi,adau7118" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adau7118_of_match);
|
||||
|
||||
static const struct i2c_device_id adau7118_id[] = {
|
||||
{"adau7118", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adau7118_id);
|
||||
|
||||
static struct i2c_driver adau7118_driver = {
|
||||
.driver = {
|
||||
.name = "adau7118",
|
||||
.of_match_table = adau7118_of_match,
|
||||
},
|
||||
.probe = adau7118_probe_i2c,
|
||||
.id_table = adau7118_id,
|
||||
};
|
||||
module_i2c_driver(adau7118_driver);
|
||||
|
||||
MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
|
||||
MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C");
|
||||
MODULE_LICENSE("GPL");
|
586
sound/soc/codecs/adau7118.c
Normal file
586
sound/soc/codecs/adau7118.c
Normal file
@ -0,0 +1,586 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver
|
||||
//
|
||||
// Copyright 2019 Analog Devices Inc.
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "adau7118.h"
|
||||
|
||||
#define ADAU7118_DEC_RATIO_MASK GENMASK(1, 0)
|
||||
#define ADAU7118_DEC_RATIO(x) FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x)
|
||||
#define ADAU7118_CLK_MAP_MASK GENMASK(7, 4)
|
||||
#define ADAU7118_SLOT_WIDTH_MASK GENMASK(5, 4)
|
||||
#define ADAU7118_SLOT_WIDTH(x) FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x)
|
||||
#define ADAU7118_TRISTATE_MASK BIT(6)
|
||||
#define ADAU7118_TRISTATE(x) FIELD_PREP(ADAU7118_TRISTATE_MASK, x)
|
||||
#define ADAU7118_DATA_FMT_MASK GENMASK(3, 1)
|
||||
#define ADAU7118_DATA_FMT(x) FIELD_PREP(ADAU7118_DATA_FMT_MASK, x)
|
||||
#define ADAU7118_SAI_MODE_MASK BIT(0)
|
||||
#define ADAU7118_SAI_MODE(x) FIELD_PREP(ADAU7118_SAI_MODE_MASK, x)
|
||||
#define ADAU7118_LRCLK_BCLK_POL_MASK GENMASK(1, 0)
|
||||
#define ADAU7118_LRCLK_BCLK_POL(x) \
|
||||
FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x)
|
||||
#define ADAU7118_SPT_SLOT_MASK GENMASK(7, 4)
|
||||
#define ADAU7118_SPT_SLOT(x) FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x)
|
||||
#define ADAU7118_FULL_SOFT_R_MASK BIT(1)
|
||||
#define ADAU7118_FULL_SOFT_R(x) FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x)
|
||||
|
||||
struct adau7118_data {
|
||||
struct regmap *map;
|
||||
struct device *dev;
|
||||
struct regulator *iovdd;
|
||||
struct regulator *dvdd;
|
||||
u32 slot_width;
|
||||
u32 slots;
|
||||
bool hw_mode;
|
||||
bool right_j;
|
||||
};
|
||||
|
||||
/* Input Enable */
|
||||
static const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = {
|
||||
SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0),
|
||||
SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0),
|
||||
SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0),
|
||||
SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget adau7118_widgets_sw[] = {
|
||||
/* Input Enable Switches */
|
||||
SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0,
|
||||
&adau7118_dapm_pdm_control[0]),
|
||||
SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0,
|
||||
&adau7118_dapm_pdm_control[1]),
|
||||
SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0,
|
||||
&adau7118_dapm_pdm_control[2]),
|
||||
SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0,
|
||||
&adau7118_dapm_pdm_control[3]),
|
||||
|
||||
/* PDM Clocks */
|
||||
SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0),
|
||||
|
||||
/* Output channels */
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0),
|
||||
0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1),
|
||||
0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2),
|
||||
0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3),
|
||||
0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4),
|
||||
0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5),
|
||||
0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6),
|
||||
0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7),
|
||||
0, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route adau7118_routes_sw[] = {
|
||||
{ "PDM0", "Capture Switch", "PDM_DAT0" },
|
||||
{ "PDM1", "Capture Switch", "PDM_DAT1" },
|
||||
{ "PDM2", "Capture Switch", "PDM_DAT2" },
|
||||
{ "PDM3", "Capture Switch", "PDM_DAT3" },
|
||||
{ "AIF1TX1", NULL, "PDM0" },
|
||||
{ "AIF1TX2", NULL, "PDM0" },
|
||||
{ "AIF1TX3", NULL, "PDM1" },
|
||||
{ "AIF1TX4", NULL, "PDM1" },
|
||||
{ "AIF1TX5", NULL, "PDM2" },
|
||||
{ "AIF1TX6", NULL, "PDM2" },
|
||||
{ "AIF1TX7", NULL, "PDM3" },
|
||||
{ "AIF1TX8", NULL, "PDM3" },
|
||||
{ "Capture", NULL, "PDM_CLK0" },
|
||||
{ "Capture", NULL, "PDM_CLK1" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget adau7118_widgets_hw[] = {
|
||||
SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route adau7118_routes_hw[] = {
|
||||
{ "AIF1TX", NULL, "PDM_DAT0" },
|
||||
{ "AIF1TX", NULL, "PDM_DAT1" },
|
||||
{ "AIF1TX", NULL, "PDM_DAT2" },
|
||||
{ "AIF1TX", NULL, "PDM_DAT3" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget adau7118_widgets[] = {
|
||||
SND_SOC_DAPM_INPUT("PDM_DAT0"),
|
||||
SND_SOC_DAPM_INPUT("PDM_DAT1"),
|
||||
SND_SOC_DAPM_INPUT("PDM_DAT2"),
|
||||
SND_SOC_DAPM_INPUT("PDM_DAT3"),
|
||||
};
|
||||
|
||||
static int adau7118_set_channel_map(struct snd_soc_dai *dai,
|
||||
unsigned int tx_num, unsigned int *tx_slot,
|
||||
unsigned int rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
struct adau7118_data *st =
|
||||
snd_soc_component_get_drvdata(dai->component);
|
||||
int chan, ret;
|
||||
|
||||
dev_dbg(st->dev, "Set channel map, %d", tx_num);
|
||||
|
||||
for (chan = 0; chan < tx_num; chan++) {
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
ADAU7118_REG_SPT_CX(chan),
|
||||
ADAU7118_SPT_SLOT_MASK,
|
||||
ADAU7118_SPT_SLOT(tx_slot[chan]));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct adau7118_data *st =
|
||||
snd_soc_component_get_drvdata(dai->component);
|
||||
int ret = 0;
|
||||
u32 regval;
|
||||
|
||||
dev_dbg(st->dev, "Set format, fmt:%d\n", fmt);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
ADAU7118_REG_SPT_CTRL1,
|
||||
ADAU7118_DATA_FMT_MASK,
|
||||
ADAU7118_DATA_FMT(0));
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
ADAU7118_REG_SPT_CTRL1,
|
||||
ADAU7118_DATA_FMT_MASK,
|
||||
ADAU7118_DATA_FMT(1));
|
||||
break;
|
||||
case SND_SOC_DAIFMT_RIGHT_J:
|
||||
st->right_j = true;
|
||||
break;
|
||||
default:
|
||||
dev_err(st->dev, "Invalid format %d",
|
||||
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
regval = ADAU7118_LRCLK_BCLK_POL(0);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
regval = ADAU7118_LRCLK_BCLK_POL(2);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
regval = ADAU7118_LRCLK_BCLK_POL(1);
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
regval = ADAU7118_LRCLK_BCLK_POL(3);
|
||||
break;
|
||||
default:
|
||||
dev_err(st->dev, "Invalid Inv mask %d",
|
||||
fmt & SND_SOC_DAIFMT_INV_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
ADAU7118_REG_SPT_CTRL2,
|
||||
ADAU7118_LRCLK_BCLK_POL_MASK,
|
||||
regval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate)
|
||||
{
|
||||
struct adau7118_data *st =
|
||||
snd_soc_component_get_drvdata(dai->component);
|
||||
int ret;
|
||||
|
||||
dev_dbg(st->dev, "Set tristate, %d\n", tristate);
|
||||
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
ADAU7118_REG_SPT_CTRL1,
|
||||
ADAU7118_TRISTATE_MASK,
|
||||
ADAU7118_TRISTATE(tristate));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
|
||||
unsigned int rx_mask, int slots,
|
||||
int slot_width)
|
||||
{
|
||||
struct adau7118_data *st =
|
||||
snd_soc_component_get_drvdata(dai->component);
|
||||
int ret = 0;
|
||||
u32 regval;
|
||||
|
||||
dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width);
|
||||
|
||||
switch (slot_width) {
|
||||
case 32:
|
||||
regval = ADAU7118_SLOT_WIDTH(0);
|
||||
break;
|
||||
case 24:
|
||||
regval = ADAU7118_SLOT_WIDTH(2);
|
||||
break;
|
||||
case 16:
|
||||
regval = ADAU7118_SLOT_WIDTH(1);
|
||||
break;
|
||||
default:
|
||||
dev_err(st->dev, "Invalid slot width:%d\n", slot_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
ADAU7118_REG_SPT_CTRL1,
|
||||
ADAU7118_SLOT_WIDTH_MASK, regval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->slot_width = slot_width;
|
||||
st->slots = slots;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adau7118_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct adau7118_data *st =
|
||||
snd_soc_component_get_drvdata(dai->component);
|
||||
u32 data_width = params_width(params), slots_width;
|
||||
int ret;
|
||||
u32 regval;
|
||||
|
||||
if (!st->slots) {
|
||||
/* set stereo mode */
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
ADAU7118_REG_SPT_CTRL1,
|
||||
ADAU7118_SAI_MODE_MASK,
|
||||
ADAU7118_SAI_MODE(0));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
slots_width = 32;
|
||||
} else {
|
||||
slots_width = st->slot_width;
|
||||
}
|
||||
|
||||
if (data_width > slots_width) {
|
||||
dev_err(st->dev, "Invalid data_width:%d, slots_width:%d",
|
||||
data_width, slots_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (st->right_j) {
|
||||
switch (slots_width - data_width) {
|
||||
case 8:
|
||||
/* delay bclck by 8 */
|
||||
regval = ADAU7118_DATA_FMT(2);
|
||||
break;
|
||||
case 12:
|
||||
/* delay bclck by 12 */
|
||||
regval = ADAU7118_DATA_FMT(3);
|
||||
break;
|
||||
case 16:
|
||||
/* delay bclck by 16 */
|
||||
regval = ADAU7118_DATA_FMT(4);
|
||||
break;
|
||||
default:
|
||||
dev_err(st->dev,
|
||||
"Cannot set right_j setting, slot_w:%d, data_w:%d\n",
|
||||
slots_width, data_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_component_update_bits(dai->component,
|
||||
ADAU7118_REG_SPT_CTRL1,
|
||||
ADAU7118_DATA_FMT_MASK,
|
||||
regval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adau7118_set_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct adau7118_data *st = snd_soc_component_get_drvdata(component);
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(st->dev, "Set bias level %d\n", level);
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
if (snd_soc_component_get_bias_level(component) ==
|
||||
SND_SOC_BIAS_OFF) {
|
||||
/* power on */
|
||||
ret = regulator_enable(st->iovdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* there's no timing constraints before enabling dvdd */
|
||||
ret = regulator_enable(st->dvdd);
|
||||
if (ret) {
|
||||
regulator_disable(st->iovdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (st->hw_mode)
|
||||
return 0;
|
||||
|
||||
regcache_cache_only(st->map, false);
|
||||
/* sync cache */
|
||||
ret = snd_soc_component_cache_sync(component);
|
||||
}
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
/* power off */
|
||||
ret = regulator_disable(st->dvdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_disable(st->iovdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (st->hw_mode)
|
||||
return 0;
|
||||
|
||||
/* cache only */
|
||||
regcache_mark_dirty(st->map);
|
||||
regcache_cache_only(st->map, true);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adau7118_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct adau7118_data *st = snd_soc_component_get_drvdata(component);
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_component_get_dapm(component);
|
||||
int ret = 0;
|
||||
|
||||
if (st->hw_mode) {
|
||||
ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw,
|
||||
ARRAY_SIZE(adau7118_widgets_hw));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw,
|
||||
ARRAY_SIZE(adau7118_routes_hw));
|
||||
} else {
|
||||
snd_soc_component_init_regmap(component, st->map);
|
||||
ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw,
|
||||
ARRAY_SIZE(adau7118_widgets_sw));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw,
|
||||
ARRAY_SIZE(adau7118_routes_sw));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops adau7118_ops = {
|
||||
.hw_params = adau7118_hw_params,
|
||||
.set_channel_map = adau7118_set_channel_map,
|
||||
.set_fmt = adau7118_set_fmt,
|
||||
.set_tdm_slot = adau7118_set_tdm_slot,
|
||||
.set_tristate = adau7118_set_tristate,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver adau7118_dai = {
|
||||
.name = "adau7118-hifi-capture",
|
||||
.capture = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
|
||||
SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_3LE,
|
||||
.rates = SNDRV_PCM_RATE_CONTINUOUS,
|
||||
.rate_min = 4000,
|
||||
.rate_max = 192000,
|
||||
.sig_bits = 24,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver adau7118_component_driver = {
|
||||
.probe = adau7118_component_probe,
|
||||
.set_bias_level = adau7118_set_bias_level,
|
||||
.dapm_widgets = adau7118_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(adau7118_widgets),
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static void adau7118_regulator_disable(void *data)
|
||||
{
|
||||
struct adau7118_data *st = data;
|
||||
int ret;
|
||||
/*
|
||||
* If we fail to disable DVDD, don't bother in trying IOVDD. We
|
||||
* actually don't want to be left in the situation where DVDD
|
||||
* is enabled and IOVDD is disabled.
|
||||
*/
|
||||
ret = regulator_disable(st->dvdd);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
regulator_disable(st->iovdd);
|
||||
}
|
||||
|
||||
static int adau7118_regulator_setup(struct adau7118_data *st)
|
||||
{
|
||||
st->iovdd = devm_regulator_get(st->dev, "iovdd");
|
||||
if (IS_ERR(st->iovdd)) {
|
||||
dev_err(st->dev, "Could not get iovdd: %ld\n",
|
||||
PTR_ERR(st->iovdd));
|
||||
return PTR_ERR(st->iovdd);
|
||||
}
|
||||
|
||||
st->dvdd = devm_regulator_get(st->dev, "dvdd");
|
||||
if (IS_ERR(st->dvdd)) {
|
||||
dev_err(st->dev, "Could not get dvdd: %ld\n",
|
||||
PTR_ERR(st->dvdd));
|
||||
return PTR_ERR(st->dvdd);
|
||||
}
|
||||
/* just assume the device is in reset */
|
||||
if (!st->hw_mode) {
|
||||
regcache_mark_dirty(st->map);
|
||||
regcache_cache_only(st->map, true);
|
||||
}
|
||||
|
||||
return devm_add_action_or_reset(st->dev, adau7118_regulator_disable,
|
||||
st);
|
||||
}
|
||||
|
||||
static int adau7118_parset_dt(const struct adau7118_data *st)
|
||||
{
|
||||
int ret;
|
||||
u32 dec_ratio = 0;
|
||||
/* 4 inputs */
|
||||
u32 clk_map[4], regval;
|
||||
|
||||
if (st->hw_mode)
|
||||
return 0;
|
||||
|
||||
ret = device_property_read_u32(st->dev, "adi,decimation-ratio",
|
||||
&dec_ratio);
|
||||
if (!ret) {
|
||||
switch (dec_ratio) {
|
||||
case 64:
|
||||
regval = ADAU7118_DEC_RATIO(0);
|
||||
break;
|
||||
case 32:
|
||||
regval = ADAU7118_DEC_RATIO(1);
|
||||
break;
|
||||
case 16:
|
||||
regval = ADAU7118_DEC_RATIO(2);
|
||||
break;
|
||||
default:
|
||||
dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(st->map,
|
||||
ADAU7118_REG_DEC_RATIO_CLK_MAP,
|
||||
ADAU7118_DEC_RATIO_MASK, regval);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map",
|
||||
clk_map, ARRAY_SIZE(clk_map));
|
||||
if (!ret) {
|
||||
int pdm;
|
||||
u32 _clk_map = 0;
|
||||
|
||||
for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++)
|
||||
_clk_map |= (clk_map[pdm] << (pdm + 4));
|
||||
|
||||
ret = regmap_update_bits(st->map,
|
||||
ADAU7118_REG_DEC_RATIO_CLK_MAP,
|
||||
ADAU7118_CLK_MAP_MASK, _clk_map);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode)
|
||||
{
|
||||
struct adau7118_data *st;
|
||||
int ret;
|
||||
|
||||
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
st->dev = dev;
|
||||
st->hw_mode = hw_mode;
|
||||
dev_set_drvdata(dev, st);
|
||||
|
||||
if (!hw_mode) {
|
||||
st->map = map;
|
||||
adau7118_dai.ops = &adau7118_ops;
|
||||
/*
|
||||
* Perform a full soft reset. This will set all register's
|
||||
* with their reset values.
|
||||
*/
|
||||
ret = regmap_update_bits(map, ADAU7118_REG_RESET,
|
||||
ADAU7118_FULL_SOFT_R_MASK,
|
||||
ADAU7118_FULL_SOFT_R(1));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adau7118_parset_dt(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adau7118_regulator_setup(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_snd_soc_register_component(dev,
|
||||
&adau7118_component_driver,
|
||||
&adau7118_dai, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adau7118_probe);
|
||||
|
||||
MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
|
||||
MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver");
|
||||
MODULE_LICENSE("GPL");
|
24
sound/soc/codecs/adau7118.h
Normal file
24
sound/soc/codecs/adau7118.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_ADAU7118_H
|
||||
#define _LINUX_ADAU7118_H
|
||||
|
||||
struct regmap;
|
||||
struct device;
|
||||
|
||||
/* register map */
|
||||
#define ADAU7118_REG_VENDOR_ID 0x00
|
||||
#define ADAU7118_REG_DEVICE_ID1 0x01
|
||||
#define ADAU7118_REG_DEVICE_ID2 0x02
|
||||
#define ADAU7118_REG_REVISION_ID 0x03
|
||||
#define ADAU7118_REG_ENABLES 0x04
|
||||
#define ADAU7118_REG_DEC_RATIO_CLK_MAP 0x05
|
||||
#define ADAU7118_REG_HPF_CONTROL 0x06
|
||||
#define ADAU7118_REG_SPT_CTRL1 0x07
|
||||
#define ADAU7118_REG_SPT_CTRL2 0x08
|
||||
#define ADAU7118_REG_SPT_CX(num) (0x09 + (num))
|
||||
#define ADAU7118_REG_DRIVE_STRENGTH 0x11
|
||||
#define ADAU7118_REG_RESET 0x12
|
||||
|
||||
int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1507,7 +1507,7 @@ static int cx2072x_probe(struct snd_soc_component *codec)
|
||||
regmap_multi_reg_write(cx2072x->regmap, cx2072x_reg_init,
|
||||
ARRAY_SIZE(cx2072x_reg_init));
|
||||
|
||||
/* configre PortC as input device */
|
||||
/* configure PortC as input device */
|
||||
regmap_update_bits(cx2072x->regmap, CX2072X_PORTC_PIN_CTRL,
|
||||
0x20, 0x20);
|
||||
|
||||
|
@ -14,13 +14,11 @@
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/hdaudio_ext.h>
|
||||
#include <sound/hda_i915.h>
|
||||
#include <sound/hda_codec.h>
|
||||
#include <sound/hda_register.h>
|
||||
#include "hdac_hda.h"
|
||||
|
||||
#define HDAC_ANALOG_DAI_ID 0
|
||||
#define HDAC_DIGITAL_DAI_ID 1
|
||||
#define HDAC_ALT_ANALOG_DAI_ID 2
|
||||
#include "hdac_hda.h"
|
||||
|
||||
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
|
||||
SNDRV_PCM_FMTBIT_U8 | \
|
||||
@ -32,6 +30,11 @@
|
||||
SNDRV_PCM_FMTBIT_U32_LE | \
|
||||
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
|
||||
|
||||
#define STUB_HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
||||
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
|
||||
SNDRV_PCM_RATE_192000)
|
||||
|
||||
static int hdac_hda_dai_open(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai);
|
||||
static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
|
||||
@ -121,7 +124,46 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = {
|
||||
.formats = STUB_FORMATS,
|
||||
.sig_bits = 24,
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
.id = HDAC_HDMI_0_DAI_ID,
|
||||
.name = "intel-hdmi-hifi1",
|
||||
.ops = &hdac_hda_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "hifi1",
|
||||
.channels_min = 1,
|
||||
.channels_max = 32,
|
||||
.rates = STUB_HDMI_RATES,
|
||||
.formats = STUB_FORMATS,
|
||||
.sig_bits = 24,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = HDAC_HDMI_1_DAI_ID,
|
||||
.name = "intel-hdmi-hifi2",
|
||||
.ops = &hdac_hda_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "hifi2",
|
||||
.channels_min = 1,
|
||||
.channels_max = 32,
|
||||
.rates = STUB_HDMI_RATES,
|
||||
.formats = STUB_FORMATS,
|
||||
.sig_bits = 24,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = HDAC_HDMI_2_DAI_ID,
|
||||
.name = "intel-hdmi-hifi3",
|
||||
.ops = &hdac_hda_dai_ops,
|
||||
.playback = {
|
||||
.stream_name = "hifi3",
|
||||
.channels_min = 1,
|
||||
.channels_max = 32,
|
||||
.rates = STUB_HDMI_RATES,
|
||||
.formats = STUB_FORMATS,
|
||||
.sig_bits = 24,
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@ -135,10 +177,11 @@ static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
|
||||
hda_pvt = snd_soc_component_get_drvdata(component);
|
||||
pcm = &hda_pvt->pcm[dai->id];
|
||||
|
||||
if (tx_mask)
|
||||
pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
|
||||
pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
|
||||
else
|
||||
pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
|
||||
pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -278,6 +321,12 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
|
||||
struct hda_pcm *cpcm;
|
||||
const char *pcm_name;
|
||||
|
||||
/*
|
||||
* map DAI ID to the closest matching PCM name, using the naming
|
||||
* scheme used by hda-codec snd_hda_gen_build_pcms() and for
|
||||
* HDMI in hda_codec patch_hdmi.c)
|
||||
*/
|
||||
|
||||
switch (dai->id) {
|
||||
case HDAC_ANALOG_DAI_ID:
|
||||
pcm_name = "Analog";
|
||||
@ -288,13 +337,22 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
|
||||
case HDAC_ALT_ANALOG_DAI_ID:
|
||||
pcm_name = "Alt Analog";
|
||||
break;
|
||||
case HDAC_HDMI_0_DAI_ID:
|
||||
pcm_name = "HDMI 0";
|
||||
break;
|
||||
case HDAC_HDMI_1_DAI_ID:
|
||||
pcm_name = "HDMI 1";
|
||||
break;
|
||||
case HDAC_HDMI_2_DAI_ID:
|
||||
pcm_name = "HDMI 2";
|
||||
break;
|
||||
default:
|
||||
dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
|
||||
if (strpbrk(cpcm->name, pcm_name))
|
||||
if (strstr(cpcm->name, pcm_name))
|
||||
return cpcm;
|
||||
}
|
||||
|
||||
@ -302,6 +360,18 @@ static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool is_hdmi_codec(struct hda_codec *hcodec)
|
||||
{
|
||||
struct hda_pcm *cpcm;
|
||||
|
||||
list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) {
|
||||
if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int hdac_hda_codec_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct hdac_hda_priv *hda_pvt =
|
||||
@ -322,6 +392,15 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
|
||||
|
||||
snd_hdac_ext_bus_link_get(hdev->bus, hlink);
|
||||
|
||||
/*
|
||||
* Ensure any HDA display is powered at codec probe.
|
||||
* After snd_hda_codec_device_new(), display power is
|
||||
* managed by runtime PM.
|
||||
*/
|
||||
if (hda_pvt->need_display_power)
|
||||
snd_hdac_display_power(hdev->bus,
|
||||
HDA_CODEC_IDX_CONTROLLER, true);
|
||||
|
||||
ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card,
|
||||
hdev->addr, hcodec);
|
||||
if (ret < 0) {
|
||||
@ -366,20 +445,31 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
|
||||
dev_dbg(&hdev->dev, "no patch file found\n");
|
||||
}
|
||||
|
||||
/* configure codec for 1:1 PCM:DAI mapping */
|
||||
hcodec->mst_no_extra_pcms = 1;
|
||||
|
||||
ret = snd_hda_codec_parse_pcms(hcodec);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = snd_hda_codec_build_controls(hcodec);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdev->dev, "unable to create controls %d\n", ret);
|
||||
goto error;
|
||||
/* HDMI controls need to be created in machine drivers */
|
||||
if (!is_hdmi_codec(hcodec)) {
|
||||
ret = snd_hda_codec_build_controls(hcodec);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdev->dev, "unable to create controls %d\n",
|
||||
ret);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
hcodec->core.lazy_cache = true;
|
||||
|
||||
if (hda_pvt->need_display_power)
|
||||
snd_hdac_display_power(hdev->bus,
|
||||
HDA_CODEC_IDX_CONTROLLER, false);
|
||||
|
||||
/*
|
||||
* hdac_device core already sets the state to active and calls
|
||||
* get_noresume. So enable runtime and set the device to suspend.
|
||||
@ -410,8 +500,8 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
|
||||
return;
|
||||
}
|
||||
|
||||
snd_hdac_ext_bus_link_put(hdev->bus, hlink);
|
||||
pm_runtime_disable(&hdev->dev);
|
||||
snd_hdac_ext_bus_link_put(hdev->bus, hlink);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {
|
||||
|
@ -6,6 +6,16 @@
|
||||
#ifndef __HDAC_HDA_H__
|
||||
#define __HDAC_HDA_H__
|
||||
|
||||
enum {
|
||||
HDAC_ANALOG_DAI_ID = 0,
|
||||
HDAC_DIGITAL_DAI_ID,
|
||||
HDAC_ALT_ANALOG_DAI_ID,
|
||||
HDAC_HDMI_0_DAI_ID,
|
||||
HDAC_HDMI_1_DAI_ID,
|
||||
HDAC_HDMI_2_DAI_ID,
|
||||
HDAC_LAST_DAI_ID = HDAC_HDMI_2_DAI_ID,
|
||||
};
|
||||
|
||||
struct hdac_hda_pcm {
|
||||
int stream_tag[2];
|
||||
unsigned int format_val[2];
|
||||
@ -13,7 +23,8 @@ struct hdac_hda_pcm {
|
||||
|
||||
struct hdac_hda_priv {
|
||||
struct hda_codec codec;
|
||||
struct hdac_hda_pcm pcm[2];
|
||||
struct hdac_hda_pcm pcm[HDAC_LAST_DAI_ID];
|
||||
bool need_display_power;
|
||||
};
|
||||
|
||||
#define hdac_to_hda_priv(_hdac) \
|
||||
|
@ -274,7 +274,7 @@ struct hdmi_codec_priv {
|
||||
uint8_t eld[MAX_ELD_BYTES];
|
||||
struct snd_pcm_chmap *chmap_info;
|
||||
unsigned int chmap_idx;
|
||||
struct mutex lock;
|
||||
unsigned long busy;
|
||||
struct snd_soc_jack *jack;
|
||||
unsigned int jack_status;
|
||||
};
|
||||
@ -390,8 +390,8 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
int ret = 0;
|
||||
|
||||
ret = mutex_trylock(&hcp->lock);
|
||||
if (!ret) {
|
||||
ret = test_and_set_bit(0, &hcp->busy);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "Only one simultaneous stream supported!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -419,7 +419,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
||||
|
||||
err:
|
||||
/* Release the exclusive lock on error */
|
||||
mutex_unlock(&hcp->lock);
|
||||
clear_bit(0, &hcp->busy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -431,7 +431,7 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
|
||||
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
|
||||
hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
|
||||
|
||||
mutex_unlock(&hcp->lock);
|
||||
clear_bit(0, &hcp->busy);
|
||||
}
|
||||
|
||||
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
||||
@ -811,8 +811,6 @@ static int hdmi_codec_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
hcp->hcd = *hcd;
|
||||
mutex_init(&hcp->lock);
|
||||
|
||||
daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
|
||||
if (!daidrv)
|
||||
return -ENOMEM;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define MADERA_FLL_SRC_NONE -1
|
||||
#define MADERA_FLL_SRC_MCLK1 0
|
||||
#define MADERA_FLL_SRC_MCLK2 1
|
||||
#define MADERA_FLL_SRC_MCLK3 2
|
||||
#define MADERA_FLL_SRC_SLIMCLK 3
|
||||
#define MADERA_FLL_SRC_FLL1 4
|
||||
#define MADERA_FLL_SRC_FLL2 5
|
||||
@ -51,6 +52,7 @@
|
||||
|
||||
#define MADERA_CLK_SRC_MCLK1 0x0
|
||||
#define MADERA_CLK_SRC_MCLK2 0x1
|
||||
#define MADERA_CLK_SRC_MCLK3 0x2
|
||||
#define MADERA_CLK_SRC_FLL1 0x4
|
||||
#define MADERA_CLK_SRC_FLL2 0x5
|
||||
#define MADERA_CLK_SRC_FLL3 0x6
|
||||
|
@ -960,11 +960,11 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
/* Power on device */
|
||||
if (gpio_is_valid(max98373->reset_gpio)) {
|
||||
ret = gpio_request(max98373->reset_gpio, "MAX98373_RESET");
|
||||
ret = devm_gpio_request(&i2c->dev, max98373->reset_gpio,
|
||||
"MAX98373_RESET");
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "%s: Failed to request gpio %d\n",
|
||||
__func__, max98373->reset_gpio);
|
||||
gpio_free(max98373->reset_gpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
gpio_direction_output(max98373->reset_gpio, 0);
|
||||
|
@ -228,6 +228,10 @@
|
||||
#define CDC_A_RX_EAR_CTL (0xf19E)
|
||||
#define RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK BIT(0)
|
||||
#define RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE BIT(0)
|
||||
#define RX_EAR_CTL_PA_EAR_PA_EN_MASK BIT(6)
|
||||
#define RX_EAR_CTL_PA_EAR_PA_EN_ENABLE BIT(6)
|
||||
#define RX_EAR_CTL_PA_SEL_MASK BIT(7)
|
||||
#define RX_EAR_CTL_PA_SEL BIT(7)
|
||||
|
||||
#define CDC_A_SPKR_DAC_CTL (0xf1B0)
|
||||
#define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4)
|
||||
@ -306,12 +310,13 @@ struct pm8916_wcd_analog_priv {
|
||||
};
|
||||
|
||||
static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
|
||||
static const char *const rdac2_mux_text[] = { "ZERO", "RX2", "RX1" };
|
||||
static const char *const rdac2_mux_text[] = { "RX1", "RX2" };
|
||||
static const char *const hph_text[] = { "ZERO", "Switch", };
|
||||
|
||||
static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT(
|
||||
ARRAY_SIZE(hph_text), hph_text);
|
||||
|
||||
static const struct snd_kcontrol_new ear_mux = SOC_DAPM_ENUM("EAR_S", hph_enum);
|
||||
static const struct snd_kcontrol_new hphl_mux = SOC_DAPM_ENUM("HPHL", hph_enum);
|
||||
static const struct snd_kcontrol_new hphr_mux = SOC_DAPM_ENUM("HPHR", hph_enum);
|
||||
|
||||
@ -321,7 +326,7 @@ static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE_VIRT(
|
||||
|
||||
/* RDAC2 MUX */
|
||||
static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE(
|
||||
CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 3, rdac2_mux_text);
|
||||
CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 2, rdac2_mux_text);
|
||||
|
||||
static const struct snd_kcontrol_new spkr_switch[] = {
|
||||
SOC_DAPM_SINGLE("Switch", CDC_A_SPKR_DAC_CTL, 7, 1, 0)
|
||||
@ -685,6 +690,34 @@ static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol,
|
||||
int event)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
|
||||
RX_EAR_CTL_PA_SEL_MASK, RX_EAR_CTL_PA_SEL);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
|
||||
RX_EAR_CTL_PA_EAR_PA_EN_MASK,
|
||||
RX_EAR_CTL_PA_EAR_PA_EN_ENABLE);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
|
||||
RX_EAR_CTL_PA_EAR_PA_EN_MASK, 0);
|
||||
/* Delay to reduce ear turn off pop */
|
||||
usleep_range(7000, 7100);
|
||||
snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
|
||||
RX_EAR_CTL_PA_SEL_MASK, 0);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct reg_default wcd_reg_defaults_2_0[] = {
|
||||
{CDC_A_RX_COM_OCP_CTL, 0xD1},
|
||||
{CDC_A_RX_COM_OCP_COUNT, 0xFF},
|
||||
@ -801,12 +834,20 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = {
|
||||
{"PDM_TX", NULL, "A_MCLK2"},
|
||||
{"A_MCLK2", NULL, "A_MCLK"},
|
||||
|
||||
/* Earpiece (RX MIX1) */
|
||||
{"EAR", NULL, "EAR_S"},
|
||||
{"EAR_S", "Switch", "EAR PA"},
|
||||
{"EAR PA", NULL, "RX_BIAS"},
|
||||
{"EAR PA", NULL, "HPHL DAC"},
|
||||
{"EAR PA", NULL, "HPHR DAC"},
|
||||
{"EAR PA", NULL, "EAR CP"},
|
||||
|
||||
/* Headset (RX MIX1 and RX MIX2) */
|
||||
{"HEADPHONE", NULL, "HPHL PA"},
|
||||
{"HEADPHONE", NULL, "HPHR PA"},
|
||||
|
||||
{"HPHL PA", NULL, "EAR_HPHL_CLK"},
|
||||
{"HPHR PA", NULL, "EAR_HPHR_CLK"},
|
||||
{"HPHL DAC", NULL, "EAR_HPHL_CLK"},
|
||||
{"HPHR DAC", NULL, "EAR_HPHR_CLK"},
|
||||
|
||||
{"CP", NULL, "NCP_CLK"},
|
||||
|
||||
@ -847,11 +888,20 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_INPUT("AMIC1"),
|
||||
SND_SOC_DAPM_INPUT("AMIC3"),
|
||||
SND_SOC_DAPM_INPUT("AMIC2"),
|
||||
SND_SOC_DAPM_OUTPUT("EAR"),
|
||||
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
|
||||
|
||||
/* RX stuff */
|
||||
SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM,
|
||||
0, 0, NULL, 0,
|
||||
pm8916_wcd_analog_enable_ear_pa,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux),
|
||||
SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux),
|
||||
SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL,
|
||||
|
@ -93,6 +93,8 @@ struct mt6358_priv {
|
||||
int mtkaif_protocol;
|
||||
|
||||
struct regulator *avdd_reg;
|
||||
|
||||
int wov_enabled;
|
||||
};
|
||||
|
||||
int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
|
||||
@ -464,6 +466,106 @@ static int mt6358_put_volsw(struct snd_kcontrol *kcontrol,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mt6358_restore_pga(struct mt6358_priv *priv);
|
||||
|
||||
static int mt6358_enable_wov_phase2(struct mt6358_priv *priv)
|
||||
{
|
||||
/* analog */
|
||||
regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
|
||||
0xffff, 0x0000);
|
||||
regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5);
|
||||
regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
|
||||
0xffff, 0x0800);
|
||||
mt6358_restore_pga(priv);
|
||||
|
||||
regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9929);
|
||||
regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
|
||||
0xffff, 0x0025);
|
||||
regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8,
|
||||
0xffff, 0x0005);
|
||||
|
||||
/* digital */
|
||||
regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
|
||||
0xffff, 0x0000);
|
||||
regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x0120);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0xffff);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0200);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2424);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xdbac);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x029e);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0000);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0,
|
||||
0xffff, 0x0000);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0,
|
||||
0xffff, 0x0451);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0x68d1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6358_disable_wov_phase2(struct mt6358_priv *priv)
|
||||
{
|
||||
/* digital */
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0xc000);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0,
|
||||
0xffff, 0x0450);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0,
|
||||
0xffff, 0x0c00);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0100);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x006c);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xa879);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2323);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0400);
|
||||
regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0x0000);
|
||||
regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x02d8);
|
||||
regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
|
||||
0xffff, 0x0000);
|
||||
|
||||
/* analog */
|
||||
regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8,
|
||||
0xffff, 0x0004);
|
||||
regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9,
|
||||
0xffff, 0x0000);
|
||||
regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9829);
|
||||
regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1,
|
||||
0xffff, 0x0000);
|
||||
mt6358_restore_pga(priv);
|
||||
regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5);
|
||||
regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
|
||||
0xffff, 0x0010);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6358_get_wov(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
|
||||
struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
|
||||
|
||||
ucontrol->value.integer.value[0] = priv->wov_enabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6358_put_wov(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
|
||||
struct mt6358_priv *priv = snd_soc_component_get_drvdata(c);
|
||||
int enabled = ucontrol->value.integer.value[0];
|
||||
|
||||
if (priv->wov_enabled != enabled) {
|
||||
if (enabled)
|
||||
mt6358_enable_wov_phase2(priv);
|
||||
else
|
||||
mt6358_disable_wov_phase2(priv);
|
||||
|
||||
priv->wov_enabled = enabled;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0);
|
||||
|
||||
@ -483,6 +585,9 @@ static const struct snd_kcontrol_new mt6358_snd_controls[] = {
|
||||
MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1,
|
||||
8, 4, 0,
|
||||
snd_soc_get_volsw, mt6358_put_volsw, pga_tlv),
|
||||
|
||||
SOC_SINGLE_BOOL_EXT("Wake-on-Voice Phase2 Switch", 0,
|
||||
mt6358_get_wov, mt6358_put_wov),
|
||||
};
|
||||
|
||||
/* MUX */
|
||||
|
@ -62,6 +62,7 @@ struct pcm3168a_priv {
|
||||
unsigned long sysclk;
|
||||
|
||||
struct pcm3168a_io_params io_params[2];
|
||||
struct snd_soc_dai_driver dai_drv[2];
|
||||
};
|
||||
|
||||
static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
|
||||
@ -314,6 +315,34 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
|
||||
u64 formats = SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE;
|
||||
unsigned int channel_max = dai->id == PCM3168A_DAI_DAC ? 8 : 6;
|
||||
|
||||
if (pcm3168a->io_params[dai->id].fmt == PCM3168A_FMT_RIGHT_J) {
|
||||
/* S16_LE is only supported in RIGHT_J mode */
|
||||
formats |= SNDRV_PCM_FMTBIT_S16_LE;
|
||||
|
||||
/*
|
||||
* If multi DIN/DOUT is not selected, RIGHT_J can only support
|
||||
* two channels (no TDM support)
|
||||
*/
|
||||
if (pcm3168a->io_params[dai->id].tdm_slots != 2)
|
||||
channel_max = 2;
|
||||
}
|
||||
|
||||
if (dai->id == PCM3168A_DAI_DAC) {
|
||||
dai->driver->playback.channels_max = channel_max;
|
||||
dai->driver->playback.formats = formats;
|
||||
} else {
|
||||
dai->driver->capture.channels_max = channel_max;
|
||||
dai->driver->capture.formats = formats;
|
||||
}
|
||||
}
|
||||
|
||||
static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
@ -376,6 +405,8 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
|
||||
|
||||
regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
|
||||
|
||||
pcm3168a_update_fixup_pcm_stream(dai);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -409,6 +440,8 @@ static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
|
||||
else
|
||||
io_params->tdm_mask = rx_mask;
|
||||
|
||||
pcm3168a_update_fixup_pcm_stream(dai);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -530,63 +563,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcm3168a_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
|
||||
unsigned int sample_min;
|
||||
unsigned int channel_max;
|
||||
unsigned int channel_maxs[] = {
|
||||
8, /* DAC */
|
||||
6 /* ADC */
|
||||
};
|
||||
|
||||
/*
|
||||
* Available Data Bits
|
||||
*
|
||||
* RIGHT_J : 24 / 16
|
||||
* LEFT_J : 24
|
||||
* I2S : 24
|
||||
*
|
||||
* TDM available
|
||||
*
|
||||
* I2S
|
||||
* LEFT_J
|
||||
*/
|
||||
switch (pcm3168a->io_params[dai->id].fmt) {
|
||||
case PCM3168A_FMT_RIGHT_J:
|
||||
sample_min = 16;
|
||||
channel_max = 2;
|
||||
break;
|
||||
case PCM3168A_FMT_LEFT_J:
|
||||
case PCM3168A_FMT_I2S:
|
||||
case PCM3168A_FMT_DSP_A:
|
||||
case PCM3168A_FMT_DSP_B:
|
||||
sample_min = 24;
|
||||
channel_max = channel_maxs[dai->id];
|
||||
break;
|
||||
default:
|
||||
sample_min = 24;
|
||||
channel_max = 2;
|
||||
}
|
||||
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
|
||||
sample_min, 32);
|
||||
|
||||
/* Allow all channels in multi DIN/DOUT mode */
|
||||
if (pcm3168a->io_params[dai->id].tdm_slots == 2)
|
||||
channel_max = channel_maxs[dai->id];
|
||||
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
2, channel_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
|
||||
.startup = pcm3168a_startup,
|
||||
.set_fmt = pcm3168a_set_dai_fmt,
|
||||
.set_sysclk = pcm3168a_set_dai_sysclk,
|
||||
.hw_params = pcm3168a_hw_params,
|
||||
@ -776,8 +753,10 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_idle(dev);
|
||||
|
||||
ret = devm_snd_soc_register_component(dev, &pcm3168a_driver, pcm3168a_dais,
|
||||
ARRAY_SIZE(pcm3168a_dais));
|
||||
memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv));
|
||||
ret = devm_snd_soc_register_component(dev, &pcm3168a_driver,
|
||||
pcm3168a->dai_drv,
|
||||
ARRAY_SIZE(pcm3168a->dai_drv));
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register component: %d\n", ret);
|
||||
goto err_regulator;
|
||||
|
@ -61,7 +61,6 @@ static const struct reg_sequence init_list[] = {
|
||||
{ RT1011_DAC_SET_1, 0xe702 },
|
||||
{ RT1011_DAC_SET_3, 0x2004 },
|
||||
};
|
||||
#define RT1011_INIT_REG_LEN ARRAY_SIZE(init_list)
|
||||
|
||||
static const struct reg_default rt1011_reg[] = {
|
||||
{0x0000, 0x0000},
|
||||
@ -684,7 +683,8 @@ static int rt1011_reg_init(struct snd_soc_component *component)
|
||||
{
|
||||
struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
regmap_multi_reg_write(rt1011->regmap, init_list, RT1011_INIT_REG_LEN);
|
||||
regmap_multi_reg_write(rt1011->regmap,
|
||||
init_list, ARRAY_SIZE(init_list));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -989,7 +989,7 @@ static SOC_ENUM_SINGLE_DECL(rt1011_din_source_enum, RT1011_CROSS_BQ_SET_1, 5,
|
||||
|
||||
static const char * const rt1011_tdm_data_out_select[] = {
|
||||
"TDM_O_LR", "BQ1", "DVOL", "BQ10", "ALC", "DMIX", "ADC_SRC_LR",
|
||||
"ADC_O_LR", "ADC_MONO", "RSPK_BPF_LR", "DMIX_ADD", "ENVELOPE_FS",
|
||||
"ADC_O_LR", "ADC_MONO", "RSPK_BPF_LR", "DMIX_ADD", "ENVELOPE_FS",
|
||||
"SEP_O_GAIN", "ALC_BK_GAIN", "STP_V_C", "DMIX_ABST"
|
||||
};
|
||||
|
||||
@ -1002,7 +1002,7 @@ static SOC_ENUM_SINGLE_DECL(rt1011_tdm2_l_dac1_enum, RT1011_TDM2_SET_4, 12,
|
||||
rt1011_tdm_l_ch_data_select);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_dat_enum,
|
||||
RT1011_ADCDAT_OUT_SOURCE, 0, rt1011_tdm_data_out_select);
|
||||
RT1011_ADCDAT_OUT_SOURCE, 0, rt1011_tdm_data_out_select);
|
||||
static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_loc_enum, RT1011_TDM1_SET_2, 0,
|
||||
rt1011_tdm_l_ch_data_select);
|
||||
|
||||
@ -1024,9 +1024,9 @@ static const char * const rt1011_tdm_adc_swap_select[] = {
|
||||
"L/R", "R/L", "L/L", "R/R"
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc1_1_enum, RT1011_TDM1_SET_3, 6,
|
||||
static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc1_1_enum, RT1011_TDM1_SET_3, 6,
|
||||
rt1011_tdm_adc_swap_select);
|
||||
static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc2_1_enum, RT1011_TDM1_SET_3, 4,
|
||||
static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc2_1_enum, RT1011_TDM1_SET_3, 4,
|
||||
rt1011_tdm_adc_swap_select);
|
||||
|
||||
static void rt1011_reset(struct regmap *regmap)
|
||||
@ -1092,9 +1092,9 @@ static bool rt1011_validate_bq_drc_coeff(unsigned short reg)
|
||||
{
|
||||
if ((reg == RT1011_DAC_SET_1) |
|
||||
(reg >= RT1011_ADC_SET && reg <= RT1011_ADC_SET_1) |
|
||||
(reg == RT1011_ADC_SET_4) | (reg == RT1011_ADC_SET_5) |
|
||||
(reg == RT1011_ADC_SET_4) | (reg == RT1011_ADC_SET_5) |
|
||||
(reg == RT1011_MIXER_1) |
|
||||
(reg == RT1011_A_TIMING_1) | (reg >= RT1011_POWER_7 &&
|
||||
(reg == RT1011_A_TIMING_1) | (reg >= RT1011_POWER_7 &&
|
||||
reg <= RT1011_POWER_8) |
|
||||
(reg == RT1011_CLASS_D_POS) | (reg == RT1011_ANALOG_CTRL) |
|
||||
(reg >= RT1011_SPK_TEMP_PROTECT_0 &&
|
||||
@ -1163,9 +1163,6 @@ static int rt1011_bq_drc_coeff_put(struct snd_kcontrol *kcontrol,
|
||||
(struct rt1011_bq_drc_params *)ucontrol->value.integer.value;
|
||||
unsigned int i, mode_idx = 0;
|
||||
|
||||
if (!component->card->instantiated)
|
||||
return 0;
|
||||
|
||||
if (strstr(ucontrol->id.name, "AdvanceMode Initial Set"))
|
||||
mode_idx = RT1011_ADVMODE_INITIAL_SET;
|
||||
else if (strstr(ucontrol->id.name, "AdvanceMode SEP BQ Coeff"))
|
||||
@ -1236,9 +1233,6 @@ static int rt1011_r0_cali_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (!component->card->instantiated)
|
||||
return 0;
|
||||
|
||||
rt1011->cali_done = 0;
|
||||
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF &&
|
||||
ucontrol->value.integer.value[0])
|
||||
@ -1284,9 +1278,6 @@ static int rt1011_r0_load_mode_put(struct snd_kcontrol *kcontrol,
|
||||
if (ucontrol->value.integer.value[0] == rt1011->r0_reg)
|
||||
return 0;
|
||||
|
||||
if (!component->card->instantiated)
|
||||
return 0;
|
||||
|
||||
if (ucontrol->value.integer.value[0] == 0)
|
||||
return -EINVAL;
|
||||
|
||||
@ -1298,7 +1289,7 @@ static int rt1011_r0_load_mode_put(struct snd_kcontrol *kcontrol,
|
||||
r0_integer = format / rt1011->r0_reg / 128;
|
||||
r0_factor = ((format / rt1011->r0_reg * 100) / 128)
|
||||
- (r0_integer * 100);
|
||||
dev_info(dev, "New r0 resistance about %d.%02d ohm, reg=0x%X\n",
|
||||
dev_info(dev, "New r0 resistance about %d.%02d ohm, reg=0x%X\n",
|
||||
r0_integer, r0_factor, rt1011->r0_reg);
|
||||
|
||||
if (rt1011->r0_reg)
|
||||
@ -1640,6 +1631,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto _set_fmt_err_;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
@ -1650,6 +1642,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto _set_fmt_err_;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
@ -1666,6 +1659,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto _set_fmt_err_;
|
||||
}
|
||||
|
||||
switch (dai->id) {
|
||||
@ -1683,6 +1677,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
_set_fmt_err_:
|
||||
snd_soc_dapm_mutex_unlock(dapm);
|
||||
return ret;
|
||||
}
|
||||
@ -1778,7 +1773,8 @@ static int rt1011_set_component_pll(struct snd_soc_component *component,
|
||||
|
||||
ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "Unsupport input clock %d\n", freq_in);
|
||||
dev_err(component->dev, "Unsupported input clock %d\n",
|
||||
freq_in);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1805,8 +1801,8 @@ static int rt1011_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_component_get_dapm(component);
|
||||
unsigned int val = 0, tdm_en = 0;
|
||||
int ret = 0;
|
||||
unsigned int val = 0, tdm_en = 0, rx_slotnum, tx_slotnum;
|
||||
int ret = 0, first_bit, last_bit;
|
||||
|
||||
snd_soc_dapm_mutex_lock(dapm);
|
||||
if (rx_mask || tx_mask)
|
||||
@ -1829,6 +1825,7 @@ static int rt1011_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto _set_tdm_err_;
|
||||
}
|
||||
|
||||
switch (slot_width) {
|
||||
@ -1848,22 +1845,153 @@ static int rt1011_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto _set_tdm_err_;
|
||||
}
|
||||
|
||||
/* Rx slot configuration */
|
||||
rx_slotnum = hweight_long(rx_mask);
|
||||
first_bit = find_next_bit((unsigned long *)&rx_mask, 32, 0);
|
||||
if (rx_slotnum > 1 || rx_slotnum == 0) {
|
||||
ret = -EINVAL;
|
||||
dev_dbg(component->dev, "too many rx slots or zero slot\n");
|
||||
goto _set_tdm_err_;
|
||||
}
|
||||
|
||||
switch (first_bit) {
|
||||
case 0:
|
||||
case 2:
|
||||
case 4:
|
||||
case 6:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK,
|
||||
RT1011_MONO_L_CHANNEL);
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_4,
|
||||
RT1011_TDM_I2S_TX_L_DAC1_1_MASK |
|
||||
RT1011_TDM_I2S_TX_R_DAC1_1_MASK,
|
||||
(first_bit << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) |
|
||||
((first_bit+1) << RT1011_TDM_I2S_TX_R_DAC1_1_SFT));
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK,
|
||||
RT1011_MONO_R_CHANNEL);
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_4,
|
||||
RT1011_TDM_I2S_TX_L_DAC1_1_MASK |
|
||||
RT1011_TDM_I2S_TX_R_DAC1_1_MASK,
|
||||
((first_bit-1) << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) |
|
||||
(first_bit << RT1011_TDM_I2S_TX_R_DAC1_1_SFT));
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto _set_tdm_err_;
|
||||
}
|
||||
|
||||
/* Tx slot configuration */
|
||||
tx_slotnum = hweight_long(tx_mask);
|
||||
first_bit = find_next_bit((unsigned long *)&tx_mask, 32, 0);
|
||||
last_bit = find_last_bit((unsigned long *)&tx_mask, 32);
|
||||
if (tx_slotnum > 2 || (last_bit-first_bit) > 1) {
|
||||
ret = -EINVAL;
|
||||
dev_dbg(component->dev, "too many tx slots or tx slot location error\n");
|
||||
goto _set_tdm_err_;
|
||||
}
|
||||
|
||||
if (tx_slotnum == 1) {
|
||||
snd_soc_component_update_bits(component, RT1011_TDM1_SET_2,
|
||||
RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK |
|
||||
RT1011_TDM_ADCDAT1_DATA_LOCATION, first_bit);
|
||||
switch (first_bit) {
|
||||
case 1:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_3,
|
||||
RT1011_TDM_I2S_RX_ADC1_1_MASK,
|
||||
RT1011_TDM_I2S_RX_ADC1_1_LL);
|
||||
break;
|
||||
case 3:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_3,
|
||||
RT1011_TDM_I2S_RX_ADC2_1_MASK,
|
||||
RT1011_TDM_I2S_RX_ADC2_1_LL);
|
||||
break;
|
||||
case 5:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_3,
|
||||
RT1011_TDM_I2S_RX_ADC3_1_MASK,
|
||||
RT1011_TDM_I2S_RX_ADC3_1_LL);
|
||||
break;
|
||||
case 7:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_3,
|
||||
RT1011_TDM_I2S_RX_ADC4_1_MASK,
|
||||
RT1011_TDM_I2S_RX_ADC4_1_LL);
|
||||
break;
|
||||
case 0:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_3,
|
||||
RT1011_TDM_I2S_RX_ADC1_1_MASK, 0);
|
||||
break;
|
||||
case 2:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_3,
|
||||
RT1011_TDM_I2S_RX_ADC2_1_MASK, 0);
|
||||
break;
|
||||
case 4:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_3,
|
||||
RT1011_TDM_I2S_RX_ADC3_1_MASK, 0);
|
||||
break;
|
||||
case 6:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_3,
|
||||
RT1011_TDM_I2S_RX_ADC4_1_MASK, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
dev_dbg(component->dev,
|
||||
"tx slot location error\n");
|
||||
goto _set_tdm_err_;
|
||||
}
|
||||
} else if (tx_slotnum == 2) {
|
||||
switch (first_bit) {
|
||||
case 0:
|
||||
case 2:
|
||||
case 4:
|
||||
case 6:
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_TDM1_SET_2,
|
||||
RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK |
|
||||
RT1011_TDM_ADCDAT1_DATA_LOCATION,
|
||||
RT1011_TDM_I2S_DOCK_ADCDAT_2CH | first_bit);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
dev_dbg(component->dev,
|
||||
"tx slot location should be paired and start from slot0/2/4/6\n");
|
||||
goto _set_tdm_err_;
|
||||
}
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(component, RT1011_TDM1_SET_1,
|
||||
RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK |
|
||||
RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
|
||||
RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
|
||||
snd_soc_component_update_bits(component, RT1011_TDM2_SET_1,
|
||||
RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK |
|
||||
RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
|
||||
RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val);
|
||||
snd_soc_component_update_bits(component, RT1011_TDM1_SET_2,
|
||||
RT1011_TDM_I2S_DOCK_EN_1_MASK, tdm_en);
|
||||
RT1011_TDM_I2S_DOCK_EN_1_MASK, tdm_en);
|
||||
snd_soc_component_update_bits(component, RT1011_TDM2_SET_2,
|
||||
RT1011_TDM_I2S_DOCK_EN_2_MASK, tdm_en);
|
||||
snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
|
||||
RT1011_ADCDAT1_PIN_CONFIG | RT1011_ADCDAT2_PIN_CONFIG,
|
||||
RT1011_ADCDAT1_OUTPUT | RT1011_ADCDAT2_OUTPUT);
|
||||
RT1011_TDM_I2S_DOCK_EN_2_MASK, tdm_en);
|
||||
if (tx_slotnum)
|
||||
snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET,
|
||||
RT1011_ADCDAT1_PIN_CONFIG | RT1011_ADCDAT2_PIN_CONFIG,
|
||||
RT1011_ADCDAT1_OUTPUT | RT1011_ADCDAT2_OUTPUT);
|
||||
|
||||
_set_tdm_err_:
|
||||
snd_soc_dapm_mutex_unlock(dapm);
|
||||
return ret;
|
||||
}
|
||||
@ -1982,7 +2110,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt1011 = {
|
||||
.remove = rt1011_remove,
|
||||
.suspend = rt1011_suspend,
|
||||
.resume = rt1011_resume,
|
||||
.set_bias_level = rt1011_set_bias_level,
|
||||
.set_bias_level = rt1011_set_bias_level,
|
||||
.controls = rt1011_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(rt1011_snd_controls),
|
||||
.dapm_widgets = rt1011_dapm_widgets,
|
||||
@ -1991,9 +2119,9 @@ static const struct snd_soc_component_driver soc_component_dev_rt1011 = {
|
||||
.num_dapm_routes = ARRAY_SIZE(rt1011_dapm_routes),
|
||||
.set_sysclk = rt1011_set_component_sysclk,
|
||||
.set_pll = rt1011_set_component_pll,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static const struct regmap_config rt1011_regmap = {
|
||||
@ -2095,17 +2223,17 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
|
||||
dc_offset = value << 16;
|
||||
regmap_read(rt1011->regmap, RT1011_EFUSE_ADC_OFFSET_15_0, &value);
|
||||
dc_offset |= (value & 0xffff);
|
||||
dev_info(dev, "ADC offset=0x%x\n", dc_offset);
|
||||
dev_info(dev, "ADC offset=0x%x\n", dc_offset);
|
||||
regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_20_16, &value);
|
||||
dc_offset = value << 16;
|
||||
regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_15_0, &value);
|
||||
dc_offset |= (value & 0xffff);
|
||||
dev_info(dev, "Gain0 offset=0x%x\n", dc_offset);
|
||||
dev_info(dev, "Gain0 offset=0x%x\n", dc_offset);
|
||||
regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_20_16, &value);
|
||||
dc_offset = value << 16;
|
||||
regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_15_0, &value);
|
||||
dc_offset |= (value & 0xffff);
|
||||
dev_info(dev, "Gain1 offset=0x%x\n", dc_offset);
|
||||
dev_info(dev, "Gain1 offset=0x%x\n", dc_offset);
|
||||
|
||||
|
||||
if (cali_flag) {
|
||||
@ -2125,7 +2253,7 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
|
||||
while (count < chk_cnt) {
|
||||
msleep(100);
|
||||
regmap_read(rt1011->regmap,
|
||||
RT1011_INIT_RECIPROCAL_SYN_24_16, &value);
|
||||
RT1011_INIT_RECIPROCAL_SYN_24_16, &value);
|
||||
r0[count%3] = value << 16;
|
||||
regmap_read(rt1011->regmap,
|
||||
RT1011_INIT_RECIPROCAL_SYN_15_0, &value);
|
||||
@ -2140,7 +2268,7 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
|
||||
break;
|
||||
}
|
||||
if (count > chk_cnt) {
|
||||
dev_err(dev, "Calibrate R0 Failure\n");
|
||||
dev_err(dev, "Calibrate R0 Failure\n");
|
||||
ret = -EAGAIN;
|
||||
} else {
|
||||
format = 2147483648U; /* 2^24 * 128 */
|
||||
@ -2149,7 +2277,7 @@ static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag)
|
||||
- (r0_integer * 100);
|
||||
rt1011->r0_reg = r0[0];
|
||||
rt1011->cali_done = 1;
|
||||
dev_info(dev, "r0 resistance about %d.%02d ohm, reg=0x%X\n",
|
||||
dev_info(dev, "r0 resistance about %d.%02d ohm, reg=0x%X\n",
|
||||
r0_integer, r0_factor, r0[0]);
|
||||
}
|
||||
}
|
||||
@ -2196,8 +2324,12 @@ static void rt1011_calibration_work(struct work_struct *work)
|
||||
struct rt1011_priv *rt1011 =
|
||||
container_of(work, struct rt1011_priv, cali_work);
|
||||
struct snd_soc_component *component = rt1011->component;
|
||||
unsigned int r0_integer, r0_factor, format;
|
||||
|
||||
rt1011_calibrate(rt1011, 1);
|
||||
if (rt1011->r0_calib)
|
||||
rt1011_calibrate(rt1011, 0);
|
||||
else
|
||||
rt1011_calibrate(rt1011, 1);
|
||||
|
||||
/*
|
||||
* This flag should reset after booting.
|
||||
@ -2208,6 +2340,40 @@ static void rt1011_calibration_work(struct work_struct *work)
|
||||
|
||||
/* initial */
|
||||
rt1011_reg_init(component);
|
||||
|
||||
/* Apply temperature and calibration data from device property */
|
||||
if (rt1011->temperature_calib <= 0xff &&
|
||||
rt1011->temperature_calib > 0) {
|
||||
snd_soc_component_update_bits(component,
|
||||
RT1011_STP_INITIAL_RESISTANCE_TEMP, 0x3ff,
|
||||
(rt1011->temperature_calib << 2));
|
||||
}
|
||||
|
||||
if (rt1011->r0_calib) {
|
||||
rt1011->r0_reg = rt1011->r0_calib;
|
||||
|
||||
format = 2147483648U; /* 2^24 * 128 */
|
||||
r0_integer = format / rt1011->r0_reg / 128;
|
||||
r0_factor = ((format / rt1011->r0_reg * 100) / 128)
|
||||
- (r0_integer * 100);
|
||||
dev_info(component->dev, "DP r0 resistance about %d.%02d ohm, reg=0x%X\n",
|
||||
r0_integer, r0_factor, rt1011->r0_reg);
|
||||
|
||||
rt1011_r0_load(rt1011);
|
||||
}
|
||||
}
|
||||
|
||||
static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev)
|
||||
{
|
||||
device_property_read_u32(dev, "realtek,temperature_calib",
|
||||
&rt1011->temperature_calib);
|
||||
device_property_read_u32(dev, "realtek,r0_calib",
|
||||
&rt1011->r0_calib);
|
||||
|
||||
dev_dbg(dev, "%s: r0_calib: 0x%x, temperature_calib: 0x%x",
|
||||
__func__, rt1011->r0_calib, rt1011->temperature_calib);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt1011_i2c_probe(struct i2c_client *i2c,
|
||||
@ -2219,11 +2385,13 @@ static int rt1011_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
rt1011 = devm_kzalloc(&i2c->dev, sizeof(struct rt1011_priv),
|
||||
GFP_KERNEL);
|
||||
if (rt1011 == NULL)
|
||||
if (!rt1011)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, rt1011);
|
||||
|
||||
rt1011_parse_dp(rt1011, &i2c->dev);
|
||||
|
||||
rt1011->regmap = devm_regmap_init_i2c(i2c, &rt1011_regmap);
|
||||
if (IS_ERR(rt1011->regmap)) {
|
||||
ret = PTR_ERR(rt1011->regmap);
|
||||
@ -2254,7 +2422,6 @@ static void rt1011_i2c_shutdown(struct i2c_client *client)
|
||||
rt1011_reset(rt1011->regmap);
|
||||
}
|
||||
|
||||
|
||||
static struct i2c_driver rt1011_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "rt1011",
|
||||
|
@ -460,6 +460,23 @@
|
||||
#define RT1011_TDM_I2S_DOCK_EN_1_MASK (0x1 << 3)
|
||||
#define RT1011_TDM_I2S_DOCK_EN_1_SFT 3
|
||||
#define RT1011_TDM_I2S_DOCK_EN_1 (0x1 << 3)
|
||||
#define RT1011_TDM_ADCDAT1_DATA_LOCATION (0x7 << 0)
|
||||
|
||||
/* TDM1 Setting-3 (0x0118) */
|
||||
#define RT1011_TDM_I2S_RX_ADC1_1_MASK (0x3 << 6)
|
||||
#define RT1011_TDM_I2S_RX_ADC2_1_MASK (0x3 << 4)
|
||||
#define RT1011_TDM_I2S_RX_ADC3_1_MASK (0x3 << 2)
|
||||
#define RT1011_TDM_I2S_RX_ADC4_1_MASK (0x3 << 0)
|
||||
#define RT1011_TDM_I2S_RX_ADC1_1_LL (0x2 << 6)
|
||||
#define RT1011_TDM_I2S_RX_ADC2_1_LL (0x2 << 4)
|
||||
#define RT1011_TDM_I2S_RX_ADC3_1_LL (0x2 << 2)
|
||||
#define RT1011_TDM_I2S_RX_ADC4_1_LL (0x2 << 0)
|
||||
|
||||
/* TDM1 Setting-4 (0x011a) */
|
||||
#define RT1011_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12)
|
||||
#define RT1011_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8)
|
||||
#define RT1011_TDM_I2S_TX_L_DAC1_1_SFT 12
|
||||
#define RT1011_TDM_I2S_TX_R_DAC1_1_SFT 8
|
||||
|
||||
/* TDM2 Setting-2 (0x0120) */
|
||||
#define RT1011_TDM_I2S_DOCK_ADCDAT_LEN_2_MASK (0x7 << 13)
|
||||
@ -585,6 +602,12 @@
|
||||
#define RT1011_STP_T0_EN_BIT 6
|
||||
#define RT1011_STP_T0_EN (0x1 << 6)
|
||||
|
||||
/* Cross Biquad Setting-1 (0x0702) */
|
||||
#define RT1011_MONO_LR_SEL_MASK (0x3 << 5)
|
||||
#define RT1011_MONO_L_CHANNEL (0x0 << 5)
|
||||
#define RT1011_MONO_R_CHANNEL (0x1 << 5)
|
||||
#define RT1011_MONO_LR_MIX_CHANNEL (0x2 << 5)
|
||||
|
||||
/* ClassD Internal Setting-1 (0x1300) */
|
||||
#define RT1011_DRIVER_READY_SPK (0x1 << 12)
|
||||
#define RT1011_DRIVER_READY_SPK_BIT 12
|
||||
@ -667,6 +690,7 @@ struct rt1011_priv {
|
||||
|
||||
int bq_drc_set;
|
||||
unsigned int r0_reg, cali_done;
|
||||
unsigned int r0_calib, temperature_calib;
|
||||
int recv_spk_mode;
|
||||
};
|
||||
|
||||
|
@ -201,18 +201,18 @@ static irqreturn_t rt5514_spi_irq(int irq, void *data)
|
||||
}
|
||||
|
||||
/* PCM for streaming audio from the DSP buffer */
|
||||
static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream)
|
||||
static int rt5514_spi_pcm_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
static int rt5514_spi_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct rt5514_dsp *rt5514_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
@ -234,10 +234,9 @@ static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
|
||||
static int rt5514_spi_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct rt5514_dsp *rt5514_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
@ -251,24 +250,22 @@ static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct rt5514_dsp *rt5514_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops rt5514_spi_pcm_ops = {
|
||||
.open = rt5514_spi_pcm_open,
|
||||
.hw_params = rt5514_spi_hw_params,
|
||||
.hw_free = rt5514_spi_hw_free,
|
||||
.pointer = rt5514_spi_pcm_pointer,
|
||||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
};
|
||||
static struct page *rt5514_spi_pcm_page(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
unsigned long offset)
|
||||
{
|
||||
return snd_pcm_lib_get_vmalloc_page(substream, offset);
|
||||
}
|
||||
|
||||
static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
|
||||
{
|
||||
@ -302,9 +299,13 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver rt5514_spi_component = {
|
||||
.name = DRV_NAME,
|
||||
.probe = rt5514_spi_pcm_probe,
|
||||
.ops = &rt5514_spi_pcm_ops,
|
||||
.name = DRV_NAME,
|
||||
.probe = rt5514_spi_pcm_probe,
|
||||
.open = rt5514_spi_pcm_open,
|
||||
.hw_params = rt5514_spi_hw_params,
|
||||
.hw_free = rt5514_spi_hw_free,
|
||||
.pointer = rt5514_spi_pcm_pointer,
|
||||
.page = rt5514_spi_pcm_page,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3636,6 +3636,12 @@ static const struct rt5645_platform_data lattepanda_board_platform_data = {
|
||||
.inv_jd1_1 = true
|
||||
};
|
||||
|
||||
static const struct rt5645_platform_data kahlee_platform_data = {
|
||||
.dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
|
||||
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
|
||||
.jd_mode = 3,
|
||||
};
|
||||
|
||||
static const struct dmi_system_id dmi_platform_data[] = {
|
||||
{
|
||||
.ident = "Chrome Buddy",
|
||||
@ -3742,6 +3748,13 @@ static const struct dmi_system_id dmi_platform_data[] = {
|
||||
},
|
||||
.driver_data = (void *)&lattepanda_board_platform_data,
|
||||
},
|
||||
{
|
||||
.ident = "Chrome Kahlee",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Kahlee"),
|
||||
},
|
||||
.driver_data = (void *)&kahlee_platform_data,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -3644,7 +3644,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
|
||||
regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_1,
|
||||
RT5663_LDO1_DVO_MASK | RT5663_AMP_HP_MASK,
|
||||
RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X);
|
||||
break;
|
||||
break;
|
||||
case CODEC_VER_0:
|
||||
regmap_update_bits(rt5663->regmap, RT5663_DIG_MISC,
|
||||
RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN);
|
||||
@ -3663,7 +3663,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c,
|
||||
regmap_update_bits(rt5663->regmap, RT5663_TDM_2,
|
||||
RT5663_DATA_SWAP_ADCDAT1_MASK,
|
||||
RT5663_DATA_SWAP_ADCDAT1_LL);
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__);
|
||||
}
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include "rt5677-spi.h"
|
||||
|
||||
#define DRV_NAME "rt5677spi"
|
||||
@ -45,9 +47,356 @@
|
||||
#define RT5677_SPI_WRITE_16 0x1
|
||||
#define RT5677_SPI_READ_16 0x0
|
||||
|
||||
#define RT5677_BUF_BYTES_TOTAL 0x20000
|
||||
#define RT5677_MIC_BUF_ADDR 0x60030000
|
||||
#define RT5677_MODEL_ADDR 0x5FFC9800
|
||||
#define RT5677_MIC_BUF_BYTES ((u32)(RT5677_BUF_BYTES_TOTAL - \
|
||||
sizeof(u32)))
|
||||
#define RT5677_MIC_BUF_FIRST_READ_SIZE 0x10000
|
||||
|
||||
static struct spi_device *g_spi;
|
||||
static DEFINE_MUTEX(spi_mutex);
|
||||
|
||||
struct rt5677_dsp {
|
||||
struct device *dev;
|
||||
struct delayed_work copy_work;
|
||||
struct mutex dma_lock;
|
||||
struct snd_pcm_substream *substream;
|
||||
size_t dma_offset; /* zero-based offset into runtime->dma_area */
|
||||
size_t avail_bytes; /* number of new bytes since last period */
|
||||
u32 mic_read_offset; /* zero-based offset into DSP's mic buffer */
|
||||
bool new_hotword; /* a new hotword is fired */
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware rt5677_spi_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.period_bytes_min = PAGE_SIZE,
|
||||
.period_bytes_max = RT5677_BUF_BYTES_TOTAL / 8,
|
||||
.periods_min = 8,
|
||||
.periods_max = 8,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.buffer_bytes_max = RT5677_BUF_BYTES_TOTAL,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver rt5677_spi_dai = {
|
||||
/* The DAI name "rt5677-dsp-cpu-dai" is not used. The actual DAI name
|
||||
* registered with ASoC is the name of the device "spi-RT5677AA:00",
|
||||
* because we only have one DAI. See snd_soc_register_dais().
|
||||
*/
|
||||
.name = "rt5677-dsp-cpu-dai",
|
||||
.id = 0,
|
||||
.capture = {
|
||||
.stream_name = "DSP Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = SNDRV_PCM_RATE_16000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
};
|
||||
|
||||
/* PCM for streaming audio from the DSP buffer */
|
||||
static int rt5677_spi_pcm_open(
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_soc_set_runtime_hwparams(substream, &rt5677_spi_pcm_hardware);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_spi_pcm_close(
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct rt5677_dsp *rt5677_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
cancel_delayed_work_sync(&rt5677_dsp->copy_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt5677_spi_hw_params(
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct rt5677_dsp *rt5677_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&rt5677_dsp->dma_lock);
|
||||
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
rt5677_dsp->substream = substream;
|
||||
mutex_unlock(&rt5677_dsp->dma_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt5677_spi_hw_free(
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct rt5677_dsp *rt5677_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
mutex_lock(&rt5677_dsp->dma_lock);
|
||||
rt5677_dsp->substream = NULL;
|
||||
mutex_unlock(&rt5677_dsp->dma_lock);
|
||||
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static int rt5677_spi_prepare(
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct rt5677_dsp *rt5677_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
rt5677_dsp->dma_offset = 0;
|
||||
rt5677_dsp->avail_bytes = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t rt5677_spi_pcm_pointer(
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct rt5677_dsp *rt5677_dsp =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
return bytes_to_frames(runtime, rt5677_dsp->dma_offset);
|
||||
}
|
||||
|
||||
static int rt5677_spi_mic_write_offset(u32 *mic_write_offset)
|
||||
{
|
||||
int ret;
|
||||
/* Grab the first 4 bytes that hold the write pointer on the
|
||||
* dsp, and check to make sure that it points somewhere inside the
|
||||
* buffer.
|
||||
*/
|
||||
ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR, mic_write_offset,
|
||||
sizeof(u32));
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Adjust the offset so that it's zero-based */
|
||||
*mic_write_offset = *mic_write_offset - sizeof(u32);
|
||||
return *mic_write_offset < RT5677_MIC_BUF_BYTES ? 0 : -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy one contiguous block of audio samples from the DSP mic buffer to the
|
||||
* dma_area of the pcm runtime. The receiving buffer may wrap around.
|
||||
* @begin: start offset of the block to copy, in bytes.
|
||||
* @end: offset of the first byte after the block to copy, must be greater
|
||||
* than or equal to begin.
|
||||
*
|
||||
* Return: Zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
static int rt5677_spi_copy_block(struct rt5677_dsp *rt5677_dsp,
|
||||
u32 begin, u32 end)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = rt5677_dsp->substream->runtime;
|
||||
size_t bytes_per_frame = frames_to_bytes(runtime, 1);
|
||||
size_t first_chunk_len, second_chunk_len;
|
||||
int ret;
|
||||
|
||||
if (begin > end || runtime->dma_bytes < 2 * bytes_per_frame) {
|
||||
dev_err(rt5677_dsp->dev,
|
||||
"Invalid copy from (%u, %u), dma_area size %zu\n",
|
||||
begin, end, runtime->dma_bytes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The block to copy is empty */
|
||||
if (begin == end)
|
||||
return 0;
|
||||
|
||||
/* If the incoming chunk is too big for the receiving buffer, only the
|
||||
* last "receiving buffer size - one frame" bytes are copied.
|
||||
*/
|
||||
if (end - begin > runtime->dma_bytes - bytes_per_frame)
|
||||
begin = end - (runtime->dma_bytes - bytes_per_frame);
|
||||
|
||||
/* May need to split to two chunks, calculate the size of each */
|
||||
first_chunk_len = end - begin;
|
||||
second_chunk_len = 0;
|
||||
if (rt5677_dsp->dma_offset + first_chunk_len > runtime->dma_bytes) {
|
||||
/* Receiving buffer wrapped around */
|
||||
second_chunk_len = first_chunk_len;
|
||||
first_chunk_len = runtime->dma_bytes - rt5677_dsp->dma_offset;
|
||||
second_chunk_len -= first_chunk_len;
|
||||
}
|
||||
|
||||
/* Copy first chunk */
|
||||
ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + begin,
|
||||
runtime->dma_area + rt5677_dsp->dma_offset,
|
||||
first_chunk_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
rt5677_dsp->dma_offset += first_chunk_len;
|
||||
if (rt5677_dsp->dma_offset == runtime->dma_bytes)
|
||||
rt5677_dsp->dma_offset = 0;
|
||||
|
||||
/* Copy second chunk */
|
||||
if (second_chunk_len) {
|
||||
ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) +
|
||||
begin + first_chunk_len, runtime->dma_area,
|
||||
second_chunk_len);
|
||||
if (!ret)
|
||||
rt5677_dsp->dma_offset = second_chunk_len;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a given amount of audio samples from the DSP mic buffer starting at
|
||||
* mic_read_offset, to the dma_area of the pcm runtime. The source buffer may
|
||||
* wrap around. mic_read_offset is updated after successful copy.
|
||||
* @amount: amount of samples to copy, in bytes.
|
||||
*
|
||||
* Return: Zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 target;
|
||||
|
||||
if (amount == 0)
|
||||
return ret;
|
||||
|
||||
target = rt5677_dsp->mic_read_offset + amount;
|
||||
/* Copy the first chunk in DSP's mic buffer */
|
||||
ret |= rt5677_spi_copy_block(rt5677_dsp, rt5677_dsp->mic_read_offset,
|
||||
min(target, RT5677_MIC_BUF_BYTES));
|
||||
|
||||
if (target >= RT5677_MIC_BUF_BYTES) {
|
||||
/* Wrap around, copy the second chunk */
|
||||
target -= RT5677_MIC_BUF_BYTES;
|
||||
ret |= rt5677_spi_copy_block(rt5677_dsp, 0, target);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
rt5677_dsp->mic_read_offset = target;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* A delayed work that streams audio samples from the DSP mic buffer to the
|
||||
* dma_area of the pcm runtime via SPI.
|
||||
*/
|
||||
static void rt5677_spi_copy_work(struct work_struct *work)
|
||||
{
|
||||
struct rt5677_dsp *rt5677_dsp =
|
||||
container_of(work, struct rt5677_dsp, copy_work.work);
|
||||
struct snd_pcm_runtime *runtime;
|
||||
u32 mic_write_offset;
|
||||
size_t new_bytes, copy_bytes, period_bytes;
|
||||
unsigned int delay;
|
||||
int ret = 0;
|
||||
|
||||
/* Ensure runtime->dma_area buffer does not go away while copying. */
|
||||
mutex_lock(&rt5677_dsp->dma_lock);
|
||||
if (!rt5677_dsp->substream) {
|
||||
dev_err(rt5677_dsp->dev, "No pcm substream\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
runtime = rt5677_dsp->substream->runtime;
|
||||
|
||||
if (rt5677_spi_mic_write_offset(&mic_write_offset)) {
|
||||
dev_err(rt5677_dsp->dev, "No mic_write_offset\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If this is the first time that we've asked for streaming data after
|
||||
* a hotword is fired, we should start reading from the previous 2
|
||||
* seconds of audio from wherever the mic_write_offset is currently.
|
||||
*/
|
||||
if (rt5677_dsp->new_hotword) {
|
||||
rt5677_dsp->new_hotword = false;
|
||||
/* See if buffer wraparound happens */
|
||||
if (mic_write_offset < RT5677_MIC_BUF_FIRST_READ_SIZE)
|
||||
rt5677_dsp->mic_read_offset = RT5677_MIC_BUF_BYTES -
|
||||
(RT5677_MIC_BUF_FIRST_READ_SIZE -
|
||||
mic_write_offset);
|
||||
else
|
||||
rt5677_dsp->mic_read_offset = mic_write_offset -
|
||||
RT5677_MIC_BUF_FIRST_READ_SIZE;
|
||||
}
|
||||
|
||||
/* Calculate the amount of new samples in bytes */
|
||||
if (rt5677_dsp->mic_read_offset <= mic_write_offset)
|
||||
new_bytes = mic_write_offset - rt5677_dsp->mic_read_offset;
|
||||
else
|
||||
new_bytes = RT5677_MIC_BUF_BYTES + mic_write_offset
|
||||
- rt5677_dsp->mic_read_offset;
|
||||
|
||||
/* Copy all new samples from DSP mic buffer, one period at a time */
|
||||
period_bytes = snd_pcm_lib_period_bytes(rt5677_dsp->substream);
|
||||
while (new_bytes) {
|
||||
copy_bytes = min(new_bytes, period_bytes
|
||||
- rt5677_dsp->avail_bytes);
|
||||
ret = rt5677_spi_copy(rt5677_dsp, copy_bytes);
|
||||
if (ret) {
|
||||
dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret);
|
||||
goto done;
|
||||
}
|
||||
rt5677_dsp->avail_bytes += copy_bytes;
|
||||
if (rt5677_dsp->avail_bytes >= period_bytes) {
|
||||
snd_pcm_period_elapsed(rt5677_dsp->substream);
|
||||
rt5677_dsp->avail_bytes = 0;
|
||||
}
|
||||
new_bytes -= copy_bytes;
|
||||
}
|
||||
|
||||
delay = bytes_to_frames(runtime, period_bytes) / (runtime->rate / 1000);
|
||||
schedule_delayed_work(&rt5677_dsp->copy_work, msecs_to_jiffies(delay));
|
||||
done:
|
||||
mutex_unlock(&rt5677_dsp->dma_lock);
|
||||
}
|
||||
|
||||
static struct page *rt5677_spi_pcm_page(
|
||||
struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
unsigned long offset)
|
||||
{
|
||||
return snd_pcm_lib_get_vmalloc_page(substream, offset);
|
||||
}
|
||||
|
||||
static int rt5677_spi_pcm_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct rt5677_dsp *rt5677_dsp;
|
||||
|
||||
rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp),
|
||||
GFP_KERNEL);
|
||||
if (!rt5677_dsp)
|
||||
return -ENOMEM;
|
||||
rt5677_dsp->dev = &g_spi->dev;
|
||||
mutex_init(&rt5677_dsp->dma_lock);
|
||||
INIT_DELAYED_WORK(&rt5677_dsp->copy_work, rt5677_spi_copy_work);
|
||||
|
||||
snd_soc_component_set_drvdata(component, rt5677_dsp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver rt5677_spi_dai_component = {
|
||||
.name = DRV_NAME,
|
||||
.probe = rt5677_spi_pcm_probe,
|
||||
.open = rt5677_spi_pcm_open,
|
||||
.close = rt5677_spi_pcm_close,
|
||||
.hw_params = rt5677_spi_hw_params,
|
||||
.hw_free = rt5677_spi_hw_free,
|
||||
.prepare = rt5677_spi_prepare,
|
||||
.pointer = rt5677_spi_pcm_pointer,
|
||||
.page = rt5677_spi_pcm_page,
|
||||
};
|
||||
|
||||
/* Select a suitable transfer command for the next transfer to ensure
|
||||
* the transfer address is always naturally aligned while minimizing
|
||||
* the total number of transfers required.
|
||||
@ -218,9 +567,45 @@ int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware);
|
||||
|
||||
void rt5677_spi_hotword_detected(void)
|
||||
{
|
||||
struct rt5677_dsp *rt5677_dsp;
|
||||
|
||||
if (!g_spi)
|
||||
return;
|
||||
|
||||
rt5677_dsp = dev_get_drvdata(&g_spi->dev);
|
||||
if (!rt5677_dsp) {
|
||||
dev_err(&g_spi->dev, "Can't get rt5677_dsp\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&rt5677_dsp->dma_lock);
|
||||
dev_info(rt5677_dsp->dev, "Hotword detected\n");
|
||||
rt5677_dsp->new_hotword = true;
|
||||
mutex_unlock(&rt5677_dsp->dma_lock);
|
||||
|
||||
schedule_delayed_work(&rt5677_dsp->copy_work, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt5677_spi_hotword_detected);
|
||||
|
||||
static int rt5677_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_spi = spi;
|
||||
|
||||
ret = snd_soc_register_component(&spi->dev, &rt5677_spi_dai_component,
|
||||
&rt5677_spi_dai, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&spi->dev, "Failed to register component.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt5677_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
snd_soc_unregister_component(&spi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -236,6 +621,7 @@ static struct spi_driver rt5677_spi_driver = {
|
||||
.acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
|
||||
},
|
||||
.probe = rt5677_spi_probe,
|
||||
.remove = rt5677_spi_remove,
|
||||
};
|
||||
module_spi_driver(rt5677_spi_driver);
|
||||
|
||||
|
@ -12,5 +12,6 @@
|
||||
int rt5677_spi_read(u32 addr, void *rxbuf, size_t len);
|
||||
int rt5677_spi_write(u32 addr, const void *txbuf, size_t len);
|
||||
int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw);
|
||||
void rt5677_spi_hotword_detected(void);
|
||||
|
||||
#endif /* __RT5677_SPI_H__ */
|
||||
|
@ -44,6 +44,7 @@ static const struct rt5682_platform_data i2s_default_platform_data = {
|
||||
.dmic1_data_pin = RT5682_DMIC1_DATA_GPIO2,
|
||||
.dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3,
|
||||
.jd_src = RT5682_JD1,
|
||||
.btndet_delay = 16,
|
||||
};
|
||||
|
||||
struct rt5682_priv {
|
||||
@ -1026,6 +1027,18 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
|
||||
regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
|
||||
RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK,
|
||||
RT5682_JD1_EN | RT5682_JD1_POL_NOR);
|
||||
regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4,
|
||||
0x7f7f, (rt5682->pdata.btndet_delay << 8 |
|
||||
rt5682->pdata.btndet_delay));
|
||||
regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5,
|
||||
0x7f7f, (rt5682->pdata.btndet_delay << 8 |
|
||||
rt5682->pdata.btndet_delay));
|
||||
regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6,
|
||||
0x7f7f, (rt5682->pdata.btndet_delay << 8 |
|
||||
rt5682->pdata.btndet_delay));
|
||||
regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7,
|
||||
0x7f7f, (rt5682->pdata.btndet_delay << 8 |
|
||||
rt5682->pdata.btndet_delay));
|
||||
mod_delayed_work(system_power_efficient_wq,
|
||||
&rt5682->jack_detect_work, msecs_to_jiffies(250));
|
||||
break;
|
||||
@ -2467,6 +2480,8 @@ static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev)
|
||||
&rt5682->pdata.dmic1_clk_pin);
|
||||
device_property_read_u32(dev, "realtek,jd-src",
|
||||
&rt5682->pdata.jd_src);
|
||||
device_property_read_u32(dev, "realtek,btndet-delay",
|
||||
&rt5682->pdata.btndet_delay);
|
||||
|
||||
rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
|
||||
"realtek,ldo1-en-gpios", 0);
|
||||
|
590
sound/soc/codecs/tas2562.c
Normal file
590
sound/soc/codecs/tas2562.c
Normal file
@ -0,0 +1,590 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Driver for the Texas Instruments TAS2562 CODEC
|
||||
// Copyright (C) 2019 Texas Instruments Inc.
|
||||
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "tas2562.h"
|
||||
|
||||
#define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FORMAT_S32_LE)
|
||||
|
||||
struct tas2562_data {
|
||||
struct snd_soc_component *component;
|
||||
struct gpio_desc *sdz_gpio;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
int v_sense_slot;
|
||||
int i_sense_slot;
|
||||
};
|
||||
|
||||
static int tas2562_set_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct tas2562_data *tas2562 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
snd_soc_component_update_bits(component,
|
||||
TAS2562_PWR_CTRL,
|
||||
TAS2562_MODE_MASK, TAS2562_ACTIVE);
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
snd_soc_component_update_bits(component,
|
||||
TAS2562_PWR_CTRL,
|
||||
TAS2562_MODE_MASK, TAS2562_MUTE);
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
snd_soc_component_update_bits(component,
|
||||
TAS2562_PWR_CTRL,
|
||||
TAS2562_MODE_MASK, TAS2562_SHUTDOWN);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(tas2562->dev,
|
||||
"wrong power level setting %d\n", level);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2562_set_samplerate(struct tas2562_data *tas2562, int samplerate)
|
||||
{
|
||||
int samp_rate;
|
||||
int ramp_rate;
|
||||
|
||||
switch (samplerate) {
|
||||
case 7350:
|
||||
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ;
|
||||
break;
|
||||
case 8000:
|
||||
ramp_rate = 0;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ;
|
||||
break;
|
||||
case 14700:
|
||||
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ;
|
||||
break;
|
||||
case 16000:
|
||||
ramp_rate = 0;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ;
|
||||
break;
|
||||
case 22050:
|
||||
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ;
|
||||
break;
|
||||
case 24000:
|
||||
ramp_rate = 0;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ;
|
||||
break;
|
||||
case 29400:
|
||||
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ;
|
||||
break;
|
||||
case 32000:
|
||||
ramp_rate = 0;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ;
|
||||
break;
|
||||
case 44100:
|
||||
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ;
|
||||
break;
|
||||
case 48000:
|
||||
ramp_rate = 0;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ;
|
||||
break;
|
||||
case 88200:
|
||||
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ;
|
||||
break;
|
||||
case 96000:
|
||||
ramp_rate = 0;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ;
|
||||
break;
|
||||
case 176400:
|
||||
ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ;
|
||||
break;
|
||||
case 192000:
|
||||
ramp_rate = 0;
|
||||
samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ;
|
||||
break;
|
||||
default:
|
||||
dev_info(tas2562->dev, "%s, unsupported sample rate, %d\n",
|
||||
__func__, samplerate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0,
|
||||
TAS2562_TDM_CFG0_RAMPRATE_MASK, ramp_rate);
|
||||
snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0,
|
||||
TAS2562_TDM_CFG0_SAMPRATE_MASK, samp_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2562_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int tx_mask, unsigned int rx_mask,
|
||||
int slots, int slot_width)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
|
||||
int ret = 0;
|
||||
|
||||
switch (slot_width) {
|
||||
case 16:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2562_TDM_CFG2,
|
||||
TAS2562_TDM_CFG2_RXLEN_MASK,
|
||||
TAS2562_TDM_CFG2_RXLEN_16B);
|
||||
break;
|
||||
case 24:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2562_TDM_CFG2,
|
||||
TAS2562_TDM_CFG2_RXLEN_MASK,
|
||||
TAS2562_TDM_CFG2_RXLEN_24B);
|
||||
break;
|
||||
case 32:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2562_TDM_CFG2,
|
||||
TAS2562_TDM_CFG2_RXLEN_MASK,
|
||||
TAS2562_TDM_CFG2_RXLEN_32B);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
/* Do not change slot width */
|
||||
break;
|
||||
default:
|
||||
dev_err(tas2562->dev, "slot width not supported");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2562_set_bitwidth(struct tas2562_data *tas2562, int bitwidth)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (bitwidth) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
snd_soc_component_update_bits(tas2562->component,
|
||||
TAS2562_TDM_CFG2,
|
||||
TAS2562_TDM_CFG2_RXWLEN_MASK,
|
||||
TAS2562_TDM_CFG2_RXWLEN_16B);
|
||||
tas2562->v_sense_slot = tas2562->i_sense_slot + 2;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
snd_soc_component_update_bits(tas2562->component,
|
||||
TAS2562_TDM_CFG2,
|
||||
TAS2562_TDM_CFG2_RXWLEN_MASK,
|
||||
TAS2562_TDM_CFG2_RXWLEN_24B);
|
||||
tas2562->v_sense_slot = tas2562->i_sense_slot + 4;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
snd_soc_component_update_bits(tas2562->component,
|
||||
TAS2562_TDM_CFG2,
|
||||
TAS2562_TDM_CFG2_RXWLEN_MASK,
|
||||
TAS2562_TDM_CFG2_RXWLEN_32B);
|
||||
tas2562->v_sense_slot = tas2562->i_sense_slot + 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_info(tas2562->dev, "Not supported params format\n");
|
||||
}
|
||||
|
||||
ret = snd_soc_component_update_bits(tas2562->component,
|
||||
TAS2562_TDM_CFG5,
|
||||
TAS2562_TDM_CFG5_VSNS_EN | TAS2562_TDM_CFG5_VSNS_SLOT_MASK,
|
||||
TAS2562_TDM_CFG5_VSNS_EN | tas2562->v_sense_slot);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_component_update_bits(tas2562->component,
|
||||
TAS2562_TDM_CFG6,
|
||||
TAS2562_TDM_CFG6_ISNS_EN | TAS2562_TDM_CFG6_ISNS_SLOT_MASK,
|
||||
TAS2562_TDM_CFG6_ISNS_EN | tas2562->i_sense_slot);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2562_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
ret = tas2562_set_bitwidth(tas2562, params_format(params));
|
||||
if (ret) {
|
||||
dev_err(tas2562->dev, "set bitwidth failed, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = tas2562_set_samplerate(tas2562, params_rate(params));
|
||||
if (ret)
|
||||
dev_err(tas2562->dev, "set bitwidth failed, %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tas2562_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
|
||||
u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
|
||||
int ret;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
asi_cfg_1 = 0;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
asi_cfg_1 |= TAS2562_TDM_CFG1_RX_FALLING;
|
||||
break;
|
||||
default:
|
||||
dev_err(tas2562->dev, "ASI format Inverse is not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1,
|
||||
TAS2562_TDM_CFG1_RX_EDGE_MASK,
|
||||
asi_cfg_1);
|
||||
if (ret < 0) {
|
||||
dev_err(tas2562->dev, "Failed to set RX edge\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case (SND_SOC_DAIFMT_I2S):
|
||||
case (SND_SOC_DAIFMT_DSP_A):
|
||||
case (SND_SOC_DAIFMT_DSP_B):
|
||||
tdm_rx_start_slot = BIT(1);
|
||||
break;
|
||||
case (SND_SOC_DAIFMT_LEFT_J):
|
||||
tdm_rx_start_slot = 0;
|
||||
break;
|
||||
default:
|
||||
dev_err(tas2562->dev, "DAI Format is not found, fmt=0x%x\n",
|
||||
fmt);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1,
|
||||
TAS2562_TDM_CFG1_RX_OFFSET_MASK,
|
||||
tdm_rx_start_slot);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2562_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
|
||||
return snd_soc_component_update_bits(component, TAS2562_PWR_CTRL,
|
||||
TAS2562_MODE_MASK,
|
||||
mute ? TAS2562_MUTE : 0);
|
||||
}
|
||||
|
||||
static int tas2562_codec_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
tas2562->component = component;
|
||||
|
||||
if (tas2562->sdz_gpio)
|
||||
gpiod_set_value_cansleep(tas2562->sdz_gpio, 1);
|
||||
|
||||
ret = snd_soc_component_update_bits(component, TAS2562_PWR_CTRL,
|
||||
TAS2562_MODE_MASK, TAS2562_MUTE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tas2562_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
regcache_cache_only(tas2562->regmap, true);
|
||||
regcache_mark_dirty(tas2562->regmap);
|
||||
|
||||
if (tas2562->sdz_gpio)
|
||||
gpiod_set_value_cansleep(tas2562->sdz_gpio, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2562_resume(struct snd_soc_component *component)
|
||||
{
|
||||
struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (tas2562->sdz_gpio)
|
||||
gpiod_set_value_cansleep(tas2562->sdz_gpio, 1);
|
||||
|
||||
regcache_cache_only(tas2562->regmap, false);
|
||||
|
||||
return regcache_sync(tas2562->regmap);
|
||||
}
|
||||
#else
|
||||
#define tas2562_suspend NULL
|
||||
#define tas2562_resume NULL
|
||||
#endif
|
||||
|
||||
static const char * const tas2562_ASI1_src[] = {
|
||||
"I2C offset", "Left", "Right", "LeftRightDiv2",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(tas2562_ASI1_src_enum, TAS2562_TDM_CFG2, 4,
|
||||
tas2562_ASI1_src);
|
||||
|
||||
static const struct snd_kcontrol_new tas2562_asi1_mux =
|
||||
SOC_DAPM_ENUM("ASI1 Source", tas2562_ASI1_src_enum);
|
||||
|
||||
static int tas2562_dac_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_to_component(w->dapm);
|
||||
struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
dev_info(tas2562->dev, "SND_SOC_DAPM_POST_PMU\n");
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
dev_info(tas2562->dev, "SND_SOC_DAPM_PRE_PMD\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DECLARE_TLV_DB_SCALE(tas2562_dac_tlv, 850, 50, 0);
|
||||
|
||||
static const struct snd_kcontrol_new isense_switch =
|
||||
SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_ISENSE_POWER_EN,
|
||||
1, 1);
|
||||
|
||||
static const struct snd_kcontrol_new vsense_switch =
|
||||
SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_VSENSE_POWER_EN,
|
||||
1, 1);
|
||||
|
||||
static const struct snd_kcontrol_new tas2562_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 0, 0x1c, 0,
|
||||
tas2562_dac_tlv),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux),
|
||||
SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_SWITCH("ISENSE", TAS2562_PWR_CTRL, 3, 1, &isense_switch),
|
||||
SND_SOC_DAPM_SWITCH("VSENSE", TAS2562_PWR_CTRL, 2, 1, &vsense_switch),
|
||||
SND_SOC_DAPM_SIGGEN("VMON"),
|
||||
SND_SOC_DAPM_SIGGEN("IMON"),
|
||||
SND_SOC_DAPM_OUTPUT("OUT"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route tas2562_audio_map[] = {
|
||||
{"ASI1 Sel", "I2C offset", "ASI1"},
|
||||
{"ASI1 Sel", "Left", "ASI1"},
|
||||
{"ASI1 Sel", "Right", "ASI1"},
|
||||
{"ASI1 Sel", "LeftRightDiv2", "ASI1"},
|
||||
{ "DAC", NULL, "DAC IN" },
|
||||
{ "OUT", NULL, "DAC" },
|
||||
{"ISENSE", "Switch", "IMON"},
|
||||
{"VSENSE", "Switch", "VMON"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver soc_component_dev_tas2562 = {
|
||||
.probe = tas2562_codec_probe,
|
||||
.suspend = tas2562_suspend,
|
||||
.resume = tas2562_resume,
|
||||
.set_bias_level = tas2562_set_bias_level,
|
||||
.controls = tas2562_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(tas2562_snd_controls),
|
||||
.dapm_widgets = tas2562_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets),
|
||||
.dapm_routes = tas2562_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(tas2562_audio_map),
|
||||
.idle_bias_on = 1,
|
||||
.use_pmdown_time = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops tas2562_speaker_dai_ops = {
|
||||
.hw_params = tas2562_hw_params,
|
||||
.set_fmt = tas2562_set_dai_fmt,
|
||||
.set_tdm_slot = tas2562_set_dai_tdm_slot,
|
||||
.digital_mute = tas2562_mute,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver tas2562_dai[] = {
|
||||
{
|
||||
.name = "tas2562-amplifier",
|
||||
.id = 0,
|
||||
.playback = {
|
||||
.stream_name = "ASI1 Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.formats = TAS2562_FORMATS,
|
||||
},
|
||||
.ops = &tas2562_speaker_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_range_cfg tas2562_ranges[] = {
|
||||
{
|
||||
.range_min = 0,
|
||||
.range_max = 5 * 128,
|
||||
.selector_reg = TAS2562_PAGE_CTRL,
|
||||
.selector_mask = 0xff,
|
||||
.selector_shift = 0,
|
||||
.window_start = 0,
|
||||
.window_len = 128,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct reg_default tas2562_reg_defaults[] = {
|
||||
{ TAS2562_PAGE_CTRL, 0x00 },
|
||||
{ TAS2562_SW_RESET, 0x00 },
|
||||
{ TAS2562_PWR_CTRL, 0x0e },
|
||||
{ TAS2562_PB_CFG1, 0x20 },
|
||||
{ TAS2562_TDM_CFG0, 0x09 },
|
||||
{ TAS2562_TDM_CFG1, 0x02 },
|
||||
};
|
||||
|
||||
static const struct regmap_config tas2562_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = 5 * 128,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.reg_defaults = tas2562_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(tas2562_reg_defaults),
|
||||
.ranges = tas2562_ranges,
|
||||
.num_ranges = ARRAY_SIZE(tas2562_ranges),
|
||||
};
|
||||
|
||||
static int tas2562_parse_dt(struct tas2562_data *tas2562)
|
||||
{
|
||||
struct device *dev = tas2562->dev;
|
||||
int ret = 0;
|
||||
|
||||
tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down-gpio",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tas2562->sdz_gpio)) {
|
||||
if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER) {
|
||||
tas2562->sdz_gpio = NULL;
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
}
|
||||
|
||||
ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
|
||||
&tas2562->i_sense_slot);
|
||||
if (ret)
|
||||
dev_err(dev, "Looking up %s property failed %d\n",
|
||||
"ti,imon-slot-no", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tas2562_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct tas2562_data *data;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->client = client;
|
||||
data->dev = &client->dev;
|
||||
|
||||
tas2562_parse_dt(data);
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &tas2562_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
ret = PTR_ERR(data->regmap);
|
||||
dev_err(dev, "failed to allocate register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&client->dev, data);
|
||||
|
||||
return devm_snd_soc_register_component(dev, &soc_component_dev_tas2562,
|
||||
tas2562_dai,
|
||||
ARRAY_SIZE(tas2562_dai));
|
||||
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tas2562_id[] = {
|
||||
{ "tas2562", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tas2562_id);
|
||||
|
||||
static const struct of_device_id tas2562_of_match[] = {
|
||||
{ .compatible = "ti,tas2562", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tas2562_of_match);
|
||||
|
||||
static struct i2c_driver tas2562_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tas2562",
|
||||
.of_match_table = of_match_ptr(tas2562_of_match),
|
||||
},
|
||||
.probe = tas2562_probe,
|
||||
.id_table = tas2562_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(tas2562_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
||||
MODULE_DESCRIPTION("TAS2562 Audio amplifier driver");
|
||||
MODULE_LICENSE("GPL");
|
85
sound/soc/codecs/tas2562.h
Normal file
85
sound/soc/codecs/tas2562.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* tas2562.h - ALSA SoC Texas Instruments TAS2562 Mono Audio Amplifier
|
||||
*
|
||||
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Dan Murphy <dmurphy@ti.com>
|
||||
*/
|
||||
|
||||
#ifndef __TAS2562_H__
|
||||
#define __TAS2562_H__
|
||||
|
||||
#define TAS2562_PAGE_CTRL 0x00
|
||||
|
||||
#define TAS2562_REG(page, reg) ((page * 128) + reg)
|
||||
|
||||
#define TAS2562_SW_RESET TAS2562_REG(0, 0x01)
|
||||
#define TAS2562_PWR_CTRL TAS2562_REG(0, 0x02)
|
||||
#define TAS2562_PB_CFG1 TAS2562_REG(0, 0x03)
|
||||
#define TAS2562_MISC_CFG1 TAS2562_REG(0, 0x04)
|
||||
#define TAS2562_MISC_CFG2 TAS2562_REG(0, 0x05)
|
||||
|
||||
#define TAS2562_TDM_CFG0 TAS2562_REG(0, 0x06)
|
||||
#define TAS2562_TDM_CFG1 TAS2562_REG(0, 0x07)
|
||||
#define TAS2562_TDM_CFG2 TAS2562_REG(0, 0x08)
|
||||
#define TAS2562_TDM_CFG3 TAS2562_REG(0, 0x09)
|
||||
#define TAS2562_TDM_CFG4 TAS2562_REG(0, 0x0a)
|
||||
#define TAS2562_TDM_CFG5 TAS2562_REG(0, 0x0b)
|
||||
#define TAS2562_TDM_CFG6 TAS2562_REG(0, 0x0c)
|
||||
#define TAS2562_TDM_CFG7 TAS2562_REG(0, 0x0d)
|
||||
#define TAS2562_TDM_CFG8 TAS2562_REG(0, 0x0e)
|
||||
#define TAS2562_TDM_CFG9 TAS2562_REG(0, 0x0f)
|
||||
#define TAS2562_TDM_CFG10 TAS2562_REG(0, 0x10)
|
||||
#define TAS2562_TDM_DET TAS2562_REG(0, 0x11)
|
||||
#define TAS2562_REV_ID TAS2562_REG(0, 0x7d)
|
||||
|
||||
/* Page 2 */
|
||||
#define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x01)
|
||||
#define TAS2562_DVC_CFG2 TAS2562_REG(2, 0x02)
|
||||
|
||||
#define TAS2562_RESET BIT(0)
|
||||
|
||||
#define TAS2562_MODE_MASK 0x3
|
||||
#define TAS2562_ACTIVE 0x0
|
||||
#define TAS2562_MUTE 0x1
|
||||
#define TAS2562_SHUTDOWN 0x2
|
||||
|
||||
#define TAS2562_TDM_CFG1_RX_EDGE_MASK BIT(0)
|
||||
#define TAS2562_TDM_CFG1_RX_FALLING 1
|
||||
#define TAS2562_TDM_CFG1_RX_OFFSET_MASK GENMASK(4, 0)
|
||||
|
||||
#define TAS2562_TDM_CFG0_RAMPRATE_MASK BIT(5)
|
||||
#define TAS2562_TDM_CFG0_RAMPRATE_44_1 BIT(5)
|
||||
#define TAS2562_TDM_CFG0_SAMPRATE_MASK GENMASK(3, 1)
|
||||
#define TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ 0x0
|
||||
#define TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ 0x1
|
||||
#define TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ 0x2
|
||||
#define TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ 0x3
|
||||
#define TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ 0x4
|
||||
#define TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ 0x5
|
||||
#define TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ 0x6
|
||||
|
||||
#define TAS2562_TDM_CFG2_RIGHT_JUSTIFY BIT(6)
|
||||
|
||||
#define TAS2562_TDM_CFG2_RXLEN_MASK GENMASK(1, 0)
|
||||
#define TAS2562_TDM_CFG2_RXLEN_16B 0x0
|
||||
#define TAS2562_TDM_CFG2_RXLEN_24B BIT(0)
|
||||
#define TAS2562_TDM_CFG2_RXLEN_32B BIT(1)
|
||||
|
||||
#define TAS2562_TDM_CFG2_RXWLEN_MASK GENMASK(3, 2)
|
||||
#define TAS2562_TDM_CFG2_RXWLEN_16B 0x0
|
||||
#define TAS2562_TDM_CFG2_RXWLEN_20B BIT(2)
|
||||
#define TAS2562_TDM_CFG2_RXWLEN_24B BIT(3)
|
||||
#define TAS2562_TDM_CFG2_RXWLEN_32B (BIT(2) | BIT(3))
|
||||
|
||||
#define TAS2562_VSENSE_POWER_EN BIT(2)
|
||||
#define TAS2562_ISENSE_POWER_EN BIT(3)
|
||||
|
||||
#define TAS2562_TDM_CFG5_VSNS_EN BIT(6)
|
||||
#define TAS2562_TDM_CFG5_VSNS_SLOT_MASK GENMASK(5, 0)
|
||||
|
||||
#define TAS2562_TDM_CFG6_ISNS_EN BIT(6)
|
||||
#define TAS2562_TDM_CFG6_ISNS_SLOT_MASK GENMASK(5, 0)
|
||||
|
||||
#endif /* __TAS2562_H__ */
|
819
sound/soc/codecs/tas2770.c
Normal file
819
sound/soc/codecs/tas2770.c
Normal file
@ -0,0 +1,819 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// ALSA SoC Texas Instruments TAS2770 20-W Digital Input Mono Class-D
|
||||
// Audio Amplifier with Speaker I/V Sense
|
||||
//
|
||||
// Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
|
||||
// Author: Tracy Yi <tracy-yi@ti.com>
|
||||
// Frank Shi <shifu0704@thundersoft.com>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "tas2770.h"
|
||||
|
||||
#define TAS2770_MDELAY 0xFFFFFFFE
|
||||
|
||||
static void tas2770_reset(struct tas2770_priv *tas2770)
|
||||
{
|
||||
if (tas2770->reset_gpio) {
|
||||
gpiod_set_value_cansleep(tas2770->reset_gpio, 0);
|
||||
msleep(20);
|
||||
gpiod_set_value_cansleep(tas2770->reset_gpio, 1);
|
||||
}
|
||||
snd_soc_component_write(tas2770->component, TAS2770_SW_RST,
|
||||
TAS2770_RST);
|
||||
}
|
||||
|
||||
static int tas2770_set_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
struct tas2770_priv *tas2770 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
snd_soc_component_update_bits(component,
|
||||
TAS2770_PWR_CTRL,
|
||||
TAS2770_PWR_CTRL_MASK,
|
||||
TAS2770_PWR_CTRL_ACTIVE);
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
snd_soc_component_update_bits(component,
|
||||
TAS2770_PWR_CTRL,
|
||||
TAS2770_PWR_CTRL_MASK,
|
||||
TAS2770_PWR_CTRL_SHUTDOWN);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(tas2770->dev,
|
||||
"wrong power level setting %d\n", level);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tas2770_codec_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_PWR_CTRL,
|
||||
TAS2770_PWR_CTRL_MASK,
|
||||
TAS2770_PWR_CTRL_SHUTDOWN);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2770_codec_resume(struct snd_soc_component *component)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_PWR_CTRL,
|
||||
TAS2770_PWR_CTRL_MASK,
|
||||
TAS2770_PWR_CTRL_ACTIVE);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define tas2770_codec_suspend NULL
|
||||
#define tas2770_codec_resume NULL
|
||||
#endif
|
||||
|
||||
static const char * const tas2770_ASI1_src[] = {
|
||||
"I2C offset", "Left", "Right", "LeftRightDiv2",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(
|
||||
tas2770_ASI1_src_enum, TAS2770_TDM_CFG_REG2,
|
||||
4, tas2770_ASI1_src);
|
||||
|
||||
static const struct snd_kcontrol_new tas2770_asi1_mux =
|
||||
SOC_DAPM_ENUM("ASI1 Source", tas2770_ASI1_src_enum);
|
||||
|
||||
static int tas2770_dac_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_to_component(w->dapm);
|
||||
struct tas2770_priv *tas2770 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_PWR_CTRL,
|
||||
TAS2770_PWR_CTRL_MASK,
|
||||
TAS2770_PWR_CTRL_MUTE);
|
||||
if (ret)
|
||||
goto end;
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_PWR_CTRL,
|
||||
TAS2770_PWR_CTRL_MASK,
|
||||
TAS2770_PWR_CTRL_SHUTDOWN);
|
||||
if (ret)
|
||||
goto end;
|
||||
break;
|
||||
default:
|
||||
dev_err(tas2770->dev, "Not supported evevt\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
end:
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new isense_switch =
|
||||
SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 3, 1, 1);
|
||||
static const struct snd_kcontrol_new vsense_switch =
|
||||
SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1);
|
||||
|
||||
static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0,
|
||||
&tas2770_asi1_mux),
|
||||
SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1,
|
||||
&isense_switch),
|
||||
SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1,
|
||||
&vsense_switch),
|
||||
SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_OUTPUT("OUT"),
|
||||
SND_SOC_DAPM_SIGGEN("VMON"),
|
||||
SND_SOC_DAPM_SIGGEN("IMON")
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route tas2770_audio_map[] = {
|
||||
{"ASI1 Sel", "I2C offset", "ASI1"},
|
||||
{"ASI1 Sel", "Left", "ASI1"},
|
||||
{"ASI1 Sel", "Right", "ASI1"},
|
||||
{"ASI1 Sel", "LeftRightDiv2", "ASI1"},
|
||||
{"DAC", NULL, "ASI1 Sel"},
|
||||
{"OUT", NULL, "DAC"},
|
||||
{"ISENSE", "Switch", "IMON"},
|
||||
{"VSENSE", "Switch", "VMON"},
|
||||
};
|
||||
|
||||
static int tas2770_mute(struct snd_soc_dai *dai, int mute)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
int ret;
|
||||
|
||||
if (mute)
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_PWR_CTRL,
|
||||
TAS2770_PWR_CTRL_MASK,
|
||||
TAS2770_PWR_CTRL_MUTE);
|
||||
else
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_PWR_CTRL,
|
||||
TAS2770_PWR_CTRL_MASK,
|
||||
TAS2770_PWR_CTRL_ACTIVE);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_component *component = tas2770->component;
|
||||
|
||||
switch (bitwidth) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG2,
|
||||
TAS2770_TDM_CFG_REG2_RXW_MASK,
|
||||
TAS2770_TDM_CFG_REG2_RXW_16BITS);
|
||||
tas2770->v_sense_slot = tas2770->i_sense_slot + 2;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG2,
|
||||
TAS2770_TDM_CFG_REG2_RXW_MASK,
|
||||
TAS2770_TDM_CFG_REG2_RXW_24BITS);
|
||||
tas2770->v_sense_slot = tas2770->i_sense_slot + 4;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG2,
|
||||
TAS2770_TDM_CFG_REG2_RXW_MASK,
|
||||
TAS2770_TDM_CFG_REG2_RXW_32BITS);
|
||||
tas2770->v_sense_slot = tas2770->i_sense_slot + 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tas2770->channel_size = bitwidth;
|
||||
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG5,
|
||||
TAS2770_TDM_CFG_REG5_VSNS_MASK |
|
||||
TAS2770_TDM_CFG_REG5_50_MASK,
|
||||
TAS2770_TDM_CFG_REG5_VSNS_ENABLE |
|
||||
tas2770->v_sense_slot);
|
||||
if (ret)
|
||||
goto end;
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG6,
|
||||
TAS2770_TDM_CFG_REG6_ISNS_MASK |
|
||||
TAS2770_TDM_CFG_REG6_50_MASK,
|
||||
TAS2770_TDM_CFG_REG6_ISNS_ENABLE |
|
||||
tas2770->i_sense_slot);
|
||||
|
||||
end:
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_component *component = tas2770->component;
|
||||
|
||||
switch (samplerate) {
|
||||
case 48000:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_SMP_MASK,
|
||||
TAS2770_TDM_CFG_REG0_SMP_48KHZ);
|
||||
if (ret)
|
||||
goto end;
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_31_MASK,
|
||||
TAS2770_TDM_CFG_REG0_31_44_1_48KHZ);
|
||||
if (ret)
|
||||
goto end;
|
||||
break;
|
||||
case 44100:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_SMP_MASK,
|
||||
TAS2770_TDM_CFG_REG0_SMP_44_1KHZ);
|
||||
if (ret)
|
||||
goto end;
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_31_MASK,
|
||||
TAS2770_TDM_CFG_REG0_31_44_1_48KHZ);
|
||||
if (ret)
|
||||
goto end;
|
||||
break;
|
||||
case 96000:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_SMP_MASK,
|
||||
TAS2770_TDM_CFG_REG0_SMP_48KHZ);
|
||||
if (ret)
|
||||
goto end;
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_31_MASK,
|
||||
TAS2770_TDM_CFG_REG0_31_88_2_96KHZ);
|
||||
break;
|
||||
case 88200:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_SMP_MASK,
|
||||
TAS2770_TDM_CFG_REG0_SMP_44_1KHZ);
|
||||
if (ret)
|
||||
goto end;
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_31_MASK,
|
||||
TAS2770_TDM_CFG_REG0_31_88_2_96KHZ);
|
||||
break;
|
||||
case 19200:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_SMP_MASK,
|
||||
TAS2770_TDM_CFG_REG0_SMP_48KHZ);
|
||||
if (ret)
|
||||
goto end;
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_31_MASK,
|
||||
TAS2770_TDM_CFG_REG0_31_176_4_192KHZ);
|
||||
if (ret)
|
||||
goto end;
|
||||
break;
|
||||
case 17640:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_SMP_MASK,
|
||||
TAS2770_TDM_CFG_REG0_SMP_44_1KHZ);
|
||||
if (ret)
|
||||
goto end;
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG0,
|
||||
TAS2770_TDM_CFG_REG0_31_MASK,
|
||||
TAS2770_TDM_CFG_REG0_31_176_4_192KHZ);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
end:
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tas2770->sampling_rate = samplerate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2770_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct tas2770_priv *tas2770 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int ret;
|
||||
|
||||
ret = tas2770_set_bitwidth(tas2770, params_format(params));
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
|
||||
ret = tas2770_set_samplerate(tas2770, params_rate(params));
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
|
||||
int ret;
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct tas2770_priv *tas2770 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
dev_err(tas2770->dev, "ASI format master is not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_RSING;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_FALING;
|
||||
break;
|
||||
default:
|
||||
dev_err(tas2770->dev, "ASI format Inverse is not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1,
|
||||
TAS2770_TDM_CFG_REG1_RX_MASK,
|
||||
asi_cfg_1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
tdm_rx_start_slot = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
tdm_rx_start_slot = 0;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
tdm_rx_start_slot = 1;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
tdm_rx_start_slot = 0;
|
||||
break;
|
||||
default:
|
||||
dev_err(tas2770->dev,
|
||||
"DAI Format is not found, fmt=0x%x\n", fmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1,
|
||||
TAS2770_TDM_CFG_REG1_MASK,
|
||||
(tdm_rx_start_slot << TAS2770_TDM_CFG_REG1_51_SHIFT));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tas2770->asi_format = fmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int tx_mask,
|
||||
unsigned int rx_mask,
|
||||
int slots, int slot_width)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct tas2770_priv *tas2770 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
int left_slot, right_slot;
|
||||
int ret;
|
||||
|
||||
if (tx_mask == 0 || rx_mask != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (slots == 1) {
|
||||
if (tx_mask != 1)
|
||||
return -EINVAL;
|
||||
left_slot = 0;
|
||||
right_slot = 0;
|
||||
} else {
|
||||
left_slot = __ffs(tx_mask);
|
||||
tx_mask &= ~(1 << left_slot);
|
||||
if (tx_mask == 0) {
|
||||
right_slot = left_slot;
|
||||
} else {
|
||||
right_slot = __ffs(tx_mask);
|
||||
tx_mask &= ~(1 << right_slot);
|
||||
}
|
||||
}
|
||||
|
||||
if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
|
||||
return -EINVAL;
|
||||
|
||||
ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3,
|
||||
TAS2770_TDM_CFG_REG3_30_MASK,
|
||||
(left_slot << TAS2770_TDM_CFG_REG3_30_SHIFT));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3,
|
||||
TAS2770_TDM_CFG_REG3_RXS_MASK,
|
||||
(right_slot << TAS2770_TDM_CFG_REG3_RXS_SHIFT));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (slot_width) {
|
||||
case 16:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG2,
|
||||
TAS2770_TDM_CFG_REG2_RXS_MASK,
|
||||
TAS2770_TDM_CFG_REG2_RXS_16BITS);
|
||||
break;
|
||||
|
||||
case 24:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG2,
|
||||
TAS2770_TDM_CFG_REG2_RXS_MASK,
|
||||
TAS2770_TDM_CFG_REG2_RXS_24BITS);
|
||||
break;
|
||||
|
||||
case 32:
|
||||
ret = snd_soc_component_update_bits(component,
|
||||
TAS2770_TDM_CFG_REG2,
|
||||
TAS2770_TDM_CFG_REG2_RXS_MASK,
|
||||
TAS2770_TDM_CFG_REG2_RXS_32BITS);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
/* Do not change slot width */
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tas2770->slot_width = slot_width;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops tas2770_dai_ops = {
|
||||
.digital_mute = tas2770_mute,
|
||||
.hw_params = tas2770_hw_params,
|
||||
.set_fmt = tas2770_set_fmt,
|
||||
.set_tdm_slot = tas2770_set_dai_tdm_slot,
|
||||
};
|
||||
|
||||
#define TAS2770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
#define TAS2770_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
|
||||
SNDRV_PCM_RATE_96000 |\
|
||||
SNDRV_PCM_RATE_192000\
|
||||
)
|
||||
|
||||
static struct snd_soc_dai_driver tas2770_dai_driver[] = {
|
||||
{
|
||||
.name = "tas2770 ASI1",
|
||||
.id = 0,
|
||||
.playback = {
|
||||
.stream_name = "ASI1 Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = TAS2770_RATES,
|
||||
.formats = TAS2770_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ASI1 Capture",
|
||||
.channels_min = 0,
|
||||
.channels_max = 2,
|
||||
.rates = TAS2770_RATES,
|
||||
.formats = TAS2770_FORMATS,
|
||||
},
|
||||
.ops = &tas2770_dai_ops,
|
||||
.symmetric_rates = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static int tas2770_codec_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct tas2770_priv *tas2770 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
|
||||
tas2770->component = component;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0);
|
||||
static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0);
|
||||
|
||||
static const struct snd_kcontrol_new tas2770_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("Speaker Playback Volume", TAS2770_PLAY_CFG_REG2,
|
||||
0, TAS2770_PLAY_CFG_REG2_VMAX, 1,
|
||||
tas2770_playback_volume),
|
||||
SOC_SINGLE_TLV("Amp Gain Volume", TAS2770_PLAY_CFG_REG0,
|
||||
0, 0x14, 0,
|
||||
tas2770_digital_tlv),
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver soc_component_driver_tas2770 = {
|
||||
.probe = tas2770_codec_probe,
|
||||
.suspend = tas2770_codec_suspend,
|
||||
.resume = tas2770_codec_resume,
|
||||
.set_bias_level = tas2770_set_bias_level,
|
||||
.controls = tas2770_snd_controls,
|
||||
.num_controls = ARRAY_SIZE(tas2770_snd_controls),
|
||||
.dapm_widgets = tas2770_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(tas2770_dapm_widgets),
|
||||
.dapm_routes = tas2770_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(tas2770_audio_map),
|
||||
.idle_bias_on = 1,
|
||||
.endianness = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static int tas2770_register_codec(struct tas2770_priv *tas2770)
|
||||
{
|
||||
return devm_snd_soc_register_component(tas2770->dev,
|
||||
&soc_component_driver_tas2770,
|
||||
tas2770_dai_driver, ARRAY_SIZE(tas2770_dai_driver));
|
||||
}
|
||||
|
||||
static const struct reg_default tas2770_reg_defaults[] = {
|
||||
{ TAS2770_PAGE, 0x00 },
|
||||
{ TAS2770_SW_RST, 0x00 },
|
||||
{ TAS2770_PWR_CTRL, 0x0e },
|
||||
{ TAS2770_PLAY_CFG_REG0, 0x10 },
|
||||
{ TAS2770_PLAY_CFG_REG1, 0x01 },
|
||||
{ TAS2770_PLAY_CFG_REG2, 0x00 },
|
||||
{ TAS2770_MSC_CFG_REG0, 0x07 },
|
||||
{ TAS2770_TDM_CFG_REG1, 0x02 },
|
||||
{ TAS2770_TDM_CFG_REG2, 0x0a },
|
||||
{ TAS2770_TDM_CFG_REG3, 0x10 },
|
||||
{ TAS2770_INT_MASK_REG0, 0xfc },
|
||||
{ TAS2770_INT_MASK_REG1, 0xb1 },
|
||||
{ TAS2770_INT_CFG, 0x05 },
|
||||
{ TAS2770_MISC_IRQ, 0x81 },
|
||||
{ TAS2770_CLK_CGF, 0x0c },
|
||||
|
||||
};
|
||||
|
||||
static bool tas2770_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TAS2770_PAGE: /* regmap implementation requires this */
|
||||
case TAS2770_SW_RST: /* always clears after write */
|
||||
case TAS2770_BO_PRV_REG0:/* has a self clearing bit */
|
||||
case TAS2770_LVE_INT_REG0:
|
||||
case TAS2770_LVE_INT_REG1:
|
||||
case TAS2770_LAT_INT_REG0:/* Sticky interrupt flags */
|
||||
case TAS2770_LAT_INT_REG1:/* Sticky interrupt flags */
|
||||
case TAS2770_VBAT_MSB:
|
||||
case TAS2770_VBAT_LSB:
|
||||
case TAS2770_TEMP_MSB:
|
||||
case TAS2770_TEMP_LSB:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool tas2770_writeable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case TAS2770_LVE_INT_REG0:
|
||||
case TAS2770_LVE_INT_REG1:
|
||||
case TAS2770_LAT_INT_REG0:
|
||||
case TAS2770_LAT_INT_REG1:
|
||||
case TAS2770_VBAT_MSB:
|
||||
case TAS2770_VBAT_LSB:
|
||||
case TAS2770_TEMP_MSB:
|
||||
case TAS2770_TEMP_LSB:
|
||||
case TAS2770_TDM_CLK_DETC:
|
||||
case TAS2770_REV_AND_GPID:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct regmap_range_cfg tas2770_regmap_ranges[] = {
|
||||
{
|
||||
.range_min = 0,
|
||||
.range_max = 1 * 128,
|
||||
.selector_reg = TAS2770_PAGE,
|
||||
.selector_mask = 0xff,
|
||||
.selector_shift = 0,
|
||||
.window_start = 0,
|
||||
.window_len = 128,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_config tas2770_i2c_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.writeable_reg = tas2770_writeable,
|
||||
.volatile_reg = tas2770_volatile,
|
||||
.reg_defaults = tas2770_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(tas2770_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.ranges = tas2770_regmap_ranges,
|
||||
.num_ranges = ARRAY_SIZE(tas2770_regmap_ranges),
|
||||
.max_register = 1 * 128,
|
||||
};
|
||||
|
||||
static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = fwnode_property_read_u32(dev->fwnode, "ti,asi-format",
|
||||
&tas2770->asi_format);
|
||||
if (rc) {
|
||||
dev_err(tas2770->dev, "Looking up %s property failed %d\n",
|
||||
"ti,asi-format", rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
|
||||
&tas2770->i_sense_slot);
|
||||
if (rc) {
|
||||
dev_err(tas2770->dev, "Looking up %s property failed %d\n",
|
||||
"ti,imon-slot-no", rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
|
||||
&tas2770->v_sense_slot);
|
||||
if (rc) {
|
||||
dev_err(tas2770->dev, "Looking up %s property failed %d\n",
|
||||
"ti,vmon-slot-no", rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int tas2770_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tas2770_priv *tas2770;
|
||||
int result;
|
||||
|
||||
tas2770 = devm_kzalloc(&client->dev,
|
||||
sizeof(struct tas2770_priv), GFP_KERNEL);
|
||||
if (!tas2770)
|
||||
return -ENOMEM;
|
||||
tas2770->dev = &client->dev;
|
||||
|
||||
i2c_set_clientdata(client, tas2770);
|
||||
dev_set_drvdata(&client->dev, tas2770);
|
||||
tas2770->power_state = TAS2770_POWER_SHUTDOWN;
|
||||
|
||||
tas2770->regmap = devm_regmap_init_i2c(client, &tas2770_i2c_regmap);
|
||||
if (IS_ERR(tas2770->regmap)) {
|
||||
result = PTR_ERR(tas2770->regmap);
|
||||
dev_err(&client->dev, "Failed to allocate register map: %d\n",
|
||||
result);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (client->dev.of_node) {
|
||||
result = tas2770_parse_dt(&client->dev, tas2770);
|
||||
if (result) {
|
||||
dev_err(tas2770->dev, "%s: Failed to parse devicetree\n",
|
||||
__func__);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
tas2770->reset_gpio = devm_gpiod_get_optional(tas2770->dev,
|
||||
"reset-gpio",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tas2770->reset_gpio)) {
|
||||
if (PTR_ERR(tas2770->reset_gpio) == -EPROBE_DEFER) {
|
||||
tas2770->reset_gpio = NULL;
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
}
|
||||
|
||||
tas2770->channel_size = 0;
|
||||
tas2770->slot_width = 0;
|
||||
|
||||
tas2770_reset(tas2770);
|
||||
|
||||
result = tas2770_register_codec(tas2770);
|
||||
if (result)
|
||||
dev_err(tas2770->dev, "Register codec failed.\n");
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int tas2770_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
pm_runtime_disable(&client->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct i2c_device_id tas2770_i2c_id[] = {
|
||||
{ "tas2770", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tas2770_i2c_id);
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id tas2770_of_match[] = {
|
||||
{ .compatible = "ti,tas2770" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tas2770_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver tas2770_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tas2770",
|
||||
.of_match_table = of_match_ptr(tas2770_of_match),
|
||||
},
|
||||
.probe = tas2770_i2c_probe,
|
||||
.remove = tas2770_i2c_remove,
|
||||
.id_table = tas2770_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(tas2770_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Shi Fu <shifu0704@thundersoft.com>");
|
||||
MODULE_DESCRIPTION("TAS2770 I2C Smart Amplifier driver");
|
||||
MODULE_LICENSE("GPL v2");
|
143
sound/soc/codecs/tas2770.h
Normal file
143
sound/soc/codecs/tas2770.h
Normal file
@ -0,0 +1,143 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* ALSA SoC TAS2770 codec driver
|
||||
*
|
||||
* Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*/
|
||||
#ifndef __TAS2770__
|
||||
#define __TAS2770__
|
||||
|
||||
/* Book Control Register (available in page0 of each book) */
|
||||
#define TAS2770_BOOKCTL_PAGE 0
|
||||
#define TAS2770_BOOKCTL_REG 127
|
||||
#define TAS2770_REG(page, reg) ((page * 128) + reg)
|
||||
/* Page */
|
||||
#define TAS2770_PAGE TAS2770_REG(0X0, 0x00)
|
||||
#define TAS2770_PAGE_PAGE_MASK 255
|
||||
/* Software Reset */
|
||||
#define TAS2770_SW_RST TAS2770_REG(0X0, 0x01)
|
||||
#define TAS2770_RST BIT(0)
|
||||
/* Power Control */
|
||||
#define TAS2770_PWR_CTRL TAS2770_REG(0X0, 0x02)
|
||||
#define TAS2770_PWR_CTRL_MASK 0x3
|
||||
#define TAS2770_PWR_CTRL_ACTIVE 0x0
|
||||
#define TAS2770_PWR_CTRL_MUTE BIT(0)
|
||||
#define TAS2770_PWR_CTRL_SHUTDOWN 0x2
|
||||
/* Playback Configuration Reg0 */
|
||||
#define TAS2770_PLAY_CFG_REG0 TAS2770_REG(0X0, 0x03)
|
||||
/* Playback Configuration Reg1 */
|
||||
#define TAS2770_PLAY_CFG_REG1 TAS2770_REG(0X0, 0x04)
|
||||
/* Playback Configuration Reg2 */
|
||||
#define TAS2770_PLAY_CFG_REG2 TAS2770_REG(0X0, 0x05)
|
||||
#define TAS2770_PLAY_CFG_REG2_VMAX 0xc9
|
||||
/* Misc Configuration Reg0 */
|
||||
#define TAS2770_MSC_CFG_REG0 TAS2770_REG(0X0, 0x07)
|
||||
/* TDM Configuration Reg0 */
|
||||
#define TAS2770_TDM_CFG_REG0 TAS2770_REG(0X0, 0x0A)
|
||||
#define TAS2770_TDM_CFG_REG0_SMP_MASK BIT(5)
|
||||
#define TAS2770_TDM_CFG_REG0_SMP_48KHZ 0x0
|
||||
#define TAS2770_TDM_CFG_REG0_SMP_44_1KHZ BIT(5)
|
||||
#define TAS2770_TDM_CFG_REG0_31_MASK 0xe
|
||||
#define TAS2770_TDM_CFG_REG0_31_44_1_48KHZ 0x6
|
||||
#define TAS2770_TDM_CFG_REG0_31_88_2_96KHZ 0x8
|
||||
#define TAS2770_TDM_CFG_REG0_31_176_4_192KHZ 0xa
|
||||
/* TDM Configuration Reg1 */
|
||||
#define TAS2770_TDM_CFG_REG1 TAS2770_REG(0X0, 0x0B)
|
||||
#define TAS2770_TDM_CFG_REG1_MASK 0x3e
|
||||
#define TAS2770_TDM_CFG_REG1_51_SHIFT 1
|
||||
#define TAS2770_TDM_CFG_REG1_RX_MASK BIT(0)
|
||||
#define TAS2770_TDM_CFG_REG1_RX_RSING 0x0
|
||||
#define TAS2770_TDM_CFG_REG1_RX_FALING BIT(0)
|
||||
/* TDM Configuration Reg2 */
|
||||
#define TAS2770_TDM_CFG_REG2 TAS2770_REG(0X0, 0x0C)
|
||||
#define TAS2770_TDM_CFG_REG2_RXW_MASK 0xc
|
||||
#define TAS2770_TDM_CFG_REG2_RXW_16BITS 0x0
|
||||
#define TAS2770_TDM_CFG_REG2_RXW_24BITS 0x8
|
||||
#define TAS2770_TDM_CFG_REG2_RXW_32BITS 0xc
|
||||
#define TAS2770_TDM_CFG_REG2_RXS_MASK 0x3
|
||||
#define TAS2770_TDM_CFG_REG2_RXS_16BITS 0x0
|
||||
#define TAS2770_TDM_CFG_REG2_RXS_24BITS BIT(0)
|
||||
#define TAS2770_TDM_CFG_REG2_RXS_32BITS 0x2
|
||||
/* TDM Configuration Reg3 */
|
||||
#define TAS2770_TDM_CFG_REG3 TAS2770_REG(0X0, 0x0D)
|
||||
#define TAS2770_TDM_CFG_REG3_RXS_MASK 0xf0
|
||||
#define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4
|
||||
#define TAS2770_TDM_CFG_REG3_30_MASK 0xf
|
||||
#define TAS2770_TDM_CFG_REG3_30_SHIFT 0
|
||||
/* TDM Configuration Reg5 */
|
||||
#define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F)
|
||||
#define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6)
|
||||
#define TAS2770_TDM_CFG_REG5_VSNS_ENABLE BIT(6)
|
||||
#define TAS2770_TDM_CFG_REG5_50_MASK 0x3f
|
||||
/* TDM Configuration Reg6 */
|
||||
#define TAS2770_TDM_CFG_REG6 TAS2770_REG(0X0, 0x10)
|
||||
#define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6)
|
||||
#define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6)
|
||||
#define TAS2770_TDM_CFG_REG6_50_MASK 0x3f
|
||||
/* Brown Out Prevention Reg0 */
|
||||
#define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B)
|
||||
/* Interrupt MASK Reg0 */
|
||||
#define TAS2770_INT_MASK_REG0 TAS2770_REG(0X0, 0x20)
|
||||
#define TAS2770_INT_REG0_DEFAULT 0xfc
|
||||
#define TAS2770_INT_MASK_REG0_DISABLE 0xff
|
||||
/* Interrupt MASK Reg1 */
|
||||
#define TAS2770_INT_MASK_REG1 TAS2770_REG(0X0, 0x21)
|
||||
#define TAS2770_INT_REG1_DEFAULT 0xb1
|
||||
#define TAS2770_INT_MASK_REG1_DISABLE 0xff
|
||||
/* Live-Interrupt Reg0 */
|
||||
#define TAS2770_LVE_INT_REG0 TAS2770_REG(0X0, 0x22)
|
||||
/* Live-Interrupt Reg1 */
|
||||
#define TAS2770_LVE_INT_REG1 TAS2770_REG(0X0, 0x23)
|
||||
/* Latched-Interrupt Reg0 */
|
||||
#define TAS2770_LAT_INT_REG0 TAS2770_REG(0X0, 0x24)
|
||||
#define TAS2770_LAT_INT_REG0_OCE_FLG BIT(1)
|
||||
#define TAS2770_LAT_INT_REG0_OTE_FLG BIT(0)
|
||||
/* Latched-Interrupt Reg1 */
|
||||
#define TAS2770_LAT_INT_REG1 TAS2770_REG(0X0, 0x25)
|
||||
#define TAS2770_LAT_INT_REG1_VBA_TOV BIT(3)
|
||||
#define TAS2770_LAT_INT_REG1_VBA_TUV BIT(2)
|
||||
#define TAS2770_LAT_INT_REG1_BOUT_FLG BIT(1)
|
||||
/* VBAT MSB */
|
||||
#define TAS2770_VBAT_MSB TAS2770_REG(0X0, 0x27)
|
||||
/* VBAT LSB */
|
||||
#define TAS2770_VBAT_LSB TAS2770_REG(0X0, 0x28)
|
||||
/* TEMP MSB */
|
||||
#define TAS2770_TEMP_MSB TAS2770_REG(0X0, 0x29)
|
||||
/* TEMP LSB */
|
||||
#define TAS2770_TEMP_LSB TAS2770_REG(0X0, 0x2A)
|
||||
/* Interrupt Configuration */
|
||||
#define TAS2770_INT_CFG TAS2770_REG(0X0, 0x30)
|
||||
/* Misc IRQ */
|
||||
#define TAS2770_MISC_IRQ TAS2770_REG(0X0, 0x32)
|
||||
/* Clock Configuration */
|
||||
#define TAS2770_CLK_CGF TAS2770_REG(0X0, 0x3C)
|
||||
/* TDM Clock detection monitor */
|
||||
#define TAS2770_TDM_CLK_DETC TAS2770_REG(0X0, 0x77)
|
||||
/* Revision and PG ID */
|
||||
#define TAS2770_REV_AND_GPID TAS2770_REG(0X0, 0x7D)
|
||||
|
||||
#define TAS2770_POWER_ACTIVE 0
|
||||
#define TAS2770_POWER_MUTE 1
|
||||
#define TAS2770_POWER_SHUTDOWN 2
|
||||
#define ERROR_OVER_CURRENT 0x0000001
|
||||
#define ERROR_DIE_OVERTEMP 0x0000002
|
||||
#define ERROR_OVER_VOLTAGE 0x0000004
|
||||
#define ERROR_UNDER_VOLTAGE 0x0000008
|
||||
#define ERROR_BROWNOUT 0x0000010
|
||||
#define ERROR_CLASSD_PWR 0x0000020
|
||||
|
||||
struct tas2770_priv {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct snd_soc_component *component;
|
||||
int power_state;
|
||||
int asi_format;
|
||||
struct gpio_desc *reset_gpio;
|
||||
int sampling_rate;
|
||||
int channel_size;
|
||||
int slot_width;
|
||||
int v_sense_slot;
|
||||
int i_sense_slot;
|
||||
};
|
||||
|
||||
#endif /* __TAS2770__ */
|
@ -573,6 +573,9 @@ static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
struct clk *pll;
|
||||
|
||||
pll = devm_clk_get(component->dev, "pll");
|
||||
if (IS_ERR(pll))
|
||||
return PTR_ERR(pll);
|
||||
|
||||
mclk = clk_get_parent(pll);
|
||||
|
||||
return clk_set_rate(mclk, freq);
|
||||
|
@ -2837,11 +2837,11 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w,
|
||||
TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
|
||||
snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x10);
|
||||
snd_soc_component_update_bits(comp, dec_cfg_reg, 0x08, 0x00);
|
||||
if (hpf_coff_freq != CF_MIN_3DB_150HZ) {
|
||||
snd_soc_component_update_bits(comp, dec_cfg_reg,
|
||||
TX_HPF_CUT_OFF_FREQ_MASK,
|
||||
hpf_coff_freq << 5);
|
||||
}
|
||||
if (hpf_coff_freq != CF_MIN_3DB_150HZ) {
|
||||
snd_soc_component_update_bits(comp, dec_cfg_reg,
|
||||
TX_HPF_CUT_OFF_FREQ_MASK,
|
||||
hpf_coff_freq << 5);
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x00);
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <linux/mfd/wm8994/pdata.h>
|
||||
#include <linux/mfd/wm8994/gpio.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "wm8994.h"
|
||||
|
||||
#define WM_FW_BLOCK_INFO 0xff
|
||||
@ -58,18 +60,15 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
|
||||
}
|
||||
|
||||
if (memcmp(fw->data, "WMFW", 4) != 0) {
|
||||
memcpy(&data32, fw->data, sizeof(data32));
|
||||
data32 = be32_to_cpu(data32);
|
||||
data32 = get_unaligned_be32(fw->data);
|
||||
dev_err(component->dev, "%s: firmware has bad file magic %08x\n",
|
||||
name, data32);
|
||||
goto err;
|
||||
}
|
||||
|
||||
memcpy(&data32, fw->data + 4, sizeof(data32));
|
||||
len = be32_to_cpu(data32);
|
||||
len = get_unaligned_be32(fw->data + 4);
|
||||
data32 = get_unaligned_be32(fw->data + 8);
|
||||
|
||||
memcpy(&data32, fw->data + 8, sizeof(data32));
|
||||
data32 = be32_to_cpu(data32);
|
||||
if ((data32 >> 24) & 0xff) {
|
||||
dev_err(component->dev, "%s: unsupported firmware version %d\n",
|
||||
name, (data32 >> 24) & 0xff);
|
||||
@ -87,9 +86,8 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
|
||||
}
|
||||
|
||||
if (check) {
|
||||
memcpy(&data64, fw->data + 24, sizeof(u64));
|
||||
dev_info(component->dev, "%s timestamp %llx\n",
|
||||
name, be64_to_cpu(data64));
|
||||
data64 = get_unaligned_be64(fw->data + 24);
|
||||
dev_info(component->dev, "%s timestamp %llx\n", name, data64);
|
||||
} else {
|
||||
snd_soc_component_write(component, 0x102, 0x2);
|
||||
snd_soc_component_write(component, 0x900, 0x2);
|
||||
@ -104,8 +102,7 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
|
||||
goto err;
|
||||
}
|
||||
|
||||
memcpy(&data32, data + 4, sizeof(data32));
|
||||
block_len = be32_to_cpu(data32);
|
||||
block_len = get_unaligned_be32(data + 4);
|
||||
if (block_len + 8 > len) {
|
||||
dev_err(component->dev, "%zd byte block longer than file\n",
|
||||
block_len);
|
||||
@ -116,8 +113,7 @@ static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
|
||||
goto err;
|
||||
}
|
||||
|
||||
memcpy(&data32, data, sizeof(data32));
|
||||
data32 = be32_to_cpu(data32);
|
||||
data32 = get_unaligned_be32(data);
|
||||
|
||||
switch ((data32 >> 24) & 0xff) {
|
||||
case WM_FW_BLOCK_INFO:
|
||||
|
@ -167,12 +167,12 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif)
|
||||
|
||||
switch (wm8994->sysclk[aif]) {
|
||||
case WM8994_SYSCLK_MCLK1:
|
||||
rate = wm8994->mclk[0];
|
||||
rate = wm8994->mclk_rate[0];
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_MCLK2:
|
||||
reg1 |= 0x8;
|
||||
rate = wm8994->mclk[1];
|
||||
rate = wm8994->mclk_rate[1];
|
||||
break;
|
||||
|
||||
case WM8994_SYSCLK_FLL1:
|
||||
@ -1038,6 +1038,45 @@ static bool wm8994_check_class_w_digital(struct snd_soc_component *component)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enable)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int offset, val, clk_idx;
|
||||
int ret;
|
||||
|
||||
if (aif)
|
||||
offset = 4;
|
||||
else
|
||||
offset = 0;
|
||||
|
||||
val = snd_soc_component_read32(component, WM8994_AIF1_CLOCKING_1 + offset);
|
||||
val &= WM8994_AIF1CLK_SRC_MASK;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
clk_idx = WM8994_MCLK1;
|
||||
break;
|
||||
case 1:
|
||||
clk_idx = WM8994_MCLK2;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "Failed to enable MCLK%d\n",
|
||||
clk_idx);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
clk_disable_unprepare(wm8994->mclk[clk_idx].clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aif1clk_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
@ -1045,7 +1084,7 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
|
||||
struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
|
||||
struct wm8994 *control = wm8994->wm8994;
|
||||
int mask = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA;
|
||||
int i;
|
||||
int ret, i;
|
||||
int dac;
|
||||
int adc;
|
||||
int val;
|
||||
@ -1061,6 +1100,10 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
ret = aif_mclk_set(component, 0, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Don't enable timeslot 2 if not in use */
|
||||
if (wm8994->channels[0] <= 2)
|
||||
mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
|
||||
@ -1133,6 +1176,12 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w,
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
aif_mclk_set(component, 0, false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1140,13 +1189,17 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
int i;
|
||||
int ret, i;
|
||||
int dac;
|
||||
int adc;
|
||||
int val;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
ret = aif_mclk_set(component, 1, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = snd_soc_component_read32(component, WM8994_AIF2_CONTROL_1);
|
||||
if ((val & WM8994_AIF2ADCL_SRC) &&
|
||||
(val & WM8994_AIF2ADCR_SRC))
|
||||
@ -1218,6 +1271,12 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
aif_mclk_set(component, 1, false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1623,10 +1682,10 @@ SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev)
|
||||
static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = {
|
||||
SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, aif1clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, aif2clk_ev,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
|
||||
SND_SOC_DAPM_PRE_PMD),
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
|
||||
left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
|
||||
@ -2141,6 +2200,7 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
|
||||
u16 reg, clk1, aif_reg, aif_src;
|
||||
unsigned long timeout;
|
||||
bool was_enabled;
|
||||
struct clk *mclk;
|
||||
|
||||
switch (id) {
|
||||
case WM8994_FLL1:
|
||||
@ -2216,6 +2276,27 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
|
||||
snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset,
|
||||
WM8994_FLL1_ENA, 0);
|
||||
|
||||
/* Disable MCLK if needed before we possibly change to new clock parent */
|
||||
if (was_enabled) {
|
||||
reg = snd_soc_component_read32(component, WM8994_FLL1_CONTROL_5
|
||||
+ reg_offset);
|
||||
reg = ((reg & WM8994_FLL1_REFCLK_SRC_MASK)
|
||||
>> WM8994_FLL1_REFCLK_SRC_SHIFT) + 1;
|
||||
|
||||
switch (reg) {
|
||||
case WM8994_FLL_SRC_MCLK1:
|
||||
mclk = wm8994->mclk[WM8994_MCLK1].clk;
|
||||
break;
|
||||
case WM8994_FLL_SRC_MCLK2:
|
||||
mclk = wm8994->mclk[WM8994_MCLK2].clk;
|
||||
break;
|
||||
default:
|
||||
mclk = NULL;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(mclk);
|
||||
}
|
||||
|
||||
if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK &&
|
||||
freq_in == freq_out && freq_out) {
|
||||
dev_dbg(component->dev, "Bypassing FLL%d\n", id + 1);
|
||||
@ -2260,10 +2341,29 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
|
||||
/* Clear any pending completion from a previous failure */
|
||||
try_wait_for_completion(&wm8994->fll_locked[id]);
|
||||
|
||||
switch (src) {
|
||||
case WM8994_FLL_SRC_MCLK1:
|
||||
mclk = wm8994->mclk[WM8994_MCLK1].clk;
|
||||
break;
|
||||
case WM8994_FLL_SRC_MCLK2:
|
||||
mclk = wm8994->mclk[WM8994_MCLK2].clk;
|
||||
break;
|
||||
default:
|
||||
mclk = NULL;
|
||||
}
|
||||
|
||||
/* Enable (with fractional mode if required) */
|
||||
if (freq_out) {
|
||||
ret = clk_prepare_enable(mclk);
|
||||
if (ret < 0) {
|
||||
dev_err(component->dev, "Failed to enable MCLK for FLL%d\n",
|
||||
id + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable VMID if we need it */
|
||||
if (!was_enabled) {
|
||||
|
||||
active_reference(component);
|
||||
|
||||
switch (control->type) {
|
||||
@ -2372,12 +2472,29 @@ static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src,
|
||||
return _wm8994_set_fll(dai->component, id, src, freq_in, freq_out);
|
||||
}
|
||||
|
||||
static int wm8994_set_mclk_rate(struct wm8994_priv *wm8994, unsigned int id,
|
||||
unsigned int *freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!wm8994->mclk[id].clk || *freq == wm8994->mclk_rate[id])
|
||||
return 0;
|
||||
|
||||
ret = clk_set_rate(wm8994->mclk[id].clk, *freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*freq = clk_get_rate(wm8994->mclk[id].clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
|
||||
int i;
|
||||
int ret, i;
|
||||
|
||||
switch (dai->id) {
|
||||
case 1:
|
||||
@ -2392,7 +2509,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
|
||||
switch (clk_id) {
|
||||
case WM8994_SYSCLK_MCLK1:
|
||||
wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1;
|
||||
wm8994->mclk[0] = freq;
|
||||
|
||||
ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
wm8994->mclk_rate[0] = freq;
|
||||
dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n",
|
||||
dai->id, freq);
|
||||
break;
|
||||
@ -2400,7 +2522,12 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
|
||||
case WM8994_SYSCLK_MCLK2:
|
||||
/* TODO: Set GPIO AF */
|
||||
wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2;
|
||||
wm8994->mclk[1] = freq;
|
||||
|
||||
ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
wm8994->mclk_rate[1] = freq;
|
||||
dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
|
||||
dai->id, freq);
|
||||
break;
|
||||
@ -4456,6 +4583,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8994 = {
|
||||
static int wm8994_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm8994_priv *wm8994;
|
||||
int ret;
|
||||
|
||||
wm8994 = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_priv),
|
||||
GFP_KERNEL);
|
||||
@ -4467,6 +4595,16 @@ static int wm8994_probe(struct platform_device *pdev)
|
||||
|
||||
wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
wm8994->mclk[WM8994_MCLK1].id = "MCLK1";
|
||||
wm8994->mclk[WM8994_MCLK2].id = "MCLK2";
|
||||
|
||||
ret = devm_clk_bulk_get_optional(pdev->dev.parent, ARRAY_SIZE(wm8994->mclk),
|
||||
wm8994->mclk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get clocks: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_idle(&pdev->dev);
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#ifndef _WM8994_H
|
||||
#define _WM8994_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/completion.h>
|
||||
@ -14,6 +15,12 @@
|
||||
|
||||
#include "wm_hubs.h"
|
||||
|
||||
enum {
|
||||
WM8994_MCLK1,
|
||||
WM8994_MCLK2,
|
||||
WM8994_NUM_MCLK
|
||||
};
|
||||
|
||||
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
|
||||
#define WM8994_SYSCLK_MCLK1 1
|
||||
#define WM8994_SYSCLK_MCLK2 2
|
||||
@ -73,9 +80,10 @@ struct wm8994;
|
||||
struct wm8994_priv {
|
||||
struct wm_hubs_data hubs;
|
||||
struct wm8994 *wm8994;
|
||||
struct clk_bulk_data mclk[WM8994_NUM_MCLK];
|
||||
int sysclk[2];
|
||||
int sysclk_rate[2];
|
||||
int mclk[2];
|
||||
int mclk_rate[2];
|
||||
int aifclk[2];
|
||||
int aifdiv[2];
|
||||
int channels[2];
|
||||
|
@ -135,7 +135,8 @@ void dw_pcm_pop_rx(struct dw_i2s_dev *dev)
|
||||
dw_pcm_transfer(dev, false);
|
||||
}
|
||||
|
||||
static int dw_pcm_open(struct snd_pcm_substream *substream)
|
||||
static int dw_pcm_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
@ -148,14 +149,16 @@ static int dw_pcm_open(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_pcm_close(struct snd_pcm_substream *substream)
|
||||
static int dw_pcm_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
synchronize_rcu();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
static int dw_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct dw_i2s_dev *dev = runtime->private_data;
|
||||
@ -192,12 +195,14 @@ static int dw_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
static int dw_pcm_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int dw_pcm_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct dw_i2s_dev *dev = runtime->private_data;
|
||||
@ -231,7 +236,8 @@ static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
static snd_pcm_uframes_t dw_pcm_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct dw_i2s_dev *dev = runtime->private_data;
|
||||
@ -245,7 +251,8 @@ static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
return pos < runtime->buffer_size ? pos : 0;
|
||||
}
|
||||
|
||||
static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int dw_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
size_t size = dw_pcm_hardware.buffer_bytes_max;
|
||||
|
||||
@ -255,25 +262,22 @@ static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_pcm_free(struct snd_pcm *pcm)
|
||||
static void dw_pcm_free(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
snd_pcm_lib_preallocate_free_for_all(pcm);
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops dw_pcm_ops = {
|
||||
.open = dw_pcm_open,
|
||||
.close = dw_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = dw_pcm_hw_params,
|
||||
.hw_free = dw_pcm_hw_free,
|
||||
.trigger = dw_pcm_trigger,
|
||||
.pointer = dw_pcm_pointer,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver dw_pcm_component = {
|
||||
.pcm_new = dw_pcm_new,
|
||||
.pcm_free = dw_pcm_free,
|
||||
.ops = &dw_pcm_ops,
|
||||
.open = dw_pcm_open,
|
||||
.close = dw_pcm_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = dw_pcm_hw_params,
|
||||
.hw_free = dw_pcm_hw_free,
|
||||
.trigger = dw_pcm_trigger,
|
||||
.pointer = dw_pcm_pointer,
|
||||
.pcm_construct = dw_pcm_new,
|
||||
.pcm_destruct = dw_pcm_free,
|
||||
};
|
||||
|
||||
int dw_pcm_register(struct platform_device *pdev)
|
||||
|
@ -25,6 +25,16 @@ config SND_SOC_FSL_SAI
|
||||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_SOC_FSL_MQS
|
||||
tristate "Medium Quality Sound (MQS) module support"
|
||||
depends on SND_SOC_FSL_SAI
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Say Y if you want to add Medium Quality Sound (MQS)
|
||||
support for the Freescale CPUs.
|
||||
This option is only useful for out-of-tree drivers since
|
||||
in-tree drivers select it automatically.
|
||||
|
||||
config SND_SOC_FSL_AUDMIX
|
||||
tristate "Audio Mixer (AUDMIX) module support"
|
||||
select REGMAP_MMIO
|
||||
|
@ -23,6 +23,7 @@ snd-soc-fsl-esai-objs := fsl_esai.o
|
||||
snd-soc-fsl-micfil-objs := fsl_micfil.o
|
||||
snd-soc-fsl-utils-objs := fsl_utils.o
|
||||
snd-soc-fsl-dma-objs := fsl_dma.o
|
||||
snd-soc-fsl-mqs-objs := fsl_mqs.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
|
||||
@ -33,6 +34,7 @@ obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
|
||||
obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o
|
||||
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
|
||||
|
||||
# MPC5200 Platform Support
|
||||
|
@ -115,7 +115,7 @@ static void fsl_asrc_sel_proc(int inrate, int outrate,
|
||||
* within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
|
||||
* while pair A and pair C are comparatively independent.
|
||||
*/
|
||||
static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
|
||||
int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
|
||||
{
|
||||
enum asrc_pair_index index = ASRC_INVALID_PAIR;
|
||||
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
||||
@ -158,7 +158,7 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
|
||||
*
|
||||
* It clears the resource from asrc_priv and releases the occupied channels.
|
||||
*/
|
||||
static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
|
||||
void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
|
||||
{
|
||||
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
@ -259,14 +259,24 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
|
||||
* It configures those ASRC registers according to a configuration instance
|
||||
* of struct asrc_config which includes in/output sample rate, width, channel
|
||||
* and clock settings.
|
||||
*
|
||||
* Note:
|
||||
* The ideal ratio configuration can work with a flexible clock rate setting.
|
||||
* Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC.
|
||||
* For a regular audio playback, the clock rate should not be slower than an
|
||||
* clock rate aligning with the output sample rate; For a use case requiring
|
||||
* faster conversion, set use_ideal_rate to have the faster speed.
|
||||
*/
|
||||
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
||||
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
|
||||
{
|
||||
struct asrc_config *config = pair->config;
|
||||
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
enum asrc_word_width input_word_width;
|
||||
enum asrc_word_width output_word_width;
|
||||
u32 inrate, outrate, indiv, outdiv;
|
||||
u32 clk_index[2], div[2];
|
||||
u32 clk_index[2], div[2], rem[2];
|
||||
u64 clk_rate;
|
||||
int in, out, channels;
|
||||
int pre_proc, post_proc;
|
||||
struct clk *clk;
|
||||
@ -283,9 +293,32 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Validate output width */
|
||||
if (config->output_word_width == ASRC_WIDTH_8_BIT) {
|
||||
pair_err("does not support 8bit width output\n");
|
||||
switch (snd_pcm_format_width(config->input_format)) {
|
||||
case 8:
|
||||
input_word_width = ASRC_WIDTH_8_BIT;
|
||||
break;
|
||||
case 16:
|
||||
input_word_width = ASRC_WIDTH_16_BIT;
|
||||
break;
|
||||
case 24:
|
||||
input_word_width = ASRC_WIDTH_24_BIT;
|
||||
break;
|
||||
default:
|
||||
pair_err("does not support this input format, %d\n",
|
||||
config->input_format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (snd_pcm_format_width(config->output_format)) {
|
||||
case 16:
|
||||
output_word_width = ASRC_WIDTH_16_BIT;
|
||||
break;
|
||||
case 24:
|
||||
output_word_width = ASRC_WIDTH_24_BIT;
|
||||
break;
|
||||
default:
|
||||
pair_err("does not support this output format, %d\n",
|
||||
config->output_format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -326,27 +359,42 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
||||
/* We only have output clock for ideal ratio mode */
|
||||
clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
|
||||
|
||||
div[IN] = clk_get_rate(clk) / inrate;
|
||||
if (div[IN] == 0) {
|
||||
clk_rate = clk_get_rate(clk);
|
||||
rem[IN] = do_div(clk_rate, inrate);
|
||||
div[IN] = (u32)clk_rate;
|
||||
|
||||
/*
|
||||
* The divider range is [1, 1024], defined by the hardware. For non-
|
||||
* ideal ratio configuration, clock rate has to be strictly aligned
|
||||
* with the sample rate. For ideal ratio configuration, clock rates
|
||||
* only result in different converting speeds. So remainder does not
|
||||
* matter, as long as we keep the divider within its valid range.
|
||||
*/
|
||||
if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) {
|
||||
pair_err("failed to support input sample rate %dHz by asrck_%x\n",
|
||||
inrate, clk_index[ideal ? OUT : IN]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
div[IN] = min_t(u32, 1024, div[IN]);
|
||||
|
||||
clk = asrc_priv->asrck_clk[clk_index[OUT]];
|
||||
|
||||
/* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
|
||||
if (ideal)
|
||||
div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
|
||||
clk_rate = clk_get_rate(clk);
|
||||
if (ideal && use_ideal_rate)
|
||||
rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
|
||||
else
|
||||
div[OUT] = clk_get_rate(clk) / outrate;
|
||||
rem[OUT] = do_div(clk_rate, outrate);
|
||||
div[OUT] = clk_rate;
|
||||
|
||||
if (div[OUT] == 0) {
|
||||
/* Output divider has the same limitation as the input one */
|
||||
if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) {
|
||||
pair_err("failed to support output sample rate %dHz by asrck_%x\n",
|
||||
outrate, clk_index[OUT]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
div[OUT] = min_t(u32, 1024, div[OUT]);
|
||||
|
||||
/* Set the channel number */
|
||||
channels = config->channel_num;
|
||||
|
||||
@ -383,8 +431,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
|
||||
/* Implement word_width configurations */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
|
||||
ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
|
||||
ASRMCR1i_OW16(config->output_word_width) |
|
||||
ASRMCR1i_IWD(config->input_word_width));
|
||||
ASRMCR1i_OW16(output_word_width) |
|
||||
ASRMCR1i_IWD(input_word_width));
|
||||
|
||||
/* Enable BUFFER STALL */
|
||||
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
|
||||
@ -497,13 +545,13 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
|
||||
int width = params_width(params);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
unsigned int channels = params_channels(params);
|
||||
unsigned int rate = params_rate(params);
|
||||
struct asrc_config config;
|
||||
int word_width, ret;
|
||||
snd_pcm_format_t format;
|
||||
int ret;
|
||||
|
||||
ret = fsl_asrc_request_pair(channels, pair);
|
||||
if (ret) {
|
||||
@ -513,15 +561,10 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
pair->config = &config;
|
||||
|
||||
if (width == 16)
|
||||
width = ASRC_WIDTH_16_BIT;
|
||||
else
|
||||
width = ASRC_WIDTH_24_BIT;
|
||||
|
||||
if (asrc_priv->asrc_width == 16)
|
||||
word_width = ASRC_WIDTH_16_BIT;
|
||||
format = SNDRV_PCM_FORMAT_S16_LE;
|
||||
else
|
||||
word_width = ASRC_WIDTH_24_BIT;
|
||||
format = SNDRV_PCM_FORMAT_S24_LE;
|
||||
|
||||
config.pair = pair->index;
|
||||
config.channel_num = channels;
|
||||
@ -529,18 +572,18 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
config.outclk = OUTCLK_ASRCK1_CLK;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
config.input_word_width = width;
|
||||
config.output_word_width = word_width;
|
||||
config.input_format = params_format(params);
|
||||
config.output_format = format;
|
||||
config.input_sample_rate = rate;
|
||||
config.output_sample_rate = asrc_priv->asrc_rate;
|
||||
} else {
|
||||
config.input_word_width = word_width;
|
||||
config.output_word_width = width;
|
||||
config.input_format = format;
|
||||
config.output_format = params_format(params);
|
||||
config.input_sample_rate = asrc_priv->asrc_rate;
|
||||
config.output_sample_rate = rate;
|
||||
}
|
||||
|
||||
ret = fsl_asrc_config_pair(pair);
|
||||
ret = fsl_asrc_config_pair(pair, false);
|
||||
if (ret) {
|
||||
dev_err(dai->dev, "fail to config asrc pair\n");
|
||||
return ret;
|
||||
@ -604,7 +647,7 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
|
||||
|
||||
#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE)
|
||||
SNDRV_PCM_FMTBIT_S24_3LE)
|
||||
|
||||
static struct snd_soc_dai_driver fsl_asrc_dai = {
|
||||
.probe = fsl_asrc_dai_probe,
|
||||
@ -615,7 +658,8 @@ static struct snd_soc_dai_driver fsl_asrc_dai = {
|
||||
.rate_min = 5512,
|
||||
.rate_max = 192000,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.formats = FSL_ASRC_FORMATS,
|
||||
.formats = FSL_ASRC_FORMATS |
|
||||
SNDRV_PCM_FMTBIT_S8,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ASRC-Capture",
|
||||
|
@ -342,8 +342,8 @@ struct asrc_config {
|
||||
unsigned int dma_buffer_size;
|
||||
unsigned int input_sample_rate;
|
||||
unsigned int output_sample_rate;
|
||||
enum asrc_word_width input_word_width;
|
||||
enum asrc_word_width output_word_width;
|
||||
snd_pcm_format_t input_format;
|
||||
snd_pcm_format_t output_format;
|
||||
enum asrc_inclk inclk;
|
||||
enum asrc_outclk outclk;
|
||||
};
|
||||
@ -462,4 +462,7 @@ struct fsl_asrc {
|
||||
#define DRV_NAME "fsl-asrc-dai"
|
||||
extern struct snd_soc_component_driver fsl_asrc_component;
|
||||
struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
|
||||
int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair);
|
||||
void fsl_asrc_release_pair(struct fsl_asrc_pair *pair);
|
||||
|
||||
#endif /* _FSL_ASRC_H */
|
||||
|
@ -16,13 +16,11 @@
|
||||
|
||||
#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
|
||||
|
||||
static const struct snd_pcm_hardware snd_imx_hardware = {
|
||||
static struct snd_pcm_hardware snd_imx_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = 65535, /* Limited by SDMA engine */
|
||||
@ -54,13 +52,12 @@ static void fsl_asrc_dma_complete(void *arg)
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
|
||||
static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct device *dev = component->dev;
|
||||
unsigned long flags = DMA_CTRL_ACK;
|
||||
|
||||
@ -97,7 +94,8 @@ static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int fsl_asrc_dma_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
@ -107,7 +105,7 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
ret = fsl_asrc_dma_prepare_and_submit(substream);
|
||||
ret = fsl_asrc_dma_prepare_and_submit(substream, component);
|
||||
if (ret)
|
||||
return ret;
|
||||
dma_async_issue_pending(pair->dma_chan[IN]);
|
||||
@ -126,7 +124,8 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
@ -134,7 +133,6 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
|
||||
struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
struct fsl_asrc *asrc_priv = pair->asrc_priv;
|
||||
@ -249,7 +247,8 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
|
||||
static int fsl_asrc_dma_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
@ -268,14 +267,27 @@ static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
|
||||
static int fsl_asrc_dma_startup(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
struct device *dev = component->dev;
|
||||
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
|
||||
struct fsl_asrc_pair *pair;
|
||||
struct dma_chan *tmp_chan = NULL;
|
||||
u8 dir = tx ? OUT : IN;
|
||||
bool release_pair = true;
|
||||
int ret = 0;
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set pcm hw params periods\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
|
||||
if (!pair)
|
||||
@ -285,14 +297,54 @@ static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
|
||||
|
||||
runtime->private_data = pair;
|
||||
|
||||
snd_pcm_hw_constraint_integer(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
/* Request a dummy pair, which will be released later.
|
||||
* Request pair function needs channel num as input, for this
|
||||
* dummy pair, we just request "1" channel temporarily.
|
||||
*/
|
||||
ret = fsl_asrc_request_pair(1, pair);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to request asrc pair\n");
|
||||
goto req_pair_err;
|
||||
}
|
||||
|
||||
/* Request a dummy dma channel, which will be released later. */
|
||||
tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
|
||||
if (!tmp_chan) {
|
||||
dev_err(dev, "failed to get dma channel\n");
|
||||
ret = -EINVAL;
|
||||
goto dma_chan_err;
|
||||
}
|
||||
|
||||
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
|
||||
/* Refine the snd_imx_hardware according to caps of DMA. */
|
||||
ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream,
|
||||
dma_data,
|
||||
&snd_imx_hardware,
|
||||
tmp_chan);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to refine runtime hwparams\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
release_pair = false;
|
||||
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
dma_release_channel(tmp_chan);
|
||||
|
||||
dma_chan_err:
|
||||
fsl_asrc_release_pair(pair);
|
||||
|
||||
req_pair_err:
|
||||
if (release_pair)
|
||||
kfree(pair);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
|
||||
static int fsl_asrc_dma_shutdown(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
@ -311,7 +363,9 @@ static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
static snd_pcm_uframes_t
|
||||
fsl_asrc_dma_pcm_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
@ -319,17 +373,8 @@ static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *subs
|
||||
return bytes_to_frames(substream->runtime, pair->pos);
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops fsl_asrc_dma_pcm_ops = {
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = fsl_asrc_dma_hw_params,
|
||||
.hw_free = fsl_asrc_dma_hw_free,
|
||||
.trigger = fsl_asrc_dma_trigger,
|
||||
.open = fsl_asrc_dma_startup,
|
||||
.close = fsl_asrc_dma_shutdown,
|
||||
.pointer = fsl_asrc_dma_pcm_pointer,
|
||||
};
|
||||
|
||||
static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm_substream *substream;
|
||||
@ -364,7 +409,8 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
|
||||
static void fsl_asrc_dma_pcm_free(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
int i;
|
||||
@ -382,8 +428,14 @@ static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
|
||||
|
||||
struct snd_soc_component_driver fsl_asrc_component = {
|
||||
.name = DRV_NAME,
|
||||
.ops = &fsl_asrc_dma_pcm_ops,
|
||||
.pcm_new = fsl_asrc_dma_pcm_new,
|
||||
.pcm_free = fsl_asrc_dma_pcm_free,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = fsl_asrc_dma_hw_params,
|
||||
.hw_free = fsl_asrc_dma_hw_free,
|
||||
.trigger = fsl_asrc_dma_trigger,
|
||||
.open = fsl_asrc_dma_startup,
|
||||
.close = fsl_asrc_dma_shutdown,
|
||||
.pointer = fsl_asrc_dma_pcm_pointer,
|
||||
.pcm_construct = fsl_asrc_dma_pcm_new,
|
||||
.pcm_destruct = fsl_asrc_dma_pcm_free,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fsl_asrc_component);
|
||||
|
@ -201,8 +201,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
|
||||
struct fsl_dma_private *dma_private = dev_id;
|
||||
struct snd_pcm_substream *substream = dma_private->substream;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct device *dev = component->dev;
|
||||
struct device *dev = rtd->dev;
|
||||
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 sr, sr2 = 0;
|
||||
@ -280,7 +279,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
|
||||
* Regardless of where the memory is actually allocated, since the device can
|
||||
* technically DMA to any 36-bit address, we do need to set the DMA mask to 36.
|
||||
*/
|
||||
static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int fsl_dma_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
@ -380,11 +380,10 @@ static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
* buffer, which is what ALSA expects. We're just dividing it into
|
||||
* contiguous parts, and creating a link descriptor for each one.
|
||||
*/
|
||||
static int fsl_dma_open(struct snd_pcm_substream *substream)
|
||||
static int fsl_dma_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct device *dev = component->dev;
|
||||
struct dma_object *dma =
|
||||
container_of(component->driver, struct dma_object, dai);
|
||||
@ -533,13 +532,12 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
|
||||
* and 8 bytes at a time). So we do not support packed 24-bit samples.
|
||||
* 24-bit data must be padded to 32 bits.
|
||||
*/
|
||||
static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
static int fsl_dma_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_dma_private *dma_private = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct device *dev = component->dev;
|
||||
|
||||
/* Number of bits per sample */
|
||||
@ -698,12 +696,11 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
|
||||
* The base address of the buffer is stored in the source_addr field of the
|
||||
* first link descriptor.
|
||||
*/
|
||||
static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
|
||||
static snd_pcm_uframes_t fsl_dma_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_dma_private *dma_private = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct device *dev = component->dev;
|
||||
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
|
||||
dma_addr_t position;
|
||||
@ -763,7 +760,8 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
|
||||
*
|
||||
* This function can be called multiple times.
|
||||
*/
|
||||
static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
|
||||
static int fsl_dma_hw_free(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_dma_private *dma_private = runtime->private_data;
|
||||
@ -796,12 +794,11 @@ static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
|
||||
/**
|
||||
* fsl_dma_close: close the stream.
|
||||
*/
|
||||
static int fsl_dma_close(struct snd_pcm_substream *substream)
|
||||
static int fsl_dma_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct fsl_dma_private *dma_private = runtime->private_data;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
|
||||
struct device *dev = component->dev;
|
||||
struct dma_object *dma =
|
||||
container_of(component->driver, struct dma_object, dai);
|
||||
@ -824,7 +821,8 @@ static int fsl_dma_close(struct snd_pcm_substream *substream)
|
||||
/*
|
||||
* Remove this PCM driver.
|
||||
*/
|
||||
static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm)
|
||||
static void fsl_dma_free_dma_buffers(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_pcm_substream *substream;
|
||||
unsigned int i;
|
||||
@ -872,15 +870,6 @@ static struct device_node *find_ssi_node(struct device_node *dma_channel_np)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops fsl_dma_ops = {
|
||||
.open = fsl_dma_open,
|
||||
.close = fsl_dma_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = fsl_dma_hw_params,
|
||||
.hw_free = fsl_dma_hw_free,
|
||||
.pointer = fsl_dma_pointer,
|
||||
};
|
||||
|
||||
static int fsl_soc_dma_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dma_object *dma;
|
||||
@ -912,9 +901,14 @@ static int fsl_soc_dma_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
dma->dai.name = DRV_NAME;
|
||||
dma->dai.ops = &fsl_dma_ops;
|
||||
dma->dai.pcm_new = fsl_dma_new;
|
||||
dma->dai.pcm_free = fsl_dma_free_dma_buffers;
|
||||
dma->dai.open = fsl_dma_open;
|
||||
dma->dai.close = fsl_dma_close;
|
||||
dma->dai.ioctl = snd_soc_pcm_lib_ioctl;
|
||||
dma->dai.hw_params = fsl_dma_hw_params;
|
||||
dma->dai.hw_free = fsl_dma_hw_free;
|
||||
dma->dai.pointer = fsl_dma_pointer;
|
||||
dma->dai.pcm_construct = fsl_dma_new;
|
||||
dma->dai.pcm_destruct = fsl_dma_free_dma_buffers;
|
||||
|
||||
/* Store the SSI-specific information that we need */
|
||||
dma->ssi_stx_phys = res.start + REG_SSI_STX0;
|
||||
|
@ -33,6 +33,7 @@
|
||||
* @fsysclk: system clock source to derive HCK, SCK and FS
|
||||
* @spbaclk: SPBA clock (optional, depending on SoC design)
|
||||
* @task: tasklet to handle the reset operation
|
||||
* @lock: spin lock between hw_reset() and trigger()
|
||||
* @fifo_depth: depth of tx/rx FIFO
|
||||
* @slot_width: width of each DAI slot
|
||||
* @slots: number of slots
|
||||
@ -56,6 +57,7 @@ struct fsl_esai {
|
||||
struct clk *fsysclk;
|
||||
struct clk *spbaclk;
|
||||
struct tasklet_struct task;
|
||||
spinlock_t lock; /* Protect hw_reset and trigger */
|
||||
u32 fifo_depth;
|
||||
u32 slot_width;
|
||||
u32 slots;
|
||||
@ -676,8 +678,10 @@ static void fsl_esai_hw_reset(unsigned long arg)
|
||||
{
|
||||
struct fsl_esai *esai_priv = (struct fsl_esai *)arg;
|
||||
bool tx = true, rx = false, enabled[2];
|
||||
unsigned long lock_flags;
|
||||
u32 tfcr, rfcr;
|
||||
|
||||
spin_lock_irqsave(&esai_priv->lock, lock_flags);
|
||||
/* Save the registers */
|
||||
regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
|
||||
regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
|
||||
@ -715,6 +719,8 @@ static void fsl_esai_hw_reset(unsigned long arg)
|
||||
fsl_esai_trigger_start(esai_priv, tx);
|
||||
if (enabled[rx])
|
||||
fsl_esai_trigger_start(esai_priv, rx);
|
||||
|
||||
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
|
||||
}
|
||||
|
||||
static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
@ -722,6 +728,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
{
|
||||
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
|
||||
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
unsigned long lock_flags;
|
||||
|
||||
esai_priv->channels[tx] = substream->runtime->channels;
|
||||
|
||||
@ -729,12 +736,16 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
spin_lock_irqsave(&esai_priv->lock, lock_flags);
|
||||
fsl_esai_trigger_start(esai_priv, tx);
|
||||
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
spin_lock_irqsave(&esai_priv->lock, lock_flags);
|
||||
fsl_esai_trigger_stop(esai_priv, tx);
|
||||
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -1002,6 +1013,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
|
||||
|
||||
dev_set_drvdata(&pdev->dev, esai_priv);
|
||||
|
||||
spin_lock_init(&esai_priv->lock);
|
||||
ret = fsl_esai_hw_init(esai_priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
335
sound/soc/fsl/fsl_mqs.c
Normal file
335
sound/soc/fsl/fsl_mqs.c
Normal file
@ -0,0 +1,335 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// ALSA SoC IMX MQS driver
|
||||
//
|
||||
// Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
|
||||
// Copyright 2019 NXP
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#define REG_MQS_CTRL 0x00
|
||||
|
||||
#define MQS_EN_MASK (0x1 << 28)
|
||||
#define MQS_EN_SHIFT (28)
|
||||
#define MQS_SW_RST_MASK (0x1 << 24)
|
||||
#define MQS_SW_RST_SHIFT (24)
|
||||
#define MQS_OVERSAMPLE_MASK (0x1 << 20)
|
||||
#define MQS_OVERSAMPLE_SHIFT (20)
|
||||
#define MQS_CLK_DIV_MASK (0xFF << 0)
|
||||
#define MQS_CLK_DIV_SHIFT (0)
|
||||
|
||||
/* codec private data */
|
||||
struct fsl_mqs {
|
||||
struct regmap *regmap;
|
||||
struct clk *mclk;
|
||||
struct clk *ipg;
|
||||
|
||||
unsigned int reg_iomuxc_gpr2;
|
||||
unsigned int reg_mqs_ctrl;
|
||||
bool use_gpr;
|
||||
};
|
||||
|
||||
#define FSL_MQS_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
|
||||
#define FSL_MQS_FORMATS SNDRV_PCM_FMTBIT_S16_LE
|
||||
|
||||
static int fsl_mqs_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
|
||||
unsigned long mclk_rate;
|
||||
int div, res;
|
||||
int lrclk;
|
||||
|
||||
mclk_rate = clk_get_rate(mqs_priv->mclk);
|
||||
lrclk = params_rate(params);
|
||||
|
||||
/*
|
||||
* mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate;
|
||||
* if repeat_rate is 8, mqs can achieve better quality.
|
||||
* oversample rate is fix to 32 currently.
|
||||
*/
|
||||
div = mclk_rate / (32 * lrclk * 2 * 8);
|
||||
res = mclk_rate % (32 * lrclk * 2 * 8);
|
||||
|
||||
if (res == 0 && div > 0 && div <= 256) {
|
||||
if (mqs_priv->use_gpr) {
|
||||
regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
|
||||
IMX6SX_GPR2_MQS_CLK_DIV_MASK,
|
||||
(div - 1) << IMX6SX_GPR2_MQS_CLK_DIV_SHIFT);
|
||||
regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
|
||||
IMX6SX_GPR2_MQS_OVERSAMPLE_MASK, 0);
|
||||
} else {
|
||||
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
MQS_CLK_DIV_MASK,
|
||||
(div - 1) << MQS_CLK_DIV_SHIFT);
|
||||
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
MQS_OVERSAMPLE_MASK, 0);
|
||||
}
|
||||
} else {
|
||||
dev_err(component->dev, "can't get proper divider\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
/* Only LEFT_J & SLAVE mode is supported. */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_mqs_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (mqs_priv->use_gpr)
|
||||
regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
|
||||
IMX6SX_GPR2_MQS_EN_MASK,
|
||||
1 << IMX6SX_GPR2_MQS_EN_SHIFT);
|
||||
else
|
||||
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
MQS_EN_MASK,
|
||||
1 << MQS_EN_SHIFT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_mqs_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (mqs_priv->use_gpr)
|
||||
regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
|
||||
IMX6SX_GPR2_MQS_EN_MASK, 0);
|
||||
else
|
||||
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
MQS_EN_MASK, 0);
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver soc_codec_fsl_mqs = {
|
||||
.idle_bias_on = 1,
|
||||
.non_legacy_dai_naming = 1,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops fsl_mqs_dai_ops = {
|
||||
.startup = fsl_mqs_startup,
|
||||
.shutdown = fsl_mqs_shutdown,
|
||||
.hw_params = fsl_mqs_hw_params,
|
||||
.set_fmt = fsl_mqs_set_dai_fmt,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver fsl_mqs_dai = {
|
||||
.name = "fsl-mqs-dai",
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = FSL_MQS_RATES,
|
||||
.formats = FSL_MQS_FORMATS,
|
||||
},
|
||||
.ops = &fsl_mqs_dai_ops,
|
||||
};
|
||||
|
||||
static const struct regmap_config fsl_mqs_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = REG_MQS_CTRL,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static int fsl_mqs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *gpr_np = NULL;
|
||||
struct fsl_mqs *mqs_priv;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
mqs_priv = devm_kzalloc(&pdev->dev, sizeof(*mqs_priv), GFP_KERNEL);
|
||||
if (!mqs_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* On i.MX6sx the MQS control register is in GPR domain
|
||||
* But in i.MX8QM/i.MX8QXP the control register is moved
|
||||
* to its own domain.
|
||||
*/
|
||||
if (of_device_is_compatible(np, "fsl,imx8qm-mqs"))
|
||||
mqs_priv->use_gpr = false;
|
||||
else
|
||||
mqs_priv->use_gpr = true;
|
||||
|
||||
if (mqs_priv->use_gpr) {
|
||||
gpr_np = of_parse_phandle(np, "gpr", 0);
|
||||
if (!gpr_np) {
|
||||
dev_err(&pdev->dev, "failed to get gpr node by phandle\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mqs_priv->regmap = syscon_node_to_regmap(gpr_np);
|
||||
if (IS_ERR(mqs_priv->regmap)) {
|
||||
dev_err(&pdev->dev, "failed to get gpr regmap\n");
|
||||
ret = PTR_ERR(mqs_priv->regmap);
|
||||
goto err_free_gpr_np;
|
||||
}
|
||||
} else {
|
||||
regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
mqs_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
|
||||
"core",
|
||||
regs,
|
||||
&fsl_mqs_regmap_config);
|
||||
if (IS_ERR(mqs_priv->regmap)) {
|
||||
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
|
||||
PTR_ERR(mqs_priv->regmap));
|
||||
return PTR_ERR(mqs_priv->regmap);
|
||||
}
|
||||
|
||||
mqs_priv->ipg = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(mqs_priv->ipg)) {
|
||||
dev_err(&pdev->dev, "failed to get the clock: %ld\n",
|
||||
PTR_ERR(mqs_priv->ipg));
|
||||
return PTR_ERR(mqs_priv->ipg);
|
||||
}
|
||||
}
|
||||
|
||||
mqs_priv->mclk = devm_clk_get(&pdev->dev, "mclk");
|
||||
if (IS_ERR(mqs_priv->mclk)) {
|
||||
dev_err(&pdev->dev, "failed to get the clock: %ld\n",
|
||||
PTR_ERR(mqs_priv->mclk));
|
||||
ret = PTR_ERR(mqs_priv->mclk);
|
||||
goto err_free_gpr_np;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, mqs_priv);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_fsl_mqs,
|
||||
&fsl_mqs_dai, 1);
|
||||
if (ret)
|
||||
goto err_free_gpr_np;
|
||||
return 0;
|
||||
|
||||
err_free_gpr_np:
|
||||
of_node_put(gpr_np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_mqs_remove(struct platform_device *pdev)
|
||||
{
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_mqs_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
|
||||
|
||||
if (mqs_priv->ipg)
|
||||
clk_prepare_enable(mqs_priv->ipg);
|
||||
|
||||
if (mqs_priv->mclk)
|
||||
clk_prepare_enable(mqs_priv->mclk);
|
||||
|
||||
if (mqs_priv->use_gpr)
|
||||
regmap_write(mqs_priv->regmap, IOMUXC_GPR2,
|
||||
mqs_priv->reg_iomuxc_gpr2);
|
||||
else
|
||||
regmap_write(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
mqs_priv->reg_mqs_ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_mqs_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
|
||||
|
||||
if (mqs_priv->use_gpr)
|
||||
regmap_read(mqs_priv->regmap, IOMUXC_GPR2,
|
||||
&mqs_priv->reg_iomuxc_gpr2);
|
||||
else
|
||||
regmap_read(mqs_priv->regmap, REG_MQS_CTRL,
|
||||
&mqs_priv->reg_mqs_ctrl);
|
||||
|
||||
if (mqs_priv->mclk)
|
||||
clk_disable_unprepare(mqs_priv->mclk);
|
||||
|
||||
if (mqs_priv->ipg)
|
||||
clk_disable_unprepare(mqs_priv->ipg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops fsl_mqs_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(fsl_mqs_runtime_suspend,
|
||||
fsl_mqs_runtime_resume,
|
||||
NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_mqs_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8qm-mqs", },
|
||||
{ .compatible = "fsl,imx6sx-mqs", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids);
|
||||
|
||||
static struct platform_driver fsl_mqs_driver = {
|
||||
.probe = fsl_mqs_probe,
|
||||
.remove = fsl_mqs_remove,
|
||||
.driver = {
|
||||
.name = "fsl-mqs",
|
||||
.of_match_table = fsl_mqs_dt_ids,
|
||||
.pm = &fsl_mqs_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(fsl_mqs_driver);
|
||||
|
||||
MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
|
||||
MODULE_DESCRIPTION("MQS codec driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform: fsl-mqs");
|
@ -69,8 +69,9 @@ static struct fiq_handler fh = {
|
||||
.name = DRV_NAME,
|
||||
};
|
||||
|
||||
static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
static int snd_imx_pcm_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
|
||||
@ -85,7 +86,8 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
static int snd_imx_pcm_prepare(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
|
||||
@ -104,7 +106,8 @@ static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
|
||||
static int imx_pcm_fiq;
|
||||
|
||||
static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int snd_imx_pcm_trigger(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
|
||||
@ -141,7 +144,9 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
static snd_pcm_uframes_t
|
||||
snd_imx_pcm_pointer(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
|
||||
@ -165,7 +170,8 @@ static const struct snd_pcm_hardware snd_imx_hardware = {
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static int snd_imx_open(struct snd_pcm_substream *substream)
|
||||
static int snd_imx_open(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct imx_pcm_runtime_data *iprtd;
|
||||
@ -194,7 +200,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_imx_close(struct snd_pcm_substream *substream)
|
||||
static int snd_imx_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
|
||||
@ -206,8 +213,9 @@ static int snd_imx_close(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
static int snd_imx_pcm_mmap(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int ret;
|
||||
@ -222,17 +230,6 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_pcm_ops imx_pcm_ops = {
|
||||
.open = snd_imx_open,
|
||||
.close = snd_imx_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_imx_pcm_hw_params,
|
||||
.prepare = snd_imx_pcm_prepare,
|
||||
.trigger = snd_imx_pcm_trigger,
|
||||
.pointer = snd_imx_pcm_pointer,
|
||||
.mmap = snd_imx_pcm_mmap,
|
||||
};
|
||||
|
||||
static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
||||
{
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
@ -279,7 +276,8 @@ static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
|
||||
static int ssi_irq;
|
||||
|
||||
static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
|
||||
static int snd_imx_pcm_new(struct snd_soc_component *component,
|
||||
struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
struct snd_pcm_substream *substream;
|
||||
@ -329,7 +327,8 @@ static void imx_pcm_free(struct snd_pcm *pcm)
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_pcm_fiq_free(struct snd_pcm *pcm)
|
||||
static void snd_imx_pcm_free(struct snd_soc_component *component,
|
||||
struct snd_pcm *pcm)
|
||||
{
|
||||
mxc_set_irq_fiq(ssi_irq, 0);
|
||||
release_fiq(&fh);
|
||||
@ -337,9 +336,16 @@ static void imx_pcm_fiq_free(struct snd_pcm *pcm)
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver imx_soc_component_fiq = {
|
||||
.ops = &imx_pcm_ops,
|
||||
.pcm_new = imx_pcm_fiq_new,
|
||||
.pcm_free = imx_pcm_fiq_free,
|
||||
.open = snd_imx_open,
|
||||
.close = snd_imx_close,
|
||||
.ioctl = snd_soc_pcm_lib_ioctl,
|
||||
.hw_params = snd_imx_pcm_hw_params,
|
||||
.prepare = snd_imx_pcm_prepare,
|
||||
.trigger = snd_imx_pcm_trigger,
|
||||
.pointer = snd_imx_pcm_pointer,
|
||||
.mmap = snd_imx_pcm_mmap,
|
||||
.pcm_construct = snd_imx_pcm_new,
|
||||
.pcm_destruct = snd_imx_pcm_free,
|
||||
};
|
||||
|
||||
int imx_pcm_fiq_init(struct platform_device *pdev,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user