Merge branch 'asoc-5.5' into asoc-next

This commit is contained in:
Mark Brown 2019-11-22 19:56:02 +00:00
commit 8c4d2a0bfb
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
228 changed files with 12500 additions and 3812 deletions

View 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>;
};

View File

@ -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";
};
...

View File

@ -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>;
};
...

View File

@ -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

View 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";
};

View File

@ -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>;
};
};

View File

@ -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>,

View File

@ -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>;
};

View File

@ -1,31 +0,0 @@
Renesas FSI
Required properties:
- compatible : "renesas,fsi2-<soctype>",
"renesas,sh_fsi2" or "renesas,sh_fsi" as
fallback.
Examples with soctypes are:
- "renesas,fsi2-r8a7740" (R-Mobile A1)
- "renesas,fsi2-sh73a0" (SH-Mobile AG5)
- reg : Should contain the register physical address and length
- interrupts : Should contain FSI interrupt
- fsia,spdif-connection : FSI is connected by S/PDIF
- fsia,stream-mode-support : FSI supports 16bit stream mode.
- fsia,use-internal-clock : FSI uses internal clock when master mode.
- fsib,spdif-connection : same as fsia
- fsib,stream-mode-support : same as fsia
- fsib,use-internal-clock : same as fsia
Example:
sh_fsi2: sh_fsi2@ec230000 {
compatible = "renesas,sh_fsi2";
reg = <0xec230000 0x400>;
interrupts = <0 146 0x4>;
fsia,spdif-connection;
fsia,stream-mode-support;
fsia,use-internal-clock;
};

View File

@ -0,0 +1,76 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/renesas,fsi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas FSI Sound Driver Device Tree Bindings
maintainers:
- Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
properties:
$nodename:
pattern: "^sound@.*"
compatible:
oneOf:
# for FSI2 SoC
- items:
- enum:
- renesas,fsi2-sh73a0
- renesas,fsi2-r8a7740
- enum:
- renesas,sh_fsi2
# for Generic
- items:
- enum:
- renesas,sh_fsi
- renesas,sh_fsi2
reg:
maxItems: 1
interrupts:
maxItems: 1
fsia,spdif-connection:
$ref: /schemas/types.yaml#/definitions/flag
description: FSI is connected by S/PDIF
fsia,stream-mode-support:
$ref: /schemas/types.yaml#/definitions/flag
description: FSI supports 16bit stream mode
fsia,use-internal-clock:
$ref: /schemas/types.yaml#/definitions/flag
description: FSI uses internal clock when master mode
fsib,spdif-connection:
$ref: /schemas/types.yaml#/definitions/flag
description: same as fsia
fsib,stream-mode-support:
$ref: /schemas/types.yaml#/definitions/flag
description: same as fsia
fsib,use-internal-clock:
$ref: /schemas/types.yaml#/definitions/flag
description: same as fsia
required:
- compatible
- reg
- interrupts
examples:
- |
sh_fsi2: sound@ec230000 {
compatible = "renesas,fsi2-r8a7740", "renesas,sh_fsi2";
reg = <0xec230000 0x400>;
interrupts = <0 146 0x4>;
fsia,spdif-connection;
fsia,stream-mode-support;
fsia,use-internal-clock;
};

View File

@ -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)

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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>;
};
};

View 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>;
};
};

View File

@ -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>;
};

View 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>;
};

View File

@ -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";
};

View File

@ -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";
};
};

View 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>;
};

View 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>;
};

View File

@ -25,6 +25,13 @@ Required properties:
For required properties on SPI/I2C, consult SPI/I2C device tree documentation
Optional properties:
- reset-gpios : Optional reset gpio line connected to RST pin of the codec.
The RST line is low active:
RST = low: device power-down
RST = high: device is enabled
Examples:
i2c0: i2c0@0 {
@ -34,6 +41,7 @@ i2c0: i2c0@0 {
pcm3168a: audio-codec@44 {
compatible = "ti,pcm3168a";
reg = <0x44>;
reset-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
clocks = <&clk_core CLK_AUDIO>;
clock-names = "scki";
VDD1-supply = <&supply3v3>;

View File

@ -29,6 +29,11 @@ Optional properties:
3 or MICBIAS_AVDD - MICBIAS output is connected to AVDD
If this node is not mentioned or if the value is unknown, then
micbias is set to 2.0V.
- ai31xx-ocmv - output common-mode voltage setting
0 - 1.35V,
1 - 1.5V,
2 - 1.65V,
3 - 1.8V
Deprecated properties:

View File

@ -16,7 +16,7 @@ properties: {}
patternProperties:
# Prefixes which are not vendors, but followed the pattern
# DO NOT ADD NEW PROPERTIES TO THIS LIST
"^(at25|devbus|dmacap|dsa|exynos|gpio-fan|gpio|gpmc|hdmi|i2c-gpio),.*": true
"^(at25|devbus|dmacap|dsa|exynos|fsi[ab]|gpio-fan|gpio|gpmc|hdmi|i2c-gpio),.*": true
"^(keypad|m25p|max8952|max8997|max8998|mpmc),.*": true
"^(pinctrl-single|#pinctrl-single|PowerPC),.*": true
"^(pl022|pxa-mmc|rcar_sound|rotary-encoder|s5m8767|sdhci),.*": true

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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), \

View File

@ -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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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) \

View File

@ -24,9 +24,12 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cfl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_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

View File

@ -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;

View File

@ -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 */
@ -381,7 +412,7 @@ struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream,
unsigned long offset);
int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma);
int snd_soc_pcm_component_new(struct snd_pcm *pcm);
void snd_soc_pcm_component_free(struct snd_pcm *pcm);
int snd_soc_pcm_component_new(struct snd_soc_pcm_runtime *rtd);
void snd_soc_pcm_component_free(struct snd_soc_pcm_runtime *rtd);
#endif /* __SOC_COMPONENT_H */

View File

@ -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,

View File

@ -299,6 +299,12 @@
.put = snd_soc_bytes_put, .private_value = \
((unsigned long)&(struct soc_bytes) \
{.base = xbase, .num_regs = xregs }) }
#define SND_SOC_BYTES_E(xname, xbase, xregs, xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_bytes_info, .get = xhandler_get, \
.put = xhandler_put, .private_value = \
((unsigned long)&(struct soc_bytes) \
{.base = xbase, .num_regs = xregs }) }
#define SND_SOC_BYTES_MASK(xname, xbase, xregs, xmask) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@ -739,10 +745,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 +853,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; \
@ -978,6 +988,7 @@ struct snd_soc_card {
const char *name;
const char *long_name;
const char *driver_name;
const char *components;
char dmi_longname[80];
char topology_shortname[32];
@ -1148,7 +1159,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 +1178,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 +1191,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 +1217,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 +1340,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 +1408,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

View File

@ -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;

View 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

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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/

View File

@ -317,12 +317,22 @@ struct snd_enc_generic {
__s32 reserved[15]; /* Can be used for SND_AUDIOCODEC_BESPOKE */
} __attribute__((packed, aligned(4)));
struct snd_dec_flac {
__u16 sample_size;
__u16 min_blk_size;
__u16 max_blk_size;
__u16 min_frame_size;
__u16 max_frame_size;
__u16 reserved;
} __attribute__((packed, aligned(4)));
union snd_codec_options {
struct snd_enc_wma wma;
struct snd_enc_vorbis vorbis;
struct snd_enc_real real;
struct snd_enc_flac flac;
struct snd_enc_generic generic;
struct snd_dec_flac flac_d;
} __attribute__((packed, aligned(4)));
/** struct snd_codec_desc - description of codec capabilities

View File

@ -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 */

View File

@ -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

View File

@ -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");

View File

@ -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");

View File

@ -2075,15 +2075,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;

View File

@ -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)

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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.
@ -646,7 +679,8 @@ config SND_SOC_DA7210
tristate
config SND_SOC_DA7213
tristate
tristate "Dialog DA7213 CODEC"
depends on I2C
config SND_SOC_DA7218
tristate
@ -1104,6 +1138,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

View File

@ -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

View File

@ -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;

View 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");

View 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
View 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");

View 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

View File

@ -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);

View File

@ -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.

View File

@ -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) \

View File

@ -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

View File

@ -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)
@ -312,6 +316,7 @@ 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);
@ -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,

View File

@ -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 */

View File

@ -9,7 +9,9 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@ -59,9 +61,11 @@ struct pcm3168a_priv {
struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
struct regmap *regmap;
struct clk *scki;
struct gpio_desc *gpio_rst;
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 +318,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 +408,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 +443,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 +566,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,
@ -666,6 +646,7 @@ static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case PCM3168A_RST_SMODE:
case PCM3168A_DAC_ZERO:
case PCM3168A_ADC_OV:
return true;
@ -725,6 +706,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, pcm3168a);
/*
* Request the reset (connected to RST pin) gpio line as non exclusive
* as the same reset line might be connected to multiple pcm3168a codec
*
* The RST is low active, we want the GPIO line to be high initially, so
* request the initial level to LOW which in practice means DEASSERTED:
* The deasserted level of GPIO_ACTIVE_LOW is HIGH.
*/
pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW |
GPIOD_FLAGS_BIT_NONEXCLUSIVE);
if (IS_ERR(pcm3168a->gpio_rst)) {
ret = PTR_ERR(pcm3168a->gpio_rst);
if (ret != -EPROBE_DEFER )
dev_err(dev, "failed to acquire RST gpio: %d\n", ret);
return ret;
}
pcm3168a->scki = devm_clk_get(dev, "scki");
if (IS_ERR(pcm3168a->scki)) {
ret = PTR_ERR(pcm3168a->scki);
@ -766,18 +766,28 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
goto err_regulator;
}
ret = pcm3168a_reset(pcm3168a);
if (ret) {
dev_err(dev, "Failed to reset device: %d\n", ret);
goto err_regulator;
if (pcm3168a->gpio_rst) {
/*
* The device is taken out from reset via GPIO line, wait for
* 3846 SCKI clock cycles for the internal reset de-assertion
*/
msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
} else {
ret = pcm3168a_reset(pcm3168a);
if (ret) {
dev_err(dev, "Failed to reset device: %d\n", ret);
goto err_regulator;
}
}
pm_runtime_set_active(dev);
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;
@ -806,6 +816,15 @@ static void pcm3168a_disable(struct device *dev)
void pcm3168a_remove(struct device *dev)
{
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
/*
* The RST is low active, we want the GPIO line to be low when the
* driver is removed, so set level to 1 which in practice means
* ASSERTED:
* The asserted level of GPIO_ACTIVE_LOW is LOW.
*/
gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1);
pm_runtime_disable(dev);
#ifndef CONFIG_PM
pcm3168a_disable(dev);

View File

@ -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",

View File

@ -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;
};

View File

@ -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,
};
/**

View File

@ -3639,6 +3639,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",
@ -3745,6 +3751,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,
},
{ }
};

View File

@ -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__);
}

View File

@ -24,6 +24,9 @@
#include <linux/firmware.h>
#include <linux/acpi.h>
#include <sound/soc.h>
#include "rt5677.h"
#include "rt5677-spi.h"
#define DRV_NAME "rt5677spi"
@ -45,9 +48,368 @@
#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 snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_component *codec_component =
snd_soc_rtdcom_lookup(rtd, "rt5677");
struct rt5677_priv *rt5677 =
snd_soc_component_get_drvdata(codec_component);
struct rt5677_dsp *rt5677_dsp =
snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt5677_dsp->copy_work);
rt5677->set_dsp_vad(codec_component, false);
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 snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_component *rt5677_component =
snd_soc_rtdcom_lookup(rtd, "rt5677");
struct rt5677_priv *rt5677 =
snd_soc_component_get_drvdata(rt5677_component);
struct rt5677_dsp *rt5677_dsp =
snd_soc_component_get_drvdata(component);
rt5677->set_dsp_vad(rt5677_component, true);
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 +580,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 +634,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);

View File

@ -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__ */

View File

@ -38,6 +38,10 @@
#define RT5677_DEVICE_ID 0x6327
/* Register controlling boot vector */
#define RT5677_DSP_BOOT_VECTOR 0x1801f090
#define RT5677_MODEL_ADDR 0x5FFC9800
#define RT5677_PR_RANGE_BASE (0xff + 1)
#define RT5677_PR_SPACING 0x100
@ -298,6 +302,7 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_I2C_MASTER_CTRL7:
case RT5677_I2C_MASTER_CTRL8:
case RT5677_HAP_GENE_CTRL2:
case RT5677_PWR_ANLG2: /* Modified by DSP firmware */
case RT5677_PWR_DSP_ST:
case RT5677_PRIV_DATA:
case RT5677_ASRC_22:
@ -308,6 +313,8 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_IRQ_CTRL1:
case RT5677_IRQ_CTRL2:
case RT5677_GPIO_ST:
case RT5677_GPIO_CTRL1: /* Modified by DSP firmware */
case RT5677_GPIO_CTRL2: /* Modified by DSP firmware */
case RT5677_DSP_INB1_SRC_CTRL4:
case RT5677_DSP_INB2_SRC_CTRL4:
case RT5677_DSP_INB3_SRC_CTRL4:
@ -686,10 +693,8 @@ static int rt5677_dsp_mode_i2c_read(
return ret;
}
static void rt5677_set_dsp_mode(struct snd_soc_component *component, bool on)
static void rt5677_set_dsp_mode(struct rt5677_priv *rt5677, bool on)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
if (on) {
regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
RT5677_PWR_DSP, RT5677_PWR_DSP);
@ -701,86 +706,259 @@ static void rt5677_set_dsp_mode(struct snd_soc_component *component, bool on)
}
}
static unsigned int rt5677_set_vad_source(struct rt5677_priv *rt5677)
{
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(rt5677->component);
/* Force dapm to sync before we enable the
* DSP to prevent write corruption
*/
snd_soc_dapm_sync(dapm);
/* DMIC1 power = enabled
* DMIC CLK = 256 * fs / 12
*/
regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1,
RT5677_DMIC_CLK_MASK, 5 << RT5677_DMIC_CLK_SFT);
/* I2S pre divide 2 = /6 (clk_sys2) */
regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
RT5677_I2S_PD2_MASK, RT5677_I2S_PD2_6);
/* DSP Clock = MCLK1 (bypassed PLL2) */
regmap_write(rt5677->regmap, RT5677_GLB_CLK2,
RT5677_DSP_CLK_SRC_BYPASS);
/* SAD Threshold1 */
regmap_write(rt5677->regmap, RT5677_VAD_CTRL2, 0x013f);
/* SAD Threshold2 */
regmap_write(rt5677->regmap, RT5677_VAD_CTRL3, 0x0ae5);
/* SAD Sample Rate Converter = Up 6 (8K to 48K)
* SAD Output Sample Rate = Same as I2S
* SAD Threshold3
*/
regmap_update_bits(rt5677->regmap, RT5677_VAD_CTRL4,
RT5677_VAD_OUT_SRC_RATE_MASK | RT5677_VAD_OUT_SRC_MASK |
RT5677_VAD_LV_DIFF_MASK, 0x7f << RT5677_VAD_LV_DIFF_SFT);
/* Minimum frame level within a pre-determined duration = 32 frames
* Bypass ADPCM Encoder/Decoder = Bypass ADPCM
* Automatic Push Data to SAD Buffer Once SAD Flag is triggered = enable
* SAD Buffer Over-Writing = enable
* SAD Buffer Pop Mode Control = disable
* SAD Buffer Push Mode Control = enable
* SAD Detector Control = enable
* SAD Function Control = enable
* SAD Function Reset = normal
*/
regmap_write(rt5677->regmap, RT5677_VAD_CTRL1,
RT5677_VAD_FUNC_RESET | RT5677_VAD_FUNC_ENABLE |
RT5677_VAD_DET_ENABLE | RT5677_VAD_BUF_PUSH |
RT5677_VAD_BUF_OW | RT5677_VAD_FG2ENC |
RT5677_VAD_ADPCM_BYPASS | 1 << RT5677_VAD_MIN_DUR_SFT);
/* VAD/SAD is not routed to the IRQ output (i.e. MX-BE[14] = 0), but it
* is routed to DSP_IRQ_0, so DSP firmware may use it to sleep and save
* power. See ALC5677 datasheet section 9.17 "GPIO, Interrupt and Jack
* Detection" for more info.
*/
/* Private register, no doc */
regmap_update_bits(rt5677->regmap, RT5677_PR_BASE + RT5677_BIAS_CUR4,
0x0f00, 0x0100);
/* LDO2 output = 1.2V
* LDO1 output = 1.2V (LDO_IN = 1.8V)
*/
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
5 << RT5677_LDO1_SEL_SFT | 5 << RT5677_LDO2_SEL_SFT);
/* Codec core power = power on
* LDO1 power = power on
*/
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
RT5677_PWR_CORE | RT5677_PWR_LDO1,
RT5677_PWR_CORE | RT5677_PWR_LDO1);
/* Isolation for DCVDD4 = normal (set during probe)
* Isolation for DCVDD2 = normal (set during probe)
* Isolation for DSP = normal
* Isolation for Band 0~7 = disable
* Isolation for InBound 4~10 and OutBound 4~10 = disable
*/
regmap_write(rt5677->regmap, RT5677_PWR_DSP2,
RT5677_PWR_CORE_ISO | RT5677_PWR_DSP_ISO |
RT5677_PWR_SR7_ISO | RT5677_PWR_SR6_ISO |
RT5677_PWR_SR5_ISO | RT5677_PWR_SR4_ISO |
RT5677_PWR_SR3_ISO | RT5677_PWR_SR2_ISO |
RT5677_PWR_SR1_ISO | RT5677_PWR_SR0_ISO |
RT5677_PWR_MLT_ISO);
/* System Band 0~7 = power on
* InBound 4~10 and OutBound 4~10 = power on
* DSP = power on
* DSP CPU = stop (will be set to "run" after firmware loaded)
*/
regmap_write(rt5677->regmap, RT5677_PWR_DSP1,
RT5677_PWR_SR7 | RT5677_PWR_SR6 |
RT5677_PWR_SR5 | RT5677_PWR_SR4 |
RT5677_PWR_SR3 | RT5677_PWR_SR2 |
RT5677_PWR_SR1 | RT5677_PWR_SR0 |
RT5677_PWR_MLT | RT5677_PWR_DSP |
RT5677_PWR_DSP_CPU);
return 0;
}
static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf,
unsigned int len)
{
struct snd_soc_component *component = rt5677->component;
Elf32_Ehdr *elf_hdr;
Elf32_Phdr *pr_hdr;
Elf32_Half i;
int ret = 0;
if (!buf || (len < sizeof(Elf32_Ehdr)))
return -ENOMEM;
elf_hdr = (Elf32_Ehdr *)buf;
#ifndef EM_XTENSA
#define EM_XTENSA 94
#endif
if (strncmp(elf_hdr->e_ident, ELFMAG, sizeof(ELFMAG) - 1))
dev_err(component->dev, "Wrong ELF header prefix\n");
if (elf_hdr->e_ehsize != sizeof(Elf32_Ehdr))
dev_err(component->dev, "Wrong Elf header size\n");
if (elf_hdr->e_machine != EM_XTENSA)
dev_err(component->dev, "Wrong DSP code file\n");
if (len < elf_hdr->e_phoff)
return -ENOMEM;
pr_hdr = (Elf32_Phdr *)(buf + elf_hdr->e_phoff);
for (i = 0; i < elf_hdr->e_phnum; i++) {
/* TODO: handle p_memsz != p_filesz */
if (pr_hdr->p_paddr && pr_hdr->p_filesz) {
dev_info(component->dev, "Load 0x%x bytes to 0x%x\n",
pr_hdr->p_filesz, pr_hdr->p_paddr);
ret = rt5677_spi_write(pr_hdr->p_paddr,
buf + pr_hdr->p_offset,
pr_hdr->p_filesz);
if (ret)
dev_err(component->dev, "Load firmware failed %d\n",
ret);
}
pr_hdr++;
}
return ret;
}
static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677)
{
const struct firmware *fwp;
struct device *dev = rt5677->component->dev;
int ret = 0;
/* Load dsp firmware from rt5677_elf_vad file */
ret = request_firmware(&fwp, "rt5677_elf_vad", dev);
if (ret) {
dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret);
return ret;
}
dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size);
ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size);
release_firmware(fwp);
return ret;
}
static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
static bool activity;
int ret;
rt5677->dsp_vad_en_request = on;
rt5677->dsp_vad_en = on;
if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI))
return -ENXIO;
if (on && !activity) {
schedule_delayed_work(&rt5677->dsp_work, 0);
return 0;
}
static void rt5677_dsp_work(struct work_struct *work)
{
struct rt5677_priv *rt5677 =
container_of(work, struct rt5677_priv, dsp_work.work);
static bool activity;
bool enable = rt5677->dsp_vad_en;
int i, val;
dev_info(rt5677->component->dev, "DSP VAD: enable=%d, activity=%d\n",
enable, activity);
if (enable && !activity) {
activity = true;
regcache_cache_only(rt5677->regmap, false);
regcache_cache_bypass(rt5677->regmap, true);
/* Before a hotword is detected, GPIO1 pin is configured as IRQ
* output so that jack detect works. When a hotword is detected,
* the DSP firmware configures the GPIO1 pin as GPIO1 and
* drives a 1. rt5677_irq() is called after a rising edge on
* the GPIO1 pin, due to either jack detect event or hotword
* event, or both. All possible events are checked and handled
* in rt5677_irq() where GPIO1 pin is configured back to IRQ
* output if a hotword is detected.
*/
regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1);
regmap_update_bits(rt5677->regmap,
RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00);
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
RT5677_LDO1_SEL_MASK, 0x0);
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
RT5677_PWR_LDO1, RT5677_PWR_LDO1);
switch (rt5677->type) {
case RT5677:
regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
RT5677_PLL2_PR_SRC_MASK |
RT5677_DSP_CLK_SRC_MASK,
RT5677_PLL2_PR_SRC_MCLK2 |
RT5677_DSP_CLK_SRC_BYPASS);
break;
case RT5676:
regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
RT5677_DSP_CLK_SRC_MASK,
RT5677_DSP_CLK_SRC_BYPASS);
break;
default:
break;
rt5677_set_vad_source(rt5677);
rt5677_set_dsp_mode(rt5677, true);
#define RT5677_BOOT_RETRY 20
for (i = 0; i < RT5677_BOOT_RETRY; i++) {
regmap_read(rt5677->regmap, RT5677_PWR_DSP_ST, &val);
if (val == 0x3ff)
break;
udelay(500);
}
regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd);
rt5677_set_dsp_mode(component, true);
ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1,
component->dev);
if (ret == 0) {
rt5677_spi_write_firmware(0x50000000, rt5677->fw1);
release_firmware(rt5677->fw1);
if (i == RT5677_BOOT_RETRY && val != 0x3ff) {
dev_err(rt5677->component->dev, "DSP Boot Timed Out!");
return;
}
ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
component->dev);
if (ret == 0) {
rt5677_spi_write_firmware(0x60000000, rt5677->fw2);
release_firmware(rt5677->fw2);
}
/* Boot the firmware from IRAM instead of SRAM0. */
rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
0x0009, 0x0003);
rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
0x0019, 0x0003);
rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
0x0009, 0x0003);
regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
rt5677_load_dsp_from_file(rt5677);
regcache_cache_bypass(rt5677->regmap, false);
regcache_cache_only(rt5677->regmap, true);
} else if (!on && activity) {
/* Set DSP CPU to Run */
regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
RT5677_PWR_DSP_CPU, 0x0);
} else if (!enable && activity) {
activity = false;
regcache_cache_only(rt5677->regmap, false);
regcache_cache_bypass(rt5677->regmap, true);
/* Don't turn off the DSP while handling irqs */
mutex_lock(&rt5677->irq_lock);
/* Set DSP CPU to Stop */
regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU);
regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
rt5677_set_dsp_mode(component, false);
regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
rt5677_set_dsp_mode(rt5677, false);
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
/* Disable and clear VAD interrupt */
regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184);
regcache_cache_bypass(rt5677->regmap, false);
regcache_mark_dirty(rt5677->regmap);
regcache_sync(rt5677->regmap);
/* Set GPIO1 pin back to be IRQ output for jack detect */
regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
mutex_unlock(&rt5677->irq_lock);
}
return 0;
}
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
@ -805,7 +983,7 @@ static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = rt5677->dsp_vad_en;
ucontrol->value.integer.value[0] = rt5677->dsp_vad_en_request;
return 0;
}
@ -814,12 +992,8 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
rt5677_set_dsp_vad(component, rt5677->dsp_vad_en);
rt5677_set_dsp_vad(component, !!ucontrol->value.integer.value[0]);
return 0;
}
@ -3010,6 +3184,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SLBRX", "SLIMBus Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("SLBTX", "SLIMBus Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("DSPTX", "DSP Buffer", 0, SND_SOC_NOPM, 0, 0),
/* Sidetone Mux */
SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
@ -3544,11 +3719,24 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "SLBTX", NULL, "SLB ADC3 Mux" },
{ "SLBTX", NULL, "SLB ADC4 Mux" },
{ "DSPTX", NULL, "IB01 Bypass Mux" },
{ "IB01 Mux", "IF1 DAC 01", "IF1 DAC01" },
{ "IB01 Mux", "IF2 DAC 01", "IF2 DAC01" },
{ "IB01 Mux", "SLB DAC 01", "SLB DAC01" },
{ "IB01 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
{ "IB01 Mux", "VAD ADC/DAC1 FS", "DAC1 FS" },
/* The IB01 Mux controls the source for InBound0 and InBound1.
* When the mux option "VAD ADC/DAC1 FS" is selected, "VAD ADC" goes to
* InBound0 and "DAC1 FS" goes to InBound1. "VAD ADC" is used for
* hotwording. "DAC1 FS" is not used currently.
*
* Creating a common widget node for "VAD ADC" + "DAC1 FS" and
* connecting the common widget to IB01 Mux causes the issue where
* there is an active path going from system playback -> "DAC1 FS" ->
* IB01 Mux -> DSP Buffer -> hotword stream. This wrong path confuses
* DAPM. Therefore "DAC1 FS" is ignored for now.
*/
{ "IB01 Mux", "VAD ADC/DAC1 FS", "VAD ADC Mux" },
{ "IB01 Bypass Mux", "Bypass", "IB01 Mux" },
{ "IB01 Bypass Mux", "Pass SRC", "IB01 Mux" },
@ -4457,14 +4645,15 @@ static int rt5677_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
enum snd_soc_bias_level prev_bias =
snd_soc_component_get_bias_level(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) {
rt5677_set_dsp_vad(component, false);
if (prev_bias == SND_SOC_BIAS_STANDBY) {
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
@ -4488,9 +4677,25 @@ static int rt5677_set_bias_level(struct snd_soc_component *component,
break;
case SND_SOC_BIAS_STANDBY:
if (prev_bias == SND_SOC_BIAS_OFF &&
rt5677->dsp_vad_en_request) {
/* Re-enable the DSP if it was turned off at suspend */
rt5677->dsp_vad_en = true;
/* The delay is to wait for MCLK */
schedule_delayed_work(&rt5677->dsp_work,
msecs_to_jiffies(1000));
}
break;
case SND_SOC_BIAS_OFF:
flush_delayed_work(&rt5677->dsp_work);
if (rt5677->is_dsp_mode) {
/* Turn off the DSP before suspend */
rt5677->dsp_vad_en = false;
schedule_delayed_work(&rt5677->dsp_work, 0);
flush_delayed_work(&rt5677->dsp_work);
}
regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x0);
regmap_write(rt5677->regmap, RT5677_PWR_DIG1, 0x0000);
regmap_write(rt5677->regmap, RT5677_PWR_ANLG1,
@ -4740,6 +4945,8 @@ static void rt5677_remove(struct snd_soc_component *component)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt5677->dsp_work);
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
@ -4750,6 +4957,11 @@ static int rt5677_suspend(struct snd_soc_component *component)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
if (rt5677->irq) {
cancel_delayed_work_sync(&rt5677->resume_irq_check);
disable_irq(rt5677->irq);
}
if (!rt5677->dsp_vad_en) {
regcache_cache_only(rt5677->regmap, true);
regcache_mark_dirty(rt5677->regmap);
@ -4778,6 +4990,11 @@ static int rt5677_resume(struct snd_soc_component *component)
regcache_sync(rt5677->regmap);
}
if (rt5677->irq) {
enable_irq(rt5677->irq);
schedule_delayed_work(&rt5677->resume_irq_check, 0);
}
return 0;
}
#else
@ -4842,6 +5059,11 @@ static const struct snd_soc_dai_ops rt5677_aif_dai_ops = {
.set_tdm_slot = rt5677_set_tdm_slot,
};
static const struct snd_soc_dai_ops rt5677_dsp_dai_ops = {
.set_sysclk = rt5677_set_dai_sysclk,
.set_pll = rt5677_set_dai_pll,
};
static struct snd_soc_dai_driver rt5677_dai[] = {
{
.name = "rt5677-aif1",
@ -4938,6 +5160,18 @@ static struct snd_soc_dai_driver rt5677_dai[] = {
},
.ops = &rt5677_aif_dai_ops,
},
{
.name = "rt5677-dspbuffer",
.id = RT5677_DSPBUFF,
.capture = {
.stream_name = "DSP Buffer",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &rt5677_dsp_dai_ops,
},
};
static const struct snd_soc_component_driver soc_component_dev_rt5677 = {
@ -5073,6 +5307,28 @@ static const struct rt5677_irq_desc rt5677_irq_descs[] = {
},
};
static bool rt5677_check_hotword(struct rt5677_priv *rt5677)
{
int reg_gpio;
if (!rt5677->is_dsp_mode)
return false;
if (regmap_read(rt5677->regmap, RT5677_GPIO_CTRL1, &reg_gpio))
return false;
/* Firmware sets GPIO1 pin to be GPIO1 after hotword is detected */
if ((reg_gpio & RT5677_GPIO1_PIN_MASK) == RT5677_GPIO1_PIN_IRQ)
return false;
/* Set GPIO1 pin back to be IRQ output for jack detect */
regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
rt5677_spi_hotword_detected();
return true;
}
static irqreturn_t rt5677_irq(int unused, void *data)
{
struct rt5677_priv *rt5677 = data;
@ -5118,7 +5374,13 @@ static irqreturn_t rt5677_irq(int unused, void *data)
reg_irq ^= rt5677_irq_descs[i].polarity_mask;
}
}
if (!irq_fired)
/* Exit the loop only when we know for sure that GPIO1 pin
* was low at some point since irq_lock was acquired. Any event
* after that point creates a rising edge that triggers another
* call to rt5677_irq().
*/
if (!irq_fired && !rt5677_check_hotword(rt5677))
goto exit;
ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
@ -5129,6 +5391,7 @@ static irqreturn_t rt5677_irq(int unused, void *data)
}
}
exit:
WARN_ON_ONCE(loop == 20);
mutex_unlock(&rt5677->irq_lock);
if (irq_fired)
return IRQ_HANDLED;
@ -5136,6 +5399,39 @@ exit:
return IRQ_NONE;
}
static void rt5677_resume_irq_check(struct work_struct *work)
{
int i, virq;
struct rt5677_priv *rt5677 =
container_of(work, struct rt5677_priv, resume_irq_check.work);
/* This is needed to check and clear the interrupt status register
* at resume. If the headset is plugged/unplugged when the device is
* fully suspended, there won't be a rising edge at resume to trigger
* the interrupt. Without this, we miss the next unplug/plug event.
*/
rt5677_irq(0, rt5677);
/* Call all enabled jack detect irq handlers again. This is needed in
* addition to the above check for a corner case caused by jack gpio
* debounce. After codec irq is disabled at suspend, the delayed work
* scheduled by soc-jack may run and read wrong jack gpio values, since
* the regmap is in cache only mode. At resume, there is no irq because
* rt5677_irq has already ran and cleared the irq status at suspend.
* Without this explicit check, unplug the headset right after suspend
* starts, then after resume the headset is still shown as plugged in.
*/
mutex_lock(&rt5677->irq_lock);
for (i = 0; i < RT5677_IRQ_NUM; i++) {
if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) {
virq = irq_find_mapping(rt5677->domain, i);
if (virq)
handle_nested_irq(virq);
}
}
mutex_unlock(&rt5677->irq_lock);
}
static void rt5677_irq_bus_lock(struct irq_data *data)
{
struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
@ -5211,6 +5507,7 @@ static int rt5677_init_irq(struct i2c_client *i2c)
}
mutex_init(&rt5677->irq_lock);
INIT_DELAYED_WORK(&rt5677->resume_irq_check, rt5677_resume_irq_check);
/*
* Select RC as the debounce clock so that GPIO works even when
@ -5256,6 +5553,8 @@ static int rt5677_init_irq(struct i2c_client *i2c)
if (ret)
dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
rt5677->irq = i2c->irq;
return ret;
}
@ -5271,6 +5570,8 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
return -ENOMEM;
rt5677->dev = &i2c->dev;
rt5677->set_dsp_vad = rt5677_set_dsp_vad;
INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work);
i2c_set_clientdata(i2c, rt5677);
if (i2c->dev.of_node) {

View File

@ -1336,6 +1336,8 @@
#define RT5677_PLL_M_SFT 12
#define RT5677_PLL_M_BP (0x1 << 11)
#define RT5677_PLL_M_BP_SFT 11
#define RT5677_PLL_UPDATE_PLL1 (0x1 << 1)
#define RT5677_PLL_UPDATE_PLL1_SFT 1
/* Global Clock Control 1 (0x80) */
#define RT5677_SCLK_SRC_MASK (0x3 << 14)
@ -1730,6 +1732,7 @@ enum {
RT5677_AIF4,
RT5677_AIF5,
RT5677_AIFS,
RT5677_DSPBUFF,
};
enum {
@ -1845,14 +1848,20 @@ struct rt5677_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
bool dsp_vad_en;
bool dsp_vad_en_request; /* DSP VAD enable/disable request */
bool dsp_vad_en; /* dsp_work parameter */
bool is_dsp_mode;
bool is_vref_slow;
struct delayed_work dsp_work;
/* Interrupt handling */
struct irq_domain *domain;
struct mutex irq_lock;
unsigned int irq_en;
struct delayed_work resume_irq_check;
int irq;
int (*set_dsp_vad)(struct snd_soc_component *component, bool on);
};
int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,

View File

@ -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 {
@ -1027,6 +1028,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;
@ -2445,6 +2458,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
View 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");

View 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
View 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
View 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__ */

View File

@ -171,6 +171,7 @@ struct aic31xx_priv {
int rate_div_line;
bool master_dapm_route_applied;
int irq;
u8 ocmv; /* output common-mode voltage */
};
struct aic31xx_rate_divs {
@ -1312,6 +1313,11 @@ static int aic31xx_codec_probe(struct snd_soc_component *component)
if (ret)
return ret;
/* set output common-mode voltage */
snd_soc_component_update_bits(component, AIC31XX_HPDRIVER,
AIC31XX_HPD_OCMV_MASK,
aic31xx->ocmv << AIC31XX_HPD_OCMV_SHIFT);
return 0;
}
@ -1501,6 +1507,43 @@ exit:
return IRQ_NONE;
}
static void aic31xx_configure_ocmv(struct aic31xx_priv *priv)
{
struct device *dev = priv->dev;
int dvdd, avdd;
u32 value;
if (dev->fwnode &&
fwnode_property_read_u32(dev->fwnode, "ai31xx-ocmv", &value)) {
/* OCMV setting is forced by DT */
if (value <= 3) {
priv->ocmv = value;
return;
}
}
avdd = regulator_get_voltage(priv->supplies[3].consumer);
dvdd = regulator_get_voltage(priv->supplies[5].consumer);
if (avdd > 3600000 || dvdd > 1950000) {
dev_warn(dev,
"Too high supply voltage(s) AVDD: %d, DVDD: %d\n",
avdd, dvdd);
} else if (avdd == 3600000 && dvdd == 1950000) {
priv->ocmv = AIC31XX_HPD_OCMV_1_8V;
} else if (avdd >= 3300000 && dvdd >= 1800000) {
priv->ocmv = AIC31XX_HPD_OCMV_1_65V;
} else if (avdd >= 3000000 && dvdd >= 1650000) {
priv->ocmv = AIC31XX_HPD_OCMV_1_5V;
} else if (avdd >= 2700000 && dvdd >= 1525000) {
priv->ocmv = AIC31XX_HPD_OCMV_1_35V;
} else {
dev_warn(dev,
"Invalid supply voltage(s) AVDD: %d, DVDD: %d\n",
avdd, dvdd);
}
}
static int aic31xx_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@ -1570,6 +1613,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
return ret;
}
aic31xx_configure_ocmv(aic31xx);
if (aic31xx->irq > 0) {
regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
AIC31XX_GPIO1_FUNC_MASK,

View File

@ -232,6 +232,14 @@ struct aic31xx_pdata {
#define AIC31XX_HSD_HP 0x01
#define AIC31XX_HSD_HS 0x03
/* AIC31XX_HPDRIVER */
#define AIC31XX_HPD_OCMV_MASK GENMASK(4, 3)
#define AIC31XX_HPD_OCMV_SHIFT 3
#define AIC31XX_HPD_OCMV_1_35V 0x0
#define AIC31XX_HPD_OCMV_1_5V 0x1
#define AIC31XX_HPD_OCMV_1_65V 0x2
#define AIC31XX_HPD_OCMV_1_8V 0x3
/* AIC31XX_MICBIAS */
#define AIC31XX_MICBIAS_MASK GENMASK(1, 0)
#define AIC31XX_MICBIAS_SHIFT 0

View File

@ -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);

View File

@ -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);

View File

@ -2410,6 +2410,8 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
err_pm_runtime:
pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
err_reset:
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
@ -2426,12 +2428,15 @@ static int wm2200_i2c_remove(struct i2c_client *i2c)
{
struct wm2200_priv *wm2200 = i2c_get_clientdata(i2c);
pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
if (wm2200->pdata.ldo_ena)
gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
wm2200->core_supplies);
return 0;
}

View File

@ -2617,6 +2617,7 @@ static int wm5100_i2c_probe(struct i2c_client *i2c,
return ret;
err_reset:
pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
@ -2640,6 +2641,7 @@ static int wm5100_i2c_remove(struct i2c_client *i2c)
{
struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);

View File

@ -1410,34 +1410,6 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct snd_soc_component *component = dai->component;
struct wm8904_priv *priv = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case WM8904_CLK_MCLK:
priv->sysclk_src = clk_id;
priv->mclk_rate = freq;
break;
case WM8904_CLK_FLL:
priv->sysclk_src = clk_id;
break;
default:
return -EINVAL;
}
dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
wm8904_configure_clocking(component);
return 0;
}
static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
@ -1824,6 +1796,50 @@ out:
return 0;
}
static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct snd_soc_component *component = dai->component;
struct wm8904_priv *priv = snd_soc_component_get_drvdata(component);
unsigned long mclk_freq;
int ret;
switch (clk_id) {
case WM8904_CLK_AUTO:
mclk_freq = clk_get_rate(priv->mclk);
/* enable FLL if a different sysclk is desired */
if (mclk_freq != freq) {
priv->sysclk_src = WM8904_CLK_FLL;
ret = wm8904_set_fll(dai, WM8904_FLL_MCLK,
WM8904_FLL_MCLK,
mclk_freq, freq);
if (ret)
return ret;
break;
}
clk_id = WM8904_CLK_MCLK;
/* fallthrough */
case WM8904_CLK_MCLK:
priv->sysclk_src = clk_id;
priv->mclk_rate = freq;
break;
case WM8904_CLK_FLL:
priv->sysclk_src = clk_id;
break;
default:
return -EINVAL;
}
dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
wm8904_configure_clocking(component);
return 0;
}
static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_component *component = codec_dai->component;

View File

@ -10,6 +10,7 @@
#ifndef _WM8904_H
#define _WM8904_H
#define WM8904_CLK_AUTO 0
#define WM8904_CLK_MCLK 1
#define WM8904_CLK_FLL 2

View File

@ -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:

View File

@ -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);

Some files were not shown because too many files have changed in this diff Show More