forked from luck/tmp_suning_uos_patched
Merge remote-tracking branch 'asoc/for-5.10' into asoc-next
This commit is contained in:
commit
c890e30b06
16
Documentation/ABI/testing/sysfs-bus-pci-devices-catpt
Normal file
16
Documentation/ABI/testing/sysfs-bus-pci-devices-catpt
Normal 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.
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
74
Documentation/devicetree/bindings/sound/cirrus,cs4234.yaml
Normal file
74
Documentation/devicetree/bindings/sound/cirrus,cs4234.yaml
Normal 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>;
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
};
|
110
Documentation/devicetree/bindings/sound/fsl,spdif.yaml
Normal file
110
Documentation/devicetree/bindings/sound/fsl,spdif.yaml
Normal 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;
|
||||
};
|
|
@ -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.
|
||||
|
|
|
@ -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";
|
||||
};
|
|
@ -17,6 +17,7 @@ properties:
|
|||
compatible:
|
||||
enum:
|
||||
- intel,keembay-i2s
|
||||
- intel,keembay-tdm
|
||||
|
||||
"#sound-dai-cells":
|
||||
const: 0
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
73
Documentation/devicetree/bindings/sound/mchp,spdifrx.yaml
Normal file
73
Documentation/devicetree/bindings/sound/mchp,spdifrx.yaml
Normal 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";
|
||||
};
|
75
Documentation/devicetree/bindings/sound/mchp,spdiftx.yaml
Normal file
75
Documentation/devicetree/bindings/sound/mchp,spdiftx.yaml
Normal 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>;
|
||||
};
|
61
Documentation/devicetree/bindings/sound/mt6359.yaml
Normal file
61
Documentation/devicetree/bindings/sound/mt6359.yaml
Normal 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>;
|
||||
};
|
||||
|
||||
...
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
219
Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
Normal file
219
Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
36
Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml
Normal file
36
Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml
Normal 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>;
|
||||
};
|
|
@ -27,6 +27,7 @@ properties:
|
|||
- enum:
|
||||
- rockchip,rk3188-spdif
|
||||
- rockchip,rk3288-spdif
|
||||
- rockchip,rk3308-spdif
|
||||
- const: rockchip,rk3066-spdif
|
||||
|
||||
reg:
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
76
Documentation/devicetree/bindings/sound/tas2764.yaml
Normal file
76
Documentation/devicetree/bindings/sound/tas2764.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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 = {
|
|||
>a02_buttons_device,
|
||||
&s3c_device_adc,
|
||||
&s3c_device_ts,
|
||||
>a02_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(>a02_audio_gpio_table);
|
||||
platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices));
|
||||
pm_power_off = gta02_poweroff;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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__ */
|
||||
|
||||
|
|
11
include/dt-bindings/sound/sc7180-lpass.h
Normal file
11
include/dt-bindings/sound/sc7180-lpass.h
Normal 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 */
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
953
sound/soc/atmel/mchp-spdifrx.c
Normal file
953
sound/soc/atmel/mchp-spdifrx.c
Normal 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");
|
871
sound/soc/atmel/mchp-spdiftx.c
Normal file
871
sound/soc/atmel/mchp-spdiftx.c
Normal 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");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
918
sound/soc/codecs/cs4234.c
Normal 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
287
sound/soc/codecs/cs4234.h
Normal 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
|
|
@ -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),
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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, ®);
|
||||
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, ®);
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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
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
2640
sound/soc/codecs/mt6359.h
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -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
148
sound/soc/codecs/rt1015p.c
Normal 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");
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue
Block a user