Merge remote-tracking branch 'asoc/for-5.10' into asoc-next

This commit is contained in:
Mark Brown 2020-10-09 15:42:31 +01:00
commit c890e30b06
No known key found for this signature in database
GPG key ID: 24D68B725D5487D0
319 changed files with 23229 additions and 12584 deletions

View file

@ -0,0 +1,16 @@
What: /sys/devices/pci0000:00/<dev>/fw_version
Date: September 2020
Contact: Cezary Rojewski <cezary.rojewski@intel.com>
Description:
Version of AudioDSP firmware ASoC catpt driver is
communicating with.
Format: %d.%d.%d.%d, type:major:minor:build.
What: /sys/devices/pci0000:00/<dev>/fw_info
Date: September 2020
Contact: Cezary Rojewski <cezary.rojewski@intel.com>
Description:
Detailed AudioDSP firmware build information including
build hash and log-providers hash. This information is
obtained during initial handshake with firmware.
Format: %s.

View file

@ -10,6 +10,11 @@ Required properties:
Optional properties:
- reset-gpios: A GPIO specifier for the power down & reset pin
- mute-gpios: A GPIO specifier for the soft mute pin
- AVDD-supply: Analog power supply
- DVDD-supply: Digital power supply
- dsd-path: Select DSD input pins for ak4497
0: select #16, #17, #19 pins
1: select #3, #4, #5 pins
Example:

View file

@ -10,6 +10,8 @@ Required properties:
Optional properties:
- reset-gpios: A GPIO specifier for the power down & reset pin.
- AVDD-supply: Analog power supply
- DVDD-supply: Digital power supply
Example:

View file

@ -15,7 +15,11 @@ properties:
const: 0
compatible:
const: allwinner,sun8i-a33-codec
oneOf:
- items:
- const: allwinner,sun50i-a64-codec
- const: allwinner,sun8i-a33-codec
- const: allwinner,sun8i-a33-codec
reg:
maxItems: 1

View file

@ -0,0 +1,74 @@
# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/cirrus,cs4234.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Cirrus Logic cs4234 audio CODEC
maintainers:
- patches@opensource.cirrus.com
description:
The CS4234 is a highly versatile CODEC that combines 4 channels of
high performance analog to digital conversion, 4 channels of high
performance digital to analog conversion for audio, and 1 channel of
digital to analog conversion to provide a nondelayed audio reference
signal to an external Class H tracking power supply. If not used to
drive a tracking power supply, the 5th DAC can instead be used as a
standard audio grade DAC, with performance specifications identical
to that of the 4 DACs in the audio path. Additionally, the CS4234
includes tunable group delay for each of the 4 audio DAC paths to
provide lead time for the external switch-mode power supply, and a
nondelayed path into the DAC outputs for input signals requiring a
low-latency path to the outputs.
properties:
compatible:
enum:
- cirrus,cs4234
reg:
description:
The 7-bit I2C address depends on the state of the ADx pins, in
binary given by [0 0 1 0 AD2 AD1 AD0 0].
items:
minimum: 0x10
maximum: 0x17
VA-supply:
description:
Analogue power supply.
VL-supply:
description:
Interface power supply.
reset-gpios:
maxItems: 1
required:
- compatible
- reg
- VA-supply
- VL-supply
additionalProperties: false
examples:
- |
i2c@e0004000 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0xe0004000 0x1000>;
cs4234: codec@11 {
compatible = "cirrus,cs4234";
reg = <0x11>;
VA-supply = <&vdd3v3>;
VL-supply = <&vdd3v3>;
reset-gpios = <&gpio 0>;
};
};

View file

@ -1,68 +0,0 @@
Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller
The Freescale S/PDIF audio block is a stereo transceiver that allows the
processor to receive and transmit digital audio via an coaxial cable or
a fibre cable.
Required properties:
- compatible : Compatible list, should contain one of the following
compatibles:
"fsl,imx35-spdif",
"fsl,vf610-spdif",
"fsl,imx6sx-spdif",
- reg : Offset and length of the register set for the device.
- interrupts : Contains the spdif interrupt.
- dmas : Generic dma devicetree binding as described in
Documentation/devicetree/bindings/dma/dma.txt.
- dma-names : Two dmas have to be defined, "tx" and "rx".
- clocks : Contains an entry for each entry in clock-names.
- clock-names : Includes the following entries:
"core" The core clock of spdif controller.
"rxtx<0-7>" Clock source list for tx and rx clock.
This clock list should be identical to the source
list connecting to the spdif clock mux in "SPDIF
Transceiver Clock Diagram" of SoC reference manual.
It can also be referred to TxClk_Source bit of
register SPDIF_STC.
"spba" The spba clock is required when SPDIF is placed as a
bus slave of the Shared Peripheral Bus and when two
or more bus masters (CPU, DMA or DSP) try to access
it. This property is optional depending on the SoC
design.
Optional properties:
- big-endian : If this property is absent, the native endian mode
will be in use as default, or the big endian mode
will be in use for all the device registers.
Example:
spdif: spdif@2004000 {
compatible = "fsl,imx35-spdif";
reg = <0x02004000 0x4000>;
interrupts = <0 52 0x04>;
dmas = <&sdma 14 18 0>,
<&sdma 15 18 0>;
dma-names = "rx", "tx";
clocks = <&clks 197>, <&clks 3>,
<&clks 197>, <&clks 107>,
<&clks 0>, <&clks 118>,
<&clks 62>, <&clks 139>,
<&clks 0>;
clock-names = "core", "rxtx0",
"rxtx1", "rxtx2",
"rxtx3", "rxtx4",
"rxtx5", "rxtx6",
"rxtx7";
big-endian;
};

View file

@ -0,0 +1,110 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/fsl,spdif.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller
maintainers:
- Shengjiu Wang <shengjiu.wang@nxp.com>
description: |
The Freescale S/PDIF audio block is a stereo transceiver that allows the
processor to receive and transmit digital audio via an coaxial cable or
a fibre cable.
properties:
compatible:
enum:
- fsl,imx35-spdif
- fsl,vf610-spdif
- fsl,imx6sx-spdif
reg:
maxItems: 1
interrupts:
maxItems: 1
dmas:
items:
- description: DMA controller phandle and request line for RX
- description: DMA controller phandle and request line for TX
dma-names:
items:
- const: rx
- const: tx
clocks:
items:
- description: The core clock of spdif controller.
- description: Clock for tx0 and rx0.
- description: Clock for tx1 and rx1.
- description: Clock for tx2 and rx2.
- description: Clock for tx3 and rx3.
- description: Clock for tx4 and rx4.
- description: Clock for tx5 and rx5.
- description: Clock for tx6 and rx6.
- description: Clock for tx7 and rx7.
- description: The spba clock is required when SPDIF is placed as a bus
slave of the Shared Peripheral Bus and when two or more bus masters
(CPU, DMA or DSP) try to access it. This property is optional depending
on the SoC design.
minItems: 9
clock-names:
items:
- const: core
- const: rxtx0
- const: rxtx1
- const: rxtx2
- const: rxtx3
- const: rxtx4
- const: rxtx5
- const: rxtx6
- const: rxtx7
- const: spba
minItems: 9
big-endian:
$ref: /schemas/types.yaml#/definitions/flag
description: |
If this property is absent, the native endian mode will be in use
as default, or the big endian mode will be in use for all the device
registers. Set this flag for HCDs with big endian descriptors and big
endian registers.
required:
- compatible
- reg
- interrupts
- dmas
- dma-names
- clocks
- clock-names
additionalProperties: false
examples:
- |
spdif@2004000 {
compatible = "fsl,imx35-spdif";
reg = <0x02004000 0x4000>;
interrupts = <0 52 0x04>;
dmas = <&sdma 14 18 0>,
<&sdma 15 18 0>;
dma-names = "rx", "tx";
clocks = <&clks 197>, <&clks 3>,
<&clks 197>, <&clks 107>,
<&clks 0>, <&clks 118>,
<&clks 62>, <&clks 139>,
<&clks 0>;
clock-names = "core", "rxtx0",
"rxtx1", "rxtx2",
"rxtx3", "rxtx4",
"rxtx5", "rxtx6",
"rxtx7";
big-endian;
};

View file

@ -38,6 +38,8 @@ The compatible list for this generic sound card currently:
"fsl,imx-audio-wm8524"
"fsl,imx-audio-tlv320aic32x4"
Required properties:
- compatible : Contains one of entries in the compatible list.

View file

@ -1,16 +0,0 @@
Device-Tree bindings for dummy HDMI codec
Required properties:
- compatible: should be "linux,hdmi-audio".
CODEC output pins:
* TX
CODEC input pins:
* RX
Example node:
hdmi_audio: hdmi_audio@0 {
compatible = "linux,hdmi-audio";
};

View file

@ -17,6 +17,7 @@ properties:
compatible:
enum:
- intel,keembay-i2s
- intel,keembay-tdm
"#sound-dai-cells":
const: 0

View file

@ -55,5 +55,5 @@ audio-codec@10 {
compatible = "maxim,max98090";
reg = <0x10>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(H, 4) GPIO_ACTIVE_HIGH>;
interrupts = <TEGRA_GPIO(H, 4) IRQ_TYPE_LEVEL_HIGH>;
};

View file

@ -0,0 +1,73 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/mchp,spdifrx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip S/PDIF Rx Controller Device Tree Bindings
maintainers:
- Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
description:
The Microchip Sony/Philips Digital Interface Receiver is a
serial port compliant with the IEC-60958 standard.
properties:
"#sound-dai-cells":
const: 0
compatible:
const: microchip,sama7g5-spdifrx
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: Peripheral Bus Clock
- description: Generic Clock
clock-names:
items:
- const: pclk
- const: gclk
dmas:
description: RX DMA Channel
maxItems: 1
dma-names:
const: rx
required:
- "#sound-dai-cells"
- compatible
- reg
- interrupts
- clocks
- clock-names
- dmas
- dma-names
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/at91.h>
#include <dt-bindings/dma/at91.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
spdifrx: spdifrx@e1614000 {
#sound-dai-cells = <0>;
compatible = "microchip,sama7g5-spdifrx";
reg = <0xe1614000 0x4000>;
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&dma0 AT91_XDMAC_DT_PERID(49)>;
dma-names = "rx";
clocks = <&pmc PMC_TYPE_PERIPHERAL 84>, <&pmc PMC_TYPE_GCK 84>;
clock-names = "pclk", "gclk";
};

View file

@ -0,0 +1,75 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/mchp,spdiftx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip S/PDIF Tx Controller Device Tree Bindings
maintainers:
- Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
description:
The Microchip Sony/Philips Digital Interface Transmitter is a
serial port compliant with the IEC-60958 standard.
properties:
"#sound-dai-cells":
const: 0
compatible:
const: microchip,sama7g5-spdiftx
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: Peripheral Bus Clock
- description: Generic Clock
clock-names:
items:
- const: pclk
- const: gclk
dmas:
description: TX DMA Channel
maxItems: 1
dma-names:
const: tx
required:
- "#sound-dai-cells"
- compatible
- reg
- interrupts
- clocks
- clock-names
- dmas
- dma-names
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/at91.h>
#include <dt-bindings/dma/at91.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
spdiftx@e1618000 {
#sound-dai-cells = <0>;
compatible = "microchip,sama7g5-spdiftx";
reg = <0xe1618000 0x4000>;
interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
dmas = <&dma0 AT91_XDMAC_DT_PERID(50)>;
dma-names = "tx";
clocks = <&pmc PMC_TYPE_PERIPHERAL 85>, <&pmc PMC_TYPE_GCK 85>;
clock-names = "pclk", "gclk";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spdiftx_default>;
};

View file

@ -0,0 +1,61 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/mt6359.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Mediatek MT6359 Codec Device Tree Bindings
maintainers:
- Eason Yen <eason.yen@mediatek.com>
- Jiaxin Yu <jiaxin.yu@mediatek.com>
- Shane Chien <shane.chien@mediatek.com>
description: |
The communication between MT6359 and SoC is through Mediatek PMIC wrapper.
For more detail, please visit Mediatek PMIC wrapper documentation.
Must be a child node of PMIC wrapper.
properties:
mediatek,dmic-mode:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Indicates how many data pins are used to transmit two channels of PDM
signal. 0 means two wires, 1 means one wire. Default value is 0.
enum:
- 0 # one wire
- 1 # two wires
mediatek,mic-type-0:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Specifies the type of mic type connected to adc0
enum:
- 0 # IDLE - mic in turn-off status
- 1 # ACC - analog mic with alternating coupling
- 2 # DMIC - digital mic
- 3 # DCC - analog mic with direct couping
- 4 # DCC_ECM_DIFF - analog electret condenser mic with differential mode
- 5 # DCC_ECM_SINGLE - analog electret condenser mic with single mode
mediatek,mic-type-1:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Specifies the type of mic type connected to adc1
mediatek,mic-type-2:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Specifies the type of mic type connected to adc2
additionalProperties: false
examples:
- |
mt6359codec: mt6359codec {
mediatek,dmic-mode = <0>;
mediatek,mic-type-0 = <2>;
};
...

View file

@ -3,6 +3,7 @@ MT8183 with MT6358, DA7219, MAX98357, and RT1015 CODECS
Required properties:
- compatible : "mediatek,mt8183_da7219_max98357" for MAX98357A codec
"mediatek,mt8183_da7219_rt1015" for RT1015 codec
"mediatek,mt8183_da7219_rt1015p" for RT1015P codec
- mediatek,headset-codec: the phandles of da7219 codecs
- mediatek,platform: the phandle of MT8183 ASoC platform

View file

@ -34,6 +34,13 @@ Required properties:
* DMIC
* Ext Spk
Optional properties:
- aux-devs : A list of phandles for auxiliary devices (e.g. analog
amplifiers) that do not appear directly within the DAI
links. Should be connected to another audio component
using "qcom,audio-routing".
Dai-link subnode properties and subnodes:
Required dai-link subnodes:

View file

@ -55,6 +55,14 @@ This binding describes the APQ8096 sound card, which uses qdsp for audio.
Value type: <stringlist>
Definition: The user-visible name of this sound card.
- aux-devs
Usage: optional
Value type: <array of phandles>
Definition: A list of phandles for auxiliary devices (e.g. analog
amplifiers) that do not appear directly within the DAI
links. Should be connected to another audio component
using "audio-routing".
= dailinks
Each subnode of sndcard represents either a dailink, and subnodes of each
dailinks would be cpu/codec/platform dais.

View file

@ -1,79 +0,0 @@
* Qualcomm Technologies LPASS CPU DAI
This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS).
Required properties:
- compatible : "qcom,lpass-cpu" or "qcom,apq8016-lpass-cpu"
- clocks : Must contain an entry for each entry in clock-names.
- clock-names : A list which must include the following entries:
* "ahbix-clk"
* "mi2s-osr-clk"
* "mi2s-bit-clk"
: required clocks for "qcom,lpass-cpu-apq8016"
* "ahbix-clk"
* "mi2s-bit-clk0"
* "mi2s-bit-clk1"
* "mi2s-bit-clk2"
* "mi2s-bit-clk3"
* "pcnoc-mport-clk"
* "pcnoc-sway-clk"
- interrupts : Must contain an entry for each entry in
interrupt-names.
- interrupt-names : A list which must include the following entries:
* "lpass-irq-lpaif"
- pinctrl-N : One property must exist for each entry in
pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
for details of the property values.
- pinctrl-names : Must contain a "default" entry.
- reg : Must contain an address for each entry in reg-names.
- reg-names : A list which must include the following entries:
* "lpass-lpaif"
- #address-cells : Must be 1
- #size-cells : Must be 0
Optional properties:
- qcom,adsp : Phandle for the audio DSP node
By default, the driver uses up to 4 MI2S SD lines, for a total of 8 channels.
The SD lines to use can be configured by adding subnodes for each of the DAIs.
Required properties for each DAI (represented by a subnode):
- reg : Must be one of the DAI IDs
(usually part of dt-bindings header)
- qcom,playback-sd-lines: List of serial data lines to use for playback
Each SD line should be represented by a number from 0-3.
- qcom,capture-sd-lines : List of serial data lines to use for capture
Each SD line should be represented by a number from 0-3.
Note that adding a subnode changes the default to "no lines configured",
so both playback and capture lines should be configured when a subnode is added.
Example:
lpass@28100000 {
compatible = "qcom,lpass-cpu";
clocks = <&lcc AHBIX_CLK>, <&lcc MI2S_OSR_CLK>, <&lcc MI2S_BIT_CLK>;
clock-names = "ahbix-clk", "mi2s-osr-clk", "mi2s-bit-clk";
interrupts = <0 85 1>;
interrupt-names = "lpass-irq-lpaif";
pinctrl-names = "default", "idle";
pinctrl-0 = <&mi2s_default>;
pinctrl-1 = <&mi2s_idle>;
reg = <0x28100000 0x10000>;
reg-names = "lpass-lpaif";
qcom,adsp = <&adsp>;
#address-cells = <1>;
#size-cells = <0>;
/* Optional to set different MI2S SD lines */
dai@3 {
reg = <MI2S_QUATERNARY>;
qcom,playback-sd-lines = <0 1>;
};
};

View file

@ -0,0 +1,219 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/qcom,lpass-cpu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Technologies Inc. LPASS CPU dai driver bindings
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
- Rohit kumar <rohitkr@codeaurora.org>
description: |
Qualcomm Technologies Inc. SOC Low-Power Audio SubSystem (LPASS) that consist
of MI2S interface for audio data transfer on external codecs. LPASS cpu driver
is a module to configure Low-Power Audio Interface(LPAIF) core registers
across different IP versions.
properties:
compatible:
enum:
- qcom,lpass-cpu
- qcom,apq8016-lpass-cpu
- qcom,sc7180-lpass-cpu
reg:
maxItems: 2
description: LPAIF core registers
reg-names:
maxItems: 2
clocks:
minItems: 3
maxItems: 6
clock-names:
minItems: 3
maxItems: 6
interrupts:
maxItems: 2
description: LPAIF DMA buffer interrupt
interrupt-names:
maxItems: 2
qcom,adsp:
$ref: /schemas/types.yaml#/definitions/phandle
description: Phandle for the audio DSP node
iommus:
maxItems: 2
description: Phandle to apps_smmu node with sid mask
power-domains:
maxItems: 1
'#sound-dai-cells':
const: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^dai-link@[0-9a-f]$":
type: object
description: |
LPASS CPU dai node for each I2S device. Bindings of each node
depends on the specific driver providing the functionality and
properties.
properties:
reg:
maxItems: 1
description: Must be one of the DAI ID
qcom,playback-sd-lines:
$ref: /schemas/types.yaml#/definitions/uint32-array
description: list of MI2S data lines for playback
qcom,capture-sd-lines:
$ref: /schemas/types.yaml#/definitions/uint32-array
description: list of MI2S data lines for capture
required:
- reg
additionalProperties: false
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
- interrupts
- interrupt-names
- '#sound-dai-cells'
additionalProperties: false
allOf:
- if:
properties:
compatible:
contains:
const: qcom,lpass-cpu
then:
properties:
clock-names:
items:
- const: ahbix-clk
- const: mi2s-osr-clk
- const: mi2s-bit-clk
- if:
properties:
compatible:
contains:
const: qcom,apq8016-lpass-cpu
then:
properties:
clock-names:
items:
- const: ahbix-clk
- const: mi2s-bit-clk0
- const: mi2s-bit-clk1
- const: mi2s-bit-clk2
- const: mi2s-bit-clk3
- const: pcnoc-mport-clk
- const: pcnoc-sway-clk
- if:
properties:
compatible:
contains:
const: qcom,sc7180-lpass-cpu
then:
properties:
clock-names:
oneOf:
- items: #for I2S
- const: pcnoc-sway-clk
- const: audio-core
- const: mclk0
- const: pcnoc-mport-clk
- const: mi2s-bit-clk0
- const: mi2s-bit-clk1
- items: #for HDMI
- const: pcnoc-sway-clk
- const: audio-core
- const: pcnoc-mport-clk
reg-names:
anyOf:
- items: #for I2S
- const: lpass-lpaif
- items: #for I2S and HDMI
- const: lpass-hdmiif
- const: lpass-lpaif
interrupt-names:
anyOf:
- items: #for I2S
- const: lpass-irq-lpaif
- items: #for I2S and HDMI
- const: lpass-irq-lpaif
- const: lpass-irq-hdmi
required:
- iommus
- power-domains
examples:
- |
#include <dt-bindings/sound/sc7180-lpass.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
lpass@62d80000 {
compatible = "qcom,sc7180-lpass-cpu";
reg = <0 0x62d87000 0 0x68000>,
<0 0x62f00000 0 0x29000>;
reg-names = "lpass-hdmiif",
"lpass-lpaif";
iommus = <&apps_smmu 0x1020 0>,
<&apps_smmu 0x1032 0>;
power-domains = <&lpass_hm 0>;
clocks = <&gcc 131>,
<&lpasscorecc 6>,
<&lpasscorecc 7>,
<&lpasscorecc 10>,
<&lpasscorecc 8>,
<&lpasscorecc 9>;
clock-names = "pcnoc-sway-clk", "audio-core",
"mclk0", "pcnoc-mport-clk",
"mi2s-bit-clk0", "mi2s-bit-clk1";
interrupts = <0 160 1>,
<0 268 1>;
interrupt-names = "lpass-irq-lpaif",
"lpass-irq-hdmi";
#sound-dai-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
/* Optional to set different MI2S SD lines */
dai-link@0 {
reg = <MI2S_PRIMARY>;
qcom,playback-sd-lines = <1>;
qcom,capture-sd-lines = <0>;
};
};
};
...

View file

@ -98,6 +98,24 @@ configuration of each dai. Must contain the following properties.
0 - MSB
1 - LSB
= AFE CLOCKSS
"clocks" subnode of the AFE node. It represents q6afe clocks
"clocks" node should have following properties.
- compatible:
Usage: required
Value type: <stringlist>
Definition: must be "qcom,q6afe-clocks"
- #clock-cells:
Usage: required
Value type: <u32>
Definition: Must be 2. Clock Id followed by
below valid clock coupling attributes.
1 - for no coupled clock
2 - for dividend of the coupled clock
3 - for divisor of the coupled clock
4 - for inverted and no couple clock
= EXAMPLE
apr-service@4 {
@ -175,4 +193,9 @@ apr-service@4 {
qcom,sd-lines = <1>;
};
};
clocks {
compatible = "qcom,q6afe-clocks";
#clock-cells = <2>;
};
};

View file

@ -24,6 +24,14 @@ This binding describes the SDM845 sound card, which uses qdsp for audio.
Value type: <stringlist>
Definition: The user-visible name of this sound card.
- aux-devs
Usage: optional
Value type: <array of phandles>
Definition: A list of phandles for auxiliary devices (e.g. analog
amplifiers) that do not appear directly within the DAI
links. Should be connected to another audio component
using "audio-routing".
= dailinks
Each subnode of sndcard represents either a dailink, and subnodes of each
dailinks would be cpu/codec/platform dais.

View file

@ -0,0 +1,36 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/realtek,rt1015p.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek rt1015p codec devicetree bindings
maintainers:
- Tzung-Bi Shih <tzungbi@google.com>
description: |
Rt1015p is a rt1015 variant which does not support I2C and
only supports S24, 48kHz, 64FS.
properties:
compatible:
const: realtek,rt1015p
sdb-gpios:
description:
GPIO used for shutdown control.
0 means shut down; 1 means power on.
maxItems: 1
required:
- compatible
examples:
- |
#include <dt-bindings/gpio/gpio.h>
rt1015p: rt1015p {
compatible = "realtek,rt1015p";
sdb-gpios = <&pio 175 GPIO_ACTIVE_HIGH>;
};

View file

@ -27,6 +27,7 @@ properties:
- enum:
- rockchip,rk3188-spdif
- rockchip,rk3288-spdif
- rockchip,rk3308-spdif
- const: rockchip,rk3066-spdif
reg:

View file

@ -88,7 +88,7 @@ rt5640 {
compatible = "realtek,rt5640";
reg = <0x1c>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(W, 3) GPIO_ACTIVE_HIGH>;
interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_HIGH>;
realtek,ldo1-en-gpios =
<&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
};

View file

@ -72,7 +72,7 @@ rt5659 {
compatible = "realtek,rt5659";
reg = <0x1b>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(W, 3) GPIO_ACTIVE_HIGH>;
interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_HIGH>;
realtek,ldo1-en-gpios =
<&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
};

View file

@ -62,7 +62,7 @@ rt5659 {
compatible = "realtek,rt5665";
reg = <0x1b>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(W, 3) GPIO_ACTIVE_HIGH>;
interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_HIGH>;
realtek,ldo1-en-gpios =
<&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
};

View file

@ -41,7 +41,7 @@ rt5668 {
compatible = "realtek,rt5668b";
reg = <0x1a>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(U, 6) GPIO_ACTIVE_HIGH>;
interrupts = <TEGRA_GPIO(U, 6) IRQ_TYPE_LEVEL_HIGH>;
realtek,ldo1-en-gpios =
<&gpio TEGRA_GPIO(R, 2) GPIO_ACTIVE_HIGH>;
realtek,dmic1-data-pin = <1>;

View file

@ -64,7 +64,7 @@ rt5677 {
compatible = "realtek,rt5677";
reg = <0x2c>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(W, 3) GPIO_ACTIVE_HIGH>;
interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;

View file

@ -58,7 +58,7 @@ rt5682 {
compatible = "realtek,rt5682i";
reg = <0x1a>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(U, 6) GPIO_ACTIVE_HIGH>;
interrupts = <TEGRA_GPIO(U, 6) IRQ_TYPE_LEVEL_HIGH>;
realtek,ldo1-en-gpios =
<&gpio TEGRA_GPIO(R, 2) GPIO_ACTIVE_HIGH>;
realtek,dmic1-data-pin = <1>;

View file

@ -11,12 +11,11 @@ maintainers:
properties:
compatible:
oneOf:
- const: samsung,aries-wm8994
description: With FM radio and modem master
- const: samsung,fascinate4g-wm8994
description: Without FM radio and modem slave
enum:
# With FM radio and modem master
- samsung,aries-wm8994
# Without FM radio and modem slave
- samsung,fascinate4g-wm8994
model:
$ref: /schemas/types.yaml#/definitions/string

View file

@ -21,7 +21,8 @@ properties:
type: object
properties:
sound-dai:
$ref: /schemas/types.yaml#/definitions/phandle
$ref: /schemas/types.yaml#/definitions/phandle-array
maxItems: 1
description: phandle to the I2S controller
required:
- sound-dai
@ -30,7 +31,8 @@ properties:
type: object
properties:
sound-dai:
$ref: /schemas/types.yaml#/definitions/phandle
$ref: /schemas/types.yaml#/definitions/phandle-array
maxItems: 1
description: phandle to the WM1811 CODEC
required:
- sound-dai

View file

@ -28,6 +28,11 @@ properties:
$ref: /schemas/types.yaml#/definitions/string
description: The user-visible name of this sound complex.
assigned-clock-parents: true
assigned-clock-rates: true
assigned-clocks: true
clocks: true
cpu:
type: object
properties:

View file

@ -41,6 +41,12 @@ properties:
- samsung,exynos7-i2s
- samsung,exynos7-i2s1
'#address-cells':
const: 1
'#size-cells':
const: 0
reg:
maxItems: 1
@ -58,6 +64,9 @@ properties:
- const: rx
- const: tx-sec
assigned-clock-parents: true
assigned-clocks: true
clocks:
minItems: 1
maxItems: 3
@ -92,6 +101,9 @@ properties:
- const: i2s_cdclk2
description: Names of the CDCLK I2S output clocks.
interrupts:
maxItems: 1
samsung,idma-addr:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
@ -104,6 +116,9 @@ properties:
pinctrl-names:
const: default
power-domains:
maxItems: 1
"#sound-dai-cells":
const: 1

View file

@ -19,6 +19,10 @@ properties:
"#sound-dai-cells":
const: 0
assigned-clock-parents: true
assigned-clock-rates: true
assigned-clocks: true
clocks:
items:
- description: the clock provider of SYS_MCLK

View file

@ -1,37 +0,0 @@
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", "ti,tas2563".
- reg: - The i2c address. Should be 0x4c, 0x4d, 0x4e or 0x4f.
- ti,imon-slot-no:- TDM TX current sense time slot.
- ti,vmon-slot-no:- TDM TX voltage sense time slot. This slot must always be
greater then ti,imon-slot-no.
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: 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-gpio = <&gpio1 15 0>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <1>;
};

View file

@ -16,11 +16,19 @@ description: |
Integrated speaker voltage and current sense provides for
real time monitoring of loudspeaker behavior.
Specifications about the audio amplifier can be found at:
https://www.ti.com/lit/gpn/tas2562
https://www.ti.com/lit/gpn/tas2563
https://www.ti.com/lit/gpn/tas2564
https://www.ti.com/lit/gpn/tas2110
properties:
compatible:
enum:
- ti,tas2562
- ti,tas2563
- ti,tas2564
- ti,tas2110
reg:
maxItems: 1

View file

@ -0,0 +1,76 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2020 Texas Instruments Incorporated
%YAML 1.2
---
$id: "http://devicetree.org/schemas/sound/tas2764.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Texas Instruments TAS2764 Smart PA
maintainers:
- Dan Murphy <dmurphy@ti.com>
description: |
The TAS2764 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.
properties:
compatible:
enum:
- ti,tas2764
reg:
maxItems: 1
description: |
I2C address of the device can be between 0x38 to 0x45.
reset-gpios:
maxItems: 1
description: GPIO used to reset the device.
shutdown-gpios:
maxItems: 1
description: GPIO used to control the state of the device.
interrupts:
maxItems: 1
ti,imon-slot-no:
$ref: /schemas/types.yaml#/definitions/uint32
description: TDM TX current sense time slot.
ti,vmon-slot-no:
$ref: /schemas/types.yaml#/definitions/uint32
description: TDM TX voltage sense time slot.
'#sound-dai-cells':
const: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
codec: codec@38 {
compatible = "ti,tas2764";
reg = <0x38>;
#sound-dai-cells = <1>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpios = <&gpio1 15 0>;
shutdown-gpios = <&gpio1 15 0>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};
};
...

View file

@ -24,11 +24,14 @@ properties:
reg:
maxItems: 1
description: |
I2C address of the device can be one of these 0x4c, 0x4d, 0x4e or 0x4f
I2C address of the device can be between 0x41 to 0x48.
reset-gpio:
description: GPIO used to reset the device.
shutdown-gpios:
description: GPIO used to control the state of the device.
interrupts:
maxItems: 1
@ -41,6 +44,7 @@ properties:
description: TDM TX voltage sense time slot.
ti,asi-format:
deprecated: true
$ref: /schemas/types.yaml#/definitions/uint32
description: Sets TDM RX capture edge.
enum:
@ -62,13 +66,14 @@ examples:
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
codec: codec@4c {
codec: codec@41 {
compatible = "ti,tas2770";
reg = <0x4c>;
reg = <0x41>;
#sound-dai-cells = <1>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpio = <&gpio1 15 0>;
shutdown-gpios = <&gpio1 14 0>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};

View file

@ -18,18 +18,25 @@ description: |
PLL15 (for 44.1KHz). The same PLLs are used for McASP10's AUXCLK clock via
different HSDIVIDER.
Clocking setup for 48KHz family:
PLL4 ---> PLL4_HSDIV0 ---> MCASP10_AUXCLK ---> McASP10.auxclk
|-> PLL4_HSDIV2 ---> AUDIO_REFCLK2 ---> pcm3168a.SCKI
Clocking setup for j721e:
48KHz family:
PLL4 ---> PLL4_HSDIV0 ---> MCASP10_AUXCLK ---> McASP10.auxclk
|-> PLL4_HSDIV2 ---> AUDIO_REFCLK2 ---> pcm3168a.SCKI
Clocking setup for 44.1KHz family:
PLL15 ---> PLL15_HSDIV0 ---> MCASP10_AUXCLK ---> McASP10.auxclk
|-> PLL15_HSDIV2 ---> AUDIO_REFCLK2 ---> pcm3168a.SCKI
44.1KHz family:
PLL15 ---> PLL15_HSDIV0 ---> MCASP10_AUXCLK ---> McASP10.auxclk
|-> PLL15_HSDIV2 ---> AUDIO_REFCLK2 ---> pcm3168a.SCKI
Clocking setup for j7200:
48KHz family:
PLL4 ---> PLL4_HSDIV0 ---> MCASP0_AUXCLK ---> McASP0.auxclk
|-> PLL4_HSDIV2 ---> AUDIO_REFCLK2 ---> pcm3168a.SCKI
properties:
compatible:
items:
- const: ti,j721e-cpb-audio
enum:
- ti,j721e-cpb-audio
- ti,j7200-cpb-audio
model:
$ref: /schemas/types.yaml#/definitions/string
@ -44,22 +51,12 @@ properties:
$ref: /schemas/types.yaml#/definitions/phandle
clocks:
items:
- description: AUXCLK clock for McASP used by CPB audio
- description: Parent for CPB_McASP auxclk (for 48KHz)
- description: Parent for CPB_McASP auxclk (for 44.1KHz)
- description: SCKI clock for the pcm3168a codec on CPB
- description: Parent for CPB_SCKI clock (for 48KHz)
- description: Parent for CPB_SCKI clock (for 44.1KHz)
minItems: 4
maxItems: 6
clock-names:
items:
- const: cpb-mcasp-auxclk
- const: cpb-mcasp-auxclk-48000
- const: cpb-mcasp-auxclk-44100
- const: cpb-codec-scki
- const: cpb-codec-scki-48000
- const: cpb-codec-scki-44100
minItems: 4
maxItems: 6
required:
- compatible
@ -71,6 +68,57 @@ required:
additionalProperties: false
allOf:
- if:
properties:
compatible:
contains:
const: ti,j721e-cpb-audio
then:
properties:
clocks:
minItems: 6
items:
- description: AUXCLK clock for McASP used by CPB audio
- description: Parent for CPB_McASP auxclk (for 48KHz)
- description: Parent for CPB_McASP auxclk (for 44.1KHz)
- description: SCKI clock for the pcm3168a codec on CPB
- description: Parent for CPB_SCKI clock (for 48KHz)
- description: Parent for CPB_SCKI clock (for 44.1KHz)
clock-names:
items:
- const: cpb-mcasp-auxclk
- const: cpb-mcasp-auxclk-48000
- const: cpb-mcasp-auxclk-44100
- const: cpb-codec-scki
- const: cpb-codec-scki-48000
- const: cpb-codec-scki-44100
- if:
properties:
compatible:
contains:
const: ti,j7200-cpb-audio
then:
properties:
clocks:
maxItems: 4
items:
- description: AUXCLK clock for McASP used by CPB audio
- description: Parent for CPB_McASP auxclk (for 48KHz)
- description: SCKI clock for the pcm3168a codec on CPB
- description: Parent for CPB_SCKI clock (for 48KHz)
clock-names:
items:
- const: cpb-mcasp-auxclk
- const: cpb-mcasp-auxclk-48000
- const: cpb-codec-scki
- const: cpb-codec-scki-48000
examples:
- |+
sound {

View file

@ -108,6 +108,12 @@ properties:
maximum: 7
default: [0, 0, 0, 0]
ti,asi-tx-drive:
type: boolean
description: |
When set the device will set the Tx ASI output to a Hi-Z state for unused
data cycles. Default is to drive the output low on unused ASI cycles.
patternProperties:
'^ti,gpo-config-[1-4]$':
$ref: /schemas/types.yaml#/definitions/uint32-array
@ -134,6 +140,49 @@ patternProperties:
4d - Drive weak low and active high
5d - Drive Hi-Z and active high
ti,gpio-config:
description: |
Defines the configuration and output drive for the General Purpose
Input and Output pin (GPIO1). Its value is a pair, the first value is for
the configuration type and the second value is for the output drive
type. The array is defined as <GPIO1_CFG GPIO1_DRV>
configuration for the GPIO pin can be one of the following:
0 - disabled
1 - GPIO1 is configured as a general-purpose output (GPO)
2 - (default) GPIO1 is configured as a device interrupt output (IRQ)
3 - GPIO1 is configured as a secondary ASI output (SDOUT2)
4 - GPIO1 is configured as a PDM clock output (PDMCLK)
8 - GPIO1 is configured as an input to control when MICBIAS turns on or
off (MICBIAS_EN)
9 - GPIO1 is configured as a general-purpose input (GPI)
10 - GPIO1 is configured as a master clock input (MCLK)
11 - GPIO1 is configured as an ASI input for daisy-chain (SDIN)
12 - GPIO1 is configured as a PDM data input for channel 1 and channel 2
(PDMDIN1)
13 - GPIO1 is configured as a PDM data input for channel 3 and channel 4
(PDMDIN2)
14 - GPIO1 is configured as a PDM data input for channel 5 and channel 6
(PDMDIN3)
15 - GPIO1 is configured as a PDM data input for channel 7 and channel 8
(PDMDIN4)
output drive type for the GPIO pin can be one of the following:
0 - Hi-Z output
1 - Drive active low and active high
2 - (default) Drive active low and weak high
3 - Drive active low and Hi-Z
4 - Drive weak low and active high
5 - Drive Hi-Z and active high
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32-array
- minItems: 2
maxItems: 2
items:
maximum: 15
default: [2, 2]
required:
- compatible
- reg
@ -150,6 +199,7 @@ examples:
ti,mic-bias-source = <6>;
ti,pdm-edge-select = <0 1 0 1>;
ti,gpi-config = <4 5 6 7>;
ti,gpio-config = <10 2>;
ti,gpo-config-1 = <0 0>;
ti,gpo-config-2 = <0 0>;
reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;

View file

@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/gpio/machine.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
@ -474,6 +475,20 @@ static struct platform_device gta02_buttons_device = {
},
};
static struct gpiod_lookup_table gta02_audio_gpio_table = {
.dev_id = "neo1973-audio",
.table = {
GPIO_LOOKUP("GPIOJ", 2, "amp-shut", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("GPIOJ", 1, "hp", GPIO_ACTIVE_HIGH),
{ },
},
};
static struct platform_device gta02_audio = {
.name = "neo1973-audio",
.id = -1,
};
static void __init gta02_map_io(void)
{
s3c24xx_init_io(gta02_iodesc, ARRAY_SIZE(gta02_iodesc));
@ -498,6 +513,7 @@ static struct platform_device *gta02_devices[] __initdata = {
&gta02_buttons_device,
&s3c_device_adc,
&s3c_device_ts,
&gta02_audio,
};
static void gta02_poweroff(void)
@ -524,6 +540,7 @@ static void __init gta02_machine_init(void)
i2c_register_board_info(0, gta02_i2c_devs, ARRAY_SIZE(gta02_i2c_devs));
gpiod_add_lookup_table(&gta02_audio_gpio_table);
platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices));
pm_power_off = gta02_poweroff;

View file

@ -475,6 +475,22 @@ static struct gpiod_lookup_table h1940_mmc_gpio_table = {
},
};
static struct gpiod_lookup_table h1940_audio_gpio_table = {
.dev_id = "h1940-audio",
.table = {
GPIO_LOOKUP("H1940_LATCH",
H1940_LATCH_AUDIO_POWER - H1940_LATCH_GPIO(0),
"speaker-power", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("GPIOG", 4, "hp", GPIO_ACTIVE_HIGH),
{ },
},
};
static struct platform_device h1940_audio = {
.name = "h1940-audio",
.id = -1,
};
static struct pwm_lookup h1940_pwm_lookup[] = {
PWM_LOOKUP("samsung-pwm", 0, "pwm-backlight", NULL, 36296,
PWM_POLARITY_NORMAL),
@ -651,6 +667,7 @@ static struct platform_device *h1940_devices[] __initdata = {
&s3c_device_ts,
&power_supply,
&h1940_battery,
&h1940_audio,
};
static void __init h1940_map_io(void)
@ -690,6 +707,7 @@ static void __init h1940_init(void)
s3c24xx_fb_set_platdata(&h1940_fb_info);
gpiod_add_lookup_table(&h1940_mmc_gpio_table);
gpiod_add_lookup_table(&h1940_audio_gpio_table);
s3c24xx_mci_set_platdata(&h1940_mmc_cfg);
s3c24xx_udc_set_platdata(&h1940_udc_cfg);
s3c24xx_ts_set_platdata(&h1940_ts_cfg);

View file

@ -728,6 +728,20 @@ static struct i2c_board_info rx1950_i2c_devices[] = {
},
};
static struct gpiod_lookup_table rx1950_audio_gpio_table = {
.dev_id = "rx1950-audio",
.table = {
GPIO_LOOKUP("GPIOG", 12, "hp-gpio", GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("GPIOA", 1, "speaker-power", GPIO_ACTIVE_HIGH),
{ },
},
};
static struct platform_device rx1950_audio = {
.name = "rx1950-audio",
.id = -1,
};
static struct platform_device *rx1950_devices[] __initdata = {
&s3c2410_device_dclk,
&s3c_device_lcd,
@ -746,6 +760,7 @@ static struct platform_device *rx1950_devices[] __initdata = {
&power_supply,
&rx1950_battery,
&rx1950_leds,
&rx1950_audio,
};
static void __init rx1950_map_io(void)
@ -813,6 +828,7 @@ static void __init rx1950_init_machine(void)
gpio_direction_output(S3C2410_GPJ(6), 0);
pwm_add_table(rx1950_pwm_lookup, ARRAY_SIZE(rx1950_pwm_lookup));
gpiod_add_lookup_table(&rx1950_audio_gpio_table);
platform_add_devices(rx1950_devices, ARRAY_SIZE(rx1950_devices));
i2c_register_board_info(0, rx1950_i2c_devices,

View file

@ -1243,6 +1243,106 @@ struct regmap_field *devm_regmap_field_alloc(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_regmap_field_alloc);
/**
* regmap_field_bulk_alloc() - Allocate and initialise a bulk register field.
*
* @regmap: regmap bank in which this register field is located.
* @rm_field: regmap register fields within the bank.
* @reg_field: Register fields within the bank.
* @num_fields: Number of register fields.
*
* The return value will be an -ENOMEM on error or zero for success.
* Newly allocated regmap_fields should be freed by calling
* regmap_field_bulk_free()
*/
int regmap_field_bulk_alloc(struct regmap *regmap,
struct regmap_field **rm_field,
struct reg_field *reg_field,
int num_fields)
{
struct regmap_field *rf;
int i;
rf = kcalloc(num_fields, sizeof(*rf), GFP_KERNEL);
if (!rf)
return -ENOMEM;
for (i = 0; i < num_fields; i++) {
regmap_field_init(&rf[i], regmap, reg_field[i]);
rm_field[i] = &rf[i];
}
return 0;
}
EXPORT_SYMBOL_GPL(regmap_field_bulk_alloc);
/**
* devm_regmap_field_bulk_alloc() - Allocate and initialise a bulk register
* fields.
*
* @dev: Device that will be interacted with
* @regmap: regmap bank in which this register field is located.
* @rm_field: regmap register fields within the bank.
* @reg_field: Register fields within the bank.
* @num_fields: Number of register fields.
*
* The return value will be an -ENOMEM on error or zero for success.
* Newly allocated regmap_fields will be automatically freed by the
* device management code.
*/
int devm_regmap_field_bulk_alloc(struct device *dev,
struct regmap *regmap,
struct regmap_field **rm_field,
struct reg_field *reg_field,
int num_fields)
{
struct regmap_field *rf;
int i;
rf = devm_kcalloc(dev, num_fields, sizeof(*rf), GFP_KERNEL);
if (!rf)
return -ENOMEM;
for (i = 0; i < num_fields; i++) {
regmap_field_init(&rf[i], regmap, reg_field[i]);
rm_field[i] = &rf[i];
}
return 0;
}
EXPORT_SYMBOL_GPL(devm_regmap_field_bulk_alloc);
/**
* regmap_field_bulk_free() - Free register field allocated using
* regmap_field_bulk_alloc.
*
* @field: regmap fields which should be freed.
*/
void regmap_field_bulk_free(struct regmap_field *field)
{
kfree(field);
}
EXPORT_SYMBOL_GPL(regmap_field_bulk_free);
/**
* devm_regmap_field_bulk_free() - Free a bulk register field allocated using
* devm_regmap_field_bulk_alloc.
*
* @dev: Device that will be interacted with
* @field: regmap field which should be freed.
*
* Free register field allocated using devm_regmap_field_bulk_alloc(). Usually
* drivers need not call this function, as the memory allocated via devm
* will be freed as per device-driver life-cycle.
*/
void devm_regmap_field_bulk_free(struct device *dev,
struct regmap_field *field)
{
devm_kfree(dev, field);
}
EXPORT_SYMBOL_GPL(devm_regmap_field_bulk_free);
/**
* devm_regmap_field_free() - Free a register field allocated using
* devm_regmap_field_alloc.

View file

@ -293,6 +293,7 @@ static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl,
max >>= flo->rshift;
}
flo->max = (s32)max;
flo->bits = bits;
pr_debug("%s: fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n",
__func__, fast, flo->fosr, flo->iosr,
@ -476,6 +477,9 @@ static int stm32_dfsdm_channels_configure(struct iio_dev *indio_dev,
if (!flo->res)
return -EINVAL;
dev_dbg(&indio_dev->dev, "Samples actual resolution: %d bits",
min(flo->bits, (u32)DFSDM_DATA_RES - 1));
for_each_set_bit(bit, &adc->smask,
sizeof(adc->smask) * BITS_PER_BYTE) {
chan = indio_dev->channels + bit;

View file

@ -249,6 +249,7 @@ enum stm32_dfsdm_sinc_order {
* @rshift: output sample right shift (hardware shift)
* @lshift: output sample left shift (software shift)
* @res: output sample resolution
* @bits: output sample resolution in bits
* @max: output sample maximum positive value
*/
struct stm32_dfsdm_filter_osr {
@ -257,6 +258,7 @@ struct stm32_dfsdm_filter_osr {
unsigned int rshift;
unsigned int lshift;
u64 res;
u32 bits;
s32 max;
};

View file

@ -16,7 +16,6 @@
#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/platform_data/dma-atmel.h>
#include <linux/of.h>
#include <linux/io.h>

View file

@ -107,6 +107,100 @@
#define QUINARY_TDM_RX_7 102
#define QUINARY_TDM_TX_7 103
#define DISPLAY_PORT_RX 104
#define WSA_CODEC_DMA_RX_0 105
#define WSA_CODEC_DMA_TX_0 106
#define WSA_CODEC_DMA_RX_1 107
#define WSA_CODEC_DMA_TX_1 108
#define WSA_CODEC_DMA_TX_2 109
#define VA_CODEC_DMA_TX_0 110
#define VA_CODEC_DMA_TX_1 111
#define VA_CODEC_DMA_TX_2 112
#define RX_CODEC_DMA_RX_0 113
#define TX_CODEC_DMA_TX_0 114
#define RX_CODEC_DMA_RX_1 115
#define TX_CODEC_DMA_TX_1 116
#define RX_CODEC_DMA_RX_2 117
#define TX_CODEC_DMA_TX_2 118
#define RX_CODEC_DMA_RX_3 119
#define TX_CODEC_DMA_TX_3 120
#define RX_CODEC_DMA_RX_4 121
#define TX_CODEC_DMA_TX_4 122
#define RX_CODEC_DMA_RX_5 123
#define TX_CODEC_DMA_TX_5 124
#define RX_CODEC_DMA_RX_6 125
#define RX_CODEC_DMA_RX_7 126
#define LPASS_CLK_ID_PRI_MI2S_IBIT 1
#define LPASS_CLK_ID_PRI_MI2S_EBIT 2
#define LPASS_CLK_ID_SEC_MI2S_IBIT 3
#define LPASS_CLK_ID_SEC_MI2S_EBIT 4
#define LPASS_CLK_ID_TER_MI2S_IBIT 5
#define LPASS_CLK_ID_TER_MI2S_EBIT 6
#define LPASS_CLK_ID_QUAD_MI2S_IBIT 7
#define LPASS_CLK_ID_QUAD_MI2S_EBIT 8
#define LPASS_CLK_ID_SPEAKER_I2S_IBIT 9
#define LPASS_CLK_ID_SPEAKER_I2S_EBIT 10
#define LPASS_CLK_ID_SPEAKER_I2S_OSR 11
#define LPASS_CLK_ID_QUI_MI2S_IBIT 12
#define LPASS_CLK_ID_QUI_MI2S_EBIT 13
#define LPASS_CLK_ID_SEN_MI2S_IBIT 14
#define LPASS_CLK_ID_SEN_MI2S_EBIT 15
#define LPASS_CLK_ID_INT0_MI2S_IBIT 16
#define LPASS_CLK_ID_INT1_MI2S_IBIT 17
#define LPASS_CLK_ID_INT2_MI2S_IBIT 18
#define LPASS_CLK_ID_INT3_MI2S_IBIT 19
#define LPASS_CLK_ID_INT4_MI2S_IBIT 20
#define LPASS_CLK_ID_INT5_MI2S_IBIT 21
#define LPASS_CLK_ID_INT6_MI2S_IBIT 22
#define LPASS_CLK_ID_QUI_MI2S_OSR 23
#define LPASS_CLK_ID_PRI_PCM_IBIT 24
#define LPASS_CLK_ID_PRI_PCM_EBIT 25
#define LPASS_CLK_ID_SEC_PCM_IBIT 26
#define LPASS_CLK_ID_SEC_PCM_EBIT 27
#define LPASS_CLK_ID_TER_PCM_IBIT 28
#define LPASS_CLK_ID_TER_PCM_EBIT 29
#define LPASS_CLK_ID_QUAD_PCM_IBIT 30
#define LPASS_CLK_ID_QUAD_PCM_EBIT 31
#define LPASS_CLK_ID_QUIN_PCM_IBIT 32
#define LPASS_CLK_ID_QUIN_PCM_EBIT 33
#define LPASS_CLK_ID_QUI_PCM_OSR 34
#define LPASS_CLK_ID_PRI_TDM_IBIT 35
#define LPASS_CLK_ID_PRI_TDM_EBIT 36
#define LPASS_CLK_ID_SEC_TDM_IBIT 37
#define LPASS_CLK_ID_SEC_TDM_EBIT 38
#define LPASS_CLK_ID_TER_TDM_IBIT 39
#define LPASS_CLK_ID_TER_TDM_EBIT 40
#define LPASS_CLK_ID_QUAD_TDM_IBIT 41
#define LPASS_CLK_ID_QUAD_TDM_EBIT 42
#define LPASS_CLK_ID_QUIN_TDM_IBIT 43
#define LPASS_CLK_ID_QUIN_TDM_EBIT 44
#define LPASS_CLK_ID_QUIN_TDM_OSR 45
#define LPASS_CLK_ID_MCLK_1 46
#define LPASS_CLK_ID_MCLK_2 47
#define LPASS_CLK_ID_MCLK_3 48
#define LPASS_CLK_ID_MCLK_4 49
#define LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE 50
#define LPASS_CLK_ID_INT_MCLK_0 51
#define LPASS_CLK_ID_INT_MCLK_1 52
#define LPASS_CLK_ID_MCLK_5 53
#define LPASS_CLK_ID_WSA_CORE_MCLK 54
#define LPASS_CLK_ID_WSA_CORE_NPL_MCLK 55
#define LPASS_CLK_ID_VA_CORE_MCLK 56
#define LPASS_CLK_ID_TX_CORE_MCLK 57
#define LPASS_CLK_ID_TX_CORE_NPL_MCLK 58
#define LPASS_CLK_ID_RX_CORE_MCLK 59
#define LPASS_CLK_ID_RX_CORE_NPL_MCLK 60
#define LPASS_CLK_ID_VA_CORE_2X_MCLK 61
#define LPASS_HW_AVTIMER_VOTE 101
#define LPASS_HW_MACRO_VOTE 102
#define LPASS_HW_DCODEC_VOTE 103
#define Q6AFE_MAX_CLK_ID 104
#define LPASS_CLK_ATTRIBUTE_INVALID 0x0
#define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1
#define LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND 0x2
#define LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR 0x3
#endif /* __DT_BINDINGS_Q6_AFE_H__ */

View file

@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __DT_SC7180_LPASS_H
#define __DT_SC7180_LPASS_H
#define MI2S_PRIMARY 0
#define MI2S_SECONDARY 1
#define LPASS_DP_RX 2
#define LPASS_MCLK0 0
#endif /* __DT_APQ8016_LPASS_H */

View file

@ -186,6 +186,10 @@
#define SSIRF 0x48 /* RX FIFO trigger level */
#define SSIRF_RxThresh(x) ((x) - 1)
/* LPT/WPT SSP */
#define SSCR2 (0x40) /* SSP Command / Status 2 */
#define SSPSP2 (0x44) /* SSP Programmable Serial Protocol 2 */
enum pxa_ssp_type {
SSP_UNDEFINED = 0,
PXA25x_SSP, /* pxa 210, 250, 255, 26x */

View file

@ -1150,6 +1150,17 @@ struct regmap_field *devm_regmap_field_alloc(struct device *dev,
struct regmap *regmap, struct reg_field reg_field);
void devm_regmap_field_free(struct device *dev, struct regmap_field *field);
int regmap_field_bulk_alloc(struct regmap *regmap,
struct regmap_field **rm_field,
struct reg_field *reg_field,
int num_fields);
void regmap_field_bulk_free(struct regmap_field *field);
int devm_regmap_field_bulk_alloc(struct device *dev, struct regmap *regmap,
struct regmap_field **field,
struct reg_field *reg_field, int num_fields);
void devm_regmap_field_bulk_free(struct device *dev,
struct regmap_field *field);
int regmap_field_read(struct regmap_field *field, unsigned int *val);
int regmap_field_update_bits_base(struct regmap_field *field,
unsigned int mask, unsigned int val,

View file

@ -119,7 +119,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define AZX_REG_VS_EM3U 0x103C
#define AZX_REG_VS_EM4L 0x1040
#define AZX_REG_VS_EM4U 0x1044
#define AZX_REG_VS_LTRC 0x1048
#define AZX_REG_VS_LTRP 0x1048
#define AZX_REG_VS_D0I3C 0x104A
#define AZX_REG_VS_PCE 0x104B
#define AZX_REG_VS_L2MAGC 0x1050

View file

@ -10,7 +10,7 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
void snd_hdac_ext_bus_exit(struct hdac_bus *bus);
int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr,
struct hdac_device *hdev);
struct hdac_device *hdev, int type);
void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev);
void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus);

View file

@ -117,9 +117,6 @@ struct hdmi_codec_pdata {
struct snd_soc_component;
struct snd_soc_jack;
int hdmi_codec_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack);
#define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
#endif /* __HDMI_CODEC_H__ */

View file

@ -16,7 +16,6 @@
*/
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_intel_skl_machines[];

View file

@ -58,7 +58,7 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg)
* snd_soc_acpi_mach_params: interface for machine driver configuration
*
* @acpi_ipc_irq_index: used for BYT-CR detection
* @platform: string used for HDaudio codec support
* @platform: string used for HDAudio codec support
* @codec_mask: used for HDAudio support
* @common_hdmi_codec_drv: use commom HDAudio HDMI codec driver
* @link_mask: links enabled on the board
@ -93,11 +93,13 @@ struct snd_soc_acpi_endpoint {
* @adr: 64 bit ACPI _ADR value
* @num_endpoints: number of endpoints for this device
* @endpoints: array of endpoints
* @name_prefix: string used for codec controls
*/
struct snd_soc_acpi_adr_device {
const u64 adr;
const u8 num_endpoints;
const struct snd_soc_acpi_endpoint *endpoints;
const char *name_prefix;
};
/**

View file

@ -217,6 +217,11 @@ struct snd_soc_component {
/* machine specific init */
int (*init)(struct snd_soc_component *component);
/* function mark */
struct snd_pcm_substream *mark_module;
struct snd_pcm_substream *mark_open;
void *mark_pm;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
const char *debugfs_prefix;
@ -370,17 +375,19 @@ void snd_soc_component_exit_regmap(struct snd_soc_component *component);
#endif
#define snd_soc_component_module_get_when_probe(component)\
snd_soc_component_module_get(component, 0)
#define snd_soc_component_module_get_when_open(component) \
snd_soc_component_module_get(component, 1)
snd_soc_component_module_get(component, NULL, 0)
#define snd_soc_component_module_get_when_open(component, substream) \
snd_soc_component_module_get(component, substream, 1)
int snd_soc_component_module_get(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
int upon_open);
#define snd_soc_component_module_put_when_remove(component) \
snd_soc_component_module_put(component, 0)
#define snd_soc_component_module_put_when_close(component) \
snd_soc_component_module_put(component, 1)
snd_soc_component_module_put(component, NULL, 0, 0)
#define snd_soc_component_module_put_when_close(component, substream, rollback) \
snd_soc_component_module_put(component, substream, 1, rollback)
void snd_soc_component_module_put(struct snd_soc_component *component,
int upon_open);
struct snd_pcm_substream *substream,
int upon_open, int rollback);
static inline void snd_soc_component_set_drvdata(struct snd_soc_component *c,
void *data)
@ -424,7 +431,8 @@ int snd_soc_component_force_enable_pin_unlocked(
int snd_soc_component_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream);
int snd_soc_component_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream);
struct snd_pcm_substream *substream,
int rollback);
void snd_soc_component_suspend(struct snd_soc_component *component);
void snd_soc_component_resume(struct snd_soc_component *component);
int snd_soc_component_is_suspended(struct snd_soc_component *component);
@ -457,5 +465,9 @@ void snd_soc_pcm_component_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_component *last);
int snd_soc_pcm_component_trigger(struct snd_pcm_substream *substream,
int cmd);
int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd,
void *stream);
void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd,
void *stream, int rollback);
#endif /* __SOC_COMPONENT_H */

View file

@ -153,7 +153,7 @@ void snd_soc_dai_hw_free(struct snd_soc_dai *dai,
int snd_soc_dai_startup(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream);
void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream);
struct snd_pcm_substream *substream, int rollback);
snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream);
void snd_soc_dai_suspend(struct snd_soc_dai *dai);
@ -388,6 +388,9 @@ struct snd_soc_dai {
struct list_head list;
/* function mark */
struct snd_pcm_substream *mark_startup;
/* bit field */
unsigned int probed:1;
};
@ -471,7 +474,8 @@ static inline int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai,
* This routine only retrieves that was previously configured
* with snd_soc_dai_get_sdw_stream()
*
* Returns pointer to stream or -ENOTSUPP if callback is not supported;
* Returns pointer to stream or an ERR_PTR value, e.g.
* ERR_PTR(-ENOTSUPP) if callback is not supported;
*/
static inline void *snd_soc_dai_get_sdw_stream(struct snd_soc_dai *dai,
int direction)

View file

@ -14,7 +14,8 @@ int snd_soc_link_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params);
int snd_soc_link_startup(struct snd_pcm_substream *substream);
void snd_soc_link_shutdown(struct snd_pcm_substream *substream);
void snd_soc_link_shutdown(struct snd_pcm_substream *substream,
int rollback);
int snd_soc_link_prepare(struct snd_pcm_substream *substream);
int snd_soc_link_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params);

View file

@ -1159,6 +1159,9 @@ struct snd_soc_pcm_runtime {
unsigned int num; /* 0-based and monotonic increasing */
struct list_head list; /* rtd list of the soc card */
/* function mark */
struct snd_pcm_substream *mark_startup;
/* bit field */
unsigned int pop_wait:1;
unsigned int fe_compr:1; /* for Dynamic PCM */
@ -1333,6 +1336,7 @@ void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card,
int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
const char *propname);
int snd_soc_of_parse_aux_devs(struct snd_soc_card *card, const char *propname);
unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
const char *prefix,
struct device_node **bitclkmaster,

View file

@ -66,6 +66,8 @@ struct sof_dev_desc {
/* alternate list of machines using this configuration */
struct snd_soc_acpi_mach *alt_machines;
bool use_acpi_target_states;
/* 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

@ -60,6 +60,7 @@ enum sof_ext_man_elem_type {
SOF_EXT_MAN_ELEM_FW_VERSION = 0,
SOF_EXT_MAN_ELEM_WINDOW = SOF_IPC_EXT_WINDOW,
SOF_EXT_MAN_ELEM_CC_VERSION = SOF_IPC_EXT_CC_INFO,
SOF_EXT_MAN_ELEM_DBG_ABI = SOF_IPC_EXT_USER_ABI_INFO,
};
/* extended manifest element header */
@ -92,4 +93,10 @@ struct sof_ext_man_cc_version {
struct sof_ipc_cc_version cc_version;
} __packed;
struct ext_man_dbg_abi {
struct sof_ext_man_elem_header hdr;
/* use sof_ipc struct because of code re-use */
struct sof_ipc_user_abi_version dbg_abi;
} __packed;
#endif /* __SOF_FIRMWARE_EXT_MANIFEST_H__ */

View file

@ -46,9 +46,11 @@ struct sof_ipc_fw_version {
uint8_t time[10];
uint8_t tag[6];
uint32_t abi_version;
/* used to check FW and ldc file compatibility, reproducible value */
uint32_t src_hash;
/* reserved for future use */
uint32_t reserved[4];
uint32_t reserved[3];
} __packed;
/* FW ready Message - sent by firmware when boot has completed */
@ -99,7 +101,7 @@ struct sof_ipc_window_elem {
struct sof_ipc_window {
struct sof_ipc_ext_data_hdr ext_hdr;
uint32_t num_windows;
struct sof_ipc_window_elem window[];
struct sof_ipc_window_elem window[SOF_IPC_MAX_ELEMS];
} __packed;
struct sof_ipc_cc_version {

View file

@ -57,8 +57,8 @@ struct sof_ipc_comp {
uint32_t pipeline_id;
uint32_t core;
/* reserved for future use */
uint32_t reserved[1];
/* extended data length, 0 if no extended data */
uint32_t ext_data_length;
} __packed;
/*
@ -87,6 +87,9 @@ struct sof_ipc_comp {
*/
#define SOF_BUF_UNDERRUN_PERMITTED BIT(1)
/* the UUID size in bytes, shared between FW and host */
#define SOF_UUID_SIZE 16
/* create new component buffer - SOF_IPC_TPLG_BUFFER_NEW */
struct sof_ipc_buffer {
struct sof_ipc_comp comp;
@ -300,4 +303,9 @@ enum sof_event_types {
SOF_KEYWORD_DETECT_DAPM_EVENT,
};
/* extended data struct for UUID components */
struct sof_ipc_comp_ext {
uint8_t uuid[SOF_UUID_SIZE];
} __packed;
#endif

View file

@ -1,385 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hswadsp
#if !defined(_TRACE_HSWADSP_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HSWADSP_H
#include <linux/types.h>
#include <linux/ktime.h>
#include <linux/tracepoint.h>
struct sst_hsw;
struct sst_hsw_stream;
struct sst_hsw_ipc_stream_free_req;
struct sst_hsw_ipc_volume_req;
struct sst_hsw_ipc_stream_alloc_req;
struct sst_hsw_audio_data_format_ipc;
struct sst_hsw_ipc_stream_info_reply;
struct sst_hsw_ipc_device_config_req;
DECLARE_EVENT_CLASS(sst_irq,
TP_PROTO(uint32_t status, uint32_t mask),
TP_ARGS(status, mask),
TP_STRUCT__entry(
__field( unsigned int, status )
__field( unsigned int, mask )
),
TP_fast_assign(
__entry->status = status;
__entry->mask = mask;
),
TP_printk("status 0x%8.8x mask 0x%8.8x",
(unsigned int)__entry->status, (unsigned int)__entry->mask)
);
DEFINE_EVENT(sst_irq, sst_irq_busy,
TP_PROTO(unsigned int status, unsigned int mask),
TP_ARGS(status, mask)
);
DEFINE_EVENT(sst_irq, sst_irq_done,
TP_PROTO(unsigned int status, unsigned int mask),
TP_ARGS(status, mask)
);
DECLARE_EVENT_CLASS(ipc,
TP_PROTO(const char *name, int val),
TP_ARGS(name, val),
TP_STRUCT__entry(
__string( name, name )
__field( unsigned int, val )
),
TP_fast_assign(
__assign_str(name, name);
__entry->val = val;
),
TP_printk("%s 0x%8.8x", __get_str(name), (unsigned int)__entry->val)
);
DEFINE_EVENT(ipc, ipc_request,
TP_PROTO(const char *name, int val),
TP_ARGS(name, val)
);
DEFINE_EVENT(ipc, ipc_reply,
TP_PROTO(const char *name, int val),
TP_ARGS(name, val)
);
DEFINE_EVENT(ipc, ipc_pending_reply,
TP_PROTO(const char *name, int val),
TP_ARGS(name, val)
);
DEFINE_EVENT(ipc, ipc_notification,
TP_PROTO(const char *name, int val),
TP_ARGS(name, val)
);
DEFINE_EVENT(ipc, ipc_error,
TP_PROTO(const char *name, int val),
TP_ARGS(name, val)
);
DECLARE_EVENT_CLASS(stream_position,
TP_PROTO(unsigned int id, unsigned int pos),
TP_ARGS(id, pos),
TP_STRUCT__entry(
__field( unsigned int, id )
__field( unsigned int, pos )
),
TP_fast_assign(
__entry->id = id;
__entry->pos = pos;
),
TP_printk("id %d position 0x%x",
(unsigned int)__entry->id, (unsigned int)__entry->pos)
);
DEFINE_EVENT(stream_position, stream_read_position,
TP_PROTO(unsigned int id, unsigned int pos),
TP_ARGS(id, pos)
);
DEFINE_EVENT(stream_position, stream_write_position,
TP_PROTO(unsigned int id, unsigned int pos),
TP_ARGS(id, pos)
);
TRACE_EVENT(hsw_stream_buffer,
TP_PROTO(struct sst_hsw_stream *stream),
TP_ARGS(stream),
TP_STRUCT__entry(
__field( int, id )
__field( int, pt_addr )
__field( int, num_pages )
__field( int, ring_size )
__field( int, ring_offset )
__field( int, first_pfn )
),
TP_fast_assign(
__entry->id = stream->host_id;
__entry->pt_addr = stream->request.ringinfo.ring_pt_address;
__entry->num_pages = stream->request.ringinfo.num_pages;
__entry->ring_size = stream->request.ringinfo.ring_size;
__entry->ring_offset = stream->request.ringinfo.ring_offset;
__entry->first_pfn = stream->request.ringinfo.ring_first_pfn;
),
TP_printk("stream %d ring addr 0x%x pages %d size 0x%x offset 0x%x PFN 0x%x",
(int) __entry->id, (int)__entry->pt_addr,
(int)__entry->num_pages, (int)__entry->ring_size,
(int)__entry->ring_offset, (int)__entry->first_pfn)
);
TRACE_EVENT(hsw_stream_alloc_reply,
TP_PROTO(struct sst_hsw_stream *stream),
TP_ARGS(stream),
TP_STRUCT__entry(
__field( int, id )
__field( int, stream_id )
__field( int, mixer_id )
__field( int, peak0 )
__field( int, peak1 )
__field( int, vol0 )
__field( int, vol1 )
),
TP_fast_assign(
__entry->id = stream->host_id;
__entry->stream_id = stream->reply.stream_hw_id;
__entry->mixer_id = stream->reply.mixer_hw_id;
__entry->peak0 = stream->reply.peak_meter_register_address[0];
__entry->peak1 = stream->reply.peak_meter_register_address[1];
__entry->vol0 = stream->reply.volume_register_address[0];
__entry->vol1 = stream->reply.volume_register_address[1];
),
TP_printk("stream %d hw id %d mixer %d peak 0x%x:0x%x vol 0x%x,0x%x",
(int) __entry->id, (int) __entry->stream_id, (int)__entry->mixer_id,
(int)__entry->peak0, (int)__entry->peak1,
(int)__entry->vol0, (int)__entry->vol1)
);
TRACE_EVENT(hsw_mixer_info_reply,
TP_PROTO(struct sst_hsw_ipc_stream_info_reply *reply),
TP_ARGS(reply),
TP_STRUCT__entry(
__field( int, mixer_id )
__field( int, peak0 )
__field( int, peak1 )
__field( int, vol0 )
__field( int, vol1 )
),
TP_fast_assign(
__entry->mixer_id = reply->mixer_hw_id;
__entry->peak0 = reply->peak_meter_register_address[0];
__entry->peak1 = reply->peak_meter_register_address[1];
__entry->vol0 = reply->volume_register_address[0];
__entry->vol1 = reply->volume_register_address[1];
),
TP_printk("mixer id %d peak 0x%x:0x%x vol 0x%x,0x%x",
(int)__entry->mixer_id,
(int)__entry->peak0, (int)__entry->peak1,
(int)__entry->vol0, (int)__entry->vol1)
);
TRACE_EVENT(hsw_stream_data_format,
TP_PROTO(struct sst_hsw_stream *stream,
struct sst_hsw_audio_data_format_ipc *req),
TP_ARGS(stream, req),
TP_STRUCT__entry(
__field( uint32_t, id )
__field( uint32_t, frequency )
__field( uint32_t, bitdepth )
__field( uint32_t, map )
__field( uint32_t, config )
__field( uint32_t, style )
__field( uint8_t, ch_num )
__field( uint8_t, valid_bit )
),
TP_fast_assign(
__entry->id = stream->host_id;
__entry->frequency = req->frequency;
__entry->bitdepth = req->bitdepth;
__entry->map = req->map;
__entry->config = req->config;
__entry->style = req->style;
__entry->ch_num = req->ch_num;
__entry->valid_bit = req->valid_bit;
),
TP_printk("stream %d freq %d depth %d map 0x%x config 0x%x style 0x%x ch %d bits %d",
(int) __entry->id, (uint32_t)__entry->frequency,
(uint32_t)__entry->bitdepth, (uint32_t)__entry->map,
(uint32_t)__entry->config, (uint32_t)__entry->style,
(uint8_t)__entry->ch_num, (uint8_t)__entry->valid_bit)
);
TRACE_EVENT(hsw_stream_alloc_request,
TP_PROTO(struct sst_hsw_stream *stream,
struct sst_hsw_ipc_stream_alloc_req *req),
TP_ARGS(stream, req),
TP_STRUCT__entry(
__field( uint32_t, id )
__field( uint8_t, path_id )
__field( uint8_t, stream_type )
__field( uint8_t, format_id )
),
TP_fast_assign(
__entry->id = stream->host_id;
__entry->path_id = req->path_id;
__entry->stream_type = req->stream_type;
__entry->format_id = req->format_id;
),
TP_printk("stream %d path %d type %d format %d",
(int) __entry->id, (uint8_t)__entry->path_id,
(uint8_t)__entry->stream_type, (uint8_t)__entry->format_id)
);
TRACE_EVENT(hsw_stream_free_req,
TP_PROTO(struct sst_hsw_stream *stream,
struct sst_hsw_ipc_stream_free_req *req),
TP_ARGS(stream, req),
TP_STRUCT__entry(
__field( int, id )
__field( int, stream_id )
),
TP_fast_assign(
__entry->id = stream->host_id;
__entry->stream_id = req->stream_id;
),
TP_printk("stream %d hw id %d",
(int) __entry->id, (int) __entry->stream_id)
);
TRACE_EVENT(hsw_volume_req,
TP_PROTO(struct sst_hsw_stream *stream,
struct sst_hsw_ipc_volume_req *req),
TP_ARGS(stream, req),
TP_STRUCT__entry(
__field( int, id )
__field( uint32_t, channel )
__field( uint32_t, target_volume )
__field( uint64_t, curve_duration )
__field( uint32_t, curve_type )
),
TP_fast_assign(
__entry->id = stream->host_id;
__entry->channel = req->channel;
__entry->target_volume = req->target_volume;
__entry->curve_duration = req->curve_duration;
__entry->curve_type = req->curve_type;
),
TP_printk("stream %d chan 0x%x vol %d duration %llu type %d",
(int) __entry->id, (uint32_t) __entry->channel,
(uint32_t)__entry->target_volume,
(uint64_t)__entry->curve_duration,
(uint32_t)__entry->curve_type)
);
TRACE_EVENT(hsw_device_config_req,
TP_PROTO(struct sst_hsw_ipc_device_config_req *req),
TP_ARGS(req),
TP_STRUCT__entry(
__field( uint32_t, ssp )
__field( uint32_t, clock_freq )
__field( uint32_t, mode )
__field( uint16_t, clock_divider )
),
TP_fast_assign(
__entry->ssp = req->ssp_interface;
__entry->clock_freq = req->clock_frequency;
__entry->mode = req->mode;
__entry->clock_divider = req->clock_divider;
),
TP_printk("SSP %d Freq %d mode %d div %d",
(uint32_t)__entry->ssp,
(uint32_t)__entry->clock_freq, (uint32_t)__entry->mode,
(uint32_t)__entry->clock_divider)
);
#endif /* _TRACE_HSWADSP_H */
/* This part must be outside protection */
#include <trace/define_trace.h>

View file

@ -26,7 +26,7 @@
/* SOF ABI version major, minor and patch numbers */
#define SOF_ABI_MAJOR 3
#define SOF_ABI_MINOR 16
#define SOF_ABI_MINOR 17
#define SOF_ABI_PATCH 0
/* SOF ABI version number. Format within 32bit word is MMmmmppp */

View file

@ -24,6 +24,9 @@
#define SOF_TPLG_KCTL_ENUM_ID 257
#define SOF_TPLG_KCTL_BYTES_ID 258
#define SOF_TPLG_KCTL_SWITCH_ID 259
#define SOF_TPLG_KCTL_BYTES_VOLATILE_RO 260
#define SOF_TPLG_KCTL_BYTES_VOLATILE_RW 261
#define SOF_TPLG_KCTL_BYTES_WO_ID 262
/*
* Tokens - must match values in topology configurations
@ -73,6 +76,8 @@
/* Token retired with ABI 3.2, do not use for new capabilities
* #define SOF_TKN_COMP_PRELOAD_COUNT 403
*/
#define SOF_TKN_COMP_CORE_ID 404
#define SOF_TKN_COMP_UUID 405
/* SSP */
#define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500

View file

@ -70,11 +70,12 @@ static void default_release(struct device *dev)
* @bus: hdac bus to attach to
* @addr: codec address
* @hdev: hdac device to init
* @type: codec type (HDAC_DEV_*) to use for this device
*
* Returns zero for success or a negative error code.
*/
int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr,
struct hdac_device *hdev)
struct hdac_device *hdev, int type)
{
char name[15];
int ret;
@ -88,7 +89,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr,
dev_err(bus->dev, "device init failed for hdac device\n");
return ret;
}
hdev->type = HDA_DEV_ASOC;
hdev->type = type;
hdev->dev.release = default_release;
ret = snd_hdac_device_register(hdev);

View file

@ -472,12 +472,17 @@ static int acp3x_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"devm_snd_soc_register_card(%s) failed: %d\n",
card->name, ret);
return ret;
else
dev_dbg(&pdev->dev,
"devm_snd_soc_register_card(%s) probe deferred: %d\n",
card->name, ret);
}
return 0;
return ret;
}
static const struct acpi_device_id acp3x_audio_acpi_match[] = {

View file

@ -132,4 +132,29 @@ config SND_MCHP_SOC_I2S_MCC
and supports a Time Division Multiplexed (TDM) interface with
external multi-channel audio codecs.
config SND_MCHP_SOC_SPDIFTX
tristate "Microchip ASoC driver for boards using S/PDIF TX"
depends on OF && (ARCH_AT91 || COMPILE_TEST)
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Say Y or M if you want to add support for Microchip S/PDIF TX ASoc
driver on the following Microchip platforms:
- sama7g5
This S/PDIF TX driver is compliant with IEC-60958 standard and
includes programable User Data and Channel Status fields.
config SND_MCHP_SOC_SPDIFRX
tristate "Microchip ASoC driver for boards using S/PDIF RX"
depends on OF && (ARCH_AT91 || COMPILE_TEST)
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
Say Y or M if you want to add support for Microchip S/PDIF RX ASoc
driver on the following Microchip platforms:
- sama7g5
This S/PDIF RX driver is compliant with IEC-60958 standard and
includes programable User Data and Channel Status fields.
endif

View file

@ -5,6 +5,8 @@ snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
snd-soc-atmel-i2s-objs := atmel-i2s.o
snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o
snd-soc-mchp-spdiftx-objs := mchp-spdiftx.o
snd-soc-mchp-spdifrx-objs := mchp-spdifrx.o
# pdc and dma need to both be built-in if any user of
# ssc is built-in.
@ -17,6 +19,8 @@ endif
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o
obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o
obj-$(CONFIG_SND_MCHP_SOC_SPDIFTX) += snd-soc-mchp-spdiftx.o
obj-$(CONFIG_SND_MCHP_SOC_SPDIFRX) += snd-soc-mchp-spdifrx.o
# AT91 Machine Support
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o

View file

@ -18,7 +18,6 @@
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/atmel-ssc.h>
#include <linux/platform_data/dma-atmel.h>
#include <sound/core.h>
#include <sound/pcm.h>

View file

@ -0,0 +1,953 @@
// SPDX-License-Identifier: GPL-2.0
//
// Driver for Microchip S/PDIF RX Controller
//
// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
//
// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
/*
* ---- S/PDIF Receiver Controller Register map ----
*/
#define SPDIFRX_CR 0x00 /* Control Register */
#define SPDIFRX_MR 0x04 /* Mode Register */
#define SPDIFRX_IER 0x10 /* Interrupt Enable Register */
#define SPDIFRX_IDR 0x14 /* Interrupt Disable Register */
#define SPDIFRX_IMR 0x18 /* Interrupt Mask Register */
#define SPDIFRX_ISR 0x1c /* Interrupt Status Register */
#define SPDIFRX_RSR 0x20 /* Status Register */
#define SPDIFRX_RHR 0x24 /* Holding Register */
#define SPDIFRX_CHSR(channel, reg) \
(0x30 + (channel) * 0x30 + (reg) * 4) /* Channel x Status Registers */
#define SPDIFRX_CHUD(channel, reg) \
(0x48 + (channel) * 0x30 + (reg) * 4) /* Channel x User Data Registers */
#define SPDIFRX_WPMR 0xE4 /* Write Protection Mode Register */
#define SPDIFRX_WPSR 0xE8 /* Write Protection Status Register */
#define SPDIFRX_VERSION 0xFC /* Version Register */
/*
* ---- Control Register (Write-only) ----
*/
#define SPDIFRX_CR_SWRST BIT(0) /* Software Reset */
/*
* ---- Mode Register (Read/Write) ----
*/
/* Receive Enable */
#define SPDIFRX_MR_RXEN_MASK GENMASK(0, 0)
#define SPDIFRX_MR_RXEN_DISABLE (0 << 0) /* SPDIF Receiver Disabled */
#define SPDIFRX_MR_RXEN_ENABLE (1 << 0) /* SPDIF Receiver Enabled */
/* Validity Bit Mode */
#define SPDIFRX_MR_VBMODE_MASK GENAMSK(1, 1)
#define SPDIFRX_MR_VBMODE_ALWAYS_LOAD \
(0 << 1) /* Load sample regardles of validity bit value */
#define SPDIFRX_MR_VBMODE_DISCARD_IF_VB1 \
(1 << 1) /* Load sample only if validity bit is 0 */
/* Data Word Endian Mode */
#define SPDIFRX_MR_ENDIAN_MASK GENMASK(2, 2)
#define SPDIFRX_MR_ENDIAN_LITTLE (0 << 2) /* Little Endian Mode */
#define SPDIFRX_MR_ENDIAN_BIG (1 << 2) /* Big Endian Mode */
/* Parity Bit Mode */
#define SPDIFRX_MR_PBMODE_MASK GENMASK(3, 3)
#define SPDIFRX_MR_PBMODE_PARCHECK (0 << 3) /* Parity Check Enabled */
#define SPDIFRX_MR_PBMODE_NOPARCHECK (1 << 3) /* Parity Check Disabled */
/* Sample Data Width */
#define SPDIFRX_MR_DATAWIDTH_MASK GENMASK(5, 4)
#define SPDIFRX_MR_DATAWIDTH(width) \
(((6 - (width) / 4) << 4) & SPDIFRX_MR_DATAWIDTH_MASK)
/* Packed Data Mode in Receive Holding Register */
#define SPDIFRX_MR_PACK_MASK GENMASK(7, 7)
#define SPDIFRX_MR_PACK_DISABLED (0 << 7)
#define SPDIFRX_MR_PACK_ENABLED (1 << 7)
/* Start of Block Bit Mode */
#define SPDIFRX_MR_SBMODE_MASK GENMASK(8, 8)
#define SPDIFRX_MR_SBMODE_ALWAYS_LOAD (0 << 8)
#define SPDIFRX_MR_SBMODE_DISCARD (1 << 8)
/* Consecutive Preamble Error Threshold Automatic Restart */
#define SPDIFRX_MR_AUTORST_MASK GENMASK(24, 24)
#define SPDIFRX_MR_AUTORST_NOACTION (0 << 24)
#define SPDIFRX_MR_AUTORST_UNLOCK_ON_PRE_ERR (1 << 24)
/*
* ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ----
*/
#define SPDIFRX_IR_RXRDY BIT(0)
#define SPDIFRX_IR_LOCKED BIT(1)
#define SPDIFRX_IR_LOSS BIT(2)
#define SPDIFRX_IR_BLOCKEND BIT(3)
#define SPDIFRX_IR_SFE BIT(4)
#define SPDIFRX_IR_PAR_ERR BIT(5)
#define SPDIFRX_IR_OVERRUN BIT(6)
#define SPDIFRX_IR_RXFULL BIT(7)
#define SPDIFRX_IR_CSC(ch) BIT((ch) + 8)
#define SPDIFRX_IR_SECE BIT(10)
#define SPDIFRX_IR_BLOCKST BIT(11)
#define SPDIFRX_IR_NRZ_ERR BIT(12)
#define SPDIFRX_IR_PRE_ERR BIT(13)
#define SPDIFRX_IR_CP_ERR BIT(14)
/*
* ---- Receiver Status Register (Read/Write) ----
*/
/* Enable Status */
#define SPDIFRX_RSR_ULOCK BIT(0)
#define SPDIFRX_RSR_BADF BIT(1)
#define SPDIFRX_RSR_LOWF BIT(2)
#define SPDIFRX_RSR_NOSIGNAL BIT(3)
#define SPDIFRX_RSR_IFS_MASK GENMASK(27, 16)
#define SPDIFRX_RSR_IFS(reg) \
(((reg) & SPDIFRX_RSR_IFS_MASK) >> 16)
/*
* ---- Version Register (Read-only) ----
*/
#define SPDIFRX_VERSION_MASK GENMASK(11, 0)
#define SPDIFRX_VERSION_MFN_MASK GENMASK(18, 16)
#define SPDIFRX_VERSION_MFN(reg) (((reg) & SPDIFRX_VERSION_MFN_MASK) >> 16)
static bool mchp_spdifrx_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPDIFRX_MR:
case SPDIFRX_IMR:
case SPDIFRX_ISR:
case SPDIFRX_RSR:
case SPDIFRX_CHSR(0, 0):
case SPDIFRX_CHSR(0, 1):
case SPDIFRX_CHSR(0, 2):
case SPDIFRX_CHSR(0, 3):
case SPDIFRX_CHSR(0, 4):
case SPDIFRX_CHSR(0, 5):
case SPDIFRX_CHUD(0, 0):
case SPDIFRX_CHUD(0, 1):
case SPDIFRX_CHUD(0, 2):
case SPDIFRX_CHUD(0, 3):
case SPDIFRX_CHUD(0, 4):
case SPDIFRX_CHUD(0, 5):
case SPDIFRX_CHSR(1, 0):
case SPDIFRX_CHSR(1, 1):
case SPDIFRX_CHSR(1, 2):
case SPDIFRX_CHSR(1, 3):
case SPDIFRX_CHSR(1, 4):
case SPDIFRX_CHSR(1, 5):
case SPDIFRX_CHUD(1, 0):
case SPDIFRX_CHUD(1, 1):
case SPDIFRX_CHUD(1, 2):
case SPDIFRX_CHUD(1, 3):
case SPDIFRX_CHUD(1, 4):
case SPDIFRX_CHUD(1, 5):
case SPDIFRX_WPMR:
case SPDIFRX_WPSR:
case SPDIFRX_VERSION:
return true;
default:
return false;
}
}
static bool mchp_spdifrx_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPDIFRX_CR:
case SPDIFRX_MR:
case SPDIFRX_IER:
case SPDIFRX_IDR:
case SPDIFRX_WPMR:
return true;
default:
return false;
}
}
static bool mchp_spdifrx_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPDIFRX_ISR:
case SPDIFRX_RHR:
return true;
default:
return false;
}
}
static const struct regmap_config mchp_spdifrx_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = SPDIFRX_VERSION,
.readable_reg = mchp_spdifrx_readable_reg,
.writeable_reg = mchp_spdifrx_writeable_reg,
.precious_reg = mchp_spdifrx_precious_reg,
};
#define SPDIFRX_GCLK_RATIO_MIN (12 * 64)
#define SPDIFRX_CS_BITS 192
#define SPDIFRX_UD_BITS 192
#define SPDIFRX_CHANNELS 2
struct mchp_spdifrx_ch_stat {
unsigned char data[SPDIFRX_CS_BITS / 8];
struct completion done;
};
struct mchp_spdifrx_user_data {
unsigned char data[SPDIFRX_UD_BITS / 8];
struct completion done;
spinlock_t lock; /* protect access to user data */
};
struct mchp_spdifrx_mixer_control {
struct mchp_spdifrx_ch_stat ch_stat[SPDIFRX_CHANNELS];
struct mchp_spdifrx_user_data user_data[SPDIFRX_CHANNELS];
bool ulock;
bool badf;
bool signal;
};
struct mchp_spdifrx_dev {
struct snd_dmaengine_dai_dma_data capture;
struct mchp_spdifrx_mixer_control control;
spinlock_t blockend_lock; /* protect access to blockend_refcount */
int blockend_refcount;
struct device *dev;
struct regmap *regmap;
struct clk *pclk;
struct clk *gclk;
unsigned int fmt;
unsigned int gclk_enabled:1;
};
static void mchp_spdifrx_channel_status_read(struct mchp_spdifrx_dev *dev,
int channel)
{
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
u8 *ch_stat = &ctrl->ch_stat[channel].data[0];
u32 val;
int i;
for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat[channel].data) / 4; i++) {
regmap_read(dev->regmap, SPDIFRX_CHSR(channel, i), &val);
*ch_stat++ = val & 0xFF;
*ch_stat++ = (val >> 8) & 0xFF;
*ch_stat++ = (val >> 16) & 0xFF;
*ch_stat++ = (val >> 24) & 0xFF;
}
}
static void mchp_spdifrx_channel_user_data_read(struct mchp_spdifrx_dev *dev,
int channel)
{
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
u8 *user_data = &ctrl->user_data[channel].data[0];
u32 val;
int i;
for (i = 0; i < ARRAY_SIZE(ctrl->user_data[channel].data) / 4; i++) {
regmap_read(dev->regmap, SPDIFRX_CHUD(channel, i), &val);
*user_data++ = val & 0xFF;
*user_data++ = (val >> 8) & 0xFF;
*user_data++ = (val >> 16) & 0xFF;
*user_data++ = (val >> 24) & 0xFF;
}
}
/* called from non-atomic context only */
static void mchp_spdifrx_isr_blockend_en(struct mchp_spdifrx_dev *dev)
{
unsigned long flags;
spin_lock_irqsave(&dev->blockend_lock, flags);
dev->blockend_refcount++;
/* don't enable BLOCKEND interrupt if it's already enabled */
if (dev->blockend_refcount == 1)
regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND);
spin_unlock_irqrestore(&dev->blockend_lock, flags);
}
/* called from atomic context only */
static void mchp_spdifrx_isr_blockend_dis(struct mchp_spdifrx_dev *dev)
{
spin_lock(&dev->blockend_lock);
dev->blockend_refcount--;
/* don't enable BLOCKEND interrupt if it's already enabled */
if (dev->blockend_refcount == 0)
regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
spin_unlock(&dev->blockend_lock);
}
static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
{
struct mchp_spdifrx_dev *dev = dev_id;
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
u32 sr, imr, pending, idr = 0;
irqreturn_t ret = IRQ_NONE;
int ch;
regmap_read(dev->regmap, SPDIFRX_ISR, &sr);
regmap_read(dev->regmap, SPDIFRX_IMR, &imr);
pending = sr & imr;
dev_dbg(dev->dev, "ISR: %#x, IMR: %#x, pending: %#x\n", sr, imr,
pending);
if (!pending)
return IRQ_NONE;
if (pending & SPDIFRX_IR_BLOCKEND) {
for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
spin_lock(&ctrl->user_data[ch].lock);
mchp_spdifrx_channel_user_data_read(dev, ch);
spin_unlock(&ctrl->user_data[ch].lock);
complete(&ctrl->user_data[ch].done);
}
mchp_spdifrx_isr_blockend_dis(dev);
ret = IRQ_HANDLED;
}
for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
if (pending & SPDIFRX_IR_CSC(ch)) {
mchp_spdifrx_channel_status_read(dev, ch);
complete(&ctrl->ch_stat[ch].done);
idr |= SPDIFRX_IR_CSC(ch);
ret = IRQ_HANDLED;
}
}
if (pending & SPDIFRX_IR_OVERRUN) {
dev_warn(dev->dev, "Overrun detected\n");
ret = IRQ_HANDLED;
}
regmap_write(dev->regmap, SPDIFRX_IDR, idr);
return ret;
}
static int mchp_spdifrx_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
u32 mr;
int running;
int ret;
regmap_read(dev->regmap, SPDIFRX_MR, &mr);
running = !!(mr & SPDIFRX_MR_RXEN_ENABLE);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (!running) {
mr &= ~SPDIFRX_MR_RXEN_MASK;
mr |= SPDIFRX_MR_RXEN_ENABLE;
/* enable overrun interrupts */
regmap_write(dev->regmap, SPDIFRX_IER,
SPDIFRX_IR_OVERRUN);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (running) {
mr &= ~SPDIFRX_MR_RXEN_MASK;
mr |= SPDIFRX_MR_RXEN_DISABLE;
/* disable overrun interrupts */
regmap_write(dev->regmap, SPDIFRX_IDR,
SPDIFRX_IR_OVERRUN);
}
break;
default:
return -EINVAL;
}
ret = regmap_write(dev->regmap, SPDIFRX_MR, mr);
if (ret) {
dev_err(dev->dev, "unable to enable/disable RX: %d\n", ret);
return ret;
}
return 0;
}
static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
u32 mr;
int ret;
dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
__func__, params_rate(params), params_format(params),
params_width(params), params_channels(params));
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dev_err(dev->dev, "Playback is not supported\n");
return -EINVAL;
}
regmap_read(dev->regmap, SPDIFRX_MR, &mr);
if (mr & SPDIFRX_MR_RXEN_ENABLE) {
dev_err(dev->dev, "PCM already running\n");
return -EBUSY;
}
if (params_channels(params) != SPDIFRX_CHANNELS) {
dev_err(dev->dev, "unsupported number of channels: %d\n",
params_channels(params));
return -EINVAL;
}
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_BE:
case SNDRV_PCM_FORMAT_S20_3BE:
case SNDRV_PCM_FORMAT_S24_3BE:
case SNDRV_PCM_FORMAT_S24_BE:
mr |= SPDIFRX_MR_ENDIAN_BIG;
fallthrough;
case SNDRV_PCM_FORMAT_S16_LE:
case SNDRV_PCM_FORMAT_S20_3LE:
case SNDRV_PCM_FORMAT_S24_3LE:
case SNDRV_PCM_FORMAT_S24_LE:
mr |= SPDIFRX_MR_DATAWIDTH(params_width(params));
break;
default:
dev_err(dev->dev, "unsupported PCM format: %d\n",
params_format(params));
return -EINVAL;
}
if (dev->gclk_enabled) {
clk_disable_unprepare(dev->gclk);
dev->gclk_enabled = 0;
}
ret = clk_set_min_rate(dev->gclk, params_rate(params) *
SPDIFRX_GCLK_RATIO_MIN + 1);
if (ret) {
dev_err(dev->dev,
"unable to set gclk min rate: rate %u * ratio %u + 1\n",
params_rate(params), SPDIFRX_GCLK_RATIO_MIN);
return ret;
}
ret = clk_prepare_enable(dev->gclk);
if (ret) {
dev_err(dev->dev, "unable to enable gclk: %d\n", ret);
return ret;
}
dev->gclk_enabled = 1;
dev_dbg(dev->dev, "GCLK range min set to %d\n",
params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1);
return regmap_write(dev->regmap, SPDIFRX_MR, mr);
}
static int mchp_spdifrx_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
if (dev->gclk_enabled) {
clk_disable_unprepare(dev->gclk);
dev->gclk_enabled = 0;
}
return 0;
}
static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = {
.trigger = mchp_spdifrx_trigger,
.hw_params = mchp_spdifrx_hw_params,
.hw_free = mchp_spdifrx_hw_free,
};
#define MCHP_SPDIF_RATES SNDRV_PCM_RATE_8000_192000
#define MCHP_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_U16_BE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S20_3BE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
SNDRV_PCM_FMTBIT_S24_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S24_BE \
)
static int mchp_spdifrx_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev,
int channel,
struct snd_ctl_elem_value *uvalue)
{
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel];
int ret;
regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel));
/* check for new data available */
ret = wait_for_completion_interruptible_timeout(&ch_stat->done,
msecs_to_jiffies(100));
/* IP might not be started or valid stream might not be prezent */
if (ret < 0) {
dev_dbg(dev->dev, "channel status for channel %d timeout\n",
channel);
}
memcpy(uvalue->value.iec958.status, ch_stat->data,
sizeof(ch_stat->data));
return 0;
}
static int mchp_spdifrx_cs1_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
return mchp_spdifrx_cs_get(dev, 0, uvalue);
}
static int mchp_spdifrx_cs2_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
return mchp_spdifrx_cs_get(dev, 1, uvalue);
}
static int mchp_spdifrx_cs_mask(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
memset(uvalue->value.iec958.status, 0xff,
sizeof(uvalue->value.iec958.status));
return 0;
}
static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev,
int channel,
struct snd_ctl_elem_value *uvalue)
{
unsigned long flags;
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel];
int ret;
reinit_completion(&user_data->done);
mchp_spdifrx_isr_blockend_en(dev);
ret = wait_for_completion_interruptible_timeout(&user_data->done,
msecs_to_jiffies(100));
/* IP might not be started or valid stream might not be prezent */
if (ret <= 0) {
dev_dbg(dev->dev, "user data for channel %d timeout\n",
channel);
return ret;
}
spin_lock_irqsave(&user_data->lock, flags);
memcpy(uvalue->value.iec958.subcode, user_data->data,
sizeof(user_data->data));
spin_unlock_irqrestore(&user_data->lock, flags);
return 0;
}
static int mchp_spdifrx_subcode_ch1_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
return mchp_spdifrx_subcode_ch_get(dev, 0, uvalue);
}
static int mchp_spdifrx_subcode_ch2_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
return mchp_spdifrx_subcode_ch_get(dev, 1, uvalue);
}
static int mchp_spdifrx_boolean_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
u32 val;
bool ulock_old = ctrl->ulock;
regmap_read(dev->regmap, SPDIFRX_RSR, &val);
ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK);
uvalue->value.integer.value[0] = ctrl->ulock;
return ulock_old != ctrl->ulock;
}
static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
u32 val;
bool badf_old = ctrl->badf;
regmap_read(dev->regmap, SPDIFRX_RSR, &val);
ctrl->badf = !!(val & SPDIFRX_RSR_BADF);
uvalue->value.integer.value[0] = ctrl->badf;
return badf_old != ctrl->badf;
}
static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
u32 val;
bool signal_old = ctrl->signal;
regmap_read(dev->regmap, SPDIFRX_RSR, &val);
ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL);
uvalue->value.integer.value[0] = ctrl->signal;
return signal_old != ctrl->signal;
}
static int mchp_spdifrx_rate_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 192000;
return 0;
}
static int mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
u32 val;
int rate;
regmap_read(dev->regmap, SPDIFRX_RSR, &val);
/* if the receiver is not locked, ISF data is invalid */
if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) {
ucontrol->value.integer.value[0] = 0;
return 0;
}
rate = clk_get_rate(dev->gclk);
ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val));
return 0;
}
static struct snd_kcontrol_new mchp_spdifrx_ctrls[] = {
/* Channel status controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT)
" Channel 1",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdifrx_info,
.get = mchp_spdifrx_cs1_get,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT)
" Channel 2",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdifrx_info,
.get = mchp_spdifrx_cs2_get,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.info = mchp_spdifrx_info,
.get = mchp_spdifrx_cs_mask,
},
/* User bits controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 Subcode Capture Default Channel 1",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdifrx_info,
.get = mchp_spdifrx_subcode_ch1_get,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 Subcode Capture Default Channel 2",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdifrx_info,
.get = mchp_spdifrx_subcode_ch2_get,
},
/* Lock status */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Unlocked",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdifrx_boolean_info,
.get = mchp_spdifrx_ulock_get,
},
/* Bad format */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE)"Bad Format",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdifrx_boolean_info,
.get = mchp_spdifrx_badf_get,
},
/* Signal */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Signal",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdifrx_boolean_info,
.get = mchp_spdifrx_signal_get,
},
/* Sampling rate */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Rate",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdifrx_rate_info,
.get = mchp_spdifrx_rate_get,
},
};
static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai)
{
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
int ch;
int err;
err = clk_prepare_enable(dev->pclk);
if (err) {
dev_err(dev->dev,
"failed to enable the peripheral clock: %d\n", err);
return err;
}
snd_soc_dai_init_dma_data(dai, NULL, &dev->capture);
/* Software reset the IP */
regmap_write(dev->regmap, SPDIFRX_CR, SPDIFRX_CR_SWRST);
/* Default configuration */
regmap_write(dev->regmap, SPDIFRX_MR,
SPDIFRX_MR_VBMODE_DISCARD_IF_VB1 |
SPDIFRX_MR_SBMODE_DISCARD |
SPDIFRX_MR_AUTORST_NOACTION |
SPDIFRX_MR_PACK_DISABLED);
dev->blockend_refcount = 0;
for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
init_completion(&ctrl->ch_stat[ch].done);
init_completion(&ctrl->user_data[ch].done);
spin_lock_init(&ctrl->user_data[ch].lock);
}
/* Add controls */
snd_soc_add_dai_controls(dai, mchp_spdifrx_ctrls,
ARRAY_SIZE(mchp_spdifrx_ctrls));
return 0;
}
static int mchp_spdifrx_dai_remove(struct snd_soc_dai *dai)
{
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
/* Disable interrupts */
regmap_write(dev->regmap, SPDIFRX_IDR, 0xFF);
clk_disable_unprepare(dev->pclk);
return 0;
}
static struct snd_soc_dai_driver mchp_spdifrx_dai = {
.name = "mchp-spdifrx",
.probe = mchp_spdifrx_dai_probe,
.remove = mchp_spdifrx_dai_remove,
.capture = {
.stream_name = "S/PDIF Capture",
.channels_min = SPDIFRX_CHANNELS,
.channels_max = SPDIFRX_CHANNELS,
.rates = MCHP_SPDIF_RATES,
.formats = MCHP_SPDIF_FORMATS,
},
.ops = &mchp_spdifrx_dai_ops,
};
static const struct snd_soc_component_driver mchp_spdifrx_component = {
.name = "mchp-spdifrx",
};
static const struct of_device_id mchp_spdifrx_dt_ids[] = {
{
.compatible = "microchip,sama7g5-spdifrx",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mchp_spdifrx_dt_ids);
static int mchp_spdifrx_probe(struct platform_device *pdev)
{
struct mchp_spdifrx_dev *dev;
struct resource *mem;
struct regmap *regmap;
void __iomem *base;
int irq;
int err;
u32 vers;
/* Get memory for driver data. */
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
/* Map I/O registers. */
base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
if (IS_ERR(base))
return PTR_ERR(base);
regmap = devm_regmap_init_mmio(&pdev->dev, base,
&mchp_spdifrx_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
/* Request IRQ. */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
err = devm_request_irq(&pdev->dev, irq, mchp_spdif_interrupt, 0,
dev_name(&pdev->dev), dev);
if (err)
return err;
/* Get the peripheral clock */
dev->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(dev->pclk)) {
err = PTR_ERR(dev->pclk);
dev_err(&pdev->dev, "failed to get the peripheral clock: %d\n",
err);
return err;
}
/* Get the generated clock */
dev->gclk = devm_clk_get(&pdev->dev, "gclk");
if (IS_ERR(dev->gclk)) {
err = PTR_ERR(dev->gclk);
dev_err(&pdev->dev,
"failed to get the PMC generated clock: %d\n", err);
return err;
}
spin_lock_init(&dev->blockend_lock);
dev->dev = &pdev->dev;
dev->regmap = regmap;
platform_set_drvdata(pdev, dev);
dev->capture.addr = (dma_addr_t)mem->start + SPDIFRX_RHR;
dev->capture.maxburst = 1;
err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (err) {
dev_err(&pdev->dev, "failed to register PMC: %d\n", err);
return err;
}
err = devm_snd_soc_register_component(&pdev->dev,
&mchp_spdifrx_component,
&mchp_spdifrx_dai, 1);
if (err) {
dev_err(&pdev->dev, "fail to register dai\n");
return err;
}
regmap_read(regmap, SPDIFRX_VERSION, &vers);
dev_info(&pdev->dev, "hw version: %#lx\n", vers & SPDIFRX_VERSION_MASK);
return 0;
}
static struct platform_driver mchp_spdifrx_driver = {
.probe = mchp_spdifrx_probe,
.driver = {
.name = "mchp_spdifrx",
.of_match_table = of_match_ptr(mchp_spdifrx_dt_ids),
},
};
module_platform_driver(mchp_spdifrx_driver);
MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
MODULE_DESCRIPTION("Microchip S/PDIF RX Controller Driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,871 @@
// SPDX-License-Identifier: GPL-2.0
//
// Driver for Microchip S/PDIF TX Controller
//
// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
//
// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <sound/asoundef.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
/*
* ---- S/PDIF Transmitter Controller Register map ----
*/
#define SPDIFTX_CR 0x00 /* Control Register */
#define SPDIFTX_MR 0x04 /* Mode Register */
#define SPDIFTX_CDR 0x0C /* Common Data Register */
#define SPDIFTX_IER 0x14 /* Interrupt Enable Register */
#define SPDIFTX_IDR 0x18 /* Interrupt Disable Register */
#define SPDIFTX_IMR 0x1C /* Interrupt Mask Register */
#define SPDIFTX_ISR 0x20 /* Interrupt Status Register */
#define SPDIFTX_CH1UD(reg) (0x50 + (reg) * 4) /* User Data 1 Register x */
#define SPDIFTX_CH1S(reg) (0x80 + (reg) * 4) /* Channel Status 1 Register x */
#define SPDIFTX_VERSION 0xF0
/*
* ---- Control Register (Write-only) ----
*/
#define SPDIFTX_CR_SWRST BIT(0) /* Software Reset */
#define SPDIFTX_CR_FCLR BIT(1) /* FIFO clear */
/*
* ---- Mode Register (Read/Write) ----
*/
/* Transmit Enable */
#define SPDIFTX_MR_TXEN_MASK GENMASK(0, 0)
#define SPDIFTX_MR_TXEN_DISABLE (0 << 0)
#define SPDIFTX_MR_TXEN_ENABLE (1 << 0)
/* Multichannel Transfer */
#define SPDIFTX_MR_MULTICH_MASK GENAMSK(1, 1)
#define SPDIFTX_MR_MULTICH_MONO (0 << 1)
#define SPDIFTX_MR_MULTICH_DUAL (1 << 1)
/* Data Word Endian Mode */
#define SPDIFTX_MR_ENDIAN_MASK GENMASK(2, 2)
#define SPDIFTX_MR_ENDIAN_LITTLE (0 << 2)
#define SPDIFTX_MR_ENDIAN_BIG (1 << 2)
/* Data Justification */
#define SPDIFTX_MR_JUSTIFY_MASK GENMASK(3, 3)
#define SPDIFTX_MR_JUSTIFY_LSB (0 << 3)
#define SPDIFTX_MR_JUSTIFY_MSB (1 << 3)
/* Common Audio Register Transfer Mode */
#define SPDIFTX_MR_CMODE_MASK GENMASK(5, 4)
#define SPDIFTX_MR_CMODE_INDEX_ACCESS (0 << 4)
#define SPDIFTX_MR_CMODE_TOGGLE_ACCESS (1 << 4)
#define SPDIFTX_MR_CMODE_INTERLVD_ACCESS (2 << 4)
/* Valid Bits per Sample */
#define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8)
#define SPDIFTX_MR_VBPS(bps) (((bps) << 8) & SPDIFTX_MR_VBPS_MASK)
/* Chunk Size */
#define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16)
#define SPDIFTX_MR_CHUNK(size) (((size) << 16) & SPDIFTX_MR_CHUNK_MASK)
/* Validity Bits for Channels 1 and 2 */
#define SPDIFTX_MR_VALID1 BIT(24)
#define SPDIFTX_MR_VALID2 BIT(25)
/* Disable Null Frame on underrrun */
#define SPDIFTX_MR_DNFR_MASK GENMASK(27, 27)
#define SPDIFTX_MR_DNFR_INVALID (0 << 27)
#define SPDIFTX_MR_DNFR_VALID (1 << 27)
/* Bytes per Sample */
#define SPDIFTX_MR_BPS_MASK GENMASK(29, 28)
#define SPDIFTX_MR_BPS(bytes) \
((((bytes) - 1) << 28) & SPDIFTX_MR_BPS_MASK)
/*
* ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ----
*/
#define SPDIFTX_IR_TXRDY BIT(0)
#define SPDIFTX_IR_TXEMPTY BIT(1)
#define SPDIFTX_IR_TXFULL BIT(2)
#define SPDIFTX_IR_TXCHUNK BIT(3)
#define SPDIFTX_IR_TXUDR BIT(4)
#define SPDIFTX_IR_TXOVR BIT(5)
#define SPDIFTX_IR_CSRDY BIT(6)
#define SPDIFTX_IR_UDRDY BIT(7)
#define SPDIFTX_IR_TXRDYCH(ch) BIT((ch) + 8)
#define SPDIFTX_IR_SECE BIT(10)
#define SPDIFTX_IR_TXUDRCH(ch) BIT((ch) + 11)
#define SPDIFTX_IR_BEND BIT(13)
static bool mchp_spdiftx_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPDIFTX_MR:
case SPDIFTX_IMR:
case SPDIFTX_ISR:
case SPDIFTX_CH1UD(0):
case SPDIFTX_CH1UD(1):
case SPDIFTX_CH1UD(2):
case SPDIFTX_CH1UD(3):
case SPDIFTX_CH1UD(4):
case SPDIFTX_CH1UD(5):
case SPDIFTX_CH1S(0):
case SPDIFTX_CH1S(1):
case SPDIFTX_CH1S(2):
case SPDIFTX_CH1S(3):
case SPDIFTX_CH1S(4):
case SPDIFTX_CH1S(5):
return true;
default:
return false;
}
}
static bool mchp_spdiftx_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPDIFTX_CR:
case SPDIFTX_MR:
case SPDIFTX_CDR:
case SPDIFTX_IER:
case SPDIFTX_IDR:
case SPDIFTX_CH1UD(0):
case SPDIFTX_CH1UD(1):
case SPDIFTX_CH1UD(2):
case SPDIFTX_CH1UD(3):
case SPDIFTX_CH1UD(4):
case SPDIFTX_CH1UD(5):
case SPDIFTX_CH1S(0):
case SPDIFTX_CH1S(1):
case SPDIFTX_CH1S(2):
case SPDIFTX_CH1S(3):
case SPDIFTX_CH1S(4):
case SPDIFTX_CH1S(5):
return true;
default:
return false;
}
}
static bool mchp_spdiftx_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPDIFTX_CDR:
case SPDIFTX_ISR:
return true;
default:
return false;
}
}
static const struct regmap_config mchp_spdiftx_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = SPDIFTX_VERSION,
.readable_reg = mchp_spdiftx_readable_reg,
.writeable_reg = mchp_spdiftx_writeable_reg,
.precious_reg = mchp_spdiftx_precious_reg,
};
#define SPDIFTX_GCLK_RATIO 128
#define SPDIFTX_CS_BITS 192
#define SPDIFTX_UD_BITS 192
struct mchp_spdiftx_mixer_control {
unsigned char ch_stat[SPDIFTX_CS_BITS / 8];
unsigned char user_data[SPDIFTX_UD_BITS / 8];
spinlock_t lock; /* exclusive access to control data */
};
struct mchp_spdiftx_dev {
struct mchp_spdiftx_mixer_control control;
struct snd_dmaengine_dai_dma_data playback;
struct device *dev;
struct regmap *regmap;
struct clk *pclk;
struct clk *gclk;
unsigned int fmt;
const struct mchp_i2s_caps *caps;
int gclk_enabled:1;
};
static inline int mchp_spdiftx_is_running(struct mchp_spdiftx_dev *dev)
{
u32 mr;
regmap_read(dev->regmap, SPDIFTX_MR, &mr);
return !!(mr & SPDIFTX_MR_TXEN_ENABLE);
}
static void mchp_spdiftx_channel_status_write(struct mchp_spdiftx_dev *dev)
{
struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
u32 val;
int i;
for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat) / 4; i++) {
val = (ctrl->ch_stat[(i * 4) + 0] << 0) |
(ctrl->ch_stat[(i * 4) + 1] << 8) |
(ctrl->ch_stat[(i * 4) + 2] << 16) |
(ctrl->ch_stat[(i * 4) + 3] << 24);
regmap_write(dev->regmap, SPDIFTX_CH1S(i), val);
}
}
static void mchp_spdiftx_user_data_write(struct mchp_spdiftx_dev *dev)
{
struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
u32 val;
int i;
for (i = 0; i < ARRAY_SIZE(ctrl->user_data) / 4; i++) {
val = (ctrl->user_data[(i * 4) + 0] << 0) |
(ctrl->user_data[(i * 4) + 1] << 8) |
(ctrl->user_data[(i * 4) + 2] << 16) |
(ctrl->user_data[(i * 4) + 3] << 24);
regmap_write(dev->regmap, SPDIFTX_CH1UD(i), val);
}
}
static irqreturn_t mchp_spdiftx_interrupt(int irq, void *dev_id)
{
struct mchp_spdiftx_dev *dev = dev_id;
struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
u32 sr, imr, pending, idr = 0;
regmap_read(dev->regmap, SPDIFTX_ISR, &sr);
regmap_read(dev->regmap, SPDIFTX_IMR, &imr);
pending = sr & imr;
if (!pending)
return IRQ_NONE;
if (pending & SPDIFTX_IR_TXUDR) {
dev_warn(dev->dev, "underflow detected\n");
idr |= SPDIFTX_IR_TXUDR;
}
if (pending & SPDIFTX_IR_TXOVR) {
dev_warn(dev->dev, "overflow detected\n");
idr |= SPDIFTX_IR_TXOVR;
}
if (pending & SPDIFTX_IR_UDRDY) {
spin_lock(&ctrl->lock);
mchp_spdiftx_user_data_write(dev);
spin_unlock(&ctrl->lock);
idr |= SPDIFTX_IR_UDRDY;
}
if (pending & SPDIFTX_IR_CSRDY) {
spin_lock(&ctrl->lock);
mchp_spdiftx_channel_status_write(dev);
spin_unlock(&ctrl->lock);
idr |= SPDIFTX_IR_CSRDY;
}
regmap_write(dev->regmap, SPDIFTX_IDR, idr);
return IRQ_HANDLED;
}
static int mchp_spdiftx_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
/* Software reset the IP */
regmap_write(dev->regmap, SPDIFTX_CR,
SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR);
return 0;
}
static void mchp_spdiftx_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
/* Disable interrupts */
regmap_write(dev->regmap, SPDIFTX_IDR, 0xffffffff);
}
static int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
u32 mr;
int running;
int ret;
/* do not start/stop while channel status or user data is updated */
spin_lock(&ctrl->lock);
regmap_read(dev->regmap, SPDIFTX_MR, &mr);
running = !!(mr & SPDIFTX_MR_TXEN_ENABLE);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (!running) {
mr &= ~SPDIFTX_MR_TXEN_MASK;
mr |= SPDIFTX_MR_TXEN_ENABLE;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (running) {
mr &= ~SPDIFTX_MR_TXEN_MASK;
mr |= SPDIFTX_MR_TXEN_DISABLE;
}
break;
default:
spin_unlock(&ctrl->lock);
return -EINVAL;
}
ret = regmap_write(dev->regmap, SPDIFTX_MR, mr);
spin_unlock(&ctrl->lock);
if (ret) {
dev_err(dev->dev, "unable to disable TX: %d\n", ret);
return ret;
}
return 0;
}
static int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
unsigned long flags;
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
u32 mr;
unsigned int bps = params_physical_width(params) / 8;
int ret;
dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
__func__, params_rate(params), params_format(params),
params_width(params), params_channels(params));
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
dev_err(dev->dev, "Capture is not supported\n");
return -EINVAL;
}
regmap_read(dev->regmap, SPDIFTX_MR, &mr);
if (mr & SPDIFTX_MR_TXEN_ENABLE) {
dev_err(dev->dev, "PCM already running\n");
return -EBUSY;
}
/* Defaults: Toggle mode, justify to LSB, chunksize 1 */
mr = SPDIFTX_MR_CMODE_TOGGLE_ACCESS | SPDIFTX_MR_JUSTIFY_LSB;
dev->playback.maxburst = 1;
switch (params_channels(params)) {
case 1:
mr |= SPDIFTX_MR_MULTICH_MONO;
break;
case 2:
mr |= SPDIFTX_MR_MULTICH_DUAL;
if (bps > 2)
dev->playback.maxburst = 2;
break;
default:
dev_err(dev->dev, "unsupported number of channels: %d\n",
params_channels(params));
return -EINVAL;
}
mr |= SPDIFTX_MR_CHUNK(dev->playback.maxburst);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
mr |= SPDIFTX_MR_VBPS(8);
break;
case SNDRV_PCM_FORMAT_S16_BE:
mr |= SPDIFTX_MR_ENDIAN_BIG;
fallthrough;
case SNDRV_PCM_FORMAT_S16_LE:
mr |= SPDIFTX_MR_VBPS(16);
break;
case SNDRV_PCM_FORMAT_S18_3BE:
mr |= SPDIFTX_MR_ENDIAN_BIG;
fallthrough;
case SNDRV_PCM_FORMAT_S18_3LE:
mr |= SPDIFTX_MR_VBPS(18);
break;
case SNDRV_PCM_FORMAT_S20_3BE:
mr |= SPDIFTX_MR_ENDIAN_BIG;
fallthrough;
case SNDRV_PCM_FORMAT_S20_3LE:
mr |= SPDIFTX_MR_VBPS(20);
break;
case SNDRV_PCM_FORMAT_S24_3BE:
mr |= SPDIFTX_MR_ENDIAN_BIG;
fallthrough;
case SNDRV_PCM_FORMAT_S24_3LE:
mr |= SPDIFTX_MR_VBPS(24);
break;
case SNDRV_PCM_FORMAT_S24_BE:
mr |= SPDIFTX_MR_ENDIAN_BIG;
fallthrough;
case SNDRV_PCM_FORMAT_S24_LE:
mr |= SPDIFTX_MR_VBPS(24);
break;
case SNDRV_PCM_FORMAT_S32_BE:
mr |= SPDIFTX_MR_ENDIAN_BIG;
fallthrough;
case SNDRV_PCM_FORMAT_S32_LE:
mr |= SPDIFTX_MR_VBPS(32);
break;
default:
dev_err(dev->dev, "unsupported PCM format: %d\n",
params_format(params));
return -EINVAL;
}
mr |= SPDIFTX_MR_BPS(bps);
spin_lock_irqsave(&ctrl->lock, flags);
ctrl->ch_stat[3] &= ~IEC958_AES3_CON_FS;
switch (params_rate(params)) {
case 22050:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_22050;
break;
case 24000:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_24000;
break;
case 32000:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_32000;
break;
case 44100:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_44100;
break;
case 48000:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_48000;
break;
case 88200:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_88200;
break;
case 96000:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_96000;
break;
case 176400:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_176400;
break;
case 192000:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_192000;
break;
case 8000:
case 11025:
case 16000:
case 64000:
ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_NOTID;
break;
default:
dev_err(dev->dev, "unsupported sample frequency: %u\n",
params_rate(params));
spin_unlock_irqrestore(&ctrl->lock, flags);
return -EINVAL;
}
mchp_spdiftx_channel_status_write(dev);
spin_unlock_irqrestore(&ctrl->lock, flags);
mr |= SPDIFTX_MR_VALID1 | SPDIFTX_MR_VALID2;
if (dev->gclk_enabled) {
clk_disable_unprepare(dev->gclk);
dev->gclk_enabled = 0;
}
ret = clk_set_rate(dev->gclk, params_rate(params) *
SPDIFTX_GCLK_RATIO);
if (ret) {
dev_err(dev->dev,
"unable to change gclk rate to: rate %u * ratio %u\n",
params_rate(params), SPDIFTX_GCLK_RATIO);
return ret;
}
ret = clk_prepare_enable(dev->gclk);
if (ret) {
dev_err(dev->dev, "unable to enable gclk: %d\n", ret);
return ret;
}
dev->gclk_enabled = 1;
dev_dbg(dev->dev, "%s(): GCLK set to %d\n", __func__,
params_rate(params) * SPDIFTX_GCLK_RATIO);
/* Enable interrupts */
regmap_write(dev->regmap, SPDIFTX_IER,
SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR);
regmap_write(dev->regmap, SPDIFTX_MR, mr);
return 0;
}
static int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
regmap_write(dev->regmap, SPDIFTX_IDR,
SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR);
if (dev->gclk_enabled) {
clk_disable_unprepare(dev->gclk);
dev->gclk_enabled = 0;
}
return regmap_write(dev->regmap, SPDIFTX_CR,
SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR);
}
static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = {
.startup = mchp_spdiftx_dai_startup,
.shutdown = mchp_spdiftx_dai_shutdown,
.trigger = mchp_spdiftx_trigger,
.hw_params = mchp_spdiftx_hw_params,
.hw_free = mchp_spdiftx_hw_free,
};
#define MCHP_SPDIFTX_RATES SNDRV_PCM_RATE_8000_192000
#define MCHP_SPDIFTX_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_U16_BE | \
SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S18_3BE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S20_3BE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
SNDRV_PCM_FMTBIT_S24_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S24_BE | \
SNDRV_PCM_FMTBIT_S32_LE | \
SNDRV_PCM_FMTBIT_S32_BE \
)
static int mchp_spdiftx_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
static int mchp_spdiftx_cs_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
unsigned long flags;
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
spin_lock_irqsave(&ctrl->lock, flags);
memcpy(uvalue->value.iec958.status, ctrl->ch_stat,
sizeof(ctrl->ch_stat));
spin_unlock_irqrestore(&ctrl->lock, flags);
return 0;
}
static int mchp_spdiftx_cs_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
unsigned long flags;
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
int changed = 0;
int i;
spin_lock_irqsave(&ctrl->lock, flags);
for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat); i++) {
if (ctrl->ch_stat[i] != uvalue->value.iec958.status[i])
changed = 1;
ctrl->ch_stat[i] = uvalue->value.iec958.status[i];
}
if (changed) {
/* don't enable IP while we copy the channel status */
if (mchp_spdiftx_is_running(dev)) {
/*
* if SPDIF is running, wait for interrupt to write
* channel status
*/
regmap_write(dev->regmap, SPDIFTX_IER,
SPDIFTX_IR_CSRDY);
} else {
mchp_spdiftx_channel_status_write(dev);
}
}
spin_unlock_irqrestore(&ctrl->lock, flags);
return changed;
}
static int mchp_spdiftx_cs_mask(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
memset(uvalue->value.iec958.status, 0xff,
sizeof(uvalue->value.iec958.status));
return 0;
}
static int mchp_spdiftx_subcode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
unsigned long flags;
spin_lock_irqsave(&ctrl->lock, flags);
memcpy(uvalue->value.iec958.subcode, ctrl->user_data,
sizeof(ctrl->user_data));
spin_unlock_irqrestore(&ctrl->lock, flags);
return 0;
}
static int mchp_spdiftx_subcode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uvalue)
{
unsigned long flags;
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdiftx_mixer_control *ctrl = &dev->control;
int changed = 0;
int i;
spin_lock_irqsave(&ctrl->lock, flags);
for (i = 0; i < ARRAY_SIZE(ctrl->user_data); i++) {
if (ctrl->user_data[i] != uvalue->value.iec958.subcode[i])
changed = 1;
ctrl->user_data[i] = uvalue->value.iec958.subcode[i];
}
if (changed) {
if (mchp_spdiftx_is_running(dev)) {
/*
* if SPDIF is running, wait for interrupt to write
* user data
*/
regmap_write(dev->regmap, SPDIFTX_IER,
SPDIFTX_IR_UDRDY);
} else {
mchp_spdiftx_user_data_write(dev);
}
}
spin_unlock_irqrestore(&ctrl->lock, flags);
return changed;
}
static struct snd_kcontrol_new mchp_spdiftx_ctrls[] = {
/* Channel status controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdiftx_info,
.get = mchp_spdiftx_cs_get,
.put = mchp_spdiftx_cs_put,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
.access = SNDRV_CTL_ELEM_ACCESS_READ,
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = mchp_spdiftx_info,
.get = mchp_spdiftx_cs_mask,
},
/* User bits controller */
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 Subcode Playback Default",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = mchp_spdiftx_info,
.get = mchp_spdiftx_subcode_get,
.put = mchp_spdiftx_subcode_put,
},
};
static int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai)
{
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
int ret;
snd_soc_dai_init_dma_data(dai, &dev->playback, NULL);
ret = clk_prepare_enable(dev->pclk);
if (ret) {
dev_err(dev->dev,
"failed to enable the peripheral clock: %d\n", ret);
return ret;
}
/* Add controls */
snd_soc_add_dai_controls(dai, mchp_spdiftx_ctrls,
ARRAY_SIZE(mchp_spdiftx_ctrls));
return 0;
}
static int mchp_spdiftx_dai_remove(struct snd_soc_dai *dai)
{
struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai);
clk_disable_unprepare(dev->pclk);
return 0;
}
static struct snd_soc_dai_driver mchp_spdiftx_dai = {
.name = "mchp-spdiftx",
.probe = mchp_spdiftx_dai_probe,
.remove = mchp_spdiftx_dai_remove,
.playback = {
.stream_name = "S/PDIF Playback",
.channels_min = 1,
.channels_max = 2,
.rates = MCHP_SPDIFTX_RATES,
.formats = MCHP_SPDIFTX_FORMATS,
},
.ops = &mchp_spdiftx_dai_ops,
};
static const struct snd_soc_component_driver mchp_spdiftx_component = {
.name = "mchp-spdiftx",
};
static const struct of_device_id mchp_spdiftx_dt_ids[] = {
{
.compatible = "microchip,sama7g5-spdiftx",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mchp_spdiftx_dt_ids);
static int mchp_spdiftx_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct mchp_spdiftx_dev *dev;
struct resource *mem;
struct regmap *regmap;
void __iomem *base;
struct mchp_spdiftx_mixer_control *ctrl;
int irq;
int err;
/* Get memory for driver data. */
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
/* Get hardware capabilities. */
match = of_match_node(mchp_spdiftx_dt_ids, np);
if (match)
dev->caps = match->data;
/* Map I/O registers. */
base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
if (IS_ERR(base))
return PTR_ERR(base);
regmap = devm_regmap_init_mmio(&pdev->dev, base,
&mchp_spdiftx_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
/* Request IRQ */
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
err = devm_request_irq(&pdev->dev, irq, mchp_spdiftx_interrupt, 0,
dev_name(&pdev->dev), dev);
if (err)
return err;
/* Get the peripheral clock */
dev->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(dev->pclk)) {
err = PTR_ERR(dev->pclk);
dev_err(&pdev->dev,
"failed to get the peripheral clock: %d\n", err);
return err;
}
/* Get the generic clock */
dev->gclk = devm_clk_get(&pdev->dev, "gclk");
if (IS_ERR(dev->gclk)) {
err = PTR_ERR(dev->gclk);
dev_err(&pdev->dev,
"failed to get the PMC generic clock: %d\n", err);
return err;
}
ctrl = &dev->control;
spin_lock_init(&ctrl->lock);
/* Init channel status */
ctrl->ch_stat[0] = IEC958_AES0_CON_NOT_COPYRIGHT |
IEC958_AES0_CON_EMPHASIS_NONE;
dev->dev = &pdev->dev;
dev->regmap = regmap;
platform_set_drvdata(pdev, dev);
dev->playback.addr = (dma_addr_t)mem->start + SPDIFTX_CDR;
dev->playback.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (err) {
dev_err(&pdev->dev, "failed to register PMC: %d\n", err);
return err;
}
err = devm_snd_soc_register_component(&pdev->dev,
&mchp_spdiftx_component,
&mchp_spdiftx_dai, 1);
if (err) {
dev_err(&pdev->dev, "failed to register component: %d\n", err);
return err;
}
return 0;
}
static struct platform_driver mchp_spdiftx_driver = {
.probe = mchp_spdiftx_probe,
.driver = {
.name = "mchp_spdiftx",
.of_match_table = of_match_ptr(mchp_spdiftx_dt_ids),
},
};
module_platform_driver(mchp_spdiftx_driver);
MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
MODULE_DESCRIPTION("Microchip S/PDIF TX Controller Driver");
MODULE_LICENSE("GPL v2");

View file

@ -64,6 +64,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_CS42L52
imply SND_SOC_CS42L56
imply SND_SOC_CS42L73
imply SND_SOC_CS4234
imply SND_SOC_CS4265
imply SND_SOC_CS4270
imply SND_SOC_CS4271_I2C
@ -127,6 +128,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_ML26124
imply SND_SOC_MT6351
imply SND_SOC_MT6358
imply SND_SOC_MT6359
imply SND_SOC_MT6660
imply SND_SOC_NAU8540
imply SND_SOC_NAU8810
@ -154,6 +156,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT298
imply SND_SOC_RT1011
imply SND_SOC_RT1015
imply SND_SOC_RT1015P
imply SND_SOC_RT1305
imply SND_SOC_RT1308
imply SND_SOC_RT5514
@ -192,6 +195,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_STI_SAS
imply SND_SOC_TAS2552
imply SND_SOC_TAS2562
imply SND_SOC_TAS2764
imply SND_SOC_TAS2770
imply SND_SOC_TAS5086
imply SND_SOC_TAS571X
@ -540,6 +544,7 @@ config SND_SOC_CQ0093VC
config SND_SOC_CROS_EC_CODEC
tristate "codec driver for ChromeOS EC"
depends on CROS_EC
select CRYPTO
select CRYPTO_LIB_SHA256
help
If you say yes here you will get support for the
@ -589,6 +594,11 @@ config SND_SOC_CS42L73
tristate "Cirrus Logic CS42L73 CODEC"
depends on I2C
config SND_SOC_CS4234
tristate "Cirrus Logic CS4234 CODEC"
depends on I2C
select REGMAP_I2C
config SND_SOC_CS4265
tristate "Cirrus Logic CS4265 CODEC"
depends on I2C
@ -1031,6 +1041,7 @@ config SND_SOC_RL6231
default y if SND_SOC_RT5682=y
default y if SND_SOC_RT1011=y
default y if SND_SOC_RT1015=y
default y if SND_SOC_RT1015P=y
default y if SND_SOC_RT1305=y
default y if SND_SOC_RT1308=y
default m if SND_SOC_RT5514=m
@ -1048,6 +1059,7 @@ config SND_SOC_RL6231
default m if SND_SOC_RT5682=m
default m if SND_SOC_RT1011=m
default m if SND_SOC_RT1015=m
default m if SND_SOC_RT1015P=m
default m if SND_SOC_RT1305=m
default m if SND_SOC_RT1308=m
@ -1080,6 +1092,10 @@ config SND_SOC_RT1015
tristate
depends on I2C
config SND_SOC_RT1015P
tristate
depends on GPIOLIB
config SND_SOC_RT1305
tristate
depends on I2C
@ -1288,6 +1304,10 @@ config SND_SOC_TAS2562
tristate "Texas Instruments TAS2562 Mono Audio amplifier"
depends on I2C
config SND_SOC_TAS2764
tristate "Texas Instruments TAS2764 Mono Audio amplifier"
depends on I2C
config SND_SOC_TAS2770
tristate "Texas Instruments TAS2770 speaker amplifier"
depends on I2C
@ -1724,6 +1744,13 @@ config SND_SOC_MT6358
Enable support for the platform which uses MT6358 as
external codec device.
config SND_SOC_MT6359
tristate "MediaTek MT6359 Codec"
depends on MTK_PMIC_WRAP
help
Enable support for the platform which uses MT6359 as
external codec device.
config SND_SOC_MT6660
tristate "Mediatek MT6660 Speaker Amplifier"
depends on I2C

View file

@ -57,6 +57,7 @@ snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
snd-soc-cs42l52-objs := cs42l52.o
snd-soc-cs42l56-objs := cs42l56.o
snd-soc-cs42l73-objs := cs42l73.o
snd-soc-cs4234-objs := cs4234.o
snd-soc-cs4265-objs := cs4265.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cs4271-objs := cs4271.o
@ -126,6 +127,7 @@ snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o
snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o
snd-soc-mt6351-objs := mt6351.o
snd-soc-mt6358-objs := mt6358.o
snd-soc-mt6359-objs := mt6359.o
snd-soc-mt6660-objs := mt6660.o
snd-soc-nau8540-objs := nau8540.o
snd-soc-nau8810-objs := nau8810.o
@ -158,6 +160,7 @@ snd-soc-rl6231-objs := rl6231.o
snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt1011-objs := rt1011.o
snd-soc-rt1015-objs := rt1015.o
snd-soc-rt1015p-objs := rt1015p.o
snd-soc-rt1305-objs := rt1305.o
snd-soc-rt1308-objs := rt1308.o
snd-soc-rt1308-sdw-objs := rt1308-sdw.o
@ -301,6 +304,7 @@ 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
snd-soc-tas2764-objs := tas2764.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
@ -362,6 +366,7 @@ obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o
obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o
obj-$(CONFIG_SND_SOC_CS4234) += snd-soc-cs4234.o
obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o
@ -431,6 +436,7 @@ obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o
obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o
obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o
obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o
obj-$(CONFIG_SND_SOC_MT6359) += snd-soc-mt6359.o
obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o
obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o
obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o
@ -463,6 +469,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o
obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o
obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o
obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o
obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o
@ -511,6 +518,7 @@ 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_TAS2764) += snd-soc-tas2764.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

View file

@ -12,6 +12,7 @@
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
@ -21,13 +22,27 @@
#include "ak4458.h"
#define AK4458_NUM_SUPPLIES 2
static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = {
"DVDD",
"AVDD",
};
enum ak4458_type {
AK4458 = 0,
AK4497 = 1,
};
struct ak4458_drvdata {
struct snd_soc_dai_driver *dai_drv;
const struct snd_soc_component_driver *comp_drv;
enum ak4458_type type;
};
/* AK4458 Codec Private Data */
struct ak4458_priv {
struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES];
const struct ak4458_drvdata *drvdata;
struct device *dev;
struct regmap *regmap;
struct gpio_desc *reset_gpiod;
@ -37,6 +52,7 @@ struct ak4458_priv {
int fmt;
int slots;
int slot_width;
u32 dsd_path; /* For ak4497 */
};
static const struct reg_default ak4458_reg_defaults[] = {
@ -317,12 +333,54 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
int pcm_width = max(params_physical_width(params), ak4458->slot_width);
int nfs1;
u8 format;
u8 format, dsdsel0, dsdsel1;
int nfs1, dsd_bclk;
nfs1 = params_rate(params);
ak4458->fs = nfs1;
/* calculate bit clock */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_DSD_U8:
case SNDRV_PCM_FORMAT_DSD_U16_LE:
case SNDRV_PCM_FORMAT_DSD_U16_BE:
case SNDRV_PCM_FORMAT_DSD_U32_LE:
case SNDRV_PCM_FORMAT_DSD_U32_BE:
dsd_bclk = nfs1 * params_physical_width(params);
switch (dsd_bclk) {
case 2822400:
dsdsel0 = 0;
dsdsel1 = 0;
break;
case 5644800:
dsdsel0 = 1;
dsdsel1 = 0;
break;
case 11289600:
dsdsel0 = 0;
dsdsel1 = 1;
break;
case 22579200:
if (ak4458->drvdata->type == AK4497) {
dsdsel0 = 1;
dsdsel1 = 1;
} else {
dev_err(dai->dev, "DSD512 not supported.\n");
return -EINVAL;
}
break;
default:
dev_err(dai->dev, "Unsupported dsd bclk.\n");
return -EINVAL;
}
snd_soc_component_update_bits(component, AK4458_06_DSD1,
AK4458_DSDSEL_MASK, dsdsel0);
snd_soc_component_update_bits(component, AK4458_09_DSD2,
AK4458_DSDSEL_MASK, dsdsel1);
break;
}
/* Master Clock Frequency Auto Setting Mode Enable */
snd_soc_component_update_bits(component, AK4458_00_CONTROL1, 0x80, 0x80);
@ -347,6 +405,9 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream,
case SND_SOC_DAIFMT_DSP_B:
format = AK4458_DIF_32BIT_MSB;
break;
case SND_SOC_DAIFMT_PDM:
format = AK4458_DIF_32BIT_MSB;
break;
default:
return -EINVAL;
}
@ -385,6 +446,7 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_DSP_B:
case SND_SOC_DAIFMT_PDM:
ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
@ -393,6 +455,12 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
/* DSD mode */
snd_soc_component_update_bits(component, AK4458_02_CONTROL3,
AK4458_DP_MASK,
ak4458->fmt == SND_SOC_DAIFMT_PDM ?
AK4458_DP_MASK : 0);
ak4458_rstn_control(component, 0);
ak4458_rstn_control(component, 1);
@ -464,7 +532,10 @@ static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
#define AK4458_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
SNDRV_PCM_FMTBIT_S32_LE |\
SNDRV_PCM_FMTBIT_DSD_U8 |\
SNDRV_PCM_FMTBIT_DSD_U16_LE |\
SNDRV_PCM_FMTBIT_DSD_U32_LE)
static const unsigned int ak4458_rates[] = {
8000, 11025, 16000, 22050,
@ -556,6 +627,13 @@ static int ak4458_init(struct snd_soc_component *component)
if (ret < 0)
return ret;
if (ak4458->drvdata->type == AK4497) {
ret = snd_soc_component_update_bits(component, AK4458_09_DSD2,
0x4, (ak4458->dsd_path << 2));
if (ret < 0)
return ret;
}
return ak4458_rstn_control(component, 1);
}
@ -587,12 +665,22 @@ static int __maybe_unused ak4458_runtime_suspend(struct device *dev)
if (ak4458->mute_gpiod)
gpiod_set_value_cansleep(ak4458->mute_gpiod, 0);
regulator_bulk_disable(ARRAY_SIZE(ak4458->supplies),
ak4458->supplies);
return 0;
}
static int __maybe_unused ak4458_runtime_resume(struct device *dev)
{
struct ak4458_priv *ak4458 = dev_get_drvdata(dev);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ak4458->supplies),
ak4458->supplies);
if (ret != 0) {
dev_err(ak4458->dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
if (ak4458->mute_gpiod)
gpiod_set_value_cansleep(ak4458->mute_gpiod, 1);
@ -650,11 +738,13 @@ static const struct regmap_config ak4458_regmap = {
static const struct ak4458_drvdata ak4458_drvdata = {
.dai_drv = &ak4458_dai,
.comp_drv = &soc_codec_dev_ak4458,
.type = AK4458,
};
static const struct ak4458_drvdata ak4497_drvdata = {
.dai_drv = &ak4497_dai,
.comp_drv = &soc_codec_dev_ak4497,
.type = AK4497,
};
static const struct dev_pm_ops ak4458_pm = {
@ -666,8 +756,7 @@ static const struct dev_pm_ops ak4458_pm = {
static int ak4458_i2c_probe(struct i2c_client *i2c)
{
struct ak4458_priv *ak4458;
const struct ak4458_drvdata *drvdata;
int ret;
int ret, i;
ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL);
if (!ak4458)
@ -680,7 +769,7 @@ static int ak4458_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, ak4458);
ak4458->dev = &i2c->dev;
drvdata = of_device_get_match_data(&i2c->dev);
ak4458->drvdata = of_device_get_match_data(&i2c->dev);
ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset",
GPIOD_OUT_LOW);
@ -692,14 +781,29 @@ static int ak4458_i2c_probe(struct i2c_client *i2c)
if (IS_ERR(ak4458->mute_gpiod))
return PTR_ERR(ak4458->mute_gpiod);
ret = devm_snd_soc_register_component(ak4458->dev, drvdata->comp_drv,
drvdata->dai_drv, 1);
/* Optional property for ak4497 */
of_property_read_u32(i2c->dev.of_node, "dsd-path", &ak4458->dsd_path);
for (i = 0; i < ARRAY_SIZE(ak4458->supplies); i++)
ak4458->supplies[i].supply = ak4458_supply_names[i];
ret = devm_regulator_bulk_get(ak4458->dev, ARRAY_SIZE(ak4458->supplies),
ak4458->supplies);
if (ret != 0) {
dev_err(ak4458->dev, "Failed to request supplies: %d\n", ret);
return ret;
}
ret = devm_snd_soc_register_component(ak4458->dev,
ak4458->drvdata->comp_drv,
ak4458->drvdata->dai_drv, 1);
if (ret < 0) {
dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret);
return ret;
}
pm_runtime_enable(&i2c->dev);
regcache_cache_only(ak4458->regmap, true);
return 0;
}

View file

@ -83,4 +83,7 @@
#define AK4458_ATS_SHIFT 6
#define AK4458_ATS_MASK GENMASK(7, 6)
#endif /* _AK4458_H */
#define AK4458_DSDSEL_MASK (0x1 << 0)
#define AK4458_DP_MASK (0x1 << 7)
#endif

View file

@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/initval.h>
@ -22,8 +23,15 @@
#include "ak5558.h"
#define AK5558_NUM_SUPPLIES 2
static const char *ak5558_supply_names[AK5558_NUM_SUPPLIES] = {
"DVDD",
"AVDD",
};
/* AK5558 Codec Private Data */
struct ak5558_priv {
struct regulator_bulk_data supplies[AK5558_NUM_SUPPLIES];
struct snd_soc_component component;
struct regmap *regmap;
struct i2c_client *i2c;
@ -299,12 +307,22 @@ static int __maybe_unused ak5558_runtime_suspend(struct device *dev)
regcache_cache_only(ak5558->regmap, true);
ak5558_power_off(ak5558);
regulator_bulk_disable(ARRAY_SIZE(ak5558->supplies),
ak5558->supplies);
return 0;
}
static int __maybe_unused ak5558_runtime_resume(struct device *dev)
{
struct ak5558_priv *ak5558 = dev_get_drvdata(dev);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ak5558->supplies),
ak5558->supplies);
if (ret != 0) {
dev_err(dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
ak5558_power_off(ak5558);
ak5558_power_on(ak5558);
@ -350,6 +368,7 @@ static int ak5558_i2c_probe(struct i2c_client *i2c)
{
struct ak5558_priv *ak5558;
int ret = 0;
int i;
ak5558 = devm_kzalloc(&i2c->dev, sizeof(*ak5558), GFP_KERNEL);
if (!ak5558)
@ -367,6 +386,16 @@ static int ak5558_i2c_probe(struct i2c_client *i2c)
if (IS_ERR(ak5558->reset_gpiod))
return PTR_ERR(ak5558->reset_gpiod);
for (i = 0; i < ARRAY_SIZE(ak5558->supplies); i++)
ak5558->supplies[i].supply = ak5558_supply_names[i];
ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(ak5558->supplies),
ak5558->supplies);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
return ret;
}
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_codec_dev_ak5558,
&ak5558_dai, 1);
@ -374,6 +403,7 @@ static int ak5558_i2c_probe(struct i2c_client *i2c)
return ret;
pm_runtime_enable(&i2c->dev);
regcache_cache_only(ak5558->regmap, true);
return 0;
}

918
sound/soc/codecs/cs4234.c Normal file
View file

@ -0,0 +1,918 @@
// SPDX-License-Identifier: GPL-2.0-only
// cs4234.c -- ALSA SoC CS4234 driver
//
// Copyright (C) 2020 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
//
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/jiffies.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/workqueue.h>
#include "cs4234.h"
struct cs4234 {
struct device *dev;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data core_supplies[2];
int num_core_supplies;
struct completion vq_ramp_complete;
struct delayed_work vq_ramp_delay;
struct clk *mclk;
unsigned long mclk_rate;
unsigned long lrclk_rate;
unsigned int format;
struct snd_ratnum rate_dividers[2];
struct snd_pcm_hw_constraint_ratnums rate_constraint;
};
/* -89.92dB to +6.02dB with step of 0.38dB */
static const DECLARE_TLV_DB_SCALE(dac_tlv, -8992, 38, 0);
static const char * const cs4234_dac14_delay_text[] = {
"0us", "100us", "150us", "200us", "225us", "250us", "275us", "300us",
"325us", "350us", "375us", "400us", "425us", "450us", "475us", "500us",
};
static SOC_ENUM_SINGLE_DECL(cs4234_dac14_group_delay, CS4234_TPS_CTRL,
CS4234_GRP_DELAY_SHIFT, cs4234_dac14_delay_text);
static const char * const cs4234_noise_gate_text[] = {
"72dB", "78dB", "84dB", "90dB", "96dB", "102dB", "138dB", "Disabled",
};
static SOC_ENUM_SINGLE_DECL(cs4234_ll_noise_gate, CS4234_LOW_LAT_CTRL1,
CS4234_LL_NG_SHIFT, cs4234_noise_gate_text);
static SOC_ENUM_SINGLE_DECL(cs4234_dac14_noise_gate, CS4234_DAC_CTRL1,
CS4234_DAC14_NG_SHIFT, cs4234_noise_gate_text);
static SOC_ENUM_SINGLE_DECL(cs4234_dac5_noise_gate, CS4234_DAC_CTRL2,
CS4234_DAC5_NG_SHIFT, cs4234_noise_gate_text);
static const char * const cs4234_dac5_config_fltr_sel_text[] = {
"Interpolation Filter", "Sample and Hold"
};
static SOC_ENUM_SINGLE_DECL(cs4234_dac5_config_fltr_sel, CS4234_DAC_CTRL1,
CS4234_DAC5_CFG_FLTR_SHIFT,
cs4234_dac5_config_fltr_sel_text);
static const char * const cs4234_mute_delay_text[] = {
"1x", "4x", "16x", "64x",
};
static SOC_ENUM_SINGLE_DECL(cs4234_mute_delay, CS4234_VOLUME_MODE,
CS4234_MUTE_DELAY_SHIFT, cs4234_mute_delay_text);
static const char * const cs4234_minmax_delay_text[] = {
"1x", "2x", "4x", "8x", "16x", "32x", "64x", "128x",
};
static SOC_ENUM_SINGLE_DECL(cs4234_min_delay, CS4234_VOLUME_MODE,
CS4234_MIN_DELAY_SHIFT, cs4234_minmax_delay_text);
static SOC_ENUM_SINGLE_DECL(cs4234_max_delay, CS4234_VOLUME_MODE,
CS4234_MAX_DELAY_SHIFT, cs4234_minmax_delay_text);
static int cs4234_dac14_grp_delay_put(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *uctrl)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kctrl);
struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
unsigned int val = 0;
int ret = 0;
snd_soc_dapm_mutex_lock(dapm);
regmap_read(cs4234->regmap, CS4234_ADC_CTRL2, &val);
if ((val & 0x0F) != 0x0F) { // are all the ADCs powerdown
ret = -EBUSY;
dev_err(component->dev, "Can't change group delay while ADC are ON\n");
goto exit;
}
regmap_read(cs4234->regmap, CS4234_DAC_CTRL4, &val);
if ((val & 0x1F) != 0x1F) { // are all the DACs powerdown
ret = -EBUSY;
dev_err(component->dev, "Can't change group delay while DAC are ON\n");
goto exit;
}
ret = snd_soc_put_enum_double(kctrl, uctrl);
exit:
snd_soc_dapm_mutex_unlock(dapm);
return ret;
}
static void cs4234_vq_ramp_done(struct work_struct *work)
{
struct delayed_work *dw = to_delayed_work(work);
struct cs4234 *cs4234 = container_of(dw, struct cs4234, vq_ramp_delay);
complete_all(&cs4234->vq_ramp_complete);
}
static int cs4234_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_PREPARE:
switch (snd_soc_component_get_bias_level(component)) {
case SND_SOC_BIAS_STANDBY:
wait_for_completion(&cs4234->vq_ramp_complete);
break;
default:
break;
}
break;
default:
break;
}
return 0;
}
static const struct snd_soc_dapm_widget cs4234_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("SDRX1", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SDRX2", NULL, 1, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SDRX3", NULL, 2, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SDRX4", NULL, 3, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SDRX5", NULL, 4, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC1", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC1_SHIFT, 1),
SND_SOC_DAPM_DAC("DAC2", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC2_SHIFT, 1),
SND_SOC_DAPM_DAC("DAC3", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC3_SHIFT, 1),
SND_SOC_DAPM_DAC("DAC4", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC4_SHIFT, 1),
SND_SOC_DAPM_DAC("DAC5", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC5_SHIFT, 1),
SND_SOC_DAPM_OUTPUT("AOUT1"),
SND_SOC_DAPM_OUTPUT("AOUT2"),
SND_SOC_DAPM_OUTPUT("AOUT3"),
SND_SOC_DAPM_OUTPUT("AOUT4"),
SND_SOC_DAPM_OUTPUT("AOUT5"),
SND_SOC_DAPM_INPUT("AIN1"),
SND_SOC_DAPM_INPUT("AIN2"),
SND_SOC_DAPM_INPUT("AIN3"),
SND_SOC_DAPM_INPUT("AIN4"),
SND_SOC_DAPM_ADC("ADC1", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC1_SHIFT, 1),
SND_SOC_DAPM_ADC("ADC2", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC2_SHIFT, 1),
SND_SOC_DAPM_ADC("ADC3", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC3_SHIFT, 1),
SND_SOC_DAPM_ADC("ADC4", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC4_SHIFT, 1),
SND_SOC_DAPM_AIF_OUT("SDTX1", NULL, 0, SND_SOC_NOPM, 0, 1),
SND_SOC_DAPM_AIF_OUT("SDTX2", NULL, 1, SND_SOC_NOPM, 0, 1),
SND_SOC_DAPM_AIF_OUT("SDTX3", NULL, 2, SND_SOC_NOPM, 0, 1),
SND_SOC_DAPM_AIF_OUT("SDTX4", NULL, 3, SND_SOC_NOPM, 0, 1),
};
static const struct snd_soc_dapm_route cs4234_dapm_routes[] = {
/* Playback */
{ "AOUT1", NULL, "DAC1" },
{ "AOUT2", NULL, "DAC2" },
{ "AOUT3", NULL, "DAC3" },
{ "AOUT4", NULL, "DAC4" },
{ "AOUT5", NULL, "DAC5" },
{ "DAC1", NULL, "SDRX1" },
{ "DAC2", NULL, "SDRX2" },
{ "DAC3", NULL, "SDRX3" },
{ "DAC4", NULL, "SDRX4" },
{ "DAC5", NULL, "SDRX5" },
{ "SDRX1", NULL, "Playback" },
{ "SDRX2", NULL, "Playback" },
{ "SDRX3", NULL, "Playback" },
{ "SDRX4", NULL, "Playback" },
{ "SDRX5", NULL, "Playback" },
/* Capture */
{ "ADC1", NULL, "AIN1" },
{ "ADC2", NULL, "AIN2" },
{ "ADC3", NULL, "AIN3" },
{ "ADC4", NULL, "AIN4" },
{ "SDTX1", NULL, "ADC1" },
{ "SDTX2", NULL, "ADC2" },
{ "SDTX3", NULL, "ADC3" },
{ "SDTX4", NULL, "ADC4" },
{ "Capture", NULL, "SDTX1" },
{ "Capture", NULL, "SDTX2" },
{ "Capture", NULL, "SDTX3" },
{ "Capture", NULL, "SDTX4" },
};
static const struct snd_kcontrol_new cs4234_snd_controls[] = {
SOC_SINGLE_TLV("Master Volume", CS4234_MASTER_VOL, 0, 0xff, 1, dac_tlv),
SOC_SINGLE_TLV("DAC1 Volume", CS4234_DAC1_VOL, 0, 0xff, 1, dac_tlv),
SOC_SINGLE_TLV("DAC2 Volume", CS4234_DAC2_VOL, 0, 0xff, 1, dac_tlv),
SOC_SINGLE_TLV("DAC3 Volume", CS4234_DAC3_VOL, 0, 0xff, 1, dac_tlv),
SOC_SINGLE_TLV("DAC4 Volume", CS4234_DAC4_VOL, 0, 0xff, 1, dac_tlv),
SOC_SINGLE_TLV("DAC5 Volume", CS4234_DAC5_VOL, 0, 0xff, 1, dac_tlv),
SOC_SINGLE("DAC5 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC5_ATT_SHIFT, 1, 1),
SOC_SINGLE("DAC1-4 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC14_ATT_SHIFT, 1, 1),
SOC_SINGLE("ADC HPF Switch", CS4234_ADC_CTRL1, CS4234_ENA_HPF_SHIFT, 1, 0),
SOC_ENUM_EXT("DAC1-4 Group Delay", cs4234_dac14_group_delay,
snd_soc_get_enum_double, cs4234_dac14_grp_delay_put),
SOC_SINGLE("ADC1 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC1_SHIFT, 1, 0),
SOC_SINGLE("ADC2 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC2_SHIFT, 1, 0),
SOC_SINGLE("ADC3 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC3_SHIFT, 1, 0),
SOC_SINGLE("ADC4 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC4_SHIFT, 1, 0),
SOC_SINGLE("DAC1 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC1_SHIFT, 1, 0),
SOC_SINGLE("DAC2 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC2_SHIFT, 1, 0),
SOC_SINGLE("DAC3 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC3_SHIFT, 1, 0),
SOC_SINGLE("DAC4 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC4_SHIFT, 1, 0),
SOC_SINGLE("DAC5 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC5_SHIFT, 1, 0),
SOC_SINGLE("ADC1 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC1_SHIFT, 1, 1),
SOC_SINGLE("ADC2 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC2_SHIFT, 1, 1),
SOC_SINGLE("ADC3 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC3_SHIFT, 1, 1),
SOC_SINGLE("ADC4 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC4_SHIFT, 1, 1),
SOC_SINGLE("DAC1 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC1_SHIFT, 1, 1),
SOC_SINGLE("DAC2 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC2_SHIFT, 1, 1),
SOC_SINGLE("DAC3 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC3_SHIFT, 1, 1),
SOC_SINGLE("DAC4 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC4_SHIFT, 1, 1),
SOC_SINGLE("DAC5 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC5_SHIFT, 1, 1),
SOC_SINGLE("Low-latency Switch", CS4234_DAC_CTRL3, CS4234_MUTE_LL_SHIFT, 1, 1),
SOC_SINGLE("DAC1 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
CS4234_INV_LL1_SHIFT, 1, 0),
SOC_SINGLE("DAC2 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
CS4234_INV_LL2_SHIFT, 1, 0),
SOC_SINGLE("DAC3 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
CS4234_INV_LL3_SHIFT, 1, 0),
SOC_SINGLE("DAC4 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1,
CS4234_INV_LL4_SHIFT, 1, 0),
SOC_ENUM("Low-latency Noise Gate", cs4234_ll_noise_gate),
SOC_ENUM("DAC1-4 Noise Gate", cs4234_dac14_noise_gate),
SOC_ENUM("DAC5 Noise Gate", cs4234_dac5_noise_gate),
SOC_SINGLE("DAC1-4 De-emphasis Switch", CS4234_DAC_CTRL1,
CS4234_DAC14_DE_SHIFT, 1, 0),
SOC_SINGLE("DAC5 De-emphasis Switch", CS4234_DAC_CTRL1,
CS4234_DAC5_DE_SHIFT, 1, 0),
SOC_SINGLE("DAC5 Master Controlled Switch", CS4234_DAC_CTRL1,
CS4234_DAC5_MVC_SHIFT, 1, 0),
SOC_ENUM("DAC5 Filter", cs4234_dac5_config_fltr_sel),
SOC_ENUM("Mute Delay", cs4234_mute_delay),
SOC_ENUM("Ramp Minimum Delay", cs4234_min_delay),
SOC_ENUM("Ramp Maximum Delay", cs4234_max_delay),
};
static int cs4234_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int format)
{
struct snd_soc_component *component = codec_dai->component;
struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
unsigned int sp_ctrl = 0;
cs4234->format = format & SND_SOC_DAIFMT_FORMAT_MASK;
switch (cs4234->format) {
case SND_SOC_DAIFMT_LEFT_J:
sp_ctrl |= CS4234_LEFT_J << CS4234_SP_FORMAT_SHIFT;
break;
case SND_SOC_DAIFMT_I2S:
sp_ctrl |= CS4234_I2S << CS4234_SP_FORMAT_SHIFT;
break;
case SND_SOC_DAIFMT_DSP_A: /* TDM mode in datasheet */
sp_ctrl |= CS4234_TDM << CS4234_SP_FORMAT_SHIFT;
break;
default:
dev_err(component->dev, "Unsupported dai format\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
break;
case SND_SOC_DAIFMT_CBM_CFM:
if (cs4234->format == SND_SOC_DAIFMT_DSP_A) {
dev_err(component->dev, "Unsupported DSP A format in master mode\n");
return -EINVAL;
}
sp_ctrl |= CS4234_MST_SLV_MASK;
break;
default:
dev_err(component->dev, "Unsupported master/slave mode\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
sp_ctrl |= CS4234_INVT_SCLK_MASK;
break;
default:
dev_err(component->dev, "Unsupported inverted clock setting\n");
return -EINVAL;
}
regmap_update_bits(cs4234->regmap, CS4234_SP_CTRL,
CS4234_SP_FORMAT_MASK | CS4234_MST_SLV_MASK | CS4234_INVT_SCLK_MASK,
sp_ctrl);
return 0;
}
static int cs4234_dai_hw_params(struct snd_pcm_substream *sub,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
unsigned int mclk_mult, double_speed = 0;
int ret = 0, rate_ad, sample_width;
cs4234->lrclk_rate = params_rate(params);
mclk_mult = cs4234->mclk_rate / cs4234->lrclk_rate;
if (cs4234->lrclk_rate > 48000) {
double_speed = 1;
mclk_mult *= 2;
}
switch (mclk_mult) {
case 256:
case 384:
case 512:
regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP,
CS4234_SPEED_MODE_MASK,
double_speed << CS4234_SPEED_MODE_SHIFT);
regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP,
CS4234_MCLK_RATE_MASK,
((mclk_mult / 128) - 2) << CS4234_MCLK_RATE_SHIFT);
break;
default:
dev_err(component->dev, "Unsupported mclk/lrclk rate\n");
return -EINVAL;
}
switch (cs4234->lrclk_rate) {
case 48000:
case 96000:
rate_ad = CS4234_48K;
break;
case 44100:
case 88200:
rate_ad = CS4234_44K1;
break;
case 32000:
case 64000:
rate_ad = CS4234_32K;
break;
default:
dev_err(component->dev, "Unsupported LR clock\n");
return -EINVAL;
}
regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP, CS4234_BASE_RATE_MASK,
rate_ad << CS4234_BASE_RATE_SHIFT);
sample_width = params_width(params);
switch (sample_width) {
case 16:
sample_width = 0;
break;
case 18:
sample_width = 1;
break;
case 20:
sample_width = 2;
break;
case 24:
sample_width = 3;
break;
default:
dev_err(component->dev, "Unsupported sample width\n");
return -EINVAL;
}
if (sub->stream == SNDRV_PCM_STREAM_CAPTURE)
regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH,
CS4234_SDOUTX_SW_MASK,
sample_width << CS4234_SDOUTX_SW_SHIFT);
else
regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH,
CS4234_INPUT_SW_MASK | CS4234_LOW_LAT_SW_MASK | CS4234_DAC5_SW_MASK,
sample_width << CS4234_INPUT_SW_SHIFT |
sample_width << CS4234_LOW_LAT_SW_SHIFT |
sample_width << CS4234_DAC5_SW_SHIFT);
return ret;
}
/* Scale MCLK rate by 64 to avoid overflow in the ratnum calculation */
#define CS4234_MCLK_SCALE 64
static const struct snd_ratnum cs4234_dividers[] = {
{
.num = 0,
.den_min = 256 / CS4234_MCLK_SCALE,
.den_max = 512 / CS4234_MCLK_SCALE,
.den_step = 128 / CS4234_MCLK_SCALE,
},
{
.num = 0,
.den_min = 128 / CS4234_MCLK_SCALE,
.den_max = 192 / CS4234_MCLK_SCALE,
.den_step = 64 / CS4234_MCLK_SCALE,
},
};
static int cs4234_dai_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
struct cs4234 *cs4234 = rule->private;
int mclk = cs4234->mclk_rate;
struct snd_interval ranges[] = {
{ /* Single Speed Mode */
.min = mclk / clamp(mclk / 30000, 256, 512),
.max = mclk / clamp(mclk / 50000, 256, 512),
},
{ /* Double Speed Mode */
.min = mclk / clamp(mclk / 60000, 128, 256),
.max = mclk / clamp(mclk / 100000, 128, 256),
},
};
return snd_interval_ranges(hw_param_interval(params, rule->var),
ARRAY_SIZE(ranges), ranges, 0);
}
static int cs4234_dai_startup(struct snd_pcm_substream *sub, struct snd_soc_dai *dai)
{
struct snd_soc_component *comp = dai->component;
struct cs4234 *cs4234 = snd_soc_component_get_drvdata(comp);
int i, ret;
switch (cs4234->format) {
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_I2S:
cs4234->rate_constraint.nrats = 2;
/*
* Playback only supports 24-bit samples in these modes.
* Note: SNDRV_PCM_HW_PARAM_SAMPLE_BITS constrains the physical
* width, which we don't care about, so constrain the format.
*/
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = snd_pcm_hw_constraint_mask64(
sub->runtime,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S24_3LE);
if (ret < 0)
return ret;
ret = snd_pcm_hw_constraint_minmax(sub->runtime,
SNDRV_PCM_HW_PARAM_CHANNELS,
1, 4);
if (ret < 0)
return ret;
}
break;
case SND_SOC_DAIFMT_DSP_A:
cs4234->rate_constraint.nrats = 1;
break;
default:
dev_err(comp->dev, "Startup unsupported DAI format\n");
return -EINVAL;
}
for (i = 0; i < cs4234->rate_constraint.nrats; i++)
cs4234->rate_dividers[i].num = cs4234->mclk_rate / CS4234_MCLK_SCALE;
ret = snd_pcm_hw_constraint_ratnums(sub->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&cs4234->rate_constraint);
if (ret < 0)
return ret;
/*
* MCLK/rate may be a valid ratio but out-of-spec (e.g. 24576000/64000)
* so this rule limits the range of sample rate for given MCLK.
*/
return snd_pcm_hw_rule_add(sub->runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
cs4234_dai_rule_rate, cs4234, -1);
}
static int cs4234_dai_set_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 cs4234 *cs4234 = snd_soc_component_get_drvdata(component);
unsigned int slot_offset, dac5_slot, dac5_mask_group;
uint8_t dac5_masks[4];
if (slot_width != 32) {
dev_err(component->dev, "Unsupported slot width\n");
return -EINVAL;
}
/* Either 4 or 5 consecutive bits, DAC5 is optional */
slot_offset = ffs(tx_mask) - 1;
tx_mask >>= slot_offset;
if ((slot_offset % 4) || ((tx_mask != 0x0F) && (tx_mask != 0x1F))) {
dev_err(component->dev, "Unsupported tx slots allocation\n");
return -EINVAL;
}
regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_DAC14_SRC_MASK,
(slot_offset / 4) << CS4234_DAC14_SRC_SHIFT);
regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_LL_SRC_MASK,
(slot_offset / 4) << CS4234_LL_SRC_SHIFT);
if (tx_mask == 0x1F) {
dac5_slot = slot_offset + 4;
memset(dac5_masks, 0xFF, sizeof(dac5_masks));
dac5_mask_group = dac5_slot / 8;
dac5_slot %= 8;
dac5_masks[dac5_mask_group] ^= BIT(7 - dac5_slot);
regmap_bulk_write(cs4234->regmap,
CS4234_SDIN1_MASK1,
dac5_masks,
ARRAY_SIZE(dac5_masks));
}
return 0;
}
static const struct snd_soc_dai_ops cs4234_dai_ops = {
.set_fmt = cs4234_dai_set_fmt,
.hw_params = cs4234_dai_hw_params,
.startup = cs4234_dai_startup,
.set_tdm_slot = cs4234_dai_set_tdm_slot,
};
static struct snd_soc_dai_driver cs4234_dai[] = {
{
.name = "cs4234-dai",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 5,
.rates = CS4234_PCM_RATES,
.formats = CS4234_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 4,
.rates = CS4234_PCM_RATES,
.formats = CS4234_FORMATS,
},
.ops = &cs4234_dai_ops,
.symmetric_rates = 1,
},
};
static const struct reg_default cs4234_default_reg[] = {
{ CS4234_CLOCK_SP, 0x04},
{ CS4234_SAMPLE_WIDTH, 0xFF},
{ CS4234_SP_CTRL, 0x48},
{ CS4234_SP_DATA_SEL, 0x01},
{ CS4234_SDIN1_MASK1, 0xFF},
{ CS4234_SDIN1_MASK2, 0xFF},
{ CS4234_SDIN2_MASK1, 0xFF},
{ CS4234_SDIN2_MASK2, 0xFF},
{ CS4234_TPS_CTRL, 0x00},
{ CS4234_ADC_CTRL1, 0xC0},
{ CS4234_ADC_CTRL2, 0xFF},
{ CS4234_LOW_LAT_CTRL1, 0xE0},
{ CS4234_DAC_CTRL1, 0xE0},
{ CS4234_DAC_CTRL2, 0xE0},
{ CS4234_DAC_CTRL3, 0xBF},
{ CS4234_DAC_CTRL4, 0x1F},
{ CS4234_VOLUME_MODE, 0x87},
{ CS4234_MASTER_VOL, 0x10},
{ CS4234_DAC1_VOL, 0x10},
{ CS4234_DAC2_VOL, 0x10},
{ CS4234_DAC3_VOL, 0x10},
{ CS4234_DAC4_VOL, 0x10},
{ CS4234_DAC5_VOL, 0x10},
{ CS4234_INT_CTRL, 0x40},
{ CS4234_INT_MASK1, 0x10},
{ CS4234_INT_MASK2, 0x20},
};
static bool cs4234_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS4234_DEVID_AB ... CS4234_DEVID_EF:
case CS4234_REVID ... CS4234_DAC5_VOL:
case CS4234_INT_CTRL ... CS4234_MAX_REGISTER:
return true;
default:
return false;
}
}
static bool cs4234_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS4234_INT_NOTIFY1:
case CS4234_INT_NOTIFY2:
return true;
default:
return false;
}
}
static bool cs4234_writeable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS4234_DEVID_AB ... CS4234_REVID:
case CS4234_INT_NOTIFY1 ... CS4234_INT_NOTIFY2:
return false;
default:
return true;
}
}
static const struct snd_soc_component_driver soc_component_cs4234 = {
.dapm_widgets = cs4234_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs4234_dapm_widgets),
.dapm_routes = cs4234_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(cs4234_dapm_routes),
.controls = cs4234_snd_controls,
.num_controls = ARRAY_SIZE(cs4234_snd_controls),
.set_bias_level = cs4234_set_bias_level,
.non_legacy_dai_naming = 1,
.idle_bias_on = 1,
.suspend_bias_off = 1,
};
static const struct regmap_config cs4234_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = CS4234_MAX_REGISTER,
.readable_reg = cs4234_readable_register,
.volatile_reg = cs4234_volatile_reg,
.writeable_reg = cs4234_writeable_register,
.reg_defaults = cs4234_default_reg,
.num_reg_defaults = ARRAY_SIZE(cs4234_default_reg),
.cache_type = REGCACHE_RBTREE,
.use_single_read = true,
.use_single_write = true,
};
static const char * const cs4234_core_supplies[] = {
"VA",
"VL",
};
static void cs4234_shutdown(struct cs4234 *cs4234)
{
cancel_delayed_work_sync(&cs4234->vq_ramp_delay);
reinit_completion(&cs4234->vq_ramp_complete);
regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK,
CS4234_VQ_RAMP_MASK);
msleep(50);
regcache_cache_only(cs4234->regmap, true);
/* Clear VQ Ramp Bit in cache for the next PowerUp */
regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK, 0);
gpiod_set_value_cansleep(cs4234->reset_gpio, 0);
regulator_bulk_disable(cs4234->num_core_supplies, cs4234->core_supplies);
clk_disable_unprepare(cs4234->mclk);
}
static int cs4234_powerup(struct cs4234 *cs4234)
{
int ret;
ret = clk_prepare_enable(cs4234->mclk);
if (ret) {
dev_err(cs4234->dev, "Failed to enable mclk: %d\n", ret);
return ret;
}
ret = regulator_bulk_enable(cs4234->num_core_supplies, cs4234->core_supplies);
if (ret) {
dev_err(cs4234->dev, "Failed to enable core supplies: %d\n", ret);
clk_disable_unprepare(cs4234->mclk);
return ret;
}
usleep_range(CS4234_HOLD_RESET_TIME_US, 2 * CS4234_HOLD_RESET_TIME_US);
gpiod_set_value_cansleep(cs4234->reset_gpio, 1);
/* Make sure hardware reset done 2 ms + (3000/MCLK) */
usleep_range(CS4234_BOOT_TIME_US, CS4234_BOOT_TIME_US * 2);
queue_delayed_work(system_power_efficient_wq,
&cs4234->vq_ramp_delay,
msecs_to_jiffies(CS4234_VQ_CHARGE_MS));
return 0;
}
static int cs4234_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id)
{
struct cs4234 *cs4234;
struct device *dev = &i2c_client->dev;
unsigned int revid;
uint32_t devid;
uint8_t ids[3];
int ret = 0, i;
cs4234 = devm_kzalloc(dev, sizeof(*cs4234), GFP_KERNEL);
if (!cs4234)
return -ENOMEM;
i2c_set_clientdata(i2c_client, cs4234);
cs4234->dev = dev;
init_completion(&cs4234->vq_ramp_complete);
INIT_DELAYED_WORK(&cs4234->vq_ramp_delay, cs4234_vq_ramp_done);
cs4234->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(cs4234->reset_gpio))
return PTR_ERR(cs4234->reset_gpio);
BUILD_BUG_ON(ARRAY_SIZE(cs4234->core_supplies) < ARRAY_SIZE(cs4234_core_supplies));
cs4234->num_core_supplies = ARRAY_SIZE(cs4234_core_supplies);
for (i = 0; i < ARRAY_SIZE(cs4234_core_supplies); i++)
cs4234->core_supplies[i].supply = cs4234_core_supplies[i];
ret = devm_regulator_bulk_get(dev, cs4234->num_core_supplies, cs4234->core_supplies);
if (ret) {
dev_err(dev, "Failed to request core supplies %d\n", ret);
return ret;
}
cs4234->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(cs4234->mclk)) {
ret = PTR_ERR(cs4234->mclk);
dev_err(dev, "Failed to get the mclk: %d\n", ret);
return ret;
}
cs4234->mclk_rate = clk_get_rate(cs4234->mclk);
if (cs4234->mclk_rate < 7680000 || cs4234->mclk_rate > 25600000) {
dev_err(dev, "Invalid Master Clock rate\n");
return -EINVAL;
}
cs4234->regmap = devm_regmap_init_i2c(i2c_client, &cs4234_regmap);
if (IS_ERR(cs4234->regmap)) {
ret = PTR_ERR(cs4234->regmap);
dev_err(dev, "regmap_init() failed: %d\n", ret);
return ret;
}
ret = cs4234_powerup(cs4234);
if (ret)
return ret;
ret = regmap_bulk_read(cs4234->regmap, CS4234_DEVID_AB, ids, ARRAY_SIZE(ids));
if (ret < 0) {
dev_err(dev, "Failed to read DEVID: %d\n", ret);
goto fail_shutdown;
}
devid = (ids[0] << 16) | (ids[1] << 8) | ids[2];
if (devid != CS4234_SUPPORTED_ID) {
dev_err(dev, "Unknown device ID: %x\n", devid);
ret = -EINVAL;
goto fail_shutdown;
}
ret = regmap_read(cs4234->regmap, CS4234_REVID, &revid);
if (ret < 0) {
dev_err(dev, "Failed to read CS4234_REVID: %d\n", ret);
goto fail_shutdown;
}
dev_info(dev, "Cirrus Logic CS4234, Alpha Rev: %02X, Numeric Rev: %02X\n",
(revid & 0xF0) >> 4, revid & 0x0F);
ret = regulator_get_voltage(cs4234->core_supplies[CS4234_SUPPLY_VA].consumer);
switch (ret) {
case 3135000 ... 3650000:
regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1,
CS4234_VA_SEL_MASK,
CS4234_3V3 << CS4234_VA_SEL_SHIFT);
break;
case 4750000 ... 5250000:
regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1,
CS4234_VA_SEL_MASK,
CS4234_5V << CS4234_VA_SEL_SHIFT);
break;
default:
dev_err(dev, "Invalid VA voltage\n");
ret = -EINVAL;
goto fail_shutdown;
}
pm_runtime_set_active(&i2c_client->dev);
pm_runtime_enable(&i2c_client->dev);
memcpy(&cs4234->rate_dividers, &cs4234_dividers, sizeof(cs4234_dividers));
cs4234->rate_constraint.rats = cs4234->rate_dividers;
ret = snd_soc_register_component(dev, &soc_component_cs4234, cs4234_dai,
ARRAY_SIZE(cs4234_dai));
if (ret < 0) {
dev_err(dev, "Failed to register component:%d\n", ret);
pm_runtime_disable(&i2c_client->dev);
goto fail_shutdown;
}
return ret;
fail_shutdown:
cs4234_shutdown(cs4234);
return ret;
}
static int cs4234_i2c_remove(struct i2c_client *i2c_client)
{
struct cs4234 *cs4234 = i2c_get_clientdata(i2c_client);
struct device *dev = &i2c_client->dev;
snd_soc_unregister_component(dev);
pm_runtime_disable(dev);
cs4234_shutdown(cs4234);
return 0;
}
static int __maybe_unused cs4234_runtime_resume(struct device *dev)
{
struct cs4234 *cs4234 = dev_get_drvdata(dev);
int ret;
ret = cs4234_powerup(cs4234);
if (ret)
return ret;
regcache_mark_dirty(cs4234->regmap);
regcache_cache_only(cs4234->regmap, false);
ret = regcache_sync(cs4234->regmap);
if (ret) {
dev_err(dev, "Failed to sync regmap: %d\n", ret);
cs4234_shutdown(cs4234);
return ret;
}
return 0;
}
static int __maybe_unused cs4234_runtime_suspend(struct device *dev)
{
struct cs4234 *cs4234 = dev_get_drvdata(dev);
cs4234_shutdown(cs4234);
return 0;
}
static const struct dev_pm_ops cs4234_pm = {
SET_RUNTIME_PM_OPS(cs4234_runtime_suspend, cs4234_runtime_resume, NULL)
};
static const struct of_device_id cs4234_of_match[] = {
{ .compatible = "cirrus,cs4234", },
{ }
};
MODULE_DEVICE_TABLE(of, cs4234_of_match);
static struct i2c_driver cs4234_i2c_driver = {
.driver = {
.name = "cs4234",
.pm = &cs4234_pm,
.of_match_table = cs4234_of_match,
},
.probe = cs4234_i2c_probe,
.remove = cs4234_i2c_remove,
};
module_i2c_driver(cs4234_i2c_driver);
MODULE_DESCRIPTION("ASoC Cirrus Logic CS4234 driver");
MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");

287
sound/soc/codecs/cs4234.h Normal file
View file

@ -0,0 +1,287 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC Audio driver for CS4234 codec
*
* Copyright (C) 2020 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#ifndef CS4234_H
#define CS4234_H
#define CS4234_DEVID_AB 0x01
#define CS4234_DEVID_CD 0x02
#define CS4234_DEVID_EF 0x03
#define CS4234_REVID 0x05
#define CS4234_CLOCK_SP 0x06
#define CS4234_BASE_RATE_MASK 0xC0
#define CS4234_BASE_RATE_SHIFT 6
#define CS4234_SPEED_MODE_MASK 0x30
#define CS4234_SPEED_MODE_SHIFT 4
#define CS4234_MCLK_RATE_MASK 0x0E
#define CS4234_MCLK_RATE_SHIFT 1
#define CS4234_SAMPLE_WIDTH 0x07
#define CS4234_SDOUTX_SW_MASK 0xC0
#define CS4234_SDOUTX_SW_SHIFT 6
#define CS4234_INPUT_SW_MASK 0x30
#define CS4234_INPUT_SW_SHIFT 4
#define CS4234_LOW_LAT_SW_MASK 0x0C
#define CS4234_LOW_LAT_SW_SHIFT 2
#define CS4234_DAC5_SW_MASK 0x03
#define CS4234_DAC5_SW_SHIFT 0
#define CS4234_SP_CTRL 0x08
#define CS4234_INVT_SCLK_MASK 0x80
#define CS4234_INVT_SCLK_SHIFT 7
#define CS4234_DAC5_SRC_MASK 0x70
#define CS4234_DAC5_SRC_SHIFT 4
#define CS4234_SP_FORMAT_MASK 0x0C
#define CS4234_SP_FORMAT_SHIFT 2
#define CS4234_SDO_CHAIN_MASK 0x02
#define CS4234_SDO_CHAIN_SHIFT 1
#define CS4234_MST_SLV_MASK 0x01
#define CS4234_MST_SLV_SHIFT 0
#define CS4234_SP_DATA_SEL 0x09
#define CS4234_DAC14_SRC_MASK 0x38
#define CS4234_DAC14_SRC_SHIFT 3
#define CS4234_LL_SRC_MASK 0x07
#define CS4234_LL_SRC_SHIFT 0
#define CS4234_SDIN1_MASK1 0x0A
#define CS4234_SDIN1_MASK2 0x0B
#define CS4234_SDIN2_MASK1 0x0C
#define CS4234_SDIN2_MASK2 0x0D
#define CS4234_TPS_CTRL 0x0E
#define CS4234_TPS_MODE_MASK 0x80
#define CS4234_TPS_MODE_SHIFT 7
#define CS4234_TPS_OFST_MASK 0x70
#define CS4234_TPS_OFST_SHIFT 4
#define CS4234_GRP_DELAY_MASK 0x0F
#define CS4234_GRP_DELAY_SHIFT 0
#define CS4234_ADC_CTRL1 0x0F
#define CS4234_VA_SEL_MASK 0x20
#define CS4234_VA_SEL_SHIFT 5
#define CS4234_ENA_HPF_MASK 0x10
#define CS4234_ENA_HPF_SHIFT 4
#define CS4234_INV_ADC_MASK 0x0F
#define CS4234_INV_ADC4_MASK 0x08
#define CS4234_INV_ADC4_SHIFT 3
#define CS4234_INV_ADC3_MASK 0x04
#define CS4234_INV_ADC3_SHIFT 2
#define CS4234_INV_ADC2_MASK 0x02
#define CS4234_INV_ADC2_SHIFT 1
#define CS4234_INV_ADC1_MASK 0x01
#define CS4234_INV_ADC1_SHIFT 0
#define CS4234_ADC_CTRL2 0x10
#define CS4234_MUTE_ADC4_MASK 0x80
#define CS4234_MUTE_ADC4_SHIFT 7
#define CS4234_MUTE_ADC3_MASK 0x40
#define CS4234_MUTE_ADC3_SHIFT 6
#define CS4234_MUTE_ADC2_MASK 0x20
#define CS4234_MUTE_ADC2_SHIFT 5
#define CS4234_MUTE_ADC1_MASK 0x10
#define CS4234_MUTE_ADC1_SHIFT 4
#define CS4234_PDN_ADC4_MASK 0x08
#define CS4234_PDN_ADC4_SHIFT 3
#define CS4234_PDN_ADC3_MASK 0x04
#define CS4234_PDN_ADC3_SHIFT 2
#define CS4234_PDN_ADC2_MASK 0x02
#define CS4234_PDN_ADC2_SHIFT 1
#define CS4234_PDN_ADC1_MASK 0x01
#define CS4234_PDN_ADC1_SHIFT 0
#define CS4234_LOW_LAT_CTRL1 0x11
#define CS4234_LL_NG_MASK 0xE0
#define CS4234_LL_NG_SHIFT 5
#define CS4234_INV_LL_MASK 0x0F
#define CS4234_INV_LL4_MASK 0x08
#define CS4234_INV_LL4_SHIFT 3
#define CS4234_INV_LL3_MASK 0x04
#define CS4234_INV_LL3_SHIFT 2
#define CS4234_INV_LL2_MASK 0x02
#define CS4234_INV_LL2_SHIFT 1
#define CS4234_INV_LL1_MASK 0x01
#define CS4234_INV_LL1_SHIFT 0
#define CS4234_DAC_CTRL1 0x12
#define CS4234_DAC14_NG_MASK 0xE0
#define CS4234_DAC14_NG_SHIFT 5
#define CS4234_DAC14_DE_MASK 0x10
#define CS4234_DAC14_DE_SHIFT 4
#define CS4234_DAC5_DE_MASK 0x08
#define CS4234_DAC5_DE_SHIFT 3
#define CS4234_DAC5_MVC_MASK 0x04
#define CS4234_DAC5_MVC_SHIFT 2
#define CS4234_DAC5_CFG_FLTR_MASK 0x03
#define CS4234_DAC5_CFG_FLTR_SHIFT 0
#define CS4234_DAC_CTRL2 0x13
#define CS4234_DAC5_NG_MASK 0xE0
#define CS4234_DAC5_NG_SHIFT 5
#define CS4234_INV_DAC_MASK 0x1F
#define CS4234_INV_DAC5_MASK 0x10
#define CS4234_INV_DAC5_SHIFT 4
#define CS4234_INV_DAC4_MASK 0x08
#define CS4234_INV_DAC4_SHIFT 3
#define CS4234_INV_DAC3_MASK 0x04
#define CS4234_INV_DAC3_SHIFT 2
#define CS4234_INV_DAC2_MASK 0x02
#define CS4234_INV_DAC2_SHIFT 1
#define CS4234_INV_DAC1_MASK 0x01
#define CS4234_INV_DAC1_SHIFT 0
#define CS4234_DAC_CTRL3 0x14
#define CS4234_DAC5_ATT_MASK 0x80
#define CS4234_DAC5_ATT_SHIFT 7
#define CS4234_DAC14_ATT_MASK 0x40
#define CS4234_DAC14_ATT_SHIFT 6
#define CS4234_MUTE_LL_MASK 0x20
#define CS4234_MUTE_LL_SHIFT 5
#define CS4234_MUTE_DAC5_MASK 0x10
#define CS4234_MUTE_DAC5_SHIFT 4
#define CS4234_MUTE_DAC4_MASK 0x08
#define CS4234_MUTE_DAC4_SHIFT 3
#define CS4234_MUTE_DAC3_MASK 0x04
#define CS4234_MUTE_DAC3_SHIFT 2
#define CS4234_MUTE_DAC2_MASK 0x02
#define CS4234_MUTE_DAC2_SHIFT 1
#define CS4234_MUTE_DAC1_MASK 0x01
#define CS4234_MUTE_DAC1_SHIFT 0
#define CS4234_DAC_CTRL4 0x15
#define CS4234_VQ_RAMP_MASK 0x80
#define CS4234_VQ_RAMP_SHIFT 7
#define CS4234_TPS_GAIN_MASK 0x40
#define CS4234_TPS_GAIN_SHIFT 6
#define CS4234_PDN_DAC5_MASK 0x10
#define CS4234_PDN_DAC5_SHIFT 4
#define CS4234_PDN_DAC4_MASK 0x08
#define CS4234_PDN_DAC4_SHIFT 3
#define CS4234_PDN_DAC3_MASK 0x04
#define CS4234_PDN_DAC3_SHIFT 2
#define CS4234_PDN_DAC2_MASK 0x02
#define CS4234_PDN_DAC2_SHIFT 1
#define CS4234_PDN_DAC1_MASK 0x01
#define CS4234_PDN_DAC1_SHIFT 0
#define CS4234_VOLUME_MODE 0x16
#define CS4234_MUTE_DELAY_MASK 0xC0
#define CS4234_MUTE_DELAY_SHIFT 6
#define CS4234_MIN_DELAY_MASK 0x38
#define CS4234_MIN_DELAY_SHIFT 3
#define CS4234_MAX_DELAY_MASK 0x07
#define CS4234_MAX_DELAY_SHIFT 0
#define CS4234_MASTER_VOL 0x17
#define CS4234_DAC1_VOL 0x18
#define CS4234_DAC2_VOL 0x19
#define CS4234_DAC3_VOL 0x1A
#define CS4234_DAC4_VOL 0x1B
#define CS4234_DAC5_VOL 0x1C
#define CS4234_INT_CTRL 0x1E
#define CS4234_INT_MODE_MASK 0x80
#define CS4234_INT_MODE_SHIFT 7
#define CS4234_INT_PIN_MASK 0x60
#define CS4234_INT_PIN_SHIFT 5
#define CS4234_INT_MASK1 0x1F
#define CS4234_MSK_TST_MODE_MASK 0x80
#define CS4234_MSK_TST_MODE_ERR_SHIFT 7
#define CS4234_MSK_SP_ERR_MASK 0x40
#define CS4234_MSK_SP_ERR_SHIFT 6
#define CS4234_MSK_CLK_ERR_MASK 0x08
#define CS4234_MSK_CLK_ERR_SHIFT 5
#define CS4234_MSK_ADC4_OVFL_MASK 0x08
#define CS4234_MSK_ADC4_OVFL_SHIFT 3
#define CS4234_MSK_ADC3_OVFL_MASK 0x04
#define CS4234_MSK_ADC3_OVFL_SHIFT 2
#define CS4234_MSK_ADC2_OVFL_MASK 0x02
#define CS4234_MSK_ADC2_OVFL_SHIFT 1
#define CS4234_MSK_ADC1_OVFL_MASK 0x01
#define CS4234_MSK_ADC1_OVFL_SHIFT 0
#define CS4234_INT_MASK2 0x20
#define CS4234_MSK_DAC5_CLIP_MASK 0x10
#define CS4234_MSK_DAC5_CLIP_SHIFT 4
#define CS4234_MSK_DAC4_CLIP_MASK 0x08
#define CS4234_MSK_DAC4_CLIP_SHIFT 3
#define CS4234_MSK_DAC3_CLIP_MASK 0x04
#define CS4234_MSK_DAC3_CLIP_SHIFT 2
#define CS4234_MSK_DAC2_CLIP_MASK 0x02
#define CS4234_MSK_DAC2_CLIP_SHIFT 1
#define CS4234_MSK_DAC1_CLIP_MASK 0x01
#define CS4234_MSK_DAC1_CLIP_SHIFT 0
#define CS4234_INT_NOTIFY1 0x21
#define CS4234_TST_MODE_MASK 0x80
#define CS4234_TST_MODE_SHIFT 7
#define CS4234_SP_ERR_MASK 0x40
#define CS4234_SP_ERR_SHIFT 6
#define CS4234_CLK_MOD_ERR_MASK 0x08
#define CS4234_CLK_MOD_ERR_SHIFT 5
#define CS4234_ADC4_OVFL_MASK 0x08
#define CS4234_ADC4_OVFL_SHIFT 3
#define CS4234_ADC3_OVFL_MASK 0x04
#define CS4234_ADC3_OVFL_SHIFT 2
#define CS4234_ADC2_OVFL_MASK 0x02
#define CS4234_ADC2_OVFL_SHIFT 1
#define CS4234_ADC1_OVFL_MASK 0x01
#define CS4234_ADC1_OVFL_SHIFT 0
#define CS4234_INT_NOTIFY2 0x22
#define CS4234_DAC5_CLIP_MASK 0x10
#define CS4234_DAC5_CLIP_SHIFT 4
#define CS4234_DAC4_CLIP_MASK 0x08
#define CS4234_DAC4_CLIP_SHIFT 3
#define CS4234_DAC3_CLIP_MASK 0x04
#define CS4234_DAC3_CLIP_SHIFT 2
#define CS4234_DAC2_CLIP_MASK 0x02
#define CS4234_DAC2_CLIP_SHIFT 1
#define CS4234_DAC1_CLIP_MASK 0x01
#define CS4234_DAC1_CLIP_SHIFT 0
#define CS4234_MAX_REGISTER CS4234_INT_NOTIFY2
#define CS4234_SUPPORTED_ID 0x423400
#define CS4234_BOOT_TIME_US 3000
#define CS4234_HOLD_RESET_TIME_US 1000
#define CS4234_VQ_CHARGE_MS 1000
#define CS4234_PCM_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
#define CS4234_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S24_3LE)
enum cs4234_supplies {
CS4234_SUPPLY_VA = 0,
CS4234_SUPPLY_VL,
};
enum cs4234_va_sel {
CS4234_3V3 = 0,
CS4234_5V,
};
enum cs4234_sp_format {
CS4234_LEFT_J = 0,
CS4234_I2S,
CS4234_TDM,
};
enum cs4234_base_rate_advisory {
CS4234_48K = 0,
CS4234_44K1,
CS4234_32K,
};
#endif

View file

@ -122,6 +122,9 @@ static const char *chan_mix[] = {
"R L",
};
static const DECLARE_TLV_DB_SCALE(pga_tlv, -300, 50, 0);
static const DECLARE_TLV_DB_SCALE(adc_att_tlv, -9600, 100, 0);
static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix);
static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
@ -138,6 +141,12 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
0, 0x19, 0x7F, adc_pcm_tlv),
SOC_DOUBLE_R("ADC Mixer Switch",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
SOC_DOUBLE_R_SX_TLV("ADC Attenuator Volume",
CS42L51_ADCA_ATT, CS42L51_ADCB_ATT,
0, 0xA0, 96, adc_att_tlv),
SOC_DOUBLE_R_SX_TLV("PGA Volume",
CS42L51_ALC_PGA_CTL, CS42L51_ALC_PGB_CTL,
0, 0x1A, 30, pga_tlv),
SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),

View file

@ -460,7 +460,7 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
*/
static enum da7219_aad_micbias_pulse_lvl
da7219_aad_fw_micbias_pulse_lvl(struct snd_soc_component *component, u32 val)
da7219_aad_fw_micbias_pulse_lvl(struct device *dev, u32 val)
{
switch (val) {
case 2800:
@ -468,13 +468,13 @@ static enum da7219_aad_micbias_pulse_lvl
case 2900:
return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
default:
dev_warn(component->dev, "Invalid micbias pulse level");
dev_warn(dev, "Invalid micbias pulse level");
return DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
}
}
static enum da7219_aad_btn_cfg
da7219_aad_fw_btn_cfg(struct snd_soc_component *component, u32 val)
da7219_aad_fw_btn_cfg(struct device *dev, u32 val)
{
switch (val) {
case 2:
@ -492,13 +492,13 @@ static enum da7219_aad_btn_cfg
case 500:
return DA7219_AAD_BTN_CFG_500MS;
default:
dev_warn(component->dev, "Invalid button config");
dev_warn(dev, "Invalid button config");
return DA7219_AAD_BTN_CFG_10MS;
}
}
static enum da7219_aad_mic_det_thr
da7219_aad_fw_mic_det_thr(struct snd_soc_component *component, u32 val)
da7219_aad_fw_mic_det_thr(struct device *dev, u32 val)
{
switch (val) {
case 200:
@ -510,13 +510,13 @@ static enum da7219_aad_mic_det_thr
case 1000:
return DA7219_AAD_MIC_DET_THR_1000_OHMS;
default:
dev_warn(component->dev, "Invalid mic detect threshold");
dev_warn(dev, "Invalid mic detect threshold");
return DA7219_AAD_MIC_DET_THR_500_OHMS;
}
}
static enum da7219_aad_jack_ins_deb
da7219_aad_fw_jack_ins_deb(struct snd_soc_component *component, u32 val)
da7219_aad_fw_jack_ins_deb(struct device *dev, u32 val)
{
switch (val) {
case 5:
@ -536,13 +536,13 @@ static enum da7219_aad_jack_ins_deb
case 1000:
return DA7219_AAD_JACK_INS_DEB_1S;
default:
dev_warn(component->dev, "Invalid jack insert debounce");
dev_warn(dev, "Invalid jack insert debounce");
return DA7219_AAD_JACK_INS_DEB_20MS;
}
}
static enum da7219_aad_jack_det_rate
da7219_aad_fw_jack_det_rate(struct snd_soc_component *component, const char *str)
da7219_aad_fw_jack_det_rate(struct device *dev, const char *str)
{
if (!strcmp(str, "32ms_64ms")) {
return DA7219_AAD_JACK_DET_RATE_32_64MS;
@ -553,13 +553,13 @@ static enum da7219_aad_jack_det_rate
} else if (!strcmp(str, "256ms_512ms")) {
return DA7219_AAD_JACK_DET_RATE_256_512MS;
} else {
dev_warn(component->dev, "Invalid jack detect rate");
dev_warn(dev, "Invalid jack detect rate");
return DA7219_AAD_JACK_DET_RATE_256_512MS;
}
}
static enum da7219_aad_jack_rem_deb
da7219_aad_fw_jack_rem_deb(struct snd_soc_component *component, u32 val)
da7219_aad_fw_jack_rem_deb(struct device *dev, u32 val)
{
switch (val) {
case 1:
@ -571,13 +571,13 @@ static enum da7219_aad_jack_rem_deb
case 20:
return DA7219_AAD_JACK_REM_DEB_20MS;
default:
dev_warn(component->dev, "Invalid jack removal debounce");
dev_warn(dev, "Invalid jack removal debounce");
return DA7219_AAD_JACK_REM_DEB_1MS;
}
}
static enum da7219_aad_btn_avg
da7219_aad_fw_btn_avg(struct snd_soc_component *component, u32 val)
da7219_aad_fw_btn_avg(struct device *dev, u32 val)
{
switch (val) {
case 1:
@ -589,13 +589,13 @@ static enum da7219_aad_btn_avg
case 8:
return DA7219_AAD_BTN_AVG_8;
default:
dev_warn(component->dev, "Invalid button average value");
dev_warn(dev, "Invalid button average value");
return DA7219_AAD_BTN_AVG_2;
}
}
static enum da7219_aad_adc_1bit_rpt
da7219_aad_fw_adc_1bit_rpt(struct snd_soc_component *component, u32 val)
da7219_aad_fw_adc_1bit_rpt(struct device *dev, u32 val)
{
switch (val) {
case 1:
@ -607,14 +607,13 @@ static enum da7219_aad_adc_1bit_rpt
case 8:
return DA7219_AAD_ADC_1BIT_RPT_8;
default:
dev_warn(component->dev, "Invalid ADC 1-bit repeat value");
dev_warn(dev, "Invalid ADC 1-bit repeat value");
return DA7219_AAD_ADC_1BIT_RPT_1;
}
}
static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_component *component)
static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev)
{
struct device *dev = component->dev;
struct i2c_client *i2c = to_i2c_client(dev);
struct fwnode_handle *aad_np;
struct da7219_aad_pdata *aad_pdata;
@ -634,7 +633,7 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_component
if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
&fw_val32) >= 0)
aad_pdata->micbias_pulse_lvl =
da7219_aad_fw_micbias_pulse_lvl(component, fw_val32);
da7219_aad_fw_micbias_pulse_lvl(dev, fw_val32);
else
aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
@ -643,31 +642,31 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_component
aad_pdata->micbias_pulse_time = fw_val32;
if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0)
aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(component, fw_val32);
aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(dev, fw_val32);
else
aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0)
aad_pdata->mic_det_thr =
da7219_aad_fw_mic_det_thr(component, fw_val32);
da7219_aad_fw_mic_det_thr(dev, fw_val32);
else
aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0)
aad_pdata->jack_ins_deb =
da7219_aad_fw_jack_ins_deb(component, fw_val32);
da7219_aad_fw_jack_ins_deb(dev, fw_val32);
else
aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str))
aad_pdata->jack_det_rate =
da7219_aad_fw_jack_det_rate(component, fw_str);
da7219_aad_fw_jack_det_rate(dev, fw_str);
else
aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0)
aad_pdata->jack_rem_deb =
da7219_aad_fw_jack_rem_deb(component, fw_val32);
da7219_aad_fw_jack_rem_deb(dev, fw_val32);
else
aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
@ -692,13 +691,13 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_component
aad_pdata->c_mic_btn_thr = 0x3E;
if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0)
aad_pdata->btn_avg = da7219_aad_fw_btn_avg(component, fw_val32);
aad_pdata->btn_avg = da7219_aad_fw_btn_avg(dev, fw_val32);
else
aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0)
aad_pdata->adc_1bit_rpt =
da7219_aad_fw_adc_1bit_rpt(component, fw_val32);
da7219_aad_fw_adc_1bit_rpt(dev, fw_val32);
else
aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
@ -887,21 +886,13 @@ void da7219_aad_resume(struct snd_soc_component *component)
int da7219_aad_init(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct da7219_aad_priv *da7219_aad;
struct da7219_aad_priv *da7219_aad = da7219->aad;
u8 mask[DA7219_AAD_IRQ_REG_MAX];
int ret;
da7219_aad = devm_kzalloc(component->dev, sizeof(*da7219_aad), GFP_KERNEL);
if (!da7219_aad)
return -ENOMEM;
da7219->aad = da7219_aad;
da7219_aad->component = component;
/* Handle any DT/ACPI/platform data */
if (da7219->pdata && !da7219->pdata->aad_pdata)
da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(component);
da7219_aad_handle_pdata(component);
/* Disable button detection */
@ -947,6 +938,30 @@ void da7219_aad_exit(struct snd_soc_component *component)
}
EXPORT_SYMBOL_GPL(da7219_aad_exit);
/*
* AAD related I2C probe handling
*/
int da7219_aad_probe(struct i2c_client *i2c)
{
struct da7219_priv *da7219 = i2c_get_clientdata(i2c);
struct device *dev = &i2c->dev;
struct da7219_aad_priv *da7219_aad;
da7219_aad = devm_kzalloc(dev, sizeof(*da7219_aad), GFP_KERNEL);
if (!da7219_aad)
return -ENOMEM;
da7219->aad = da7219_aad;
/* Retrieve any DT/ACPI/platform data */
if (da7219->pdata && !da7219->pdata->aad_pdata)
da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(dev);
return 0;
}
EXPORT_SYMBOL_GPL(da7219_aad_probe);
MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
MODULE_LICENSE("GPL");

View file

@ -212,4 +212,7 @@ void da7219_aad_resume(struct snd_soc_component *component);
int da7219_aad_init(struct snd_soc_component *component);
void da7219_aad_exit(struct snd_soc_component *component);
/* I2C Probe */
int da7219_aad_probe(struct i2c_client *i2c);
#endif /* __DA7219_AAD_H */

View file

@ -1753,9 +1753,8 @@ static enum da7219_mic_amp_in_sel
}
}
static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_component *component)
static struct da7219_pdata *da7219_fw_to_pdata(struct device *dev)
{
struct device *dev = component->dev;
struct da7219_pdata *pdata;
const char *of_str;
u32 of_val32;
@ -1847,45 +1846,43 @@ static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = {
[DA7219_SUPPLY_VDDIO] = "VDDIO",
};
static int da7219_handle_supplies(struct snd_soc_component *component)
static int da7219_handle_supplies(struct snd_soc_component *component,
u8 *io_voltage_lvl)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct regulator *vddio;
u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
int i, ret;
/* Get required supplies */
for (i = 0; i < DA7219_NUM_SUPPLIES; ++i)
da7219->supplies[i].supply = da7219_supply_names[i];
ret = devm_regulator_bulk_get(component->dev, DA7219_NUM_SUPPLIES,
da7219->supplies);
ret = regulator_bulk_get(component->dev, DA7219_NUM_SUPPLIES,
da7219->supplies);
if (ret) {
dev_err(component->dev, "Failed to get supplies");
return ret;
}
/* Default to upper range */
*io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
/* Determine VDDIO voltage provided */
vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer;
ret = regulator_get_voltage(vddio);
if (ret < 1200000)
dev_warn(component->dev, "Invalid VDDIO voltage\n");
else if (ret < 2800000)
io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
*io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
/* Enable main supplies */
ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies);
if (ret) {
dev_err(component->dev, "Failed to enable supplies");
regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
return ret;
}
/* Ensure device in active mode */
snd_soc_component_write(component, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK);
/* Update IO voltage level range */
snd_soc_component_write(component, DA7219_IO_CTRL, io_voltage_lvl);
return 0;
}
@ -2121,14 +2118,26 @@ static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
static int da7219_register_dai_clks(struct snd_soc_component *component)
{
struct device *dev = component->dev;
struct device_node *np = dev->of_node;
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct da7219_pdata *pdata = da7219->pdata;
const char *parent_name;
struct clk_hw_onecell_data *clk_data;
int i, ret;
/* For DT platforms allocate onecell data for clock registration */
if (np) {
clk_data = kzalloc(struct_size(clk_data, hws, DA7219_DAI_NUM_CLKS),
GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->num = DA7219_DAI_NUM_CLKS;
da7219->clk_hw_data = clk_data;
}
for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) {
struct clk_init_data init = {};
struct clk *dai_clk;
struct clk_lookup *dai_clk_lookup;
struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i];
@ -2164,22 +2173,20 @@ static int da7219_register_dai_clks(struct snd_soc_component *component)
init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
dai_clk_hw->init = &init;
dai_clk = devm_clk_register(dev, dai_clk_hw);
if (IS_ERR(dai_clk)) {
dev_warn(dev, "Failed to register %s: %ld\n",
init.name, PTR_ERR(dai_clk));
ret = PTR_ERR(dai_clk);
ret = clk_hw_register(dev, dai_clk_hw);
if (ret) {
dev_warn(dev, "Failed to register %s: %d\n", init.name,
ret);
goto err;
}
da7219->dai_clks[i] = dai_clk;
da7219->dai_clks[i] = dai_clk_hw->clk;
/* If we're using DT, then register as provider accordingly */
if (dev->of_node) {
devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
dai_clk_hw);
/* For DT setup onecell data, otherwise create lookup */
if (np) {
da7219->clk_hw_data->hws[i] = dai_clk_hw;
} else {
dai_clk_lookup = clkdev_create(dai_clk, init.name,
"%s", dev_name(dev));
dai_clk_lookup = clkdev_hw_create(dai_clk_hw, init.name,
"%s", dev_name(dev));
if (!dai_clk_lookup) {
ret = -ENOMEM;
goto err;
@ -2189,21 +2196,58 @@ static int da7219_register_dai_clks(struct snd_soc_component *component)
}
}
/* If we're using DT, then register as provider accordingly */
if (np) {
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
da7219->clk_hw_data);
if (ret) {
dev_err(dev, "Failed to register clock provider\n");
goto err;
}
}
return 0;
err:
do {
if (da7219->dai_clks_lookup[i])
clkdev_drop(da7219->dai_clks_lookup[i]);
clk_hw_unregister(&da7219->dai_clks_hw[i]);
} while (i-- > 0);
if (np)
kfree(da7219->clk_hw_data);
return ret;
}
static void da7219_free_dai_clks(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct device_node *np = component->dev->of_node;
int i;
if (np)
of_clk_del_provider(np);
for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
if (da7219->dai_clks_lookup[i])
clkdev_drop(da7219->dai_clks_lookup[i]);
clk_hw_unregister(&da7219->dai_clks_hw[i]);
}
if (np)
kfree(da7219->clk_hw_data);
}
#else
static inline int da7219_register_dai_clks(struct snd_soc_component *component)
{
return 0;
}
static void da7219_free_dai_clks(struct snd_soc_component *component) {}
#endif /* CONFIG_COMMON_CLK */
static void da7219_handle_pdata(struct snd_soc_component *component)
@ -2251,182 +2295,6 @@ static void da7219_handle_pdata(struct snd_soc_component *component)
}
}
static struct reg_sequence da7219_rev_aa_patch[] = {
{ DA7219_REFERENCES, 0x08 },
};
static int da7219_probe(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
unsigned int rev;
int ret;
da7219->component = component;
mutex_init(&da7219->ctrl_lock);
mutex_init(&da7219->pll_lock);
/* Regulator configuration */
ret = da7219_handle_supplies(component);
if (ret)
return ret;
ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev);
if (ret) {
dev_err(component->dev, "Failed to read chip revision: %d\n", ret);
goto err_disable_reg;
}
switch (rev & DA7219_CHIP_MINOR_MASK) {
case 0:
ret = regmap_register_patch(da7219->regmap, da7219_rev_aa_patch,
ARRAY_SIZE(da7219_rev_aa_patch));
if (ret) {
dev_err(component->dev, "Failed to register AA patch: %d\n",
ret);
goto err_disable_reg;
}
break;
default:
break;
}
/* Handle DT/ACPI/Platform data */
da7219->pdata = dev_get_platdata(component->dev);
if (!da7219->pdata)
da7219->pdata = da7219_fw_to_pdata(component);
da7219_handle_pdata(component);
/* Check if MCLK provided */
da7219->mclk = devm_clk_get(component->dev, "mclk");
if (IS_ERR(da7219->mclk)) {
if (PTR_ERR(da7219->mclk) != -ENOENT) {
ret = PTR_ERR(da7219->mclk);
goto err_disable_reg;
} else {
da7219->mclk = NULL;
}
}
/* Register CCF DAI clock control */
ret = da7219_register_dai_clks(component);
if (ret)
return ret;
/* Default PC counter to free-running */
snd_soc_component_update_bits(component, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
DA7219_PC_FREERUN_MASK);
/* Default gain ramping */
snd_soc_component_update_bits(component, DA7219_MIXIN_L_CTRL,
DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
DA7219_ADC_L_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
DA7219_DAC_L_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
DA7219_DAC_R_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_RAMP_EN_MASK,
DA7219_HP_L_AMP_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_RAMP_EN_MASK,
DA7219_HP_R_AMP_RAMP_EN_MASK);
/* Default minimum gain on HP to avoid pops during DAPM sequencing */
snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_MIN_GAIN_EN_MASK,
DA7219_HP_L_AMP_MIN_GAIN_EN_MASK);
snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_MIN_GAIN_EN_MASK,
DA7219_HP_R_AMP_MIN_GAIN_EN_MASK);
/* Default infinite tone gen, start/stop by Kcontrol */
snd_soc_component_write(component, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
/* Initialise AAD block */
ret = da7219_aad_init(component);
if (ret)
goto err_disable_reg;
return 0;
err_disable_reg:
regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
return ret;
}
static void da7219_remove(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
#ifdef CONFIG_COMMON_CLK
int i;
#endif
da7219_aad_exit(component);
#ifdef CONFIG_COMMON_CLK
for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) {
if (da7219->dai_clks_lookup[i])
clkdev_drop(da7219->dai_clks_lookup[i]);
}
#endif
/* Supplies */
regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
}
#ifdef CONFIG_PM
static int da7219_suspend(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
/* Suspend AAD if we're not a wake-up source */
if (!da7219->wakeup_source)
da7219_aad_suspend(component);
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
return 0;
}
static int da7219_resume(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
/* Resume AAD if previously suspended */
if (!da7219->wakeup_source)
da7219_aad_resume(component);
return 0;
}
#else
#define da7219_suspend NULL
#define da7219_resume NULL
#endif
static const struct snd_soc_component_driver soc_component_dev_da7219 = {
.probe = da7219_probe,
.remove = da7219_remove,
.suspend = da7219_suspend,
.resume = da7219_resume,
.set_bias_level = da7219_set_bias_level,
.controls = da7219_snd_controls,
.num_controls = ARRAY_SIZE(da7219_snd_controls),
.dapm_widgets = da7219_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets),
.dapm_routes = da7219_audio_map,
.num_dapm_routes = ARRAY_SIZE(da7219_audio_map),
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
/*
* Regmap configs
@ -2563,31 +2431,25 @@ static const struct regmap_config da7219_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
static struct reg_sequence da7219_rev_aa_patch[] = {
{ DA7219_REFERENCES, 0x08 },
};
/*
* I2C layer
*/
static int da7219_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
static int da7219_probe(struct snd_soc_component *component)
{
struct da7219_priv *da7219;
unsigned int system_active, system_status;
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
unsigned int system_active, system_status, rev;
u8 io_voltage_lvl;
int i, ret;
da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv),
GFP_KERNEL);
if (!da7219)
return -ENOMEM;
da7219->component = component;
mutex_init(&da7219->ctrl_lock);
mutex_init(&da7219->pll_lock);
i2c_set_clientdata(i2c, da7219);
da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config);
if (IS_ERR(da7219->regmap)) {
ret = PTR_ERR(da7219->regmap);
dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
/* Regulator configuration */
ret = da7219_handle_supplies(component, &io_voltage_lvl);
if (ret)
return ret;
}
regcache_cache_bypass(da7219->regmap, true);
@ -2617,15 +2479,209 @@ static int da7219_i2c_probe(struct i2c_client *i2c,
DA7219_CIF_REG_SOFT_RESET_MASK);
regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE,
DA7219_SYSTEM_ACTIVE_MASK, 0);
regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE,
DA7219_SYSTEM_ACTIVE_MASK, 1);
regcache_cache_bypass(da7219->regmap, false);
regmap_reinit_cache(da7219->regmap, &da7219_regmap_config);
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_da7219,
&da7219_dai, 1);
/* Update IO voltage level range based on supply level */
snd_soc_component_write(component, DA7219_IO_CTRL, io_voltage_lvl);
ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev);
if (ret) {
dev_err(component->dev, "Failed to read chip revision: %d\n", ret);
goto err_disable_reg;
}
switch (rev & DA7219_CHIP_MINOR_MASK) {
case 0:
ret = regmap_register_patch(da7219->regmap, da7219_rev_aa_patch,
ARRAY_SIZE(da7219_rev_aa_patch));
if (ret) {
dev_err(component->dev, "Failed to register AA patch: %d\n",
ret);
goto err_disable_reg;
}
break;
default:
break;
}
/* Handle DT/ACPI/Platform data */
da7219_handle_pdata(component);
/* Check if MCLK provided */
da7219->mclk = clk_get(component->dev, "mclk");
if (IS_ERR(da7219->mclk)) {
if (PTR_ERR(da7219->mclk) != -ENOENT) {
ret = PTR_ERR(da7219->mclk);
goto err_disable_reg;
} else {
da7219->mclk = NULL;
}
}
/* Register CCF DAI clock control */
ret = da7219_register_dai_clks(component);
if (ret)
goto err_put_clk;
/* Default PC counter to free-running */
snd_soc_component_update_bits(component, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
DA7219_PC_FREERUN_MASK);
/* Default gain ramping */
snd_soc_component_update_bits(component, DA7219_MIXIN_L_CTRL,
DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
DA7219_ADC_L_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
DA7219_DAC_L_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
DA7219_DAC_R_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_RAMP_EN_MASK,
DA7219_HP_L_AMP_RAMP_EN_MASK);
snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_RAMP_EN_MASK,
DA7219_HP_R_AMP_RAMP_EN_MASK);
/* Default minimum gain on HP to avoid pops during DAPM sequencing */
snd_soc_component_update_bits(component, DA7219_HP_L_CTRL,
DA7219_HP_L_AMP_MIN_GAIN_EN_MASK,
DA7219_HP_L_AMP_MIN_GAIN_EN_MASK);
snd_soc_component_update_bits(component, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_MIN_GAIN_EN_MASK,
DA7219_HP_R_AMP_MIN_GAIN_EN_MASK);
/* Default infinite tone gen, start/stop by Kcontrol */
snd_soc_component_write(component, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
/* Initialise AAD block */
ret = da7219_aad_init(component);
if (ret)
goto err_free_dai_clks;
return 0;
err_free_dai_clks:
da7219_free_dai_clks(component);
err_put_clk:
clk_put(da7219->mclk);
err_disable_reg:
regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
return ret;
}
static void da7219_remove(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
da7219_aad_exit(component);
da7219_free_dai_clks(component);
clk_put(da7219->mclk);
/* Supplies */
regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies);
}
#ifdef CONFIG_PM
static int da7219_suspend(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
/* Suspend AAD if we're not a wake-up source */
if (!da7219->wakeup_source)
da7219_aad_suspend(component);
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
return 0;
}
static int da7219_resume(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
/* Resume AAD if previously suspended */
if (!da7219->wakeup_source)
da7219_aad_resume(component);
return 0;
}
#else
#define da7219_suspend NULL
#define da7219_resume NULL
#endif
static const struct snd_soc_component_driver soc_component_dev_da7219 = {
.probe = da7219_probe,
.remove = da7219_remove,
.suspend = da7219_suspend,
.resume = da7219_resume,
.set_bias_level = da7219_set_bias_level,
.controls = da7219_snd_controls,
.num_controls = ARRAY_SIZE(da7219_snd_controls),
.dapm_widgets = da7219_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets),
.dapm_routes = da7219_audio_map,
.num_dapm_routes = ARRAY_SIZE(da7219_audio_map),
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
/*
* I2C layer
*/
static int da7219_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct device *dev = &i2c->dev;
struct da7219_priv *da7219;
int ret;
da7219 = devm_kzalloc(dev, sizeof(struct da7219_priv),
GFP_KERNEL);
if (!da7219)
return -ENOMEM;
i2c_set_clientdata(i2c, da7219);
da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config);
if (IS_ERR(da7219->regmap)) {
ret = PTR_ERR(da7219->regmap);
dev_err(dev, "regmap_init() failed: %d\n", ret);
return ret;
}
/* Retrieve DT/ACPI/Platform data */
da7219->pdata = dev_get_platdata(dev);
if (!da7219->pdata)
da7219->pdata = da7219_fw_to_pdata(dev);
/* AAD */
ret = da7219_aad_probe(i2c);
if (ret)
return ret;
ret = devm_snd_soc_register_component(dev, &soc_component_dev_da7219,
&da7219_dai, 1);
if (ret < 0) {
dev_err(&i2c->dev, "Failed to register da7219 component: %d\n",
ret);
dev_err(dev, "Failed to register da7219 component: %d\n", ret);
}
return ret;
}

View file

@ -817,6 +817,7 @@ struct da7219_priv {
#ifdef CONFIG_COMMON_CLK
struct clk_hw dai_clks_hw[DA7219_DAI_NUM_CLKS];
struct clk_hw_onecell_data *clk_hw_data;
#endif
struct clk_lookup *dai_clks_lookup[DA7219_DAI_NUM_CLKS];
struct clk *dai_clks[DA7219_DAI_NUM_CLKS];

View file

@ -9,6 +9,7 @@
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/module.h>
@ -107,6 +108,7 @@ struct hdac_hdmi_pcm {
unsigned char chmap[8]; /* ALSA API channel-map */
struct mutex lock;
int jack_event;
struct snd_kcontrol *eld_ctl;
};
struct hdac_hdmi_dai_port_map {
@ -1248,6 +1250,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
struct hdac_hdmi_pcm *pcm;
int size = 0;
int port_id = -1;
bool eld_valid, eld_changed;
if (!hdmi)
return;
@ -1273,6 +1276,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
size = -EINVAL;
}
eld_valid = port->eld.eld_valid;
if (size > 0) {
port->eld.eld_valid = true;
port->eld.eld_size = size;
@ -1281,6 +1286,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
port->eld.eld_size = 0;
}
eld_changed = (eld_valid != port->eld.eld_valid);
pcm = hdac_hdmi_get_pcm(hdev, port);
if (!port->eld.monitor_present || !port->eld.eld_valid) {
@ -1313,6 +1320,12 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
}
mutex_unlock(&hdmi->pin_mutex);
if (eld_changed && pcm)
snd_ctl_notify(hdmi->card,
SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO,
&pcm->eld_ctl->id);
}
static int hdac_hdmi_add_ports(struct hdac_device *hdev,
@ -1411,6 +1424,122 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev)
}
static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
struct hdac_hdmi_pcm *pcm;
struct hdac_hdmi_port *port;
struct hdac_hdmi_eld *eld;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
uinfo->count = 0;
pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
if (!pcm) {
dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
kcontrol->id.device);
return 0;
}
if (list_empty(&pcm->port_list)) {
dev_dbg(component->dev, "%s: empty port list, device %d\n",
__func__, kcontrol->id.device);
return 0;
}
mutex_lock(&hdmi->pin_mutex);
list_for_each_entry(port, &pcm->port_list, head) {
eld = &port->eld;
if (eld->eld_valid) {
uinfo->count = eld->eld_size;
break;
}
}
mutex_unlock(&hdmi->pin_mutex);
return 0;
}
static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
struct hdac_hdmi_pcm *pcm;
struct hdac_hdmi_port *port;
struct hdac_hdmi_eld *eld;
memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data));
pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
if (!pcm) {
dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
kcontrol->id.device);
return 0;
}
if (list_empty(&pcm->port_list)) {
dev_dbg(component->dev, "%s: empty port list, device %d\n",
__func__, kcontrol->id.device);
return 0;
}
mutex_lock(&hdmi->pin_mutex);
list_for_each_entry(port, &pcm->port_list, head) {
eld = &port->eld;
if (!eld->eld_valid)
continue;
if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
eld->eld_size > ELD_MAX_SIZE) {
mutex_unlock(&hdmi->pin_mutex);
dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n",
__func__, kcontrol->id.device, eld->eld_size);
snd_BUG();
return -EINVAL;
}
memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
eld->eld_size);
break;
}
mutex_unlock(&hdmi->pin_mutex);
return 0;
}
static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm)
{
struct snd_kcontrol *kctl;
struct snd_kcontrol_new hdmi_eld_ctl = {
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
.info = hdac_hdmi_eld_ctl_info,
.get = hdac_hdmi_eld_ctl_get,
.device = pcm->pcm_id,
};
/* add ELD ctl with the device number corresponding to the PCM stream */
kctl = snd_ctl_new1(&hdmi_eld_ctl, component);
if (!kctl)
return -ENOMEM;
pcm->eld_ctl = kctl;
return snd_ctl_add(component->card->snd_card, kctl);
}
static const struct snd_soc_dai_ops hdmi_dai_ops = {
.startup = hdac_hdmi_pcm_open,
.shutdown = hdac_hdmi_pcm_close,
@ -1784,6 +1913,15 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
}
}
/* add control for ELD Bytes */
err = hdac_hdmi_create_eld_ctl(component, pcm);
if (err < 0) {
dev_err(&hdev->dev,
"eld control add failed with err: %d for pcm: %d\n",
err, device);
return err;
}
list_add_tail(&pcm->head, &hdmi->pcm_list);
return 0;
@ -2097,8 +2235,6 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
if (!bus)
return 0;
clear_dapm_works(hdev);
/*
* Power down afg.
* codec_read is preferred over codec_write to set the power state.

View file

@ -698,13 +698,9 @@ static void plugged_cb(struct device *dev, bool plugged)
hdmi_codec_jack_report(hcp, 0);
}
/**
* hdmi_codec_set_jack_detect - register HDMI plugged callback
* @component: the hdmi-codec instance
* @jack: ASoC jack to report (dis)connection events on
*/
int hdmi_codec_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack)
static int hdmi_codec_set_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack,
void *data)
{
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
int ret = -EOPNOTSUPP;
@ -720,7 +716,6 @@ int hdmi_codec_set_jack_detect(struct snd_soc_component *component,
}
return ret;
}
EXPORT_SYMBOL_GPL(hdmi_codec_set_jack_detect);
static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai)
{
@ -806,6 +801,7 @@ static const struct snd_soc_component_driver hdmi_driver = {
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
.set_jack = hdmi_codec_set_jack,
};
static int hdmi_codec_probe(struct platform_device *pdev)

View file

@ -256,6 +256,9 @@ static __maybe_unused int max98373_resume(struct device *dev)
struct max98373_priv *max98373 = dev_get_drvdata(dev);
unsigned long time;
if (!max98373->hw_init)
return 0;
if (!slave->unattach_request)
goto regmap_sync;

View file

@ -15,6 +15,14 @@
#include <sound/tlv.h>
#include "max9867.h"
struct max9867_priv {
struct regmap *regmap;
const struct snd_pcm_hw_constraint_list *constraints;
unsigned int sysclk, pclk;
bool master, dsp_a;
unsigned int adc_dac_active;
};
static const char *const max9867_spmode[] = {
"Stereo Diff", "Mono Diff",
"Stereo Cap", "Mono Cap",
@ -32,8 +40,102 @@ static const char *const max9867_adc_dac_filter_text[] = {
"Butterworth/8-24"
};
static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7,
max9867_filter_text);
enum max9867_adc_dac {
MAX9867_ADC_LEFT,
MAX9867_ADC_RIGHT,
MAX9867_DAC_LEFT,
MAX9867_DAC_RIGHT,
};
static int max9867_adc_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 max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
enum max9867_adc_dac adc_dac;
if (!strcmp(w->name, "ADCL"))
adc_dac = MAX9867_ADC_LEFT;
else if (!strcmp(w->name, "ADCR"))
adc_dac = MAX9867_ADC_RIGHT;
else if (!strcmp(w->name, "DACL"))
adc_dac = MAX9867_DAC_LEFT;
else if (!strcmp(w->name, "DACR"))
adc_dac = MAX9867_DAC_RIGHT;
else
return 0;
if (SND_SOC_DAPM_EVENT_ON(event))
max9867->adc_dac_active |= BIT(adc_dac);
else if (SND_SOC_DAPM_EVENT_OFF(event))
max9867->adc_dac_active &= ~BIT(adc_dac);
return 0;
}
static int max9867_filter_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
unsigned int reg;
int ret;
ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, &reg);
if (ret)
return -EINVAL;
if (reg & MAX9867_CODECFLTR_MODE)
ucontrol->value.enumerated.item[0] = 1;
else
ucontrol->value.enumerated.item[0] = 0;
return 0;
}
static int max9867_filter_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
unsigned int reg, mode = ucontrol->value.enumerated.item[0];
int ret;
if (mode > 1)
return -EINVAL;
/* don't allow change if ADC/DAC active */
if (max9867->adc_dac_active)
return -EBUSY;
/* read current filter mode */
ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, &reg);
if (ret)
return -EINVAL;
if (mode)
mode = MAX9867_CODECFLTR_MODE;
/* check if change is needed */
if ((reg & MAX9867_CODECFLTR_MODE) == mode)
return 0;
/* shutdown codec before switching filter mode */
regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
MAX9867_PWRMAN_SHDN, 0);
/* switch filter mode */
regmap_update_bits(max9867->regmap, MAX9867_CODECFLTR,
MAX9867_CODECFLTR_MODE, mode);
/* out of shutdown now */
regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
MAX9867_PWRMAN_SHDN, MAX9867_PWRMAN_SHDN);
return 0;
}
static SOC_ENUM_SINGLE_EXT_DECL(max9867_filter, max9867_filter_text);
static SOC_ENUM_SINGLE_DECL(max9867_dac_filter, MAX9867_CODECFLTR, 0,
max9867_adc_dac_filter_text);
static SOC_ENUM_SINGLE_DECL(max9867_adc_filter, MAX9867_CODECFLTR, 4,
@ -76,7 +178,7 @@ static const struct snd_kcontrol_new max9867_snd_controls[] = {
SOC_ENUM("Speaker Mode", max9867_spkmode),
SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0),
SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0),
SOC_ENUM("DSP Filter", max9867_filter),
SOC_ENUM_EXT("DSP Filter", max9867_filter, max9867_filter_get, max9867_filter_set),
SOC_ENUM("ADC Filter", max9867_adc_filter),
SOC_ENUM("DAC Filter", max9867_dac_filter),
SOC_SINGLE("Mono Playback Switch", MAX9867_IFC1B, 3, 1, 0),
@ -134,8 +236,12 @@ static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = {
&max9867_left_dmic_mux),
SND_SOC_DAPM_MUX("DMICR Mux", SND_SOC_NOPM, 0, 0,
&max9867_right_dmic_mux),
SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC_E("ADCL", "HiFi Capture", SND_SOC_NOPM, 0, 0,
max9867_adc_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_ADC_E("ADCR", "HiFi Capture", SND_SOC_NOPM, 0, 0,
max9867_adc_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0,
max9867_sidetone_mixer_controls,
@ -143,8 +249,12 @@ static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = {
SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", SND_SOC_NOPM, 0, 0,
max9867_output_mixer_controls,
ARRAY_SIZE(max9867_output_mixer_controls)),
SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC_E("DACL", "HiFi Playback", SND_SOC_NOPM, 0, 0,
max9867_adc_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("DACR", "HiFi Playback", SND_SOC_NOPM, 0, 0,
max9867_adc_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0,
&max9867_line_out_control),
SND_SOC_DAPM_OUTPUT("LOUT"),
@ -197,13 +307,6 @@ static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = {
.count = ARRAY_SIZE(max9867_rates_48k),
};
struct max9867_priv {
struct regmap *regmap;
const struct snd_pcm_hw_constraint_list *constraints;
unsigned int sysclk, pclk;
bool master, dsp_a;
};
static int max9867_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{

View file

@ -44,7 +44,8 @@
#define MAX9867_IFC1B_PCLK_4 0x05
#define MAX9867_IFC1B_PCLK_8 0x06
#define MAX9867_IFC1B_PCLK_16 0x07
#define MAX9867_CODECFLTR 0x0a
#define MAX9867_CODECFLTR 0x0a
#define MAX9867_CODECFLTR_MODE (1<<7)
#define MAX9867_SIDETONE 0x0b
#define MAX9867_DACLEVEL 0x0c
#define MAX9867_ADCLEVEL 0x0d
@ -58,6 +59,7 @@
#define MAX9867_MICCONFIG 0x15
#define MAX9867_MODECONFIG 0x16
#define MAX9867_PWRMAN 0x17
#define MAX9867_PWRMAN_SHDN (1<<7)
#define MAX9867_REVISION 0xff
#define MAX9867_CACHEREGNUM 10

2758
sound/soc/codecs/mt6359.c Normal file

File diff suppressed because it is too large Load diff

2640
sound/soc/codecs/mt6359.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -251,7 +251,7 @@ static const unsigned short logtable[256] = {
*
* Acquires the semaphore without jiffies. Try to acquire the semaphore
* atomically. Returns 0 if the semaphore has been acquired successfully
* or 1 if it it cannot be acquired.
* or 1 if it cannot be acquired.
*/
static int nau8825_sema_acquire(struct nau8825 *nau8825, long timeout)
{

148
sound/soc/codecs/rt1015p.c Normal file
View file

@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// rt1015p.c -- RT1015P ALSA SoC audio amplifier driver
//
// Copyright 2020 The Linux Foundation. All rights reserved.
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <sound/soc-dapm.h>
struct rt1015p_priv {
struct gpio_desc *sdb;
int sdb_switch;
};
static int rt1015p_daiops_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct rt1015p_priv *rt1015p =
snd_soc_component_get_drvdata(component);
if (!rt1015p->sdb)
return 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (rt1015p->sdb_switch) {
gpiod_set_value(rt1015p->sdb, 1);
dev_dbg(component->dev, "set sdb to 1");
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
gpiod_set_value(rt1015p->sdb, 0);
dev_dbg(component->dev, "set sdb to 0");
break;
}
return 0;
}
static int rt1015p_sdb_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 rt1015p_priv *rt1015p =
snd_soc_component_get_drvdata(component);
if (event & SND_SOC_DAPM_POST_PMU)
rt1015p->sdb_switch = 1;
else if (event & SND_SOC_DAPM_POST_PMD)
rt1015p->sdb_switch = 0;
return 0;
}
static const struct snd_soc_dapm_widget rt1015p_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("Speaker"),
SND_SOC_DAPM_OUT_DRV_E("SDB", SND_SOC_NOPM, 0, 0, NULL, 0,
rt1015p_sdb_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route rt1015p_dapm_routes[] = {
{"SDB", NULL, "HiFi Playback"},
{"Speaker", NULL, "SDB"},
};
static const struct snd_soc_component_driver rt1015p_component_driver = {
.dapm_widgets = rt1015p_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rt1015p_dapm_widgets),
.dapm_routes = rt1015p_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt1015p_dapm_routes),
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static const struct snd_soc_dai_ops rt1015p_dai_ops = {
.trigger = rt1015p_daiops_trigger,
};
static struct snd_soc_dai_driver rt1015p_dai_driver = {
.name = "HiFi",
.playback = {
.stream_name = "HiFi Playback",
.formats = SNDRV_PCM_FMTBIT_S24,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 1,
.channels_max = 2,
},
.ops = &rt1015p_dai_ops,
};
static int rt1015p_platform_probe(struct platform_device *pdev)
{
struct rt1015p_priv *rt1015p;
rt1015p = devm_kzalloc(&pdev->dev, sizeof(*rt1015p), GFP_KERNEL);
if (!rt1015p)
return -ENOMEM;
rt1015p->sdb = devm_gpiod_get_optional(&pdev->dev,
"sdb", GPIOD_OUT_LOW);
if (IS_ERR(rt1015p->sdb))
return PTR_ERR(rt1015p->sdb);
dev_set_drvdata(&pdev->dev, rt1015p);
return devm_snd_soc_register_component(&pdev->dev,
&rt1015p_component_driver,
&rt1015p_dai_driver, 1);
}
#ifdef CONFIG_OF
static const struct of_device_id rt1015p_device_id[] = {
{ .compatible = "realtek,rt1015p" },
{}
};
MODULE_DEVICE_TABLE(of, rt1015p_device_id);
#endif
static struct platform_driver rt1015p_platform_driver = {
.driver = {
.name = "rt1015p",
.of_match_table = of_match_ptr(rt1015p_device_id),
},
.probe = rt1015p_platform_probe,
};
module_platform_driver(rt1015p_platform_driver);
MODULE_DESCRIPTION("ASoC RT1015P driver");
MODULE_LICENSE("GPL v2");

View file

@ -693,7 +693,7 @@ static int rt1308_sdw_probe(struct sdw_slave *slave,
}
static const struct sdw_device_id rt1308_id[] = {
SDW_SLAVE_ENTRY(0x025d, 0x1308, 0),
SDW_SLAVE_ENTRY_EXT(0x025d, 0x1308, 0x2, 0, 0),
{},
};
MODULE_DEVICE_TABLE(sdw, rt1308_id);

View file

@ -294,6 +294,7 @@ static struct i2c_driver rt5682_i2c_driver = {
.name = "rt5682",
.of_match_table = rt5682_of_match,
.acpi_match_table = rt5682_acpi_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = rt5682_i2c_probe,
.shutdown = rt5682_i2c_shutdown,

View file

@ -717,7 +717,7 @@ static int rt5682_sdw_remove(struct sdw_slave *slave)
}
static const struct sdw_device_id rt5682_id[] = {
SDW_SLAVE_ENTRY(0x025d, 0x5682, 0),
SDW_SLAVE_ENTRY_EXT(0x025d, 0x5682, 0x2, 0, 0),
{},
};
MODULE_DEVICE_TABLE(sdw, rt5682_id);

View file

@ -1529,16 +1529,35 @@ static int set_dmic_power(struct snd_soc_dapm_widget *w,
struct snd_soc_component *component =
snd_soc_dapm_to_component(w->dapm);
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
unsigned int delay = 50;
unsigned int delay = 50, val;
if (rt5682->pdata.dmic_delay)
delay = rt5682->pdata.dmic_delay;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
val = snd_soc_component_read(component, RT5682_GLB_CLK);
val &= RT5682_SCLK_SRC_MASK;
if (val == RT5682_SCLK_SRC_PLL1 || val == RT5682_SCLK_SRC_PLL2)
snd_soc_component_update_bits(component,
RT5682_PWR_ANLG_1,
RT5682_PWR_VREF2 | RT5682_PWR_MB,
RT5682_PWR_VREF2 | RT5682_PWR_MB);
/*Add delay to avoid pop noise*/
msleep(delay);
break;
case SND_SOC_DAPM_POST_PMD:
if (!rt5682->jack_type) {
if (!snd_soc_dapm_get_pin_status(w->dapm, "MICBIAS"))
snd_soc_component_update_bits(component,
RT5682_PWR_ANLG_1, RT5682_PWR_MB, 0);
if (!snd_soc_dapm_get_pin_status(w->dapm, "Vref2"))
snd_soc_component_update_bits(component,
RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0);
}
break;
}
return 0;
@ -1644,7 +1663,8 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682_DMIC_CTRL_1,
RT5682_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU),
RT5682_DMIC_1_EN_SFT, 0, set_dmic_power,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Boost */
SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM,
@ -2481,7 +2501,7 @@ static int rt5682_set_bias_level(struct snd_soc_component *component,
static bool rt5682_clk_check(struct rt5682_priv *rt5682)
{
if (!rt5682->master[RT5682_AIF1]) {
dev_err(rt5682->component->dev, "sysclk/dai not set correctly\n");
dev_dbg(rt5682->component->dev, "sysclk/dai not set correctly\n");
return false;
}
return true;
@ -2559,7 +2579,7 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw,
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
struct snd_soc_component *component = rt5682->component;
const char * const clk_name = __clk_get_name(hw->clk);
const char * const clk_name = clk_hw_get_name(hw);
if (!rt5682_clk_check(rt5682))
return 0;
@ -2583,7 +2603,7 @@ static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
struct snd_soc_component *component = rt5682->component;
const char * const clk_name = __clk_get_name(hw->clk);
const char * const clk_name = clk_hw_get_name(hw);
if (!rt5682_clk_check(rt5682))
return -EINVAL;
@ -2608,7 +2628,7 @@ static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
dai_clks_hw[RT5682_DAI_WCLK_IDX]);
struct snd_soc_component *component = rt5682->component;
struct clk *parent_clk;
const char * const clk_name = __clk_get_name(hw->clk);
const char * const clk_name = clk_hw_get_name(hw);
int pre_div;
unsigned int clk_pll2_out;
@ -2766,39 +2786,34 @@ static int rt5682_register_dai_clks(struct snd_soc_component *component)
struct device *dev = component->dev;
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
struct rt5682_platform_data *pdata = &rt5682->pdata;
struct clk_init_data init;
struct clk *dai_clk;
struct clk_lookup *dai_clk_lookup;
struct clk_hw *dai_clk_hw;
const char *parent_name;
int i, ret;
for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) {
struct clk_init_data init = { };
dai_clk_hw = &rt5682->dai_clks_hw[i];
switch (i) {
case RT5682_DAI_WCLK_IDX:
/* Make MCLK the parent of WCLK */
if (rt5682->mclk) {
parent_name = __clk_get_name(rt5682->mclk);
init.parent_names = &parent_name;
init.parent_data = &(struct clk_parent_data){
.fw_name = "mclk",
};
init.num_parents = 1;
} else {
init.parent_names = NULL;
init.num_parents = 0;
}
break;
case RT5682_DAI_BCLK_IDX:
/* Make WCLK the parent of BCLK */
parent_name = __clk_get_name(
rt5682->dai_clks[RT5682_DAI_WCLK_IDX]);
init.parent_names = &parent_name;
init.parent_hws = &(const struct clk_hw *){
&rt5682->dai_clks_hw[RT5682_DAI_WCLK_IDX]
};
init.num_parents = 1;
break;
default:
dev_err(dev, "Invalid clock index\n");
ret = -EINVAL;
goto err;
return -EINVAL;
}
init.name = pdata->dai_clk_names[i];
@ -2806,39 +2821,26 @@ static int rt5682_register_dai_clks(struct snd_soc_component *component)
init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
dai_clk_hw->init = &init;
dai_clk = devm_clk_register(dev, dai_clk_hw);
if (IS_ERR(dai_clk)) {
dev_warn(dev, "Failed to register %s: %ld\n",
init.name, PTR_ERR(dai_clk));
ret = PTR_ERR(dai_clk);
goto err;
ret = devm_clk_hw_register(dev, dai_clk_hw);
if (ret) {
dev_warn(dev, "Failed to register %s: %d\n",
init.name, ret);
return ret;
}
rt5682->dai_clks[i] = dai_clk;
if (dev->of_node) {
devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
dai_clk_hw);
} else {
dai_clk_lookup = clkdev_create(dai_clk, init.name,
"%s", dev_name(dev));
if (!dai_clk_lookup) {
ret = -ENOMEM;
goto err;
} else {
rt5682->dai_clks_lookup[i] = dai_clk_lookup;
}
ret = devm_clk_hw_register_clkdev(dev, dai_clk_hw,
init.name,
dev_name(dev));
if (ret)
return ret;
}
}
return 0;
err:
do {
if (rt5682->dai_clks_lookup[i])
clkdev_drop(rt5682->dai_clks_lookup[i]);
} while (i-- > 0);
return ret;
}
#endif /* CONFIG_COMMON_CLK */
@ -2895,15 +2897,6 @@ static void rt5682_remove(struct snd_soc_component *component)
{
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
#ifdef CONFIG_COMMON_CLK
int i;
for (i = RT5682_DAI_NUM_CLKS - 1; i >= 0; --i) {
if (rt5682->dai_clks_lookup[i])
clkdev_drop(rt5682->dai_clks_lookup[i]);
}
#endif
rt5682_reset(rt5682);
}

View file

@ -1411,8 +1411,6 @@ struct rt5682_priv {
#ifdef CONFIG_COMMON_CLK
struct clk_hw dai_clks_hw[RT5682_DAI_NUM_CLKS];
struct clk_lookup *dai_clks_lookup[RT5682_DAI_NUM_CLKS];
struct clk *dai_clks[RT5682_DAI_NUM_CLKS];
struct clk *mclk;
#endif

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