2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-17 17:53:56 +08:00

ASoC: Updates for v5.5

Some big changes in the core but more about cleanps and refactorings
 than new features, plus a collection of new drivers and lots of small
 fixes and improvements to existing ones.
 
  - Lots more cleanups from Morimoto-san.  Now that everything is a
    component this is mostly about refactorings to clarify and simplify
    the core, a combination of things that are no longer required due to
    refactorings and spotting similarities.
  - Many fixes to the Sound Open Firmware code.
  - Wake on voice support for Chromebooks.
  - SPI support for RT5677.
  - New drivers for Analog Devices ADAU7118, Intel Cannonlake systems
    with RT1011 and RT5682, Texas Instruments TAS2562 and TAS2770.
 -----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAl3EFLYTHGJyb29uaWVA
 a2VybmVsLm9yZwAKCRAk1otyXVSH0JCuB/40P/KPRGlEFBSJAwTfCkjQxzvQUGBy
 Y3w9QeMq3ONhCJt5BusmeuFqdkanzYqnl+NveGYKdKNTAwh6vEMGiMbGSB8dgrR5
 R7PLNBDRJi7ZUDdOZle7VrdUiZWyieaZk/ecWxfTPKfqzoBjnM3XYSa30i55hvbQ
 A2MTimFsO7nf0caLNBLqEqBjy68IHQ3tuHH27kA0MIpVNsYVrjaWfM400ot3odbg
 0vMpNTM+PDcQGkWcq3sKJBOVVjmGg2Xs1yM5hv6Mu+q1zXLCtCKj+Pv+ZXC3BT6e
 Yyxv/arpgvtjIU79Tv9RamVRC4jN6ZJRkThP9UW6JrX7tPCjvD+ygzPn
 =4FMp
 -----END PGP SIGNATURE-----

Merge tag 'asoc-v5.5' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next

ASoC: Updates for v5.5

Some big changes in the core but more about cleanps and refactorings
than new features, plus a collection of new drivers and lots of small
fixes and improvements to existing ones.

 - Lots more cleanups from Morimoto-san.  Now that everything is a
   component this is mostly about refactorings to clarify and simplify
   the core, a combination of things that are no longer required due to
   refactorings and spotting similarities.
 - Many fixes to the Sound Open Firmware code.
 - Wake on voice support for Chromebooks.
 - SPI support for RT5677.
 - New drivers for Analog Devices ADAU7118, Intel Cannonlake systems
   with RT1011 and RT5682, Texas Instruments TAS2562 and TAS2770.
This commit is contained in:
Takashi Iwai 2019-11-07 14:12:30 +01:00
commit 9ff7759731
210 changed files with 11104 additions and 3243 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

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

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

@ -27,6 +27,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_ehl_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[];
/*
* generic table used for HDA codec-based platforms, possibly with

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

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

@ -739,10 +739,12 @@ struct snd_soc_rtdcom_list {
struct snd_soc_component*
snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd,
const char *driver_name);
#define for_each_rtdcom(rtd, rtdcom) \
list_for_each_entry(rtdcom, &(rtd)->component_list, list)
#define for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) \
list_for_each_entry_safe(rtdcom1, rtdcom2, &(rtd)->component_list, list)
#define for_each_rtd_components(rtd, rtdcom, _component) \
for (rtdcom = list_first_entry(&(rtd)->component_list, \
typeof(*rtdcom), list); \
(&rtdcom->list != &(rtd)->component_list) && \
(_component = rtdcom->component); \
rtdcom = list_next_entry(rtdcom, list))
struct snd_soc_dai_link_component {
const char *name;
@ -845,7 +847,9 @@ struct snd_soc_dai_link {
unsigned int ignore:1;
struct list_head list; /* DAI link list of the soc card */
#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj; /* For topology */
#endif
};
#define for_each_link_codecs(link, i, codec) \
for ((i) = 0; \
@ -1148,7 +1152,6 @@ struct snd_soc_pcm_runtime {
struct list_head component_list; /* list of connected components */
/* bit field */
unsigned int dev_registered:1;
unsigned int pop_wait:1;
unsigned int fe_compr:1; /* for Dynamic PCM */
};
@ -1168,7 +1171,9 @@ struct soc_mixer_control {
unsigned int sign_bit;
unsigned int invert:1;
unsigned int autodisable:1;
#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj;
#endif
};
struct soc_bytes {
@ -1179,8 +1184,9 @@ struct soc_bytes {
struct soc_bytes_ext {
int max;
#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj;
#endif
/* used for TLV byte control */
int (*get)(struct snd_kcontrol *kcontrol, unsigned int __user *bytes,
unsigned int size);
@ -1204,7 +1210,9 @@ struct soc_enum {
const char * const *texts;
const unsigned int *values;
unsigned int autodisable:1;
#ifdef CONFIG_SND_SOC_TOPOLOGY
struct snd_soc_dobj dobj;
#endif
};
/* device driver data */
@ -1325,8 +1333,10 @@ struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
int id, const char *name,
const char *stream_name);
int snd_soc_register_dai(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv);
struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv,
bool legacy_dai_naming);
void snd_soc_unregister_dai(struct snd_soc_dai *dai);
struct snd_soc_dai *snd_soc_find_dai(
const struct snd_soc_dai_link_component *dlc);
@ -1391,6 +1401,11 @@ static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm)
mutex_unlock(&dapm->card->dapm_mutex);
}
/* bypass */
int snd_soc_pcm_lib_ioctl(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
unsigned int cmd, void *arg);
#include <sound/soc-component.h>
#endif

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

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

@ -528,7 +528,7 @@ static int snd_compress_check_input(struct snd_compr_params *params)
{
/* first let's check the buffer parameter's */
if (params->buffer.fragment_size == 0 ||
params->buffer.fragments > INT_MAX / params->buffer.fragment_size ||
params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||
params->buffer.fragments == 0)
return -EINVAL;

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

@ -2072,15 +2072,24 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
static int generic_hdmi_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int idx;
int idx, pcm_num;
/*
* for non-mst mode, pcm number is the same as before
* for DP MST mode, pcm number is (nid number + dev_num - 1)
* dev_num is the device entry number in a pin
*
* for DP MST mode without extra PCM, pcm number is same
* for DP MST mode with extra PCMs, pcm number is
* (nid number + dev_num - 1)
* dev_num is the device entry number in a pin
*/
for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) {
if (codec->mst_no_extra_pcms)
pcm_num = spec->num_nids;
else
pcm_num = spec->num_nids + spec->dev_num - 1;
codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
for (idx = 0; idx < pcm_num; idx++) {
struct hda_pcm *info;
struct hda_pcm_stream *pstr;

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.
@ -1104,6 +1137,14 @@ config SND_SOC_TAS2552
tristate "Texas Instruments TAS2552 Mono Audio amplifier"
depends on I2C
config SND_SOC_TAS2562
tristate "Texas Instruments TAS2562 Mono Audio amplifier"
depends on I2C
config SND_SOC_TAS2770
tristate "Texas Instruments TAS2770 speaker amplifier"
depends on I2C
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C

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.
@ -410,8 +500,8 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
return;
}
snd_hdac_ext_bus_link_put(hdev->bus, hlink);
pm_runtime_disable(&hdev->dev);
snd_hdac_ext_bus_link_put(hdev->bus, hlink);
}
static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {

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

@ -274,7 +274,7 @@ struct hdmi_codec_priv {
uint8_t eld[MAX_ELD_BYTES];
struct snd_pcm_chmap *chmap_info;
unsigned int chmap_idx;
struct mutex lock;
unsigned long busy;
struct snd_soc_jack *jack;
unsigned int jack_status;
};
@ -390,8 +390,8 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
int ret = 0;
ret = mutex_trylock(&hcp->lock);
if (!ret) {
ret = test_and_set_bit(0, &hcp->busy);
if (ret) {
dev_err(dai->dev, "Only one simultaneous stream supported!\n");
return -EINVAL;
}
@ -419,7 +419,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
err:
/* Release the exclusive lock on error */
mutex_unlock(&hcp->lock);
clear_bit(0, &hcp->busy);
return ret;
}
@ -431,7 +431,7 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
mutex_unlock(&hcp->lock);
clear_bit(0, &hcp->busy);
}
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
@ -811,8 +811,6 @@ static int hdmi_codec_probe(struct platform_device *pdev)
return -ENOMEM;
hcp->hcd = *hcd;
mutex_init(&hcp->lock);
daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
if (!daidrv)
return -ENOMEM;

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

@ -960,11 +960,11 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
/* Power on device */
if (gpio_is_valid(max98373->reset_gpio)) {
ret = gpio_request(max98373->reset_gpio, "MAX98373_RESET");
ret = devm_gpio_request(&i2c->dev, max98373->reset_gpio,
"MAX98373_RESET");
if (ret) {
dev_err(&i2c->dev, "%s: Failed to request gpio %d\n",
__func__, max98373->reset_gpio);
gpio_free(max98373->reset_gpio);
return -EINVAL;
}
gpio_direction_output(max98373->reset_gpio, 0);

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)
@ -306,12 +310,13 @@ struct pm8916_wcd_analog_priv {
};
static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
static const char *const rdac2_mux_text[] = { "ZERO", "RX2", "RX1" };
static const char *const rdac2_mux_text[] = { "RX1", "RX2" };
static const char *const hph_text[] = { "ZERO", "Switch", };
static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT(
ARRAY_SIZE(hph_text), hph_text);
static const struct snd_kcontrol_new ear_mux = SOC_DAPM_ENUM("EAR_S", hph_enum);
static const struct snd_kcontrol_new hphl_mux = SOC_DAPM_ENUM("HPHL", hph_enum);
static const struct snd_kcontrol_new hphr_mux = SOC_DAPM_ENUM("HPHR", hph_enum);
@ -321,7 +326,7 @@ static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE_VIRT(
/* RDAC2 MUX */
static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE(
CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 3, rdac2_mux_text);
CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 2, rdac2_mux_text);
static const struct snd_kcontrol_new spkr_switch[] = {
SOC_DAPM_SINGLE("Switch", CDC_A_SPKR_DAC_CTL, 7, 1, 0)
@ -685,6 +690,34 @@ static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w,
return 0;
}
static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
RX_EAR_CTL_PA_SEL_MASK, RX_EAR_CTL_PA_SEL);
break;
case SND_SOC_DAPM_POST_PMU:
snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
RX_EAR_CTL_PA_EAR_PA_EN_MASK,
RX_EAR_CTL_PA_EAR_PA_EN_ENABLE);
break;
case SND_SOC_DAPM_POST_PMD:
snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
RX_EAR_CTL_PA_EAR_PA_EN_MASK, 0);
/* Delay to reduce ear turn off pop */
usleep_range(7000, 7100);
snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL,
RX_EAR_CTL_PA_SEL_MASK, 0);
break;
}
return 0;
}
static const struct reg_default wcd_reg_defaults_2_0[] = {
{CDC_A_RX_COM_OCP_CTL, 0xD1},
{CDC_A_RX_COM_OCP_COUNT, 0xFF},
@ -801,12 +834,20 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = {
{"PDM_TX", NULL, "A_MCLK2"},
{"A_MCLK2", NULL, "A_MCLK"},
/* Earpiece (RX MIX1) */
{"EAR", NULL, "EAR_S"},
{"EAR_S", "Switch", "EAR PA"},
{"EAR PA", NULL, "RX_BIAS"},
{"EAR PA", NULL, "HPHL DAC"},
{"EAR PA", NULL, "HPHR DAC"},
{"EAR PA", NULL, "EAR CP"},
/* Headset (RX MIX1 and RX MIX2) */
{"HEADPHONE", NULL, "HPHL PA"},
{"HEADPHONE", NULL, "HPHR PA"},
{"HPHL PA", NULL, "EAR_HPHL_CLK"},
{"HPHR PA", NULL, "EAR_HPHR_CLK"},
{"HPHL DAC", NULL, "EAR_HPHL_CLK"},
{"HPHR DAC", NULL, "EAR_HPHR_CLK"},
{"CP", NULL, "NCP_CLK"},
@ -847,11 +888,20 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AMIC1"),
SND_SOC_DAPM_INPUT("AMIC3"),
SND_SOC_DAPM_INPUT("AMIC2"),
SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
/* RX stuff */
SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM,
0, 0, NULL, 0,
pm8916_wcd_analog_enable_ear_pa,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux),
SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0),
SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux),
SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL,

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

@ -62,6 +62,7 @@ struct pcm3168a_priv {
unsigned long sysclk;
struct pcm3168a_io_params io_params[2];
struct snd_soc_dai_driver dai_drv[2];
};
static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
@ -314,6 +315,34 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
return 0;
}
static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
u64 formats = SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE;
unsigned int channel_max = dai->id == PCM3168A_DAI_DAC ? 8 : 6;
if (pcm3168a->io_params[dai->id].fmt == PCM3168A_FMT_RIGHT_J) {
/* S16_LE is only supported in RIGHT_J mode */
formats |= SNDRV_PCM_FMTBIT_S16_LE;
/*
* If multi DIN/DOUT is not selected, RIGHT_J can only support
* two channels (no TDM support)
*/
if (pcm3168a->io_params[dai->id].tdm_slots != 2)
channel_max = 2;
}
if (dai->id == PCM3168A_DAI_DAC) {
dai->driver->playback.channels_max = channel_max;
dai->driver->playback.formats = formats;
} else {
dai->driver->capture.channels_max = channel_max;
dai->driver->capture.formats = formats;
}
}
static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
{
struct snd_soc_component *component = dai->component;
@ -376,6 +405,8 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
pcm3168a_update_fixup_pcm_stream(dai);
return 0;
}
@ -409,6 +440,8 @@ static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
else
io_params->tdm_mask = rx_mask;
pcm3168a_update_fixup_pcm_stream(dai);
return 0;
}
@ -530,63 +563,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static int pcm3168a_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
unsigned int sample_min;
unsigned int channel_max;
unsigned int channel_maxs[] = {
8, /* DAC */
6 /* ADC */
};
/*
* Available Data Bits
*
* RIGHT_J : 24 / 16
* LEFT_J : 24
* I2S : 24
*
* TDM available
*
* I2S
* LEFT_J
*/
switch (pcm3168a->io_params[dai->id].fmt) {
case PCM3168A_FMT_RIGHT_J:
sample_min = 16;
channel_max = 2;
break;
case PCM3168A_FMT_LEFT_J:
case PCM3168A_FMT_I2S:
case PCM3168A_FMT_DSP_A:
case PCM3168A_FMT_DSP_B:
sample_min = 24;
channel_max = channel_maxs[dai->id];
break;
default:
sample_min = 24;
channel_max = 2;
}
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
sample_min, 32);
/* Allow all channels in multi DIN/DOUT mode */
if (pcm3168a->io_params[dai->id].tdm_slots == 2)
channel_max = channel_maxs[dai->id];
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
2, channel_max);
return 0;
}
static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
.startup = pcm3168a_startup,
.set_fmt = pcm3168a_set_dai_fmt,
.set_sysclk = pcm3168a_set_dai_sysclk,
.hw_params = pcm3168a_hw_params,
@ -776,8 +753,10 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
pm_runtime_enable(dev);
pm_runtime_idle(dev);
ret = devm_snd_soc_register_component(dev, &pcm3168a_driver, pcm3168a_dais,
ARRAY_SIZE(pcm3168a_dais));
memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv));
ret = devm_snd_soc_register_component(dev, &pcm3168a_driver,
pcm3168a->dai_drv,
ARRAY_SIZE(pcm3168a->dai_drv));
if (ret) {
dev_err(dev, "failed to register component: %d\n", ret);
goto err_regulator;

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

@ -3636,6 +3636,12 @@ static const struct rt5645_platform_data lattepanda_board_platform_data = {
.inv_jd1_1 = true
};
static const struct rt5645_platform_data kahlee_platform_data = {
.dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
.jd_mode = 3,
};
static const struct dmi_system_id dmi_platform_data[] = {
{
.ident = "Chrome Buddy",
@ -3742,6 +3748,13 @@ static const struct dmi_system_id dmi_platform_data[] = {
},
.driver_data = (void *)&lattepanda_board_platform_data,
},
{
.ident = "Chrome Kahlee",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Kahlee"),
},
.driver_data = (void *)&kahlee_platform_data,
},
{ }
};

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,8 @@
#include <linux/firmware.h>
#include <linux/acpi.h>
#include <sound/soc.h>
#include "rt5677-spi.h"
#define DRV_NAME "rt5677spi"
@ -45,9 +47,356 @@
#define RT5677_SPI_WRITE_16 0x1
#define RT5677_SPI_READ_16 0x0
#define RT5677_BUF_BYTES_TOTAL 0x20000
#define RT5677_MIC_BUF_ADDR 0x60030000
#define RT5677_MODEL_ADDR 0x5FFC9800
#define RT5677_MIC_BUF_BYTES ((u32)(RT5677_BUF_BYTES_TOTAL - \
sizeof(u32)))
#define RT5677_MIC_BUF_FIRST_READ_SIZE 0x10000
static struct spi_device *g_spi;
static DEFINE_MUTEX(spi_mutex);
struct rt5677_dsp {
struct device *dev;
struct delayed_work copy_work;
struct mutex dma_lock;
struct snd_pcm_substream *substream;
size_t dma_offset; /* zero-based offset into runtime->dma_area */
size_t avail_bytes; /* number of new bytes since last period */
u32 mic_read_offset; /* zero-based offset into DSP's mic buffer */
bool new_hotword; /* a new hotword is fired */
};
static const struct snd_pcm_hardware rt5677_spi_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.period_bytes_min = PAGE_SIZE,
.period_bytes_max = RT5677_BUF_BYTES_TOTAL / 8,
.periods_min = 8,
.periods_max = 8,
.channels_min = 1,
.channels_max = 1,
.buffer_bytes_max = RT5677_BUF_BYTES_TOTAL,
};
static struct snd_soc_dai_driver rt5677_spi_dai = {
/* The DAI name "rt5677-dsp-cpu-dai" is not used. The actual DAI name
* registered with ASoC is the name of the device "spi-RT5677AA:00",
* because we only have one DAI. See snd_soc_register_dais().
*/
.name = "rt5677-dsp-cpu-dai",
.id = 0,
.capture = {
.stream_name = "DSP Capture",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
};
/* PCM for streaming audio from the DSP buffer */
static int rt5677_spi_pcm_open(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
snd_soc_set_runtime_hwparams(substream, &rt5677_spi_pcm_hardware);
return 0;
}
static int rt5677_spi_pcm_close(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct rt5677_dsp *rt5677_dsp =
snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt5677_dsp->copy_work);
return 0;
}
static int rt5677_spi_hw_params(
struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct rt5677_dsp *rt5677_dsp =
snd_soc_component_get_drvdata(component);
int ret;
mutex_lock(&rt5677_dsp->dma_lock);
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
rt5677_dsp->substream = substream;
mutex_unlock(&rt5677_dsp->dma_lock);
return ret;
}
static int rt5677_spi_hw_free(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct rt5677_dsp *rt5677_dsp =
snd_soc_component_get_drvdata(component);
mutex_lock(&rt5677_dsp->dma_lock);
rt5677_dsp->substream = NULL;
mutex_unlock(&rt5677_dsp->dma_lock);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int rt5677_spi_prepare(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct rt5677_dsp *rt5677_dsp =
snd_soc_component_get_drvdata(component);
rt5677_dsp->dma_offset = 0;
rt5677_dsp->avail_bytes = 0;
return 0;
}
static snd_pcm_uframes_t rt5677_spi_pcm_pointer(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct rt5677_dsp *rt5677_dsp =
snd_soc_component_get_drvdata(component);
return bytes_to_frames(runtime, rt5677_dsp->dma_offset);
}
static int rt5677_spi_mic_write_offset(u32 *mic_write_offset)
{
int ret;
/* Grab the first 4 bytes that hold the write pointer on the
* dsp, and check to make sure that it points somewhere inside the
* buffer.
*/
ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR, mic_write_offset,
sizeof(u32));
if (ret)
return ret;
/* Adjust the offset so that it's zero-based */
*mic_write_offset = *mic_write_offset - sizeof(u32);
return *mic_write_offset < RT5677_MIC_BUF_BYTES ? 0 : -EFAULT;
}
/*
* Copy one contiguous block of audio samples from the DSP mic buffer to the
* dma_area of the pcm runtime. The receiving buffer may wrap around.
* @begin: start offset of the block to copy, in bytes.
* @end: offset of the first byte after the block to copy, must be greater
* than or equal to begin.
*
* Return: Zero if successful, or a negative error code on failure.
*/
static int rt5677_spi_copy_block(struct rt5677_dsp *rt5677_dsp,
u32 begin, u32 end)
{
struct snd_pcm_runtime *runtime = rt5677_dsp->substream->runtime;
size_t bytes_per_frame = frames_to_bytes(runtime, 1);
size_t first_chunk_len, second_chunk_len;
int ret;
if (begin > end || runtime->dma_bytes < 2 * bytes_per_frame) {
dev_err(rt5677_dsp->dev,
"Invalid copy from (%u, %u), dma_area size %zu\n",
begin, end, runtime->dma_bytes);
return -EINVAL;
}
/* The block to copy is empty */
if (begin == end)
return 0;
/* If the incoming chunk is too big for the receiving buffer, only the
* last "receiving buffer size - one frame" bytes are copied.
*/
if (end - begin > runtime->dma_bytes - bytes_per_frame)
begin = end - (runtime->dma_bytes - bytes_per_frame);
/* May need to split to two chunks, calculate the size of each */
first_chunk_len = end - begin;
second_chunk_len = 0;
if (rt5677_dsp->dma_offset + first_chunk_len > runtime->dma_bytes) {
/* Receiving buffer wrapped around */
second_chunk_len = first_chunk_len;
first_chunk_len = runtime->dma_bytes - rt5677_dsp->dma_offset;
second_chunk_len -= first_chunk_len;
}
/* Copy first chunk */
ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + begin,
runtime->dma_area + rt5677_dsp->dma_offset,
first_chunk_len);
if (ret)
return ret;
rt5677_dsp->dma_offset += first_chunk_len;
if (rt5677_dsp->dma_offset == runtime->dma_bytes)
rt5677_dsp->dma_offset = 0;
/* Copy second chunk */
if (second_chunk_len) {
ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) +
begin + first_chunk_len, runtime->dma_area,
second_chunk_len);
if (!ret)
rt5677_dsp->dma_offset = second_chunk_len;
}
return ret;
}
/*
* Copy a given amount of audio samples from the DSP mic buffer starting at
* mic_read_offset, to the dma_area of the pcm runtime. The source buffer may
* wrap around. mic_read_offset is updated after successful copy.
* @amount: amount of samples to copy, in bytes.
*
* Return: Zero if successful, or a negative error code on failure.
*/
static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount)
{
int ret = 0;
u32 target;
if (amount == 0)
return ret;
target = rt5677_dsp->mic_read_offset + amount;
/* Copy the first chunk in DSP's mic buffer */
ret |= rt5677_spi_copy_block(rt5677_dsp, rt5677_dsp->mic_read_offset,
min(target, RT5677_MIC_BUF_BYTES));
if (target >= RT5677_MIC_BUF_BYTES) {
/* Wrap around, copy the second chunk */
target -= RT5677_MIC_BUF_BYTES;
ret |= rt5677_spi_copy_block(rt5677_dsp, 0, target);
}
if (!ret)
rt5677_dsp->mic_read_offset = target;
return ret;
}
/*
* A delayed work that streams audio samples from the DSP mic buffer to the
* dma_area of the pcm runtime via SPI.
*/
static void rt5677_spi_copy_work(struct work_struct *work)
{
struct rt5677_dsp *rt5677_dsp =
container_of(work, struct rt5677_dsp, copy_work.work);
struct snd_pcm_runtime *runtime;
u32 mic_write_offset;
size_t new_bytes, copy_bytes, period_bytes;
unsigned int delay;
int ret = 0;
/* Ensure runtime->dma_area buffer does not go away while copying. */
mutex_lock(&rt5677_dsp->dma_lock);
if (!rt5677_dsp->substream) {
dev_err(rt5677_dsp->dev, "No pcm substream\n");
goto done;
}
runtime = rt5677_dsp->substream->runtime;
if (rt5677_spi_mic_write_offset(&mic_write_offset)) {
dev_err(rt5677_dsp->dev, "No mic_write_offset\n");
goto done;
}
/* If this is the first time that we've asked for streaming data after
* a hotword is fired, we should start reading from the previous 2
* seconds of audio from wherever the mic_write_offset is currently.
*/
if (rt5677_dsp->new_hotword) {
rt5677_dsp->new_hotword = false;
/* See if buffer wraparound happens */
if (mic_write_offset < RT5677_MIC_BUF_FIRST_READ_SIZE)
rt5677_dsp->mic_read_offset = RT5677_MIC_BUF_BYTES -
(RT5677_MIC_BUF_FIRST_READ_SIZE -
mic_write_offset);
else
rt5677_dsp->mic_read_offset = mic_write_offset -
RT5677_MIC_BUF_FIRST_READ_SIZE;
}
/* Calculate the amount of new samples in bytes */
if (rt5677_dsp->mic_read_offset <= mic_write_offset)
new_bytes = mic_write_offset - rt5677_dsp->mic_read_offset;
else
new_bytes = RT5677_MIC_BUF_BYTES + mic_write_offset
- rt5677_dsp->mic_read_offset;
/* Copy all new samples from DSP mic buffer, one period at a time */
period_bytes = snd_pcm_lib_period_bytes(rt5677_dsp->substream);
while (new_bytes) {
copy_bytes = min(new_bytes, period_bytes
- rt5677_dsp->avail_bytes);
ret = rt5677_spi_copy(rt5677_dsp, copy_bytes);
if (ret) {
dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret);
goto done;
}
rt5677_dsp->avail_bytes += copy_bytes;
if (rt5677_dsp->avail_bytes >= period_bytes) {
snd_pcm_period_elapsed(rt5677_dsp->substream);
rt5677_dsp->avail_bytes = 0;
}
new_bytes -= copy_bytes;
}
delay = bytes_to_frames(runtime, period_bytes) / (runtime->rate / 1000);
schedule_delayed_work(&rt5677_dsp->copy_work, msecs_to_jiffies(delay));
done:
mutex_unlock(&rt5677_dsp->dma_lock);
}
static struct page *rt5677_spi_pcm_page(
struct snd_soc_component *component,
struct snd_pcm_substream *substream,
unsigned long offset)
{
return snd_pcm_lib_get_vmalloc_page(substream, offset);
}
static int rt5677_spi_pcm_probe(struct snd_soc_component *component)
{
struct rt5677_dsp *rt5677_dsp;
rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp),
GFP_KERNEL);
if (!rt5677_dsp)
return -ENOMEM;
rt5677_dsp->dev = &g_spi->dev;
mutex_init(&rt5677_dsp->dma_lock);
INIT_DELAYED_WORK(&rt5677_dsp->copy_work, rt5677_spi_copy_work);
snd_soc_component_set_drvdata(component, rt5677_dsp);
return 0;
}
static const struct snd_soc_component_driver rt5677_spi_dai_component = {
.name = DRV_NAME,
.probe = rt5677_spi_pcm_probe,
.open = rt5677_spi_pcm_open,
.close = rt5677_spi_pcm_close,
.hw_params = rt5677_spi_hw_params,
.hw_free = rt5677_spi_hw_free,
.prepare = rt5677_spi_prepare,
.pointer = rt5677_spi_pcm_pointer,
.page = rt5677_spi_pcm_page,
};
/* Select a suitable transfer command for the next transfer to ensure
* the transfer address is always naturally aligned while minimizing
* the total number of transfers required.
@ -218,9 +567,45 @@ int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw)
}
EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware);
void rt5677_spi_hotword_detected(void)
{
struct rt5677_dsp *rt5677_dsp;
if (!g_spi)
return;
rt5677_dsp = dev_get_drvdata(&g_spi->dev);
if (!rt5677_dsp) {
dev_err(&g_spi->dev, "Can't get rt5677_dsp\n");
return;
}
mutex_lock(&rt5677_dsp->dma_lock);
dev_info(rt5677_dsp->dev, "Hotword detected\n");
rt5677_dsp->new_hotword = true;
mutex_unlock(&rt5677_dsp->dma_lock);
schedule_delayed_work(&rt5677_dsp->copy_work, 0);
}
EXPORT_SYMBOL_GPL(rt5677_spi_hotword_detected);
static int rt5677_spi_probe(struct spi_device *spi)
{
int ret;
g_spi = spi;
ret = snd_soc_register_component(&spi->dev, &rt5677_spi_dai_component,
&rt5677_spi_dai, 1);
if (ret < 0)
dev_err(&spi->dev, "Failed to register component.\n");
return ret;
}
static int rt5677_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_component(&spi->dev);
return 0;
}
@ -236,6 +621,7 @@ static struct spi_driver rt5677_spi_driver = {
.acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
},
.probe = rt5677_spi_probe,
.remove = rt5677_spi_remove,
};
module_spi_driver(rt5677_spi_driver);

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

@ -44,6 +44,7 @@ static const struct rt5682_platform_data i2s_default_platform_data = {
.dmic1_data_pin = RT5682_DMIC1_DATA_GPIO2,
.dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3,
.jd_src = RT5682_JD1,
.btndet_delay = 16,
};
struct rt5682_priv {
@ -1026,6 +1027,18 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK,
RT5682_JD1_EN | RT5682_JD1_POL_NOR);
regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4,
0x7f7f, (rt5682->pdata.btndet_delay << 8 |
rt5682->pdata.btndet_delay));
regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5,
0x7f7f, (rt5682->pdata.btndet_delay << 8 |
rt5682->pdata.btndet_delay));
regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6,
0x7f7f, (rt5682->pdata.btndet_delay << 8 |
rt5682->pdata.btndet_delay));
regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7,
0x7f7f, (rt5682->pdata.btndet_delay << 8 |
rt5682->pdata.btndet_delay));
mod_delayed_work(system_power_efficient_wq,
&rt5682->jack_detect_work, msecs_to_jiffies(250));
break;
@ -2467,6 +2480,8 @@ static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev)
&rt5682->pdata.dmic1_clk_pin);
device_property_read_u32(dev, "realtek,jd-src",
&rt5682->pdata.jd_src);
device_property_read_u32(dev, "realtek,btndet-delay",
&rt5682->pdata.btndet_delay);
rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
"realtek,ldo1-en-gpios", 0);

590
sound/soc/codecs/tas2562.c Normal file
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

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

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

View File

@ -6,6 +6,7 @@
#ifndef _WM8994_H
#define _WM8994_H
#include <linux/clk.h>
#include <sound/soc.h>
#include <linux/firmware.h>
#include <linux/completion.h>
@ -14,6 +15,12 @@
#include "wm_hubs.h"
enum {
WM8994_MCLK1,
WM8994_MCLK2,
WM8994_NUM_MCLK
};
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1
#define WM8994_SYSCLK_MCLK2 2
@ -73,9 +80,10 @@ struct wm8994;
struct wm8994_priv {
struct wm_hubs_data hubs;
struct wm8994 *wm8994;
struct clk_bulk_data mclk[WM8994_NUM_MCLK];
int sysclk[2];
int sysclk_rate[2];
int mclk[2];
int mclk_rate[2];
int aifclk[2];
int aifdiv[2];
int channels[2];

View File

@ -135,7 +135,8 @@ void dw_pcm_pop_rx(struct dw_i2s_dev *dev)
dw_pcm_transfer(dev, false);
}
static int dw_pcm_open(struct snd_pcm_substream *substream)
static int dw_pcm_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@ -148,14 +149,16 @@ static int dw_pcm_open(struct snd_pcm_substream *substream)
return 0;
}
static int dw_pcm_close(struct snd_pcm_substream *substream)
static int dw_pcm_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
synchronize_rcu();
return 0;
}
static int dw_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
static int dw_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct dw_i2s_dev *dev = runtime->private_data;
@ -192,12 +195,14 @@ static int dw_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static int dw_pcm_hw_free(struct snd_pcm_substream *substream)
static int dw_pcm_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static int dw_pcm_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct dw_i2s_dev *dev = runtime->private_data;
@ -231,7 +236,8 @@ static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)
static snd_pcm_uframes_t dw_pcm_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct dw_i2s_dev *dev = runtime->private_data;
@ -245,7 +251,8 @@ static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)
return pos < runtime->buffer_size ? pos : 0;
}
static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd)
static int dw_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
size_t size = dw_pcm_hardware.buffer_bytes_max;
@ -255,25 +262,22 @@ static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static void dw_pcm_free(struct snd_pcm *pcm)
static void dw_pcm_free(struct snd_soc_component *component,
struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static const struct snd_pcm_ops dw_pcm_ops = {
.open = dw_pcm_open,
.close = dw_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = dw_pcm_hw_params,
.hw_free = dw_pcm_hw_free,
.trigger = dw_pcm_trigger,
.pointer = dw_pcm_pointer,
};
static const struct snd_soc_component_driver dw_pcm_component = {
.pcm_new = dw_pcm_new,
.pcm_free = dw_pcm_free,
.ops = &dw_pcm_ops,
.open = dw_pcm_open,
.close = dw_pcm_close,
.ioctl = snd_soc_pcm_lib_ioctl,
.hw_params = dw_pcm_hw_params,
.hw_free = dw_pcm_hw_free,
.trigger = dw_pcm_trigger,
.pointer = dw_pcm_pointer,
.pcm_construct = dw_pcm_new,
.pcm_destruct = dw_pcm_free,
};
int dw_pcm_register(struct platform_device *pdev)

View File

@ -25,6 +25,16 @@ config SND_SOC_FSL_SAI
This option is only useful for out-of-tree drivers since
in-tree drivers select it automatically.
config SND_SOC_FSL_MQS
tristate "Medium Quality Sound (MQS) module support"
depends on SND_SOC_FSL_SAI
select REGMAP_MMIO
help
Say Y if you want to add Medium Quality Sound (MQS)
support for the Freescale CPUs.
This option is only useful for out-of-tree drivers since
in-tree drivers select it automatically.
config SND_SOC_FSL_AUDMIX
tristate "Audio Mixer (AUDMIX) module support"
select REGMAP_MMIO

View File

@ -23,6 +23,7 @@ snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-micfil-objs := fsl_micfil.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
snd-soc-fsl-mqs-objs := fsl_mqs.o
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
@ -33,6 +34,7 @@ obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
obj-$(CONFIG_SND_SOC_FSL_ESAI) += snd-soc-fsl-esai.o
obj-$(CONFIG_SND_SOC_FSL_MICFIL) += snd-soc-fsl-micfil.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
# MPC5200 Platform Support

View File

@ -115,7 +115,7 @@ static void fsl_asrc_sel_proc(int inrate, int outrate,
* within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
* while pair A and pair C are comparatively independent.
*/
static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
{
enum asrc_pair_index index = ASRC_INVALID_PAIR;
struct fsl_asrc *asrc_priv = pair->asrc_priv;
@ -158,7 +158,7 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
*
* It clears the resource from asrc_priv and releases the occupied channels.
*/
static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc_priv = pair->asrc_priv;
enum asrc_pair_index index = pair->index;
@ -259,14 +259,24 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
* It configures those ASRC registers according to a configuration instance
* of struct asrc_config which includes in/output sample rate, width, channel
* and clock settings.
*
* Note:
* The ideal ratio configuration can work with a flexible clock rate setting.
* Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC.
* For a regular audio playback, the clock rate should not be slower than an
* clock rate aligning with the output sample rate; For a use case requiring
* faster conversion, set use_ideal_rate to have the faster speed.
*/
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
{
struct asrc_config *config = pair->config;
struct fsl_asrc *asrc_priv = pair->asrc_priv;
enum asrc_pair_index index = pair->index;
enum asrc_word_width input_word_width;
enum asrc_word_width output_word_width;
u32 inrate, outrate, indiv, outdiv;
u32 clk_index[2], div[2];
u32 clk_index[2], div[2], rem[2];
u64 clk_rate;
int in, out, channels;
int pre_proc, post_proc;
struct clk *clk;
@ -283,9 +293,32 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
return -EINVAL;
}
/* Validate output width */
if (config->output_word_width == ASRC_WIDTH_8_BIT) {
pair_err("does not support 8bit width output\n");
switch (snd_pcm_format_width(config->input_format)) {
case 8:
input_word_width = ASRC_WIDTH_8_BIT;
break;
case 16:
input_word_width = ASRC_WIDTH_16_BIT;
break;
case 24:
input_word_width = ASRC_WIDTH_24_BIT;
break;
default:
pair_err("does not support this input format, %d\n",
config->input_format);
return -EINVAL;
}
switch (snd_pcm_format_width(config->output_format)) {
case 16:
output_word_width = ASRC_WIDTH_16_BIT;
break;
case 24:
output_word_width = ASRC_WIDTH_24_BIT;
break;
default:
pair_err("does not support this output format, %d\n",
config->output_format);
return -EINVAL;
}
@ -326,27 +359,42 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
/* We only have output clock for ideal ratio mode */
clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
div[IN] = clk_get_rate(clk) / inrate;
if (div[IN] == 0) {
clk_rate = clk_get_rate(clk);
rem[IN] = do_div(clk_rate, inrate);
div[IN] = (u32)clk_rate;
/*
* The divider range is [1, 1024], defined by the hardware. For non-
* ideal ratio configuration, clock rate has to be strictly aligned
* with the sample rate. For ideal ratio configuration, clock rates
* only result in different converting speeds. So remainder does not
* matter, as long as we keep the divider within its valid range.
*/
if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) {
pair_err("failed to support input sample rate %dHz by asrck_%x\n",
inrate, clk_index[ideal ? OUT : IN]);
return -EINVAL;
}
div[IN] = min_t(u32, 1024, div[IN]);
clk = asrc_priv->asrck_clk[clk_index[OUT]];
/* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
if (ideal)
div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
clk_rate = clk_get_rate(clk);
if (ideal && use_ideal_rate)
rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
else
div[OUT] = clk_get_rate(clk) / outrate;
rem[OUT] = do_div(clk_rate, outrate);
div[OUT] = clk_rate;
if (div[OUT] == 0) {
/* Output divider has the same limitation as the input one */
if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) {
pair_err("failed to support output sample rate %dHz by asrck_%x\n",
outrate, clk_index[OUT]);
return -EINVAL;
}
div[OUT] = min_t(u32, 1024, div[OUT]);
/* Set the channel number */
channels = config->channel_num;
@ -383,8 +431,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
/* Implement word_width configurations */
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
ASRMCR1i_OW16(config->output_word_width) |
ASRMCR1i_IWD(config->input_word_width));
ASRMCR1i_OW16(output_word_width) |
ASRMCR1i_IWD(input_word_width));
/* Enable BUFFER STALL */
regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
@ -497,13 +545,13 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
int width = params_width(params);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
struct asrc_config config;
int word_width, ret;
snd_pcm_format_t format;
int ret;
ret = fsl_asrc_request_pair(channels, pair);
if (ret) {
@ -513,15 +561,10 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
pair->config = &config;
if (width == 16)
width = ASRC_WIDTH_16_BIT;
else
width = ASRC_WIDTH_24_BIT;
if (asrc_priv->asrc_width == 16)
word_width = ASRC_WIDTH_16_BIT;
format = SNDRV_PCM_FORMAT_S16_LE;
else
word_width = ASRC_WIDTH_24_BIT;
format = SNDRV_PCM_FORMAT_S24_LE;
config.pair = pair->index;
config.channel_num = channels;
@ -529,18 +572,18 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
config.outclk = OUTCLK_ASRCK1_CLK;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
config.input_word_width = width;
config.output_word_width = word_width;
config.input_format = params_format(params);
config.output_format = format;
config.input_sample_rate = rate;
config.output_sample_rate = asrc_priv->asrc_rate;
} else {
config.input_word_width = word_width;
config.output_word_width = width;
config.input_format = format;
config.output_format = params_format(params);
config.input_sample_rate = asrc_priv->asrc_rate;
config.output_sample_rate = rate;
}
ret = fsl_asrc_config_pair(pair);
ret = fsl_asrc_config_pair(pair, false);
if (ret) {
dev_err(dai->dev, "fail to config asrc pair\n");
return ret;
@ -604,7 +647,7 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE)
SNDRV_PCM_FMTBIT_S24_3LE)
static struct snd_soc_dai_driver fsl_asrc_dai = {
.probe = fsl_asrc_dai_probe,
@ -615,7 +658,8 @@ static struct snd_soc_dai_driver fsl_asrc_dai = {
.rate_min = 5512,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_KNOT,
.formats = FSL_ASRC_FORMATS,
.formats = FSL_ASRC_FORMATS |
SNDRV_PCM_FMTBIT_S8,
},
.capture = {
.stream_name = "ASRC-Capture",

View File

@ -342,8 +342,8 @@ struct asrc_config {
unsigned int dma_buffer_size;
unsigned int input_sample_rate;
unsigned int output_sample_rate;
enum asrc_word_width input_word_width;
enum asrc_word_width output_word_width;
snd_pcm_format_t input_format;
snd_pcm_format_t output_format;
enum asrc_inclk inclk;
enum asrc_outclk outclk;
};
@ -462,4 +462,7 @@ struct fsl_asrc {
#define DRV_NAME "fsl-asrc-dai"
extern struct snd_soc_component_driver fsl_asrc_component;
struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair);
void fsl_asrc_release_pair(struct fsl_asrc_pair *pair);
#endif /* _FSL_ASRC_H */

View File

@ -16,13 +16,11 @@
#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
static const struct snd_pcm_hardware snd_imx_hardware = {
static struct snd_pcm_hardware snd_imx_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = 65535, /* Limited by SDMA engine */
@ -54,13 +52,12 @@ static void fsl_asrc_dma_complete(void *arg)
snd_pcm_period_elapsed(substream);
}
static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream,
struct snd_soc_component *component)
{
u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
unsigned long flags = DMA_CTRL_ACK;
@ -97,7 +94,8 @@ static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
return 0;
}
static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
static int fsl_asrc_dma_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@ -107,7 +105,7 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = fsl_asrc_dma_prepare_and_submit(substream);
ret = fsl_asrc_dma_prepare_and_submit(substream, component);
if (ret)
return ret;
dma_async_issue_pending(pair->dma_chan[IN]);
@ -126,7 +124,8 @@ static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
@ -134,7 +133,6 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
struct fsl_asrc *asrc_priv = pair->asrc_priv;
@ -249,7 +247,8 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
static int fsl_asrc_dma_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@ -268,14 +267,27 @@ static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
return 0;
}
static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
static int fsl_asrc_dma_startup(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct snd_dmaengine_dai_dma_data *dma_data;
struct device *dev = component->dev;
struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
struct fsl_asrc_pair *pair;
struct dma_chan *tmp_chan = NULL;
u8 dir = tx ? OUT : IN;
bool release_pair = true;
int ret = 0;
ret = snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0) {
dev_err(dev, "failed to set pcm hw params periods\n");
return ret;
}
pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
if (!pair)
@ -285,14 +297,54 @@ static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
runtime->private_data = pair;
snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
/* Request a dummy pair, which will be released later.
* Request pair function needs channel num as input, for this
* dummy pair, we just request "1" channel temporarily.
*/
ret = fsl_asrc_request_pair(1, pair);
if (ret < 0) {
dev_err(dev, "failed to request asrc pair\n");
goto req_pair_err;
}
/* Request a dummy dma channel, which will be released later. */
tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
if (!tmp_chan) {
dev_err(dev, "failed to get dma channel\n");
ret = -EINVAL;
goto dma_chan_err;
}
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
/* Refine the snd_imx_hardware according to caps of DMA. */
ret = snd_dmaengine_pcm_refine_runtime_hwparams(substream,
dma_data,
&snd_imx_hardware,
tmp_chan);
if (ret < 0) {
dev_err(dev, "failed to refine runtime hwparams\n");
goto out;
}
release_pair = false;
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
return 0;
out:
dma_release_channel(tmp_chan);
dma_chan_err:
fsl_asrc_release_pair(pair);
req_pair_err:
if (release_pair)
kfree(pair);
return ret;
}
static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
static int fsl_asrc_dma_shutdown(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@ -311,7 +363,9 @@ static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
return 0;
}
static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream)
static snd_pcm_uframes_t
fsl_asrc_dma_pcm_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
@ -319,17 +373,8 @@ static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *subs
return bytes_to_frames(substream->runtime, pair->pos);
}
static const struct snd_pcm_ops fsl_asrc_dma_pcm_ops = {
.ioctl = snd_pcm_lib_ioctl,
.hw_params = fsl_asrc_dma_hw_params,
.hw_free = fsl_asrc_dma_hw_free,
.trigger = fsl_asrc_dma_trigger,
.open = fsl_asrc_dma_startup,
.close = fsl_asrc_dma_shutdown,
.pointer = fsl_asrc_dma_pcm_pointer,
};
static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm_substream *substream;
@ -364,7 +409,8 @@ err:
return ret;
}
static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
static void fsl_asrc_dma_pcm_free(struct snd_soc_component *component,
struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
int i;
@ -382,8 +428,14 @@ static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
struct snd_soc_component_driver fsl_asrc_component = {
.name = DRV_NAME,
.ops = &fsl_asrc_dma_pcm_ops,
.pcm_new = fsl_asrc_dma_pcm_new,
.pcm_free = fsl_asrc_dma_pcm_free,
.ioctl = snd_soc_pcm_lib_ioctl,
.hw_params = fsl_asrc_dma_hw_params,
.hw_free = fsl_asrc_dma_hw_free,
.trigger = fsl_asrc_dma_trigger,
.open = fsl_asrc_dma_startup,
.close = fsl_asrc_dma_shutdown,
.pointer = fsl_asrc_dma_pcm_pointer,
.pcm_construct = fsl_asrc_dma_pcm_new,
.pcm_destruct = fsl_asrc_dma_pcm_free,
};
EXPORT_SYMBOL_GPL(fsl_asrc_component);

View File

@ -201,8 +201,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
struct fsl_dma_private *dma_private = dev_id;
struct snd_pcm_substream *substream = dma_private->substream;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct device *dev = rtd->dev;
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
irqreturn_t ret = IRQ_NONE;
u32 sr, sr2 = 0;
@ -280,7 +279,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
* Regardless of where the memory is actually allocated, since the device can
* technically DMA to any 36-bit address, we do need to set the DMA mask to 36.
*/
static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
static int fsl_dma_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
@ -380,11 +380,10 @@ static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
* buffer, which is what ALSA expects. We're just dividing it into
* contiguous parts, and creating a link descriptor for each one.
*/
static int fsl_dma_open(struct snd_pcm_substream *substream)
static int fsl_dma_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct dma_object *dma =
container_of(component->driver, struct dma_object, dai);
@ -533,13 +532,12 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
* and 8 bytes at a time). So we do not support packed 24-bit samples.
* 24-bit data must be padded to 32 bits.
*/
static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
static int fsl_dma_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
/* Number of bits per sample */
@ -698,12 +696,11 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
* The base address of the buffer is stored in the source_addr field of the
* first link descriptor.
*/
static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
static snd_pcm_uframes_t fsl_dma_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
dma_addr_t position;
@ -763,7 +760,8 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream)
*
* This function can be called multiple times.
*/
static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
static int fsl_dma_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
@ -796,12 +794,11 @@ static int fsl_dma_hw_free(struct snd_pcm_substream *substream)
/**
* fsl_dma_close: close the stream.
*/
static int fsl_dma_close(struct snd_pcm_substream *substream)
static int fsl_dma_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
struct device *dev = component->dev;
struct dma_object *dma =
container_of(component->driver, struct dma_object, dai);
@ -824,7 +821,8 @@ static int fsl_dma_close(struct snd_pcm_substream *substream)
/*
* Remove this PCM driver.
*/
static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm)
static void fsl_dma_free_dma_buffers(struct snd_soc_component *component,
struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
unsigned int i;
@ -872,15 +870,6 @@ static struct device_node *find_ssi_node(struct device_node *dma_channel_np)
return NULL;
}
static const struct snd_pcm_ops fsl_dma_ops = {
.open = fsl_dma_open,
.close = fsl_dma_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = fsl_dma_hw_params,
.hw_free = fsl_dma_hw_free,
.pointer = fsl_dma_pointer,
};
static int fsl_soc_dma_probe(struct platform_device *pdev)
{
struct dma_object *dma;
@ -912,9 +901,14 @@ static int fsl_soc_dma_probe(struct platform_device *pdev)
}
dma->dai.name = DRV_NAME;
dma->dai.ops = &fsl_dma_ops;
dma->dai.pcm_new = fsl_dma_new;
dma->dai.pcm_free = fsl_dma_free_dma_buffers;
dma->dai.open = fsl_dma_open;
dma->dai.close = fsl_dma_close;
dma->dai.ioctl = snd_soc_pcm_lib_ioctl;
dma->dai.hw_params = fsl_dma_hw_params;
dma->dai.hw_free = fsl_dma_hw_free;
dma->dai.pointer = fsl_dma_pointer;
dma->dai.pcm_construct = fsl_dma_new;
dma->dai.pcm_destruct = fsl_dma_free_dma_buffers;
/* Store the SSI-specific information that we need */
dma->ssi_stx_phys = res.start + REG_SSI_STX0;

View File

@ -33,6 +33,7 @@
* @fsysclk: system clock source to derive HCK, SCK and FS
* @spbaclk: SPBA clock (optional, depending on SoC design)
* @task: tasklet to handle the reset operation
* @lock: spin lock between hw_reset() and trigger()
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @slots: number of slots
@ -56,6 +57,7 @@ struct fsl_esai {
struct clk *fsysclk;
struct clk *spbaclk;
struct tasklet_struct task;
spinlock_t lock; /* Protect hw_reset and trigger */
u32 fifo_depth;
u32 slot_width;
u32 slots;
@ -676,8 +678,10 @@ static void fsl_esai_hw_reset(unsigned long arg)
{
struct fsl_esai *esai_priv = (struct fsl_esai *)arg;
bool tx = true, rx = false, enabled[2];
unsigned long lock_flags;
u32 tfcr, rfcr;
spin_lock_irqsave(&esai_priv->lock, lock_flags);
/* Save the registers */
regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr);
regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr);
@ -715,6 +719,8 @@ static void fsl_esai_hw_reset(unsigned long arg)
fsl_esai_trigger_start(esai_priv, tx);
if (enabled[rx])
fsl_esai_trigger_start(esai_priv, rx);
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
}
static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
@ -722,6 +728,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned long lock_flags;
esai_priv->channels[tx] = substream->runtime->channels;
@ -729,12 +736,16 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
spin_lock_irqsave(&esai_priv->lock, lock_flags);
fsl_esai_trigger_start(esai_priv, tx);
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
spin_lock_irqsave(&esai_priv->lock, lock_flags);
fsl_esai_trigger_stop(esai_priv, tx);
spin_unlock_irqrestore(&esai_priv->lock, lock_flags);
break;
default:
return -EINVAL;
@ -1002,6 +1013,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, esai_priv);
spin_lock_init(&esai_priv->lock);
ret = fsl_esai_hw_init(esai_priv);
if (ret)
return ret;

335
sound/soc/fsl/fsl_mqs.c Normal file
View File

@ -0,0 +1,335 @@
// SPDX-License-Identifier: GPL-2.0
//
// ALSA SoC IMX MQS driver
//
// Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
// Copyright 2019 NXP
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#define REG_MQS_CTRL 0x00
#define MQS_EN_MASK (0x1 << 28)
#define MQS_EN_SHIFT (28)
#define MQS_SW_RST_MASK (0x1 << 24)
#define MQS_SW_RST_SHIFT (24)
#define MQS_OVERSAMPLE_MASK (0x1 << 20)
#define MQS_OVERSAMPLE_SHIFT (20)
#define MQS_CLK_DIV_MASK (0xFF << 0)
#define MQS_CLK_DIV_SHIFT (0)
/* codec private data */
struct fsl_mqs {
struct regmap *regmap;
struct clk *mclk;
struct clk *ipg;
unsigned int reg_iomuxc_gpr2;
unsigned int reg_mqs_ctrl;
bool use_gpr;
};
#define FSL_MQS_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define FSL_MQS_FORMATS SNDRV_PCM_FMTBIT_S16_LE
static int fsl_mqs_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
unsigned long mclk_rate;
int div, res;
int lrclk;
mclk_rate = clk_get_rate(mqs_priv->mclk);
lrclk = params_rate(params);
/*
* mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate;
* if repeat_rate is 8, mqs can achieve better quality.
* oversample rate is fix to 32 currently.
*/
div = mclk_rate / (32 * lrclk * 2 * 8);
res = mclk_rate % (32 * lrclk * 2 * 8);
if (res == 0 && div > 0 && div <= 256) {
if (mqs_priv->use_gpr) {
regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
IMX6SX_GPR2_MQS_CLK_DIV_MASK,
(div - 1) << IMX6SX_GPR2_MQS_CLK_DIV_SHIFT);
regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
IMX6SX_GPR2_MQS_OVERSAMPLE_MASK, 0);
} else {
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
MQS_CLK_DIV_MASK,
(div - 1) << MQS_CLK_DIV_SHIFT);
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
MQS_OVERSAMPLE_MASK, 0);
}
} else {
dev_err(component->dev, "can't get proper divider\n");
}
return 0;
}
static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
/* Only LEFT_J & SLAVE mode is supported. */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
return 0;
}
static int fsl_mqs_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
if (mqs_priv->use_gpr)
regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
IMX6SX_GPR2_MQS_EN_MASK,
1 << IMX6SX_GPR2_MQS_EN_SHIFT);
else
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
MQS_EN_MASK,
1 << MQS_EN_SHIFT);
return 0;
}
static void fsl_mqs_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
if (mqs_priv->use_gpr)
regmap_update_bits(mqs_priv->regmap, IOMUXC_GPR2,
IMX6SX_GPR2_MQS_EN_MASK, 0);
else
regmap_update_bits(mqs_priv->regmap, REG_MQS_CTRL,
MQS_EN_MASK, 0);
}
static const struct snd_soc_component_driver soc_codec_fsl_mqs = {
.idle_bias_on = 1,
.non_legacy_dai_naming = 1,
};
static const struct snd_soc_dai_ops fsl_mqs_dai_ops = {
.startup = fsl_mqs_startup,
.shutdown = fsl_mqs_shutdown,
.hw_params = fsl_mqs_hw_params,
.set_fmt = fsl_mqs_set_dai_fmt,
};
static struct snd_soc_dai_driver fsl_mqs_dai = {
.name = "fsl-mqs-dai",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = FSL_MQS_RATES,
.formats = FSL_MQS_FORMATS,
},
.ops = &fsl_mqs_dai_ops,
};
static const struct regmap_config fsl_mqs_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = REG_MQS_CTRL,
.cache_type = REGCACHE_NONE,
};
static int fsl_mqs_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *gpr_np = NULL;
struct fsl_mqs *mqs_priv;
void __iomem *regs;
int ret;
mqs_priv = devm_kzalloc(&pdev->dev, sizeof(*mqs_priv), GFP_KERNEL);
if (!mqs_priv)
return -ENOMEM;
/* On i.MX6sx the MQS control register is in GPR domain
* But in i.MX8QM/i.MX8QXP the control register is moved
* to its own domain.
*/
if (of_device_is_compatible(np, "fsl,imx8qm-mqs"))
mqs_priv->use_gpr = false;
else
mqs_priv->use_gpr = true;
if (mqs_priv->use_gpr) {
gpr_np = of_parse_phandle(np, "gpr", 0);
if (!gpr_np) {
dev_err(&pdev->dev, "failed to get gpr node by phandle\n");
return -EINVAL;
}
mqs_priv->regmap = syscon_node_to_regmap(gpr_np);
if (IS_ERR(mqs_priv->regmap)) {
dev_err(&pdev->dev, "failed to get gpr regmap\n");
ret = PTR_ERR(mqs_priv->regmap);
goto err_free_gpr_np;
}
} else {
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
mqs_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
"core",
regs,
&fsl_mqs_regmap_config);
if (IS_ERR(mqs_priv->regmap)) {
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
PTR_ERR(mqs_priv->regmap));
return PTR_ERR(mqs_priv->regmap);
}
mqs_priv->ipg = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(mqs_priv->ipg)) {
dev_err(&pdev->dev, "failed to get the clock: %ld\n",
PTR_ERR(mqs_priv->ipg));
return PTR_ERR(mqs_priv->ipg);
}
}
mqs_priv->mclk = devm_clk_get(&pdev->dev, "mclk");
if (IS_ERR(mqs_priv->mclk)) {
dev_err(&pdev->dev, "failed to get the clock: %ld\n",
PTR_ERR(mqs_priv->mclk));
ret = PTR_ERR(mqs_priv->mclk);
goto err_free_gpr_np;
}
dev_set_drvdata(&pdev->dev, mqs_priv);
pm_runtime_enable(&pdev->dev);
ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_fsl_mqs,
&fsl_mqs_dai, 1);
if (ret)
goto err_free_gpr_np;
return 0;
err_free_gpr_np:
of_node_put(gpr_np);
return ret;
}
static int fsl_mqs_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM
static int fsl_mqs_runtime_resume(struct device *dev)
{
struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
if (mqs_priv->ipg)
clk_prepare_enable(mqs_priv->ipg);
if (mqs_priv->mclk)
clk_prepare_enable(mqs_priv->mclk);
if (mqs_priv->use_gpr)
regmap_write(mqs_priv->regmap, IOMUXC_GPR2,
mqs_priv->reg_iomuxc_gpr2);
else
regmap_write(mqs_priv->regmap, REG_MQS_CTRL,
mqs_priv->reg_mqs_ctrl);
return 0;
}
static int fsl_mqs_runtime_suspend(struct device *dev)
{
struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
if (mqs_priv->use_gpr)
regmap_read(mqs_priv->regmap, IOMUXC_GPR2,
&mqs_priv->reg_iomuxc_gpr2);
else
regmap_read(mqs_priv->regmap, REG_MQS_CTRL,
&mqs_priv->reg_mqs_ctrl);
if (mqs_priv->mclk)
clk_disable_unprepare(mqs_priv->mclk);
if (mqs_priv->ipg)
clk_disable_unprepare(mqs_priv->ipg);
return 0;
}
#endif
static const struct dev_pm_ops fsl_mqs_pm_ops = {
SET_RUNTIME_PM_OPS(fsl_mqs_runtime_suspend,
fsl_mqs_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static const struct of_device_id fsl_mqs_dt_ids[] = {
{ .compatible = "fsl,imx8qm-mqs", },
{ .compatible = "fsl,imx6sx-mqs", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids);
static struct platform_driver fsl_mqs_driver = {
.probe = fsl_mqs_probe,
.remove = fsl_mqs_remove,
.driver = {
.name = "fsl-mqs",
.of_match_table = fsl_mqs_dt_ids,
.pm = &fsl_mqs_pm_ops,
},
};
module_platform_driver(fsl_mqs_driver);
MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
MODULE_DESCRIPTION("MQS codec driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform: fsl-mqs");

View File

@ -69,8 +69,9 @@ static struct fiq_handler fh = {
.name = DRV_NAME,
};
static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
static int snd_imx_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@ -85,7 +86,8 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
static int snd_imx_pcm_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@ -104,7 +106,8 @@ static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
static int imx_pcm_fiq;
static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static int snd_imx_pcm_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@ -141,7 +144,9 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
static snd_pcm_uframes_t
snd_imx_pcm_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@ -165,7 +170,8 @@ static const struct snd_pcm_hardware snd_imx_hardware = {
.fifo_size = 0,
};
static int snd_imx_open(struct snd_pcm_substream *substream)
static int snd_imx_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd;
@ -194,7 +200,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
return 0;
}
static int snd_imx_close(struct snd_pcm_substream *substream)
static int snd_imx_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
@ -206,8 +213,9 @@ static int snd_imx_close(struct snd_pcm_substream *substream)
return 0;
}
static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
static int snd_imx_pcm_mmap(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
@ -222,17 +230,6 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
return ret;
}
static const struct snd_pcm_ops imx_pcm_ops = {
.open = snd_imx_open,
.close = snd_imx_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_imx_pcm_hw_params,
.prepare = snd_imx_pcm_prepare,
.trigger = snd_imx_pcm_trigger,
.pointer = snd_imx_pcm_pointer,
.mmap = snd_imx_pcm_mmap,
};
static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
@ -279,7 +276,8 @@ static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
static int ssi_irq;
static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
static int snd_imx_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
struct snd_pcm *pcm = rtd->pcm;
struct snd_pcm_substream *substream;
@ -329,7 +327,8 @@ static void imx_pcm_free(struct snd_pcm *pcm)
}
}
static void imx_pcm_fiq_free(struct snd_pcm *pcm)
static void snd_imx_pcm_free(struct snd_soc_component *component,
struct snd_pcm *pcm)
{
mxc_set_irq_fiq(ssi_irq, 0);
release_fiq(&fh);
@ -337,9 +336,16 @@ static void imx_pcm_fiq_free(struct snd_pcm *pcm)
}
static const struct snd_soc_component_driver imx_soc_component_fiq = {
.ops = &imx_pcm_ops,
.pcm_new = imx_pcm_fiq_new,
.pcm_free = imx_pcm_fiq_free,
.open = snd_imx_open,
.close = snd_imx_close,
.ioctl = snd_soc_pcm_lib_ioctl,
.hw_params = snd_imx_pcm_hw_params,
.prepare = snd_imx_pcm_prepare,
.trigger = snd_imx_pcm_trigger,
.pointer = snd_imx_pcm_pointer,
.mmap = snd_imx_pcm_mmap,
.pcm_construct = snd_imx_pcm_new,
.pcm_destruct = snd_imx_pcm_free,
};
int imx_pcm_fiq_init(struct platform_device *pdev,

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