summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/adi,axi-i2s.txt7
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt39
-rw-r--r--Documentation/devicetree/bindings/sound/da7219.txt8
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,audmix.txt54
-rw-r--r--Documentation/devicetree/bindings/sound/mchp-i2s-mcc.txt43
-rw-r--r--Documentation/devicetree/bindings/sound/mt8183-da7219-max98357.txt15
-rw-r--r--Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt15
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.txt7
-rw-r--r--Documentation/devicetree/bindings/sound/rt5651.txt5
-rw-r--r--Documentation/devicetree/bindings/sound/simple-amplifier.txt2
-rw-r--r--MAINTAINERS2
-rw-r--r--include/sound/da7219.h8
-rw-r--r--include/sound/simple_card_utils.h236
-rw-r--r--include/sound/soc.h2
-rw-r--r--sound/soc/adi/axi-i2s.c68
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c4
-rw-r--r--sound/soc/atmel/Kconfig14
-rw-r--r--sound/soc/atmel/Makefile2
-rw-r--r--sound/soc/atmel/mchp-i2s-mcc.c974
-rw-r--r--sound/soc/codecs/Kconfig15
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/ab8500-codec.c1
-rw-r--r--sound/soc/codecs/cs42l51.c8
-rw-r--r--sound/soc/codecs/cs47l24.c4
-rw-r--r--sound/soc/codecs/da7219.c523
-rw-r--r--sound/soc/codecs/da7219.h6
-rw-r--r--sound/soc/codecs/es8316.c14
-rw-r--r--sound/soc/codecs/hdac_hda.c53
-rw-r--r--sound/soc/codecs/hdac_hda.h1
-rw-r--r--sound/soc/codecs/hdmi-codec.c121
-rw-r--r--sound/soc/codecs/lochnagar-sc.c266
-rw-r--r--sound/soc/codecs/nau8810.c29
-rw-r--r--sound/soc/codecs/nau8824.c46
-rw-r--r--sound/soc/codecs/rt5651.c47
-rw-r--r--sound/soc/codecs/rt5651.h1
-rw-r--r--sound/soc/codecs/rt5677-spi.c8
-rw-r--r--sound/soc/codecs/rt5682.c56
-rw-r--r--sound/soc/codecs/simple-amplifier.c3
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c3
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c61
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h12
-rw-r--r--sound/soc/codecs/tlv320aic32x4-clk.c483
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c429
-rw-r--r--sound/soc/codecs/tlv320aic32x4.h11
-rw-r--r--sound/soc/codecs/tlv320aic3x.c4
-rw-r--r--sound/soc/codecs/wcd9335.c1
-rw-r--r--sound/soc/codecs/wm5102.c4
-rw-r--r--sound/soc/codecs/wm5110.c4
-rw-r--r--sound/soc/codecs/wm_adsp.c1038
-rw-r--r--sound/soc/codecs/wm_adsp.h51
-rw-r--r--sound/soc/codecs/wmfw.h30
-rw-r--r--sound/soc/fsl/Kconfig16
-rw-r--r--sound/soc/fsl/Makefile5
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c4
-rw-r--r--sound/soc/fsl/fsl_audmix.c576
-rw-r--r--sound/soc/fsl/fsl_audmix.h102
-rw-r--r--sound/soc/fsl/fsl_utils.c1
-rw-r--r--sound/soc/fsl/imx-audmix.c327
-rw-r--r--sound/soc/generic/audio-graph-card.c429
-rw-r--r--sound/soc/generic/simple-card-utils.c383
-rw-r--r--sound/soc/generic/simple-card.c379
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c22
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c19
-rw-r--r--sound/soc/jz4740/Kconfig2
-rw-r--r--sound/soc/mediatek/Kconfig26
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c51
-rw-r--r--sound/soc/mediatek/common/mtk-btcvsd.c69
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c13
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-pcm.c16
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c16
-rw-r--r--sound/soc/mediatek/mt8183/Makefile2
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-clk.c4
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-pcm.c20
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c472
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c382
-rw-r--r--sound/soc/qcom/Kconfig2
-rw-r--r--sound/soc/samsung/i2s.c37
-rw-r--r--sound/soc/samsung/odroid.c4
-rw-r--r--sound/soc/sh/rcar/core.c2
-rw-r--r--sound/soc/sh/rcar/rsnd.h5
-rw-r--r--sound/soc/sh/rcar/src.c21
-rw-r--r--sound/soc/soc-core.c9
-rw-r--r--sound/soc/soc-dapm.c2
-rw-r--r--sound/soc/soc-pcm.c47
-rw-r--r--sound/soc/soc-topology.c39
-rw-r--r--sound/soc/sprd/Kconfig1
-rw-r--r--sound/soc/sprd/Makefile2
-rw-r--r--sound/soc/sprd/sprd-pcm-compress.c675
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.c2
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.h43
-rw-r--r--sound/soc/stm/stm32_adfsdm.c2
-rw-r--r--sound/soc/stm/stm32_i2s.c28
-rw-r--r--sound/soc/stm/stm32_sai.c88
-rw-r--r--sound/soc/stm/stm32_sai.h2
-rw-r--r--sound/soc/stm/stm32_sai_sub.c93
-rw-r--r--sound/soc/stm/stm32_spdifrx.c42
-rw-r--r--sound/soc/ti/Kconfig4
-rw-r--r--sound/soc/ti/ams-delta.c2
-rw-r--r--sound/soc/ti/davinci-mcasp.c2
-rw-r--r--sound/soc/ti/edma-pcm.c5
-rw-r--r--sound/soc/ti/sdma-pcm.c9
101 files changed, 7570 insertions, 1776 deletions
diff --git a/Documentation/devicetree/bindings/sound/adi,axi-i2s.txt b/Documentation/devicetree/bindings/sound/adi,axi-i2s.txt
index 4248b662deff..229ad1392cdc 100644
--- a/Documentation/devicetree/bindings/sound/adi,axi-i2s.txt
+++ b/Documentation/devicetree/bindings/sound/adi,axi-i2s.txt
@@ -1,5 +1,8 @@
ADI AXI-I2S controller
+The core can be generated with transmit (playback), only receive
+(capture) or both directions enabled.
+
Required properties:
- compatible : Must be "adi,axi-i2s-1.00.a"
- reg : Must contain I2S core's registers location and length
@@ -9,8 +12,8 @@ Required properties:
- clock-names : "axi" for the clock to the AXI interface, "ref" for the sample
rate reference clock.
- dmas: Pairs of phandle and specifier for the DMA channels that are used by
- the core. The core expects two dma channels, one for transmit and one for
- receive.
+ the core. The core expects two dma channels if both transmit and receive are
+ enabled, one channel otherwise.
- dma-names : "tx" for the transmit channel, "rx" for the receive channel.
For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' properties
diff --git a/Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt b/Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt
new file mode 100644
index 000000000000..41ae2699f07a
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt
@@ -0,0 +1,39 @@
+Cirrus Logic Lochnagar Audio Development Board
+
+Lochnagar is an evaluation and development board for Cirrus Logic
+Smart CODEC and Amp devices. It allows the connection of most Cirrus
+Logic devices on mini-cards, as well as allowing connection of
+various application processor systems to provide a full evaluation
+platform. Audio system topology, clocking and power can all be
+controlled through the Lochnagar, allowing the device under test
+to be used in a variety of possible use cases.
+
+This binding document describes the binding for the audio portion
+of the driver.
+
+This binding must be part of the Lochnagar MFD binding:
+ [4] ../mfd/cirrus,lochnagar.txt
+
+Required properties:
+
+ - compatible : One of the following strings:
+ "cirrus,lochnagar2-soundcard"
+
+ - #sound-dai-cells : Must be set to 1.
+
+ - clocks : Contains an entry for each entry in clock-names.
+ - clock-names : Must include the following clocks:
+ "mclk" Master clock source for the sound card, should normally
+ be set to LOCHNAGAR_SOUNDCARD_MCLK provided by the Lochnagar
+ clock driver.
+
+Example:
+
+lochnagar-sc {
+ compatible = "cirrus,lochnagar2-soundcard";
+
+ #sound-dai-cells = <1>;
+
+ clocks = <&lochnagar_clk LOCHNAGAR_SOUNDCARD_MCLK>;
+ clock-names = "mclk";
+};
diff --git a/Documentation/devicetree/bindings/sound/da7219.txt b/Documentation/devicetree/bindings/sound/da7219.txt
index e9d0baeb94e2..add1caf26ac2 100644
--- a/Documentation/devicetree/bindings/sound/da7219.txt
+++ b/Documentation/devicetree/bindings/sound/da7219.txt
@@ -23,8 +23,8 @@ Optional properties:
interrupt is to be used to wake system, otherwise "irq" should be used.
- wakeup-source: Flag to indicate this device can wake system (suspend/resume).
-- #clock-cells : Should be set to '<0>', only one clock source provided;
-- clock-output-names : Name given for DAI clocks output;
+- #clock-cells : Should be set to '<1>', two clock sources provided;
+- clock-output-names : Names given for DAI clock outputs (WCLK & BCLK);
- clocks : phandle and clock specifier for codec MCLK.
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
@@ -84,8 +84,8 @@ Example:
VDDMIC-supply = <&reg_audio>;
VDDIO-supply = <&reg_audio>;
- #clock-cells = <0>;
- clock-output-names = "dai-clks";
+ #clock-cells = <1>;
+ clock-output-names = "dai-wclk", "dai-bclk";
clocks = <&clks 201>;
clock-names = "mclk";
diff --git a/Documentation/devicetree/bindings/sound/fsl,audmix.txt b/Documentation/devicetree/bindings/sound/fsl,audmix.txt
new file mode 100644
index 000000000000..45f807ec21a5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,audmix.txt
@@ -0,0 +1,54 @@
+NXP Audio Mixer (AUDMIX).
+
+The Audio Mixer is a on-chip functional module that allows mixing of two
+audio streams into a single audio stream. Audio Mixer has two input serial
+audio interfaces. These are driven by two Synchronous Audio interface
+modules (SAI). Each input serial interface carries 8 audio channels in its
+frame in TDM manner. Mixer mixes audio samples of corresponding channels
+from two interfaces into a single sample. Before mixing, audio samples of
+two inputs can be attenuated based on configuration. The output of the
+Audio Mixer is also a serial audio interface. Like input interfaces it has
+the same TDM frame format. This output is used to drive the serial DAC TDM
+interface of audio codec and also sent to the external pins along with the
+receive path of normal audio SAI module for readback by the CPU.
+
+The output of Audio Mixer can be selected from any of the three streams
+ - serial audio input 1
+ - serial audio input 2
+ - mixed audio
+
+Mixing operation is independent of audio sample rate but the two audio
+input streams must have same audio sample rate with same number of channels
+in TDM frame to be eligible for mixing.
+
+Device driver required properties:
+=================================
+ - compatible : Compatible list, contains "fsl,imx8qm-audmix"
+
+ - reg : Offset and length of the register set for the device.
+
+ - clocks : Must contain an entry for each entry in clock-names.
+
+ - clock-names : Must include the "ipg" for register access.
+
+ - power-domains : Must contain the phandle to AUDMIX power domain node
+
+ - dais : Must contain a list of phandles to AUDMIX connected
+ DAIs. The current implementation requires two phandles
+ to SAI interfaces to be provided, the first SAI in the
+ list being used to route the AUDMIX output.
+
+ - model : Must contain machine driver name which will configure
+ and instantiate the appropriate audio card.
+
+Device driver configuration example:
+======================================
+ audmix: audmix@59840000 {
+ compatible = "fsl,imx8qm-audmix";
+ reg = <0x0 0x59840000 0x0 0x10000>;
+ clocks = <&clk IMX8QXP_AUD_AUDMIX_IPG>;
+ clock-names = "ipg";
+ power-domains = <&pd_audmix>;
+ dais = <&sai4>, <&sai5>;
+ model = "imx-audmix";
+ };
diff --git a/Documentation/devicetree/bindings/sound/mchp-i2s-mcc.txt b/Documentation/devicetree/bindings/sound/mchp-i2s-mcc.txt
new file mode 100644
index 000000000000..91ec83a6faed
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mchp-i2s-mcc.txt
@@ -0,0 +1,43 @@
+* Microchip I2S Multi-Channel Controller
+
+Required properties:
+- compatible: Should be "microchip,sam9x60-i2smcc".
+- reg: Should be the physical base address of the controller and the
+ length of memory mapped region.
+- interrupts: Should contain the interrupt for the controller.
+- dmas: Should be one per channel name listed in the dma-names property,
+ as described in atmel-dma.txt and dma.txt files.
+- dma-names: Identifier string for each DMA request line in the dmas property.
+ Two dmas have to be defined, "tx" and "rx".
+- clocks: Must contain an entry for each entry in clock-names.
+ Please refer to clock-bindings.txt.
+- clock-names: Should be one of each entry matching the clocks phandles list:
+ - "pclk" (peripheral clock) Required.
+ - "gclk" (generated clock) Optional (1).
+
+Optional properties:
+- pinctrl-0: Should specify pin control groups used for this controller.
+- princtrl-names: Should contain only one value - "default".
+
+
+(1) : Only the peripheral clock is required. The generated clock is optional
+ and should be set mostly when Master Mode is required.
+
+Example:
+
+ i2s@f001c000 {
+ compatible = "microchip,sam9x60-i2smcc";
+ reg = <0xf001c000 0x100>;
+ interrupts = <34 IRQ_TYPE_LEVEL_HIGH 7>;
+ dmas = <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(36))>,
+ <&dma0
+ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(37))>;
+ dma-names = "tx", "rx";
+ clocks = <&i2s_clk>, <&i2s_gclk>;
+ clock-names = "pclk", "gclk";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2s_default>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/mt8183-da7219-max98357.txt b/Documentation/devicetree/bindings/sound/mt8183-da7219-max98357.txt
new file mode 100644
index 000000000000..92ac86f83822
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt8183-da7219-max98357.txt
@@ -0,0 +1,15 @@
+MT8183 with MT6358, DA7219 and MAX98357 CODECS
+
+Required properties:
+- compatible : "mediatek,mt8183_da7219_max98357"
+- mediatek,headset-codec: the phandles of da7219 codecs
+- mediatek,platform: the phandle of MT8183 ASoC platform
+
+Example:
+
+ sound {
+ compatible = "mediatek,mt8183_da7219_max98357";
+ mediatek,headset-codec = <&da7219>;
+ mediatek,platform = <&afe>;
+ };
+
diff --git a/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt b/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt
new file mode 100644
index 000000000000..d6d5207fa996
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt8183-mt6358-ts3a227-max98357.txt
@@ -0,0 +1,15 @@
+MT8183 with MT6358, TS3A227 and MAX98357 CODECS
+
+Required properties:
+- compatible : "mediatek,mt8183_mt6358_ts3a227_max98357"
+- mediatek,headset-codec: the phandles of ts3a227 codecs
+- mediatek,platform: the phandle of MT8183 ASoC platform
+
+Example:
+
+ sound {
+ compatible = "mediatek,mt8183_mt6358_ts3a227_max98357";
+ mediatek,headset-codec = <&ts3a227>;
+ mediatek,platform = <&afe>;
+ };
+
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
index 648d43e1b1e9..b34afa66fb00 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
@@ -282,7 +282,12 @@ Required properties:
- reg : Should contain the register physical address.
required register is
SRU/ADG/SSI if generation1
- SRU/ADG/SSIU/SSI if generation2
+ SRU/ADG/SSIU/SSI/AUDIO-DMAC-periperi if generation2/generation3
+ Select extended AUDIO-DMAC-periperi address if SoC has it,
+ otherwise select normal AUDIO-DMAC-periperi address.
+- reg-names : Should contain the register names.
+ scu/adg/ssi if generation1
+ scu/adg/ssiu/ssi/audmapp if generation2/generation3
- rcar_sound,ssi : Should contain SSI feature.
The number of SSI subnode should be same as HW.
see below for detail.
diff --git a/Documentation/devicetree/bindings/sound/rt5651.txt b/Documentation/devicetree/bindings/sound/rt5651.txt
index a41199a5cd79..56e736a1cba9 100644
--- a/Documentation/devicetree/bindings/sound/rt5651.txt
+++ b/Documentation/devicetree/bindings/sound/rt5651.txt
@@ -22,6 +22,11 @@ Optional properties:
2: Use JD1_2 pin for jack-detect
3: Use JD2 pin for jack-detect
+- realtek,jack-detect-not-inverted
+ bool. Normal jack-detect switches give an inverted (active-low) signal,
+ set this bool in the rare case you've a jack-detect switch which is not
+ inverted.
+
- realtek,over-current-threshold-microamp
u32, micbias over-current detection threshold in µA, valid values are
600, 1500 and 2000µA.
diff --git a/Documentation/devicetree/bindings/sound/simple-amplifier.txt b/Documentation/devicetree/bindings/sound/simple-amplifier.txt
index 7182ac4f1e65..b1b097cc9b68 100644
--- a/Documentation/devicetree/bindings/sound/simple-amplifier.txt
+++ b/Documentation/devicetree/bindings/sound/simple-amplifier.txt
@@ -2,9 +2,9 @@ Simple Amplifier Audio Driver
Required properties:
- compatible : "dioo,dio2125" or "simple-audio-amplifier"
-- enable-gpios : the gpio connected to the enable pin of the simple amplifier
Optional properties:
+- enable-gpios : the gpio connected to the enable pin of the simple amplifier
- VCC-supply : power supply for the device, as covered
in Documentation/devicetree/bindings/regulator/regulator.txt
diff --git a/MAINTAINERS b/MAINTAINERS
index 43b36dbed48e..b6ed758ffd47 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3799,6 +3799,7 @@ F: drivers/clk/clk-lochnagar.c
F: drivers/mfd/lochnagar-i2c.c
F: drivers/pinctrl/cirrus/pinctrl-lochnagar.c
F: drivers/regulator/lochnagar-regulator.c
+F: sound/soc/codecs/lochnagar-sc.c
F: include/dt-bindings/clk/lochnagar.h
F: include/dt-bindings/pinctrl/lochnagar.h
F: include/linux/mfd/lochnagar*
@@ -3806,6 +3807,7 @@ F: Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt
F: Documentation/devicetree/bindings/clock/cirrus,lochnagar.txt
F: Documentation/devicetree/bindings/pinctrl/cirrus,lochnagar.txt
F: Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt
+F: Documentation/devicetree/bindings/sound/cirrus,lochnagar.txt
CISCO FCOE HBA DRIVER
M: Satish Kharat <satishkh@cisco.com>
diff --git a/include/sound/da7219.h b/include/sound/da7219.h
index 1bfcb16f2d10..4a36954c86c5 100644
--- a/include/sound/da7219.h
+++ b/include/sound/da7219.h
@@ -33,10 +33,16 @@ enum da7219_mic_amp_in_sel {
struct da7219_aad_pdata;
+enum da7219_dai_clks {
+ DA7219_DAI_WCLK_IDX = 0,
+ DA7219_DAI_BCLK_IDX,
+ DA7219_DAI_NUM_CLKS,
+};
+
struct da7219_pdata {
bool wakeup_source;
- const char *dai_clks_name;
+ const char *dai_clk_names[DA7219_DAI_NUM_CLKS];
/* Mic */
enum da7219_micbias_voltage micbias_lvl;
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index 7afe45389972..67dc3ee6ed01 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -10,10 +10,10 @@
#include <sound/soc.h>
-#define asoc_simple_card_init_hp(card, sjack, prefix) \
- asoc_simple_card_init_jack(card, sjack, 1, prefix)
-#define asoc_simple_card_init_mic(card, sjack, prefix) \
- asoc_simple_card_init_jack(card, sjack, 0, prefix)
+#define asoc_simple_init_hp(card, sjack, prefix) \
+ asoc_simple_init_jack(card, sjack, 1, prefix)
+#define asoc_simple_init_mic(card, sjack, prefix) \
+ asoc_simple_init_jack(card, sjack, 0, prefix)
struct asoc_simple_dai {
const char *name;
@@ -26,7 +26,7 @@ struct asoc_simple_dai {
struct clk *clk;
};
-struct asoc_simple_card_data {
+struct asoc_simple_data {
u32 convert_rate;
u32 convert_channels;
};
@@ -37,96 +37,178 @@ struct asoc_simple_jack {
struct snd_soc_jack_gpio gpio;
};
-int asoc_simple_card_parse_daifmt(struct device *dev,
- struct device_node *node,
- struct device_node *codec,
- char *prefix,
- unsigned int *retfmt);
+struct asoc_simple_priv {
+ struct snd_soc_card snd_card;
+ struct simple_dai_props {
+ struct asoc_simple_dai *cpu_dai;
+ struct asoc_simple_dai *codec_dai;
+ struct snd_soc_dai_link_component codecs; /* single codec */
+ struct snd_soc_dai_link_component platforms;
+ struct asoc_simple_data adata;
+ struct snd_soc_codec_conf *codec_conf;
+ unsigned int mclk_fs;
+ } *dai_props;
+ struct asoc_simple_jack hp_jack;
+ struct asoc_simple_jack mic_jack;
+ struct snd_soc_dai_link *dai_link;
+ struct asoc_simple_dai *dais;
+ struct snd_soc_codec_conf *codec_conf;
+ struct gpio_desc *pa_gpio;
+};
+#define simple_priv_to_card(priv) (&(priv)->snd_card)
+#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
+#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
+#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
+
+struct link_info {
+ int dais; /* number of dai */
+ int link; /* number of link */
+ int conf; /* number of codec_conf */
+ int cpu; /* turn for CPU / Codec */
+};
+
+int asoc_simple_parse_daifmt(struct device *dev,
+ struct device_node *node,
+ struct device_node *codec,
+ char *prefix,
+ unsigned int *retfmt);
__printf(3, 4)
-int asoc_simple_card_set_dailink_name(struct device *dev,
- struct snd_soc_dai_link *dai_link,
- const char *fmt, ...);
-int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
- char *prefix);
-
-#define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \
- asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \
+int asoc_simple_set_dailink_name(struct device *dev,
+ struct snd_soc_dai_link *dai_link,
+ const char *fmt, ...);
+int asoc_simple_parse_card_name(struct snd_soc_card *card,
+ char *prefix);
+
+#define asoc_simple_parse_clk_cpu(dev, node, dai_link, simple_dai) \
+ asoc_simple_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \
dai_link->cpu_dai_name, NULL)
-#define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai) \
- asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\
+#define asoc_simple_parse_clk_codec(dev, node, dai_link, simple_dai) \
+ asoc_simple_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\
dai_link->codec_dai_name, dai_link->codecs)
-int asoc_simple_card_parse_clk(struct device *dev,
- struct device_node *node,
- struct device_node *dai_of_node,
- struct asoc_simple_dai *simple_dai,
- const char *dai_name,
- struct snd_soc_dai_link_component *dlc);
-int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai);
-void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai);
-
-#define asoc_simple_card_parse_cpu(node, dai_link, \
- list_name, cells_name, is_single_link) \
- asoc_simple_card_parse_dai(node, NULL, \
- &dai_link->cpu_of_node, \
- &dai_link->cpu_dai_name, list_name, cells_name, is_single_link)
-#define asoc_simple_card_parse_codec(node, dai_link, list_name, cells_name) \
- asoc_simple_card_parse_dai(node, dai_link->codecs, \
+int asoc_simple_parse_clk(struct device *dev,
+ struct device_node *node,
+ struct device_node *dai_of_node,
+ struct asoc_simple_dai *simple_dai,
+ const char *dai_name,
+ struct snd_soc_dai_link_component *dlc);
+int asoc_simple_startup(struct snd_pcm_substream *substream);
+void asoc_simple_shutdown(struct snd_pcm_substream *substream);
+int asoc_simple_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd);
+int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params);
+
+#define asoc_simple_parse_cpu(node, dai_link, is_single_link) \
+ asoc_simple_parse_dai(node, NULL, \
+ &dai_link->cpu_of_node, \
+ &dai_link->cpu_dai_name, is_single_link)
+#define asoc_simple_parse_codec(node, dai_link) \
+ asoc_simple_parse_dai(node, dai_link->codecs, \
&dai_link->codec_of_node, \
- &dai_link->codec_dai_name, \
- list_name, cells_name, NULL)
-#define asoc_simple_card_parse_platform(node, dai_link, list_name, cells_name) \
- asoc_simple_card_parse_dai(node, dai_link->platforms, \
- &dai_link->platform_of_node, \
- NULL, list_name, cells_name, NULL)
-int asoc_simple_card_parse_dai(struct device_node *node,
- struct snd_soc_dai_link_component *dlc,
- struct device_node **endpoint_np,
- const char **dai_name,
- const char *list_name,
- const char *cells_name,
- int *is_single_links);
-
-#define asoc_simple_card_parse_graph_cpu(ep, dai_link) \
- asoc_simple_card_parse_graph_dai(ep, NULL, \
- &dai_link->cpu_of_node, \
- &dai_link->cpu_dai_name)
-#define asoc_simple_card_parse_graph_codec(ep, dai_link) \
- asoc_simple_card_parse_graph_dai(ep, dai_link->codecs, \
- &dai_link->codec_of_node, \
- &dai_link->codec_dai_name)
-int asoc_simple_card_parse_graph_dai(struct device_node *ep,
- struct snd_soc_dai_link_component *dlc,
- struct device_node **endpoint_np,
- const char **dai_name);
-
-#define asoc_simple_card_of_parse_tdm(np, dai) \
+ &dai_link->codec_dai_name, NULL)
+#define asoc_simple_parse_platform(node, dai_link) \
+ asoc_simple_parse_dai(node, dai_link->platforms, \
+ &dai_link->platform_of_node, NULL, NULL)
+
+#define asoc_simple_parse_tdm(np, dai) \
snd_soc_of_parse_tdm_slot(np, &(dai)->tx_slot_mask, \
&(dai)->rx_slot_mask, \
&(dai)->slots, \
&(dai)->slot_width);
-int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
- struct asoc_simple_dai *simple_dai);
-
-void asoc_simple_card_canonicalize_platform(struct snd_soc_dai_link *dai_link);
-void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
+void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link);
+void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
int is_single_links);
-int asoc_simple_card_clean_reference(struct snd_soc_card *card);
+int asoc_simple_clean_reference(struct snd_soc_card *card);
-void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
+void asoc_simple_convert_fixup(struct asoc_simple_data *data,
struct snd_pcm_hw_params *params);
-void asoc_simple_card_parse_convert(struct device *dev,
- struct device_node *np, char *prefix,
- struct asoc_simple_card_data *data);
+void asoc_simple_parse_convert(struct device *dev,
+ struct device_node *np, char *prefix,
+ struct asoc_simple_data *data);
-int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
+int asoc_simple_parse_routing(struct snd_soc_card *card,
char *prefix);
-int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
+int asoc_simple_parse_widgets(struct snd_soc_card *card,
char *prefix);
-int asoc_simple_card_init_jack(struct snd_soc_card *card,
+int asoc_simple_init_jack(struct snd_soc_card *card,
struct asoc_simple_jack *sjack,
int is_hp, char *prefix);
+int asoc_simple_init_priv(struct asoc_simple_priv *priv,
+ struct link_info *li);
+
+#ifdef DEBUG
+inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv,
+ char *name,
+ struct asoc_simple_dai *dai)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+
+ if (dai->name)
+ dev_dbg(dev, "%s dai name = %s\n",
+ name, dai->name);
+ if (dai->sysclk)
+ dev_dbg(dev, "%s sysclk = %d\n",
+ name, dai->sysclk);
+
+ dev_dbg(dev, "%s direction = %s\n",
+ name, dai->clk_direction ? "OUT" : "IN");
+
+ if (dai->slots)
+ dev_dbg(dev, "%s slots = %d\n", name, dai->slots);
+ if (dai->slot_width)
+ dev_dbg(dev, "%s slot width = %d\n", name, dai->slot_width);
+ if (dai->tx_slot_mask)
+ dev_dbg(dev, "%s tx slot mask = %d\n", name, dai->tx_slot_mask);
+ if (dai->rx_slot_mask)
+ dev_dbg(dev, "%s rx slot mask = %d\n", name, dai->rx_slot_mask);
+ if (dai->clk)
+ dev_dbg(dev, "%s clk %luHz\n", name, clk_get_rate(dai->clk));
+}
+
+inline void asoc_simple_debug_info(struct asoc_simple_priv *priv)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ struct device *dev = simple_priv_to_dev(priv);
+
+ int i;
+
+ if (card->name)
+ dev_dbg(dev, "Card Name: %s\n", card->name);
+
+ for (i = 0; i < card->num_links; i++) {
+ struct simple_dai_props *props = simple_priv_to_props(priv, i);
+ struct snd_soc_dai_link *link = simple_priv_to_link(priv, i);
+
+ dev_dbg(dev, "DAI%d\n", i);
+
+ asoc_simple_debug_dai(priv, "cpu", props->cpu_dai);
+ asoc_simple_debug_dai(priv, "codec", props->codec_dai);
+
+ if (link->name)
+ dev_dbg(dev, "dai name = %s\n", link->name);
+
+ dev_dbg(dev, "dai format = %04x\n", link->dai_fmt);
+
+ if (props->adata.convert_rate)
+ dev_dbg(dev, "convert_rate = %d\n",
+ props->adata.convert_rate);
+ if (props->adata.convert_channels)
+ dev_dbg(dev, "convert_channels = %d\n",
+ props->adata.convert_channels);
+ if (props->codec_conf && props->codec_conf->name_prefix)
+ dev_dbg(dev, "name prefix = %s\n",
+ props->codec_conf->name_prefix);
+ if (props->mclk_fs)
+ dev_dbg(dev, "mclk-fs = %d\n",
+ props->mclk_fs);
+ }
+}
+#else
+#define asoc_simple_debug_info(priv)
+#endif /* DEBUG */
#endif /* __SIMPLE_CARD_UTILS_H */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index eb7db605955b..1e2be35ed36f 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1083,6 +1083,8 @@ struct snd_soc_card {
struct mutex mutex;
struct mutex dapm_mutex;
+ spinlock_t dpcm_lock;
+
bool instantiated;
bool topology_shortname_created;
diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c
index 4c23381727a1..273c543e8ff3 100644
--- a/sound/soc/adi/axi-i2s.c
+++ b/sound/soc/adi/axi-i2s.c
@@ -43,6 +43,9 @@ struct axi_i2s {
struct clk *clk;
struct clk *clk_ref;
+ bool has_capture;
+ bool has_playback;
+
struct snd_soc_dai_driver dai_driver;
struct snd_dmaengine_dai_dma_data capture_dma_data;
@@ -136,8 +139,10 @@ static int axi_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct axi_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
- &i2s->capture_dma_data);
+ snd_soc_dai_init_dma_data(
+ dai,
+ i2s->has_playback ? &i2s->playback_dma_data : NULL,
+ i2s->has_capture ? &i2s->capture_dma_data : NULL);
return 0;
}
@@ -151,18 +156,6 @@ static const struct snd_soc_dai_ops axi_i2s_dai_ops = {
static struct snd_soc_dai_driver axi_i2s_dai = {
.probe = axi_i2s_dai_probe,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE,
- },
.ops = &axi_i2s_dai_ops,
.symmetric_rates = 1,
};
@@ -178,6 +171,19 @@ static const struct regmap_config axi_i2s_regmap_config = {
.max_register = AXI_I2S_REG_STATUS,
};
+static void axi_i2s_parse_of(struct axi_i2s *i2s, const struct device_node *np)
+{
+ struct property *dma_names;
+ const char *dma_name;
+
+ of_property_for_each_string(np, "dma-names", dma_names, dma_name) {
+ if (strcmp(dma_name, "rx") == 0)
+ i2s->has_capture = true;
+ if (strcmp(dma_name, "tx") == 0)
+ i2s->has_playback = true;
+ }
+}
+
static int axi_i2s_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -191,6 +197,8 @@ static int axi_i2s_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2s);
+ axi_i2s_parse_of(i2s, pdev->dev.of_node);
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
@@ -213,13 +221,29 @@ static int axi_i2s_probe(struct platform_device *pdev)
if (ret)
return ret;
- i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
- i2s->playback_dma_data.addr_width = 4;
- i2s->playback_dma_data.maxburst = 1;
+ if (i2s->has_playback) {
+ axi_i2s_dai.playback.channels_min = 2;
+ axi_i2s_dai.playback.channels_max = 2;
+ axi_i2s_dai.playback.rates = SNDRV_PCM_RATE_KNOT;
+ axi_i2s_dai.playback.formats =
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
+
+ i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
+ i2s->playback_dma_data.addr_width = 4;
+ i2s->playback_dma_data.maxburst = 1;
+ }
+
+ if (i2s->has_capture) {
+ axi_i2s_dai.capture.channels_min = 2;
+ axi_i2s_dai.capture.channels_max = 2;
+ axi_i2s_dai.capture.rates = SNDRV_PCM_RATE_KNOT;
+ axi_i2s_dai.capture.formats =
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE;
- i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
- i2s->capture_dma_data.addr_width = 4;
- i2s->capture_dma_data.maxburst = 1;
+ i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
+ i2s->capture_dma_data.addr_width = 4;
+ i2s->capture_dma_data.maxburst = 1;
+ }
i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
i2s->ratnum.den_step = 1;
@@ -240,6 +264,10 @@ static int axi_i2s_probe(struct platform_device *pdev)
if (ret)
goto err_clk_disable;
+ dev_info(&pdev->dev, "probed, capture %s, playback %s\n",
+ i2s->has_capture ? "enabled" : "disabled",
+ i2s->has_playback ? "enabled" : "disabled");
+
return 0;
err_clk_disable:
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index a5daad973ce5..8a619a75b3a9 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -47,7 +47,7 @@
static struct snd_soc_jack cz_jack;
static struct clk *da7219_dai_clk;
-extern int bt_uart_enable;
+extern bool bt_uart_enable;
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
{
@@ -72,7 +72,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- da7219_dai_clk = clk_get(component->dev, "da7219-dai-clks");
+ da7219_dai_clk = clk_get(component->dev, "da7219-dai-bclk");
ret = snd_soc_card_jack_new(card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_LINEOUT |
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 64f86f0b87e5..c473b9e463ab 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -109,4 +109,18 @@ config SND_SOC_MIKROE_PROTO
using I2C over SDA (MPU Data Input) and SCL (MPU Clock Input) pins.
Both playback and capture are supported.
+config SND_MCHP_SOC_I2S_MCC
+ tristate "Microchip ASoC driver for boards using I2S MCC"
+ 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 I2S Multi-Channel ASoC
+ driver on the following Microchip platforms:
+ - sam9x60
+
+ The I2SMCC complies with the Inter-IC Sound (I2S) bus specification
+ and supports a Time Division Multiplexed (TDM) interface with
+ external multi-channel audio codecs.
+
endif
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index 9f41bfa0fea3..1f6890ed3738 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -4,11 +4,13 @@ snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
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
obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
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
# AT91 Machine Support
snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c
new file mode 100644
index 000000000000..86495883ca3f
--- /dev/null
+++ b/sound/soc/atmel/mchp-i2s-mcc.c
@@ -0,0 +1,974 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for Microchip I2S Multi-channel controller
+//
+// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/lcm.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+/*
+ * ---- I2S Controller Register map ----
+ */
+#define MCHP_I2SMCC_CR 0x0000 /* Control Register */
+#define MCHP_I2SMCC_MRA 0x0004 /* Mode Register A */
+#define MCHP_I2SMCC_MRB 0x0008 /* Mode Register B */
+#define MCHP_I2SMCC_SR 0x000C /* Status Register */
+#define MCHP_I2SMCC_IERA 0x0010 /* Interrupt Enable Register A */
+#define MCHP_I2SMCC_IDRA 0x0014 /* Interrupt Disable Register A */
+#define MCHP_I2SMCC_IMRA 0x0018 /* Interrupt Mask Register A */
+#define MCHP_I2SMCC_ISRA 0X001C /* Interrupt Status Register A */
+
+#define MCHP_I2SMCC_IERB 0x0020 /* Interrupt Enable Register B */
+#define MCHP_I2SMCC_IDRB 0x0024 /* Interrupt Disable Register B */
+#define MCHP_I2SMCC_IMRB 0x0028 /* Interrupt Mask Register B */
+#define MCHP_I2SMCC_ISRB 0X002C /* Interrupt Status Register B */
+
+#define MCHP_I2SMCC_RHR 0x0030 /* Receiver Holding Register */
+#define MCHP_I2SMCC_THR 0x0034 /* Transmitter Holding Register */
+
+#define MCHP_I2SMCC_RHL0R 0x0040 /* Receiver Holding Left 0 Register */
+#define MCHP_I2SMCC_RHR0R 0x0044 /* Receiver Holding Right 0 Register */
+
+#define MCHP_I2SMCC_RHL1R 0x0048 /* Receiver Holding Left 1 Register */
+#define MCHP_I2SMCC_RHR1R 0x004C /* Receiver Holding Right 1 Register */
+
+#define MCHP_I2SMCC_RHL2R 0x0050 /* Receiver Holding Left 2 Register */
+#define MCHP_I2SMCC_RHR2R 0x0054 /* Receiver Holding Right 2 Register */
+
+#define MCHP_I2SMCC_RHL3R 0x0058 /* Receiver Holding Left 3 Register */
+#define MCHP_I2SMCC_RHR3R 0x005C /* Receiver Holding Right 3 Register */
+
+#define MCHP_I2SMCC_THL0R 0x0060 /* Transmitter Holding Left 0 Register */
+#define MCHP_I2SMCC_THR0R 0x0064 /* Transmitter Holding Right 0 Register */
+
+#define MCHP_I2SMCC_THL1R 0x0068 /* Transmitter Holding Left 1 Register */
+#define MCHP_I2SMCC_THR1R 0x006C /* Transmitter Holding Right 1 Register */
+
+#define MCHP_I2SMCC_THL2R 0x0070 /* Transmitter Holding Left 2 Register */
+#define MCHP_I2SMCC_THR2R 0x0074 /* Transmitter Holding Right 2 Register */
+
+#define MCHP_I2SMCC_THL3R 0x0078 /* Transmitter Holding Left 3 Register */
+#define MCHP_I2SMCC_THR3R 0x007C /* Transmitter Holding Right 3 Register */
+
+#define MCHP_I2SMCC_VERSION 0x00FC /* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define MCHP_I2SMCC_CR_RXEN BIT(0) /* Receiver Enable */
+#define MCHP_I2SMCC_CR_RXDIS BIT(1) /* Receiver Disable */
+#define MCHP_I2SMCC_CR_CKEN BIT(2) /* Clock Enable */
+#define MCHP_I2SMCC_CR_CKDIS BIT(3) /* Clock Disable */
+#define MCHP_I2SMCC_CR_TXEN BIT(4) /* Transmitter Enable */
+#define MCHP_I2SMCC_CR_TXDIS BIT(5) /* Transmitter Disable */
+#define MCHP_I2SMCC_CR_SWRST BIT(7) /* Software Reset */
+
+/*
+ * ---- Mode Register A (Read/Write) ----
+ */
+#define MCHP_I2SMCC_MRA_MODE_MASK GENMASK(0, 0)
+#define MCHP_I2SMCC_MRA_MODE_SLAVE (0 << 0)
+#define MCHP_I2SMCC_MRA_MODE_MASTER (1 << 0)
+
+#define MCHP_I2SMCC_MRA_DATALENGTH_MASK GENMASK(3, 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_32_BITS (0 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_24_BITS (1 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_20_BITS (2 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_18_BITS (3 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS (4 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_16_BITS_COMPACT (5 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS (6 << 1)
+#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS_COMPACT (7 << 1)
+
+#define MCHP_I2SMCC_MRA_WIRECFG_MASK GENMASK(5, 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_I2S_1_TDM_0 (0 << 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1 (1 << 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2 (2 << 4)
+#define MCHP_I2SMCC_MRA_WIRECFG_TDM_3 (3 << 4)
+
+#define MCHP_I2SMCC_MRA_FORMAT_MASK GENMASK(7, 6)
+#define MCHP_I2SMCC_MRA_FORMAT_I2S (0 << 6)
+#define MCHP_I2SMCC_MRA_FORMAT_LJ (1 << 6) /* Left Justified */
+#define MCHP_I2SMCC_MRA_FORMAT_TDM (2 << 6)
+#define MCHP_I2SMCC_MRA_FORMAT_TDMLJ (3 << 6)
+
+/* Transmitter uses one DMA channel ... */
+/* Left audio samples duplicated to right audio channel */
+#define MCHP_I2SMCC_MRA_RXMONO BIT(8)
+
+/* I2SDO output of I2SC is internally connected to I2SDI input */
+#define MCHP_I2SMCC_MRA_RXLOOP BIT(9)
+
+/* Receiver uses one DMA channel ... */
+/* Left audio samples duplicated to right audio channel */
+#define MCHP_I2SMCC_MRA_TXMONO BIT(10)
+
+/* x sample transmitted when underrun */
+#define MCHP_I2SMCC_MRA_TXSAME_ZERO (0 << 11) /* Zero sample */
+#define MCHP_I2SMCC_MRA_TXSAME_PREVIOUS (1 << 11) /* Previous sample */
+
+/* select between peripheral clock and generated clock */
+#define MCHP_I2SMCC_MRA_SRCCLK_PCLK (0 << 12)
+#define MCHP_I2SMCC_MRA_SRCCLK_GCLK (1 << 12)
+
+/* Number of TDM Channels - 1 */
+#define MCHP_I2SMCC_MRA_NBCHAN_MASK GENMASK(15, 13)
+#define MCHP_I2SMCC_MRA_NBCHAN(ch) \
+ ((((ch) - 1) << 13) & MCHP_I2SMCC_MRA_NBCHAN_MASK)
+
+/* Selected Clock to I2SMCC Master Clock ratio */
+#define MCHP_I2SMCC_MRA_IMCKDIV_MASK GENMASK(21, 16)
+#define MCHP_I2SMCC_MRA_IMCKDIV(div) \
+ (((div) << 16) & MCHP_I2SMCC_MRA_IMCKDIV_MASK)
+
+/* TDM Frame Synchronization */
+#define MCHP_I2SMCC_MRA_TDMFS_MASK GENMASK(23, 22)
+#define MCHP_I2SMCC_MRA_TDMFS_SLOT (0 << 22)
+#define MCHP_I2SMCC_MRA_TDMFS_HALF (1 << 22)
+#define MCHP_I2SMCC_MRA_TDMFS_BIT (2 << 22)
+
+/* Selected Clock to I2SMC Serial Clock ratio */
+#define MCHP_I2SMCC_MRA_ISCKDIV_MASK GENMASK(29, 24)
+#define MCHP_I2SMCC_MRA_ISCKDIV(div) \
+ (((div) << 24) & MCHP_I2SMCC_MRA_ISCKDIV_MASK)
+
+/* Master Clock mode */
+#define MCHP_I2SMCC_MRA_IMCKMODE_MASK GENMASK(30, 30)
+/* 0: No master clock generated*/
+#define MCHP_I2SMCC_MRA_IMCKMODE_NONE (0 << 30)
+/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
+#define MCHP_I2SMCC_MRA_IMCKMODE_GEN (1 << 30)
+
+/* Slot Width */
+/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
+/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
+#define MCHP_I2SMCC_MRA_IWS BIT(31)
+
+/*
+ * ---- Mode Register B (Read/Write) ----
+ */
+/* all enabled I2S left channels are filled first, then I2S right channels */
+#define MCHP_I2SMCC_MRB_CRAMODE_LEFT_FIRST (0 << 0)
+/*
+ * an enabled I2S left channel is filled, then the corresponding right
+ * channel, until all channels are filled
+ */
+#define MCHP_I2SMCC_MRB_CRAMODE_REGULAR (1 << 0)
+
+#define MCHP_I2SMCC_MRB_FIFOEN BIT(1)
+
+#define MCHP_I2SMCC_MRB_DMACHUNK_MASK GENMASK(9, 8)
+#define MCHP_I2SMCC_MRB_DMACHUNK(no_words) \
+ (((fls(no_words) - 1) << 8) & MCHP_I2SMCC_MRB_DMACHUNK_MASK)
+
+#define MCHP_I2SMCC_MRB_CLKSEL_MASK GENMASK(16, 16)
+#define MCHP_I2SMCC_MRB_CLKSEL_EXT (0 << 16)
+#define MCHP_I2SMCC_MRB_CLKSEL_INT (1 << 16)
+
+/*
+ * ---- Status Registers (Read-only) ----
+ */
+#define MCHP_I2SMCC_SR_RXEN BIT(0) /* Receiver Enabled */
+#define MCHP_I2SMCC_SR_TXEN BIT(4) /* Transmitter Enabled */
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Registers A ----
+ */
+#define MCHP_I2SMCC_INT_TXRDY_MASK(ch) GENMASK((ch) - 1, 0)
+#define MCHP_I2SMCC_INT_TXRDYCH(ch) BIT(ch)
+#define MCHP_I2SMCC_INT_TXUNF_MASK(ch) GENMASK((ch) + 7, 8)
+#define MCHP_I2SMCC_INT_TXUNFCH(ch) BIT((ch) + 8)
+#define MCHP_I2SMCC_INT_RXRDY_MASK(ch) GENMASK((ch) + 15, 16)
+#define MCHP_I2SMCC_INT_RXRDYCH(ch) BIT((ch) + 16)
+#define MCHP_I2SMCC_INT_RXOVF_MASK(ch) GENMASK((ch) + 23, 24)
+#define MCHP_I2SMCC_INT_RXOVFCH(ch) BIT((ch) + 24)
+
+/*
+ * ---- Interrupt Enable/Disable/Mask/Status Registers B ----
+ */
+#define MCHP_I2SMCC_INT_WERR BIT(0)
+#define MCHP_I2SMCC_INT_TXFFRDY BIT(8)
+#define MCHP_I2SMCC_INT_TXFFEMP BIT(9)
+#define MCHP_I2SMCC_INT_RXFFRDY BIT(12)
+#define MCHP_I2SMCC_INT_RXFFFUL BIT(13)
+
+/*
+ * ---- Version Register (Read-only) ----
+ */
+#define MCHP_I2SMCC_VERSION_MASK GENMASK(11, 0)
+
+#define MCHP_I2SMCC_MAX_CHANNELS 8
+#define MCHP_I2MCC_TDM_SLOT_WIDTH 32
+
+static const struct regmap_config mchp_i2s_mcc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = MCHP_I2SMCC_VERSION,
+};
+
+struct mchp_i2s_mcc_dev {
+ struct wait_queue_head wq_txrdy;
+ struct wait_queue_head wq_rxrdy;
+ struct device *dev;
+ struct regmap *regmap;
+ struct clk *pclk;
+ struct clk *gclk;
+ struct snd_dmaengine_dai_dma_data playback;
+ struct snd_dmaengine_dai_dma_data capture;
+ unsigned int fmt;
+ unsigned int sysclk;
+ unsigned int frame_length;
+ int tdm_slots;
+ int channels;
+ int gclk_use:1;
+ int gclk_running:1;
+ int tx_rdy:1;
+ int rx_rdy:1;
+};
+
+static irqreturn_t mchp_i2s_mcc_interrupt(int irq, void *dev_id)
+{
+ struct mchp_i2s_mcc_dev *dev = dev_id;
+ u32 sra, imra, srb, imrb, pendinga, pendingb, idra = 0;
+ irqreturn_t ret = IRQ_NONE;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_IMRA, &imra);
+ regmap_read(dev->regmap, MCHP_I2SMCC_ISRA, &sra);
+ pendinga = imra & sra;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_IMRB, &imrb);
+ regmap_read(dev->regmap, MCHP_I2SMCC_ISRB, &srb);
+ pendingb = imrb & srb;
+
+ if (!pendinga && !pendingb)
+ return IRQ_NONE;
+
+ /*
+ * Tx/Rx ready interrupts are enabled when stopping only, to assure
+ * availability and to disable clocks if necessary
+ */
+ idra |= pendinga & (MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels) |
+ MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
+ if (idra)
+ ret = IRQ_HANDLED;
+
+ if ((imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) &&
+ (imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) ==
+ (idra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels))) {
+ dev->tx_rdy = 1;
+ wake_up_interruptible(&dev->wq_txrdy);
+ }
+ if ((imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) &&
+ (imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) ==
+ (idra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels))) {
+ dev->rx_rdy = 1;
+ wake_up_interruptible(&dev->wq_rxrdy);
+ }
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
+
+ return ret;
+}
+
+static int mchp_i2s_mcc_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev, "%s() clk_id=%d freq=%u dir=%d\n",
+ __func__, clk_id, freq, dir);
+
+ /* We do not need SYSCLK */
+ if (dir == SND_SOC_CLOCK_IN)
+ return 0;
+
+ dev->sysclk = freq;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int ratio)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev, "%s() ratio=%u\n", __func__, ratio);
+
+ dev->frame_length = ratio;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev, "%s() fmt=%#x\n", __func__, fmt);
+
+ /* We don't support any kind of clock inversion */
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
+ return -EINVAL;
+
+ /* We can't generate only FSYNC */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFS)
+ return -EINVAL;
+
+ /* We can only reconfigure the IP when it's stopped */
+ if (fmt & SND_SOC_DAIFMT_CONT)
+ return -EINVAL;
+
+ dev->fmt = fmt;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(dev->dev,
+ "%s() tx_mask=0x%08x rx_mask=0x%08x slots=%d width=%d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (slots < 0 || slots > MCHP_I2SMCC_MAX_CHANNELS ||
+ slot_width != MCHP_I2MCC_TDM_SLOT_WIDTH)
+ return -EINVAL;
+
+ if (slots) {
+ /* We do not support daisy chain */
+ if (rx_mask != GENMASK(slots - 1, 0) ||
+ rx_mask != tx_mask)
+ return -EINVAL;
+ }
+
+ dev->tdm_slots = slots;
+ dev->frame_length = slots * MCHP_I2MCC_TDM_SLOT_WIDTH;
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_clk_get_rate_diff(struct clk *clk,
+ unsigned long rate,
+ struct clk **best_clk,
+ unsigned long *best_rate,
+ unsigned long *best_diff_rate)
+{
+ long round_rate;
+ unsigned int diff_rate;
+
+ round_rate = clk_round_rate(clk, rate);
+ if (round_rate < 0)
+ return (int)round_rate;
+
+ diff_rate = abs(rate - round_rate);
+ if (diff_rate < *best_diff_rate) {
+ *best_clk = clk;
+ *best_diff_rate = diff_rate;
+ *best_rate = rate;
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_config_divs(struct mchp_i2s_mcc_dev *dev,
+ unsigned int bclk, unsigned int *mra)
+{
+ unsigned long clk_rate;
+ unsigned long lcm_rate;
+ unsigned long best_rate = 0;
+ unsigned long best_diff_rate = ~0;
+ unsigned int sysclk;
+ struct clk *best_clk = NULL;
+ int ret;
+
+ /* For code simplification */
+ if (!dev->sysclk)
+ sysclk = bclk;
+ else
+ sysclk = dev->sysclk;
+
+ /*
+ * MCLK is Selected CLK / (2 * IMCKDIV),
+ * BCLK is Selected CLK / (2 * ISCKDIV);
+ * if IMCKDIV or ISCKDIV are 0, MCLK or BCLK = Selected CLK
+ */
+ lcm_rate = lcm(sysclk, bclk);
+ if ((lcm_rate / sysclk % 2 == 1 && lcm_rate / sysclk > 2) ||
+ (lcm_rate / bclk % 2 == 1 && lcm_rate / bclk > 2))
+ lcm_rate *= 2;
+
+ for (clk_rate = lcm_rate;
+ (clk_rate == sysclk || clk_rate / (sysclk * 2) <= GENMASK(5, 0)) &&
+ (clk_rate == bclk || clk_rate / (bclk * 2) <= GENMASK(5, 0));
+ clk_rate += lcm_rate) {
+ ret = mchp_i2s_mcc_clk_get_rate_diff(dev->gclk, clk_rate,
+ &best_clk, &best_rate,
+ &best_diff_rate);
+ if (ret) {
+ dev_err(dev->dev, "gclk error for rate %lu: %d",
+ clk_rate, ret);
+ } else {
+ if (!best_diff_rate) {
+ dev_dbg(dev->dev, "found perfect rate on gclk: %lu\n",
+ clk_rate);
+ break;
+ }
+ }
+
+ ret = mchp_i2s_mcc_clk_get_rate_diff(dev->pclk, clk_rate,
+ &best_clk, &best_rate,
+ &best_diff_rate);
+ if (ret) {
+ dev_err(dev->dev, "pclk error for rate %lu: %d",
+ clk_rate, ret);
+ } else {
+ if (!best_diff_rate) {
+ dev_dbg(dev->dev, "found perfect rate on pclk: %lu\n",
+ clk_rate);
+ break;
+ }
+ }
+ }
+
+ /* check if clocks returned only errors */
+ if (!best_clk) {
+ dev_err(dev->dev, "unable to change rate to clocks\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev->dev, "source CLK is %s with rate %lu, diff %lu\n",
+ best_clk == dev->pclk ? "pclk" : "gclk",
+ best_rate, best_diff_rate);
+
+ /* set the rate */
+ ret = clk_set_rate(best_clk, best_rate);
+ if (ret) {
+ dev_err(dev->dev, "unable to set rate %lu to %s: %d\n",
+ best_rate, best_clk == dev->pclk ? "PCLK" : "GCLK",
+ ret);
+ return ret;
+ }
+
+ /* Configure divisors */
+ if (dev->sysclk)
+ *mra |= MCHP_I2SMCC_MRA_IMCKDIV(best_rate / (2 * sysclk));
+ *mra |= MCHP_I2SMCC_MRA_ISCKDIV(best_rate / (2 * bclk));
+
+ if (best_clk == dev->gclk) {
+ *mra |= MCHP_I2SMCC_MRA_SRCCLK_GCLK;
+ ret = clk_prepare(dev->gclk);
+ if (ret < 0)
+ dev_err(dev->dev, "unable to prepare GCLK: %d\n", ret);
+ else
+ dev->gclk_use = 1;
+ } else {
+ *mra |= MCHP_I2SMCC_MRA_SRCCLK_PCLK;
+ dev->gclk_use = 0;
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_is_running(struct mchp_i2s_mcc_dev *dev)
+{
+ u32 sr;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
+ return !!(sr & (MCHP_I2SMCC_SR_TXEN | MCHP_I2SMCC_SR_RXEN));
+}
+
+static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+ u32 mra = 0;
+ u32 mrb = 0;
+ unsigned int channels = params_channels(params);
+ unsigned int frame_length = dev->frame_length;
+ unsigned int bclk_rate;
+ int set_divs = 0;
+ int ret;
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+ 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));
+
+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ if (dev->tdm_slots) {
+ dev_err(dev->dev, "I2S with TDM is not supported\n");
+ return -EINVAL;
+ }
+ mra |= MCHP_I2SMCC_MRA_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if (dev->tdm_slots) {
+ dev_err(dev->dev, "Left-Justified with TDM is not supported\n");
+ return -EINVAL;
+ }
+ mra |= MCHP_I2SMCC_MRA_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ mra |= MCHP_I2SMCC_MRA_FORMAT_TDM;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported bus format\n");
+ return -EINVAL;
+ }
+
+ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* cpu is BCLK and LRC master */
+ mra |= MCHP_I2SMCC_MRA_MODE_MASTER;
+ if (dev->sysclk)
+ mra |= MCHP_I2SMCC_MRA_IMCKMODE_GEN;
+ set_divs = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ /* cpu is BCLK master */
+ mrb |= MCHP_I2SMCC_MRB_CLKSEL_INT;
+ set_divs = 1;
+ /* fall through */
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* cpu is slave */
+ mra |= MCHP_I2SMCC_MRA_MODE_SLAVE;
+ if (dev->sysclk)
+ dev_warn(dev->dev, "Unable to generate MCLK in Slave mode\n");
+ break;
+ default:
+ dev_err(dev->dev, "unsupported master/slave mode\n");
+ return -EINVAL;
+ }
+
+ if (dev->fmt & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
+ switch (channels) {
+ case 1:
+ if (is_playback)
+ mra |= MCHP_I2SMCC_MRA_TXMONO;
+ else
+ mra |= MCHP_I2SMCC_MRA_RXMONO;
+ break;
+ case 2:
+ break;
+ default:
+ dev_err(dev->dev, "unsupported number of audio channels\n");
+ return -EINVAL;
+ }
+
+ if (!frame_length)
+ frame_length = 2 * params_physical_width(params);
+ } else if (dev->fmt & SND_SOC_DAIFMT_DSP_A) {
+ if (dev->tdm_slots) {
+ if (channels % 2 && channels * 2 <= dev->tdm_slots) {
+ /*
+ * Duplicate data for even-numbered channels
+ * to odd-numbered channels
+ */
+ if (is_playback)
+ mra |= MCHP_I2SMCC_MRA_TXMONO;
+ else
+ mra |= MCHP_I2SMCC_MRA_RXMONO;
+ }
+ channels = dev->tdm_slots;
+ }
+
+ mra |= MCHP_I2SMCC_MRA_NBCHAN(channels);
+ if (!frame_length)
+ frame_length = channels * MCHP_I2MCC_TDM_SLOT_WIDTH;
+ }
+
+ /*
+ * We must have the same burst size configured
+ * in the DMA transfer and in out IP
+ */
+ mrb |= MCHP_I2SMCC_MRB_DMACHUNK(channels);
+ if (is_playback)
+ dev->playback.maxburst = 1 << (fls(channels) - 1);
+ else
+ dev->capture.maxburst = 1 << (fls(channels) - 1);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_8_BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_16_BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_18_BITS |
+ MCHP_I2SMCC_MRA_IWS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_20_BITS |
+ MCHP_I2SMCC_MRA_IWS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS |
+ MCHP_I2SMCC_MRA_IWS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_24_BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ mra |= MCHP_I2SMCC_MRA_DATALENGTH_32_BITS;
+ break;
+ default:
+ dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
+ return -EINVAL;
+ }
+
+ /*
+ * If we are already running, the wanted setup must be
+ * the same with the one that's currently ongoing
+ */
+ if (mchp_i2s_mcc_is_running(dev)) {
+ u32 mra_cur;
+ u32 mrb_cur;
+
+ regmap_read(dev->regmap, MCHP_I2SMCC_MRA, &mra_cur);
+ regmap_read(dev->regmap, MCHP_I2SMCC_MRB, &mrb_cur);
+ if (mra != mra_cur || mrb != mrb_cur)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /* Save the number of channels to know what interrupts to enable */
+ dev->channels = channels;
+
+ if (set_divs) {
+ bclk_rate = frame_length * params_rate(params);
+ ret = mchp_i2s_mcc_config_divs(dev, bclk_rate, &mra);
+ if (ret) {
+ dev_err(dev->dev, "unable to configure the divisors: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = regmap_write(dev->regmap, MCHP_I2SMCC_MRA, mra);
+ if (ret < 0)
+ return ret;
+ return regmap_write(dev->regmap, MCHP_I2SMCC_MRB, mrb);
+}
+
+static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ long err;
+
+ if (is_playback) {
+ err = wait_event_interruptible_timeout(dev->wq_txrdy,
+ dev->tx_rdy,
+ msecs_to_jiffies(500));
+ } else {
+ err = wait_event_interruptible_timeout(dev->wq_rxrdy,
+ dev->rx_rdy,
+ msecs_to_jiffies(500));
+ }
+
+ if (err == 0) {
+ u32 idra;
+
+ dev_warn_once(dev->dev, "Timeout waiting for %s\n",
+ is_playback ? "Tx ready" : "Rx ready");
+ if (is_playback)
+ idra = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
+ else
+ idra = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
+ regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
+ }
+
+ if (!mchp_i2s_mcc_is_running(dev)) {
+ regmap_write(dev->regmap, MCHP_I2SMCC_CR, MCHP_I2SMCC_CR_CKDIS);
+
+ if (dev->gclk_running) {
+ clk_disable_unprepare(dev->gclk);
+ dev->gclk_running = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+ bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u32 cr = 0;
+ u32 iera = 0;
+ u32 sr;
+ int err;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (is_playback)
+ cr = MCHP_I2SMCC_CR_TXEN | MCHP_I2SMCC_CR_CKEN;
+ else
+ cr = MCHP_I2SMCC_CR_RXEN | MCHP_I2SMCC_CR_CKEN;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_read(dev->regmap, MCHP_I2SMCC_SR, &sr);
+ if (is_playback && (sr & MCHP_I2SMCC_SR_TXEN)) {
+ cr = MCHP_I2SMCC_CR_TXDIS;
+ dev->tx_rdy = 0;
+ /*
+ * Enable Tx Ready interrupts on all channels
+ * to assure all data is sent
+ */
+ iera = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
+ } else if (!is_playback && (sr & MCHP_I2SMCC_SR_RXEN)) {
+ cr = MCHP_I2SMCC_CR_RXDIS;
+ dev->rx_rdy = 0;
+ /*
+ * Enable Rx Ready interrupts on all channels
+ * to assure all data is received
+ */
+ iera = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((cr & MCHP_I2SMCC_CR_CKEN) && dev->gclk_use &&
+ !dev->gclk_running) {
+ err = clk_enable(dev->gclk);
+ if (err) {
+ dev_err_once(dev->dev, "failed to enable GCLK: %d\n",
+ err);
+ } else {
+ dev->gclk_running = 1;
+ }
+ }
+
+ regmap_write(dev->regmap, MCHP_I2SMCC_IERA, iera);
+ regmap_write(dev->regmap, MCHP_I2SMCC_CR, cr);
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ /* Software reset the IP if it's not running */
+ if (!mchp_i2s_mcc_is_running(dev)) {
+ return regmap_write(dev->regmap, MCHP_I2SMCC_CR,
+ MCHP_I2SMCC_CR_SWRST);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = {
+ .set_sysclk = mchp_i2s_mcc_set_sysclk,
+ .set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio,
+ .startup = mchp_i2s_mcc_startup,
+ .trigger = mchp_i2s_mcc_trigger,
+ .hw_params = mchp_i2s_mcc_hw_params,
+ .hw_free = mchp_i2s_mcc_hw_free,
+ .set_fmt = mchp_i2s_mcc_set_dai_fmt,
+ .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot,
+};
+
+static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai)
+{
+ struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ init_waitqueue_head(&dev->wq_txrdy);
+ init_waitqueue_head(&dev->wq_rxrdy);
+
+ snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
+
+ return 0;
+}
+
+#define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000
+
+#define MCHP_I2SMCC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mchp_i2s_mcc_dai = {
+ .probe = mchp_i2s_mcc_dai_probe,
+ .playback = {
+ .stream_name = "I2SMCC-Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MCHP_I2SMCC_RATES,
+ .formats = MCHP_I2SMCC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "I2SMCC-Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MCHP_I2SMCC_RATES,
+ .formats = MCHP_I2SMCC_FORMATS,
+ },
+ .ops = &mchp_i2s_mcc_dai_ops,
+ .symmetric_rates = 1,
+ .symmetric_samplebits = 1,
+ .symmetric_channels = 1,
+};
+
+static const struct snd_soc_component_driver mchp_i2s_mcc_component = {
+ .name = "mchp-i2s-mcc",
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id mchp_i2s_mcc_dt_ids[] = {
+ {
+ .compatible = "microchip,sam9x60-i2smcc",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_i2s_mcc_dt_ids);
+#endif
+
+static int mchp_i2s_mcc_probe(struct platform_device *pdev)
+{
+ struct mchp_i2s_mcc_dev *dev;
+ struct resource *mem;
+ struct regmap *regmap;
+ void __iomem *base;
+ u32 version;
+ int irq;
+ int err;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &mchp_i2s_mcc_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(&pdev->dev, irq, mchp_i2s_mcc_interrupt, 0,
+ dev_name(&pdev->dev), dev);
+ if (err)
+ return err;
+
+ 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 optional generated clock */
+ dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+ if (IS_ERR(dev->gclk)) {
+ if (PTR_ERR(dev->gclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ dev_warn(&pdev->dev,
+ "generated clock not found: %d\n", err);
+ dev->gclk = NULL;
+ }
+
+ dev->dev = &pdev->dev;
+ dev->regmap = regmap;
+ platform_set_drvdata(pdev, dev);
+
+ err = clk_prepare_enable(dev->pclk);
+ if (err) {
+ dev_err(&pdev->dev,
+ "failed to enable the peripheral clock: %d\n", err);
+ return err;
+ }
+
+ err = devm_snd_soc_register_component(&pdev->dev,
+ &mchp_i2s_mcc_component,
+ &mchp_i2s_mcc_dai, 1);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ dev->playback.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_THR;
+ dev->capture.addr = (dma_addr_t)mem->start + MCHP_I2SMCC_RHR;
+
+ err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
+ clk_disable_unprepare(dev->pclk);
+ return err;
+ }
+
+ /* Get IP version. */
+ regmap_read(dev->regmap, MCHP_I2SMCC_VERSION, &version);
+ dev_info(&pdev->dev, "hw version: %#lx\n",
+ version & MCHP_I2SMCC_VERSION_MASK);
+
+ return 0;
+}
+
+static int mchp_i2s_mcc_remove(struct platform_device *pdev)
+{
+ struct mchp_i2s_mcc_dev *dev = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(dev->pclk);
+
+ return 0;
+}
+
+static struct platform_driver mchp_i2s_mcc_driver = {
+ .driver = {
+ .name = "mchp_i2s_mcc",
+ .of_match_table = of_match_ptr(mchp_i2s_mcc_dt_ids),
+ },
+ .probe = mchp_i2s_mcc_probe,
+ .remove = mchp_i2s_mcc_remove,
+};
+module_platform_driver(mchp_i2s_mcc_driver);
+
+MODULE_DESCRIPTION("Microchip I2S Multi-Channel Controller driver");
+MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 419114edfd57..9981d40ef45b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -94,6 +94,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_JZ4725B_CODEC
select SND_SOC_LM4857 if I2C
select SND_SOC_LM49453 if I2C
+ select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX98090 if I2C
select SND_SOC_MAX98095 if I2C
@@ -179,8 +180,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC31XX if I2C
- select SND_SOC_TLV320AIC32X4_I2C if I2C
- select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER
+ select SND_SOC_TLV320AIC32X4_I2C if I2C && COMMON_CLK
+ select SND_SOC_TLV320AIC32X4_SPI if SPI_MASTER && COMMON_CLK
select SND_SOC_TLV320AIC3X if I2C
select SND_SOC_TPA6130A2 if I2C
select SND_SOC_TLV320DAC33 if I2C
@@ -688,6 +689,13 @@ config SND_SOC_ISABELLE
config SND_SOC_LM49453
tristate
+config SND_SOC_LOCHNAGAR_SC
+ tristate "Lochnagar Sound Card"
+ depends on MFD_LOCHNAGAR
+ help
+ This driver support the sound card functionality of the Cirrus
+ Logic Lochnagar audio development board.
+
config SND_SOC_MAX98088
tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
depends on I2C
@@ -1097,15 +1105,18 @@ config SND_SOC_TLV320AIC31XX
config SND_SOC_TLV320AIC32X4
tristate
+ depends on COMMON_CLK
config SND_SOC_TLV320AIC32X4_I2C
tristate "Texas Instruments TLV320AIC32x4 audio CODECs - I2C"
depends on I2C
+ depends on COMMON_CLK
select SND_SOC_TLV320AIC32X4
config SND_SOC_TLV320AIC32X4_SPI
tristate "Texas Instruments TLV320AIC32x4 audio CODECs - SPI"
depends on SPI_MASTER
+ depends on COMMON_CLK
select SND_SOC_TLV320AIC32X4
config SND_SOC_TLV320AIC3X
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index aab2ad95a137..aa7720a7a0aa 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -91,6 +91,7 @@ snd-soc-jz4725b-codec-objs := jz4725b.o
snd-soc-l3-objs := l3.o
snd-soc-lm4857-objs := lm4857.o
snd-soc-lm49453-objs := lm49453.o
+snd-soc-lochnagar-sc-objs := lochnagar-sc.o
snd-soc-max9759-objs := max9759.o
snd-soc-max9768-objs := max9768.o
snd-soc-max98088-objs := max98088.o
@@ -192,7 +193,7 @@ snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
-snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
+snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o tlv320aic32x4-clk.o
snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o
snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
@@ -364,6 +365,7 @@ obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
+obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 03bbbcd3b6c1..87616b126018 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -2129,6 +2129,7 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
dev_err(dai->component->dev,
"%s: ERROR: The device is either a master or a slave.\n",
__func__);
+ /* fall through */
default:
dev_err(dai->component->dev,
"%s: ERROR: Unsupporter master mask 0x%x\n",
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index fd2bd74024c1..80da3cd73e04 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -464,6 +464,13 @@ static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg);
}
+static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ /* return dai id 0, whatever the endpoint index */
+ return 0;
+}
+
static const struct snd_soc_dai_ops cs42l51_dai_ops = {
.hw_params = cs42l51_hw_params,
.set_sysclk = cs42l51_set_dai_sysclk,
@@ -526,6 +533,7 @@ static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
.num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
.dapm_routes = cs42l51_routes,
.num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
+ .of_xlate_dai_id = cs42l51_of_xlate_dai_id,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index b16832a6a9af..eebbf02e1c39 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -75,7 +75,9 @@ static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w,
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
- return wm_adsp2_early_event(w, kcontrol, event, v);
+ wm_adsp2_set_dspclk(w, v);
+
+ return wm_adsp_early_event(w, kcontrol, event);
}
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index 121a8190f93e..5f5fa3416af3 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -797,6 +797,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
u8 pll_ctrl, pll_status;
int i = 0, ret;
bool srm_lock = false;
@@ -805,11 +806,11 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMU:
if (da7219->master) {
/* Enable DAI clks for master mode */
- if (da7219->dai_clks) {
- ret = clk_prepare_enable(da7219->dai_clks);
+ if (bclk) {
+ ret = clk_prepare_enable(bclk);
if (ret) {
dev_err(component->dev,
- "Failed to enable dai_clks\n");
+ "Failed to enable DAI clks\n");
return ret;
}
} else {
@@ -852,8 +853,8 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
/* Disable DAI clks if in master mode */
if (da7219->master) {
- if (da7219->dai_clks)
- clk_disable_unprepare(da7219->dai_clks);
+ if (bclk)
+ clk_disable_unprepare(bclk);
else
snd_soc_component_update_bits(component,
DA7219_DAI_CLK_MODE,
@@ -1385,17 +1386,50 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return 0;
}
+static int da7219_set_bclks_per_wclk(struct snd_soc_component *component,
+ unsigned long factor)
+{
+ u8 bclks_per_wclk;
+
+ switch (factor) {
+ case 32:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+ break;
+ case 64:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+ break;
+ case 128:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
+ break;
+ case 256:
+ bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
+ DA7219_DAI_BCLKS_PER_WCLK_MASK,
+ bclks_per_wclk);
+
+ return 0;
+}
+
static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width)
{
struct snd_soc_component *component = dai->component;
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+ struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
unsigned int ch_mask;
- u8 dai_bclks_per_wclk, slot_offset;
+ unsigned long sr, bclk_rate;
+ u8 slot_offset;
u16 offset;
__le16 dai_offset;
u32 frame_size;
+ int ret;
/* No channels enabled so disable TDM */
if (!tx_mask) {
@@ -1432,28 +1466,26 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
*/
if (da7219->master) {
frame_size = slots * slot_width;
- switch (frame_size) {
- case 32:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
- break;
- case 64:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
- break;
- case 128:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
- break;
- case 256:
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
- break;
- default:
- dev_err(component->dev, "Invalid frame size %d\n",
- frame_size);
- return -EINVAL;
- }
- snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
- DA7219_DAI_BCLKS_PER_WCLK_MASK,
- dai_bclks_per_wclk);
+ if (bclk) {
+ sr = clk_get_rate(wclk);
+ bclk_rate = sr * frame_size;
+ ret = clk_set_rate(bclk, bclk_rate);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set TDM BCLK rate %lu: %d\n",
+ bclk_rate, ret);
+ return ret;
+ }
+ } else {
+ ret = da7219_set_bclks_per_wclk(component, frame_size);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set TDM BCLKs per WCLK %d: %d\n",
+ frame_size, ret);
+ return ret;
+ }
+ }
}
dai_offset = cpu_to_le16(offset);
@@ -1471,44 +1503,12 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
-static int da7219_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int da7219_set_sr(struct snd_soc_component *component,
+ unsigned long rate)
{
- struct snd_soc_component *component = dai->component;
- struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
- u8 dai_ctrl = 0, dai_bclks_per_wclk = 0, fs;
- unsigned int channels;
- int word_len = params_width(params);
- int frame_size;
-
- switch (word_len) {
- case 16:
- dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
- break;
- case 20:
- dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
- break;
- case 24:
- dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
- break;
- case 32:
- dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
- break;
- default:
- return -EINVAL;
- }
-
- channels = params_channels(params);
- if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
- dev_err(component->dev,
- "Invalid number of channels, only 1 to %d supported\n",
- DA7219_DAI_CH_NUM_MAX);
- return -EINVAL;
- }
- dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+ u8 fs;
- switch (params_rate(params)) {
+ switch (rate) {
case 8000:
fs = DA7219_SR_8000;
break;
@@ -1546,28 +1546,103 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ snd_soc_component_write(component, DA7219_SR, fs);
+
+ return 0;
+}
+
+static int da7219_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX];
+ struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX];
+ u8 dai_ctrl = 0;
+ unsigned int channels;
+ unsigned long sr, bclk_rate;
+ int word_len = params_width(params);
+ int frame_size, ret;
+
+ switch (word_len) {
+ case 16:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
+ break;
+ case 20:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
+ break;
+ case 24:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
+ break;
+ case 32:
+ dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ channels = params_channels(params);
+ if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) {
+ dev_err(component->dev,
+ "Invalid number of channels, only 1 to %d supported\n",
+ DA7219_DAI_CH_NUM_MAX);
+ return -EINVAL;
+ }
+ dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+
+ sr = params_rate(params);
+ if (da7219->master && wclk) {
+ ret = clk_set_rate(wclk, sr);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set WCLK SR %lu: %d\n", sr, ret);
+ return ret;
+ }
+ } else {
+ ret = da7219_set_sr(component, sr);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set SR %lu: %d\n", sr, ret);
+ return ret;
+ }
+ }
+
/*
* If we're master, then we have a limited set of BCLK rates we
* support. For slave mode this isn't the case and the codec can detect
* the BCLK rate automatically.
*/
if (da7219->master && !da7219->tdm_en) {
- frame_size = word_len * 2;
- if (frame_size <= 32)
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+ if ((word_len * DA7219_DAI_CH_NUM_MAX) <= 32)
+ frame_size = 32;
else
- dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
-
- snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
- DA7219_DAI_BCLKS_PER_WCLK_MASK,
- dai_bclks_per_wclk);
+ frame_size = 64;
+
+ if (bclk) {
+ bclk_rate = frame_size * sr;
+ ret = clk_set_rate(bclk, bclk_rate);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set BCLK rate %lu: %d\n",
+ bclk_rate, ret);
+ return ret;
+ }
+ } else {
+ ret = da7219_set_bclks_per_wclk(component, frame_size);
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to set BCLKs per WCLK %d: %d\n",
+ frame_size, ret);
+ return ret;
+ }
+ }
}
snd_soc_component_update_bits(component, DA7219_DAI_CTRL,
DA7219_DAI_WORD_LENGTH_MASK |
DA7219_DAI_CH_NUM_MASK,
dai_ctrl);
- snd_soc_component_write(component, DA7219_SR, fs);
return 0;
}
@@ -1672,11 +1747,14 @@ static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_component *compone
pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source");
- pdata->dai_clks_name = "da7219-dai-clks";
- if (device_property_read_string(dev, "clock-output-names",
- &pdata->dai_clks_name))
- dev_warn(dev, "Using default clk name: %s\n",
- pdata->dai_clks_name);
+ pdata->dai_clk_names[DA7219_DAI_WCLK_IDX] = "da7219-dai-wclk";
+ pdata->dai_clk_names[DA7219_DAI_BCLK_IDX] = "da7219-dai-bclk";
+ if (device_property_read_string_array(dev, "clock-output-names",
+ pdata->dai_clk_names,
+ DA7219_DAI_NUM_CLKS) < 0)
+ dev_warn(dev, "Using default DAI clk names: %s, %s\n",
+ pdata->dai_clk_names[DA7219_DAI_WCLK_IDX],
+ pdata->dai_clk_names[DA7219_DAI_BCLK_IDX]);
if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
@@ -1793,12 +1871,16 @@ static int da7219_handle_supplies(struct snd_soc_component *component)
}
#ifdef CONFIG_COMMON_CLK
-static int da7219_dai_clks_prepare(struct clk_hw *hw)
+static int da7219_wclk_prepare(struct clk_hw *hw)
{
struct da7219_priv *da7219 =
- container_of(hw, struct da7219_priv, dai_clks_hw);
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
struct snd_soc_component *component = da7219->component;
+ if (!da7219->master)
+ return -EINVAL;
+
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
DA7219_DAI_CLK_EN_MASK,
DA7219_DAI_CLK_EN_MASK);
@@ -1806,36 +1888,48 @@ static int da7219_dai_clks_prepare(struct clk_hw *hw)
return 0;
}
-static void da7219_dai_clks_unprepare(struct clk_hw *hw)
+static void da7219_wclk_unprepare(struct clk_hw *hw)
{
struct da7219_priv *da7219 =
- container_of(hw, struct da7219_priv, dai_clks_hw);
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
struct snd_soc_component *component = da7219->component;
+ if (!da7219->master)
+ return;
+
snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE,
DA7219_DAI_CLK_EN_MASK, 0);
}
-static int da7219_dai_clks_is_prepared(struct clk_hw *hw)
+static int da7219_wclk_is_prepared(struct clk_hw *hw)
{
struct da7219_priv *da7219 =
- container_of(hw, struct da7219_priv, dai_clks_hw);
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
struct snd_soc_component *component = da7219->component;
u8 clk_reg;
+ if (!da7219->master)
+ return -EINVAL;
+
clk_reg = snd_soc_component_read32(component, DA7219_DAI_CLK_MODE);
return !!(clk_reg & DA7219_DAI_CLK_EN_MASK);
}
-static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
struct da7219_priv *da7219 =
- container_of(hw, struct da7219_priv, dai_clks_hw);
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
struct snd_soc_component *component = da7219->component;
u8 fs = snd_soc_component_read32(component, DA7219_SR);
+ if (!da7219->master)
+ return 0;
+
switch (fs & DA7219_SR_MASK) {
case DA7219_SR_8000:
return 8000;
@@ -1864,11 +1958,151 @@ static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw,
}
}
-static const struct clk_ops da7219_dai_clks_ops = {
- .prepare = da7219_dai_clks_prepare,
- .unprepare = da7219_dai_clks_unprepare,
- .is_prepared = da7219_dai_clks_is_prepared,
- .recalc_rate = da7219_dai_clks_recalc_rate,
+static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ if (rate < 11025)
+ return 8000;
+ else if (rate < 12000)
+ return 11025;
+ else if (rate < 16000)
+ return 12000;
+ else if (rate < 22050)
+ return 16000;
+ else if (rate < 24000)
+ return 22050;
+ else if (rate < 32000)
+ return 24000;
+ else if (rate < 44100)
+ return 32000;
+ else if (rate < 48000)
+ return 44100;
+ else if (rate < 88200)
+ return 48000;
+ else if (rate < 96000)
+ return 88200;
+ else
+ return 96000;
+}
+
+static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ return da7219_set_sr(component, rate);
+}
+
+static unsigned long da7219_bclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ u8 bclks_per_wclk = snd_soc_component_read32(component,
+ DA7219_DAI_CLK_MODE);
+
+ if (!da7219->master)
+ return 0;
+
+ switch (bclks_per_wclk & DA7219_DAI_BCLKS_PER_WCLK_MASK) {
+ case DA7219_DAI_BCLKS_PER_WCLK_32:
+ return parent_rate * 32;
+ case DA7219_DAI_BCLKS_PER_WCLK_64:
+ return parent_rate * 64;
+ case DA7219_DAI_BCLKS_PER_WCLK_128:
+ return parent_rate * 128;
+ case DA7219_DAI_BCLKS_PER_WCLK_256:
+ return parent_rate * 256;
+ default:
+ return 0;
+ }
+}
+
+static unsigned long da7219_bclk_get_factor(unsigned long rate,
+ unsigned long parent_rate)
+{
+ unsigned long factor;
+
+ factor = rate / parent_rate;
+ if (factor < 64)
+ return 32;
+ else if (factor < 128)
+ return 64;
+ else if (factor < 256)
+ return 128;
+ else
+ return 256;
+}
+
+static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!*parent_rate || !da7219->master)
+ return -EINVAL;
+
+ /*
+ * We don't allow changing the parent rate as some BCLK rates can be
+ * derived from multiple parent WCLK rates (BCLK rates are set as a
+ * multiplier of WCLK in HW). We just do some rounding down based on the
+ * parent WCLK rate set and find the appropriate multiplier of BCLK to
+ * get the rounded down BCLK value.
+ */
+ factor = da7219_bclk_get_factor(rate, *parent_rate);
+
+ return *parent_rate * factor;
+}
+
+static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ struct snd_soc_component *component = da7219->component;
+ unsigned long factor;
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ factor = da7219_bclk_get_factor(rate, parent_rate);
+
+ return da7219_set_bclks_per_wclk(component, factor);
+}
+
+static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
+ [DA7219_DAI_WCLK_IDX] = {
+ .prepare = da7219_wclk_prepare,
+ .unprepare = da7219_wclk_unprepare,
+ .is_prepared = da7219_wclk_is_prepared,
+ .recalc_rate = da7219_wclk_recalc_rate,
+ .round_rate = da7219_wclk_round_rate,
+ .set_rate = da7219_wclk_set_rate,
+ },
+ [DA7219_DAI_BCLK_IDX] = {
+ .recalc_rate = da7219_bclk_recalc_rate,
+ .round_rate = da7219_bclk_round_rate,
+ .set_rate = da7219_bclk_set_rate,
+ },
};
static int da7219_register_dai_clks(struct snd_soc_component *component)
@@ -1876,47 +2110,81 @@ static int da7219_register_dai_clks(struct snd_soc_component *component)
struct device *dev = component->dev;
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
struct da7219_pdata *pdata = da7219->pdata;
- struct clk_init_data init = {};
- struct clk *dai_clks;
- struct clk_lookup *dai_clks_lookup;
const char *parent_name;
+ int i, ret;
- if (da7219->mclk) {
- parent_name = __clk_get_name(da7219->mclk);
- init.parent_names = &parent_name;
- init.num_parents = 1;
- } else {
- init.parent_names = NULL;
- init.num_parents = 0;
- }
+ 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];
- init.name = pdata->dai_clks_name;
- init.ops = &da7219_dai_clks_ops;
- init.flags = CLK_GET_RATE_NOCACHE;
- da7219->dai_clks_hw.init = &init;
+ switch (i) {
+ case DA7219_DAI_WCLK_IDX:
+ /*
+ * If we can, make MCLK the parent of WCLK to ensure
+ * it's enabled as required.
+ */
+ if (da7219->mclk) {
+ parent_name = __clk_get_name(da7219->mclk);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ } else {
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ }
+ break;
+ case DA7219_DAI_BCLK_IDX:
+ /* Make WCLK the parent of BCLK */
+ parent_name = __clk_get_name(da7219->dai_clks[DA7219_DAI_WCLK_IDX]);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ break;
+ default:
+ dev_err(dev, "Invalid clock index\n");
+ ret = -EINVAL;
+ goto err;
+ }
- dai_clks = devm_clk_register(dev, &da7219->dai_clks_hw);
- if (IS_ERR(dai_clks)) {
- dev_warn(dev, "Failed to register DAI clocks: %ld\n",
- PTR_ERR(dai_clks));
- return PTR_ERR(dai_clks);
- }
- da7219->dai_clks = dai_clks;
+ init.name = pdata->dai_clk_names[i];
+ init.ops = &da7219_dai_clk_ops[i];
+ 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;
+ }
+ da7219->dai_clks[i] = dai_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,
- &da7219->dai_clks_hw);
- } else {
- dai_clks_lookup = clkdev_create(dai_clks, pdata->dai_clks_name,
- "%s", dev_name(dev));
- if (!dai_clks_lookup)
- return -ENOMEM;
- else
- da7219->dai_clks_lookup = dai_clks_lookup;
+ /* 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);
+ } else {
+ dai_clk_lookup = clkdev_create(dai_clk, init.name,
+ "%s", dev_name(dev));
+ if (!dai_clk_lookup) {
+ ret = -ENOMEM;
+ goto err;
+ } else {
+ da7219->dai_clks_lookup[i] = dai_clk_lookup;
+ }
+ }
}
return 0;
+
+err:
+ do {
+ if (da7219->dai_clks_lookup[i])
+ clkdev_drop(da7219->dai_clks_lookup[i]);
+ } while (i-- > 0);
+
+ return ret;
}
#else
static inline int da7219_register_dai_clks(struct snd_soc_component *component)
@@ -2080,12 +2348,15 @@ err_disable_reg:
static void da7219_remove(struct snd_soc_component *component)
{
struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component);
+ int i;
da7219_aad_exit(component);
#ifdef CONFIG_COMMON_CLK
- if (da7219->dai_clks_lookup)
- clkdev_drop(da7219->dai_clks_lookup);
+ 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 */
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
index 018819c631fb..f3b180bc986f 100644
--- a/sound/soc/codecs/da7219.h
+++ b/sound/soc/codecs/da7219.h
@@ -820,10 +820,10 @@ struct da7219_priv {
struct mutex pll_lock;
#ifdef CONFIG_COMMON_CLK
- struct clk_hw dai_clks_hw;
+ struct clk_hw dai_clks_hw[DA7219_DAI_NUM_CLKS];
#endif
- struct clk_lookup *dai_clks_lookup;
- struct clk *dai_clks;
+ struct clk_lookup *dai_clks_lookup[DA7219_DAI_NUM_CLKS];
+ struct clk *dai_clks[DA7219_DAI_NUM_CLKS];
struct clk *mclk;
unsigned int mclk_rate;
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index 6d4a323f786b..ec2770b3f77d 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -43,6 +43,7 @@ struct es8316_priv {
unsigned int sysclk;
unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS];
struct snd_pcm_hw_constraint_list sysclk_constraints;
+ bool jd_inverted;
};
/*
@@ -577,6 +578,9 @@ static irqreturn_t es8316_irq(int irq, void *data)
if (!es8316->jack)
goto out;
+ if (es8316->jd_inverted)
+ flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
+
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
/* Jack removed, or spurious IRQ? */
@@ -592,6 +596,8 @@ static irqreturn_t es8316_irq(int irq, void *data)
/* Jack inserted, determine type */
es8316_enable_micbias_for_mic_gnd_short_detect(comp);
regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags);
+ if (es8316->jd_inverted)
+ flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED;
dev_dbg(comp->dev, "gpio flags %#04x\n", flags);
if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) {
/* Jack unplugged underneath us */
@@ -633,6 +639,14 @@ static void es8316_enable_jack_detect(struct snd_soc_component *component,
{
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
+ /*
+ * Init es8316->jd_inverted here and not in the probe, as we cannot
+ * guarantee that the bytchr-es8316 driver, which might set this
+ * property, will probe before us.
+ */
+ es8316->jd_inverted = device_property_read_bool(component->dev,
+ "everest,jack-detect-inverted");
+
mutex_lock(&es8316->lock);
es8316->jack = jack;
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index ffecdaaa8cf2..f889d94c8e3c 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -38,6 +38,9 @@ static void hdac_hda_dai_close(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
+static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);
static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
@@ -50,6 +53,7 @@ static const struct snd_soc_dai_ops hdac_hda_dai_ops = {
.startup = hdac_hda_dai_open,
.shutdown = hdac_hda_dai_close,
.prepare = hdac_hda_dai_prepare,
+ .hw_params = hdac_hda_dai_hw_params,
.hw_free = hdac_hda_dai_hw_free,
.set_tdm_slot = hdac_hda_dai_set_tdm_slot,
};
@@ -139,6 +143,39 @@ static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
+static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct hdac_hda_priv *hda_pvt;
+ unsigned int format_val;
+ unsigned int maxbps;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ maxbps = dai->driver->playback.sig_bits;
+ else
+ maxbps = dai->driver->capture.sig_bits;
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ format_val = snd_hdac_calc_stream_format(params_rate(params),
+ params_channels(params),
+ params_format(params),
+ maxbps,
+ 0);
+ if (!format_val) {
+ dev_err(dai->dev,
+ "invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n",
+ params_rate(params), params_channels(params),
+ params_format(params), maxbps);
+
+ return -EINVAL;
+ }
+
+ hda_pvt->pcm[dai->id].format_val[substream->stream] = format_val;
+ return 0;
+}
+
static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -162,10 +199,9 @@ static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
+ struct hda_pcm_stream *hda_stream;
struct hdac_hda_priv *hda_pvt;
- struct snd_pcm_runtime *runtime = substream->runtime;
struct hdac_device *hdev;
- struct hda_pcm_stream *hda_stream;
unsigned int format_val;
struct hda_pcm *pcm;
unsigned int stream;
@@ -179,19 +215,8 @@ static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream,
hda_stream = &pcm->stream[substream->stream];
- format_val = snd_hdac_calc_stream_format(runtime->rate,
- runtime->channels,
- runtime->format,
- hda_stream->maxbps,
- 0);
- if (!format_val) {
- dev_err(&hdev->dev,
- "invalid format_val, rate=%d, ch=%d, format=%d\n",
- runtime->rate, runtime->channels, runtime->format);
- return -EINVAL;
- }
-
stream = hda_pvt->pcm[dai->id].stream_tag[substream->stream];
+ format_val = hda_pvt->pcm[dai->id].format_val[substream->stream];
ret = snd_hda_codec_prepare(&hda_pvt->codec, hda_stream,
stream, format_val, substream);
diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h
index e444ef593360..6b1bd4f428e7 100644
--- a/sound/soc/codecs/hdac_hda.h
+++ b/sound/soc/codecs/hdac_hda.h
@@ -8,6 +8,7 @@
struct hdac_hda_pcm {
int stream_tag[2];
+ unsigned int format_val[2];
};
struct hdac_hda_priv {
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index e5b6769b9797..35df73e42cbc 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -484,9 +484,6 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
params_width(params), params_rate(params),
params_channels(params));
- if (params_width(params) > 24)
- params->msbits = 24;
-
ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
sizeof(hp.iec.status));
if (ret < 0) {
@@ -529,73 +526,71 @@ static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
struct hdmi_codec_daifmt cf = { 0 };
- int ret = 0;
dev_dbg(dai->dev, "%s()\n", __func__);
- if (dai->id == DAI_ID_SPDIF) {
- cf.fmt = HDMI_SPDIF;
- } else {
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- cf.bit_clk_master = 1;
- cf.frame_clk_master = 1;
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- cf.frame_clk_master = 1;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- cf.bit_clk_master = 1;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- break;
- default:
- return -EINVAL;
- }
+ if (dai->id == DAI_ID_SPDIF)
+ return 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ cf.bit_clk_master = 1;
+ cf.frame_clk_master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ cf.frame_clk_master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ cf.bit_clk_master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_NB_IF:
- cf.frame_clk_inv = 1;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- cf.bit_clk_inv = 1;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- cf.frame_clk_inv = 1;
- cf.bit_clk_inv = 1;
- break;
- }
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ cf.frame_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cf.bit_clk_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cf.frame_clk_inv = 1;
+ cf.bit_clk_inv = 1;
+ break;
+ }
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- cf.fmt = HDMI_I2S;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- cf.fmt = HDMI_DSP_A;
- break;
- case SND_SOC_DAIFMT_DSP_B:
- cf.fmt = HDMI_DSP_B;
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- cf.fmt = HDMI_RIGHT_J;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- cf.fmt = HDMI_LEFT_J;
- break;
- case SND_SOC_DAIFMT_AC97:
- cf.fmt = HDMI_AC97;
- break;
- default:
- dev_err(dai->dev, "Invalid DAI interface format\n");
- return -EINVAL;
- }
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cf.fmt = HDMI_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ cf.fmt = HDMI_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ cf.fmt = HDMI_DSP_B;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ cf.fmt = HDMI_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cf.fmt = HDMI_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_AC97:
+ cf.fmt = HDMI_AC97;
+ break;
+ default:
+ dev_err(dai->dev, "Invalid DAI interface format\n");
+ return -EINVAL;
}
hcp->daifmt[dai->id] = cf;
- return ret;
+ return 0;
}
static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
@@ -792,8 +787,10 @@ static int hdmi_codec_probe(struct platform_device *pdev)
i++;
}
- if (hcd->spdif)
+ if (hcd->spdif) {
hcp->daidrv[i] = hdmi_spdif_dai;
+ hcp->daifmt[DAI_ID_SPDIF].fmt = HDMI_SPDIF;
+ }
dev_set_drvdata(dev, hcp);
diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c
new file mode 100644
index 000000000000..3209b39e46af
--- /dev/null
+++ b/sound/soc/codecs/lochnagar-sc.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Lochnagar sound card driver
+//
+// Copyright (c) 2017-2019 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+// Author: Charles Keepax <ckeepax@opensource.cirrus.com>
+// Piotr Stankiewicz <piotrs@opensource.cirrus.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include <linux/mfd/lochnagar.h>
+#include <linux/mfd/lochnagar1_regs.h>
+#include <linux/mfd/lochnagar2_regs.h>
+
+struct lochnagar_sc_priv {
+ struct clk *mclk;
+};
+
+static const struct snd_soc_dapm_widget lochnagar_sc_widgets[] = {
+ SND_SOC_DAPM_LINE("Line Jack", NULL),
+ SND_SOC_DAPM_LINE("USB Audio", NULL),
+};
+
+static const struct snd_soc_dapm_route lochnagar_sc_routes[] = {
+ { "Line Jack", NULL, "AIF1 Playback" },
+ { "AIF1 Capture", NULL, "Line Jack" },
+
+ { "USB Audio", NULL, "USB1 Playback" },
+ { "USB Audio", NULL, "USB2 Playback" },
+ { "USB1 Capture", NULL, "USB Audio" },
+ { "USB2 Capture", NULL, "USB Audio" },
+};
+
+static const unsigned int lochnagar_sc_chan_vals[] = {
+ 4, 8,
+};
+
+static const struct snd_pcm_hw_constraint_list lochnagar_sc_chan_constraint = {
+ .count = ARRAY_SIZE(lochnagar_sc_chan_vals),
+ .list = lochnagar_sc_chan_vals,
+};
+
+static const unsigned int lochnagar_sc_rate_vals[] = {
+ 8000, 16000, 24000, 32000, 48000, 96000, 192000,
+ 22050, 44100, 88200, 176400,
+};
+
+static const struct snd_pcm_hw_constraint_list lochnagar_sc_rate_constraint = {
+ .count = ARRAY_SIZE(lochnagar_sc_rate_vals),
+ .list = lochnagar_sc_rate_vals,
+};
+
+static int lochnagar_sc_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval range = {
+ .min = 8000,
+ .max = 24576000 / hw_param_interval(params, rule->deps[0])->max,
+ };
+
+ return snd_interval_refine(hw_param_interval(params, rule->var),
+ &range);
+}
+
+static int lochnagar_sc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &lochnagar_sc_rate_constraint);
+ if (ret)
+ return ret;
+
+ return snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ lochnagar_sc_hw_rule_rate, priv,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
+}
+
+static int lochnagar_sc_line_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+
+ ret = lochnagar_sc_startup(substream, dai);
+ if (ret)
+ return ret;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &lochnagar_sc_chan_constraint);
+}
+
+static void lochnagar_sc_line_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp);
+
+ clk_disable_unprepare(priv->mclk);
+}
+
+static int lochnagar_sc_check_fmt(struct snd_soc_dai *dai, unsigned int fmt,
+ unsigned int tar)
+{
+ tar |= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
+
+ if ((fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) != tar)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int lochnagar_sc_set_line_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBS_CFS);
+}
+
+static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBM_CFM);
+}
+
+static const struct snd_soc_dai_ops lochnagar_sc_line_ops = {
+ .startup = lochnagar_sc_line_startup,
+ .shutdown = lochnagar_sc_line_shutdown,
+ .set_fmt = lochnagar_sc_set_line_fmt,
+};
+
+static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = {
+ .startup = lochnagar_sc_startup,
+ .set_fmt = lochnagar_sc_set_usb_fmt,
+};
+
+static struct snd_soc_dai_driver lochnagar_sc_dai[] = {
+ {
+ .name = "lochnagar-line",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 4,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 4,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &lochnagar_sc_line_ops,
+ .symmetric_rates = true,
+ .symmetric_samplebits = true,
+ },
+ {
+ .name = "lochnagar-usb1",
+ .playback = {
+ .stream_name = "USB1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "USB1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &lochnagar_sc_usb_ops,
+ .symmetric_rates = true,
+ .symmetric_samplebits = true,
+ },
+ {
+ .name = "lochnagar-usb2",
+ .playback = {
+ .stream_name = "USB2 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "USB2 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &lochnagar_sc_usb_ops,
+ .symmetric_rates = true,
+ .symmetric_samplebits = true,
+ },
+};
+
+static const struct snd_soc_component_driver lochnagar_sc_driver = {
+ .non_legacy_dai_naming = 1,
+
+ .dapm_widgets = lochnagar_sc_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(lochnagar_sc_widgets),
+ .dapm_routes = lochnagar_sc_routes,
+ .num_dapm_routes = ARRAY_SIZE(lochnagar_sc_routes),
+};
+
+static int lochnagar_sc_probe(struct platform_device *pdev)
+{
+ struct lochnagar_sc_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ ret = PTR_ERR(priv->mclk);
+ dev_err(&pdev->dev, "Failed to get MCLK: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &lochnagar_sc_driver,
+ lochnagar_sc_dai,
+ ARRAY_SIZE(lochnagar_sc_dai));
+}
+
+static const struct of_device_id lochnagar_of_match[] = {
+ { .compatible = "cirrus,lochnagar2-soundcard" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lochnagar_of_match);
+
+static struct platform_driver lochnagar_sc_codec_driver = {
+ .driver = {
+ .name = "lochnagar-soundcard",
+ .of_match_table = of_match_ptr(lochnagar_of_match),
+ },
+
+ .probe = lochnagar_sc_probe,
+};
+module_platform_driver(lochnagar_sc_codec_driver);
+
+MODULE_DESCRIPTION("ASoC Lochnagar Sound Card Driver");
+MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lochnagar-soundcard");
diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c
index bfd74b86c9d2..dd82c65cfa7f 100644
--- a/sound/soc/codecs/nau8810.c
+++ b/sound/soc/codecs/nau8810.c
@@ -411,9 +411,9 @@ static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Mono Mixer", NAU8810_REG_POWER3,
NAU8810_MOUTMX_EN_SFT, 0, &nau8810_mono_mixer_controls[0],
ARRAY_SIZE(nau8810_mono_mixer_controls)),
- SND_SOC_DAPM_DAC("DAC", "HiFi Playback", NAU8810_REG_POWER3,
+ SND_SOC_DAPM_DAC("DAC", "Playback", NAU8810_REG_POWER3,
NAU8810_DAC_EN_SFT, 0),
- SND_SOC_DAPM_ADC("ADC", "HiFi Capture", NAU8810_REG_POWER2,
+ SND_SOC_DAPM_ADC("ADC", "Capture", NAU8810_REG_POWER2,
NAU8810_ADC_EN_SFT, 0),
SND_SOC_DAPM_PGA("SpkN Out", NAU8810_REG_POWER3,
NAU8810_NSPK_EN_SFT, 0, NULL, 0),
@@ -493,7 +493,7 @@ static int nau8810_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int nau88l0_calc_pll(unsigned int pll_in,
+static int nau8810_calc_pll(unsigned int pll_in,
unsigned int fs, struct nau8810_pll *pll_param)
{
u64 f2, f2_max, pll_ratio;
@@ -505,7 +505,8 @@ static int nau88l0_calc_pll(unsigned int pll_in,
f2_max = 0;
scal_sel = ARRAY_SIZE(nau8810_mclk_scaler);
for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
- f2 = 256 * fs * 4 * nau8810_mclk_scaler[i] / 10;
+ f2 = 256ULL * fs * 4 * nau8810_mclk_scaler[i];
+ f2 = div_u64(f2, 10);
if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX &&
f2_max < f2) {
f2_max = f2;
@@ -542,7 +543,7 @@ static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id,
int ret, fs;
fs = freq_out / 256;
- ret = nau88l0_calc_pll(freq_in, fs, pll_param);
+ ret = nau8810_calc_pll(freq_in, fs, pll_param);
if (ret < 0) {
dev_err(nau8810->dev, "Unsupported input clock %d\n", freq_in);
return ret;
@@ -667,6 +668,24 @@ static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component);
int val_len = 0, val_rate = 0, ret = 0;
+ unsigned int ctrl_val, bclk_fs, bclk_div;
+
+ /* Select BCLK configuration if the codec as master. */
+ regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &ctrl_val);
+ if (ctrl_val & NAU8810_CLKIO_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params);
+ if (bclk_fs <= 32)
+ bclk_div = NAU8810_BCLKDIV_8;
+ else if (bclk_fs <= 64)
+ bclk_div = NAU8810_BCLKDIV_4;
+ else if (bclk_fs <= 128)
+ bclk_div = NAU8810_BCLKDIV_2;
+ else
+ return -EINVAL;
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_BCLKSEL_MASK, bclk_div);
+ }
switch (params_width(params)) {
case 16:
diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c
index 87ed3dc496dc..5ab05e75edea 100644
--- a/sound/soc/codecs/nau8824.c
+++ b/sound/soc/codecs/nau8824.c
@@ -681,8 +681,8 @@ static const struct snd_soc_dapm_widget nau8824_dapm_widgets[] = {
SND_SOC_DAPM_ADC("ADCR", NULL, NAU8824_REG_ANALOG_ADC_2,
NAU8824_ADCR_EN_SFT, 0),
- SND_SOC_DAPM_AIF_OUT("AIFTX", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("AIFRX", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DACL", NULL, NAU8824_REG_RDAC,
NAU8824_DACL_EN_SFT, 0),
@@ -831,6 +831,36 @@ static void nau8824_int_status_clear_all(struct regmap *regmap)
}
}
+static void nau8824_dapm_disable_pin(struct nau8824 *nau8824, const char *pin)
+{
+ struct snd_soc_dapm_context *dapm = nau8824->dapm;
+ const char *prefix = dapm->component->name_prefix;
+ char prefixed_pin[80];
+
+ if (prefix) {
+ snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s",
+ prefix, pin);
+ snd_soc_dapm_disable_pin(dapm, prefixed_pin);
+ } else {
+ snd_soc_dapm_disable_pin(dapm, pin);
+ }
+}
+
+static void nau8824_dapm_enable_pin(struct nau8824 *nau8824, const char *pin)
+{
+ struct snd_soc_dapm_context *dapm = nau8824->dapm;
+ const char *prefix = dapm->component->name_prefix;
+ char prefixed_pin[80];
+
+ if (prefix) {
+ snprintf(prefixed_pin, sizeof(prefixed_pin), "%s %s",
+ prefix, pin);
+ snd_soc_dapm_force_enable_pin(dapm, prefixed_pin);
+ } else {
+ snd_soc_dapm_force_enable_pin(dapm, pin);
+ }
+}
+
static void nau8824_eject_jack(struct nau8824 *nau8824)
{
struct snd_soc_dapm_context *dapm = nau8824->dapm;
@@ -839,8 +869,8 @@ static void nau8824_eject_jack(struct nau8824 *nau8824)
/* Clear all interruption status */
nau8824_int_status_clear_all(regmap);
- snd_soc_dapm_disable_pin(dapm, "SAR");
- snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ nau8824_dapm_disable_pin(nau8824, "SAR");
+ nau8824_dapm_disable_pin(nau8824, "MICBIAS");
snd_soc_dapm_sync(dapm);
/* Enable the insertion interruption, disable the ejection
@@ -870,8 +900,8 @@ static void nau8824_jdet_work(struct work_struct *work)
struct regmap *regmap = nau8824->regmap;
int adc_value, event = 0, event_mask = 0;
- snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
- snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ nau8824_dapm_enable_pin(nau8824, "MICBIAS");
+ nau8824_dapm_enable_pin(nau8824, "SAR");
snd_soc_dapm_sync(dapm);
msleep(100);
@@ -882,8 +912,8 @@ static void nau8824_jdet_work(struct work_struct *work)
if (adc_value < HEADSET_SARADC_THD) {
event |= SND_JACK_HEADPHONE;
- snd_soc_dapm_disable_pin(dapm, "SAR");
- snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ nau8824_dapm_disable_pin(nau8824, "SAR");
+ nau8824_dapm_disable_pin(nau8824, "MICBIAS");
snd_soc_dapm_sync(dapm);
} else {
event |= SND_JACK_HEADSET;
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 29b2d60076b0..cb8252ff31cb 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1645,7 +1645,10 @@ static bool rt5651_jack_inserted(struct snd_soc_component *component)
break;
}
- return val == 0;
+ if (rt5651->jd_active_high)
+ return val != 0;
+ else
+ return val == 0;
}
/* Jack detect and button-press timings */
@@ -1868,20 +1871,47 @@ static void rt5651_enable_jack_detect(struct snd_soc_component *component,
case RT5651_JD1_1:
snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_1);
- snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
- RT5651_JD1_1_IRQ_EN, RT5651_JD1_1_IRQ_EN);
+ /* active-low is normal, set inv flag for active-high */
+ if (rt5651->jd_active_high)
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
+ RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV);
+ else
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV,
+ RT5651_JD1_1_IRQ_EN);
break;
case RT5651_JD1_2:
snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_2);
- snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
- RT5651_JD1_2_IRQ_EN, RT5651_JD1_2_IRQ_EN);
+ /* active-low is normal, set inv flag for active-high */
+ if (rt5651->jd_active_high)
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
+ RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV);
+ else
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV,
+ RT5651_JD1_2_IRQ_EN);
break;
case RT5651_JD2:
snd_soc_component_update_bits(component, RT5651_JD_CTRL2,
RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD2);
- snd_soc_component_update_bits(component, RT5651_IRQ_CTRL1,
- RT5651_JD2_IRQ_EN, RT5651_JD2_IRQ_EN);
+ /* active-low is normal, set inv flag for active-high */
+ if (rt5651->jd_active_high)
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
+ RT5651_JD2_IRQ_EN | RT5651_JD2_INV);
+ else
+ snd_soc_component_update_bits(component,
+ RT5651_IRQ_CTRL1,
+ RT5651_JD2_IRQ_EN | RT5651_JD2_INV,
+ RT5651_JD2_IRQ_EN);
break;
default:
dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n");
@@ -1986,6 +2016,9 @@ static void rt5651_apply_properties(struct snd_soc_component *component)
"realtek,jack-detect-source", &val) == 0)
rt5651->jd_src = val;
+ if (device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted"))
+ rt5651->jd_active_high = true;
+
/*
* Testing on various boards has shown that good defaults for the OVCD
* threshold and scale-factor are 2000µA and 0.75. For an effective
diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h
index 41fcb8b5eb40..05b0f6f8b95d 100644
--- a/sound/soc/codecs/rt5651.h
+++ b/sound/soc/codecs/rt5651.h
@@ -2083,6 +2083,7 @@ struct rt5651_priv {
int release_count;
int poll_count;
unsigned int jd_src;
+ bool jd_active_high;
unsigned int ovcd_th;
unsigned int ovcd_sf;
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
index 84501c2020c7..167a02773a0b 100644
--- a/sound/soc/codecs/rt5677-spi.c
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -25,6 +25,7 @@
#include <linux/sysfs.h>
#include <linux/clk.h>
#include <linux/firmware.h>
+#include <linux/acpi.h>
#include "rt5677-spi.h"
@@ -226,9 +227,16 @@ static int rt5677_spi_probe(struct spi_device *spi)
return 0;
}
+static const struct acpi_device_id rt5677_spi_acpi_id[] = {
+ { "RT5677AA", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id);
+
static struct spi_driver rt5677_spi_driver = {
.driver = {
.name = "rt5677",
+ .acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
},
.probe = rt5677_spi_probe,
};
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 9d5acd2d04ab..86a7fa31c294 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -910,13 +910,21 @@ static int rt5682_headset_detect(struct snd_soc_component *component,
int jack_insert)
{
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(component);
unsigned int val, count;
if (jack_insert) {
- snd_soc_dapm_force_enable_pin(dapm, "CBJ Power");
- snd_soc_dapm_sync(dapm);
+
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB);
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0);
+ usleep_range(15000, 20000);
+ snd_soc_component_update_bits(component,
+ RT5682_PWR_ANLG_1, RT5682_PWR_FV2, RT5682_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3,
+ RT5682_PWR_CBJ, RT5682_PWR_CBJ);
+
snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH);
@@ -944,8 +952,10 @@ static int rt5682_headset_detect(struct snd_soc_component *component,
rt5682_enable_push_button_irq(component, false);
snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1,
RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW);
- snd_soc_dapm_disable_pin(dapm, "CBJ Power");
- snd_soc_dapm_sync(dapm);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1,
+ RT5682_PWR_VREF2 | RT5682_PWR_MB, 0);
+ snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3,
+ RT5682_PWR_CBJ, 0);
rt5682->jack_type = 0;
}
@@ -1198,7 +1208,7 @@ static int set_filter_clk(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);
- int ref, val, reg, sft, mask, idx = -EINVAL;
+ int ref, val, reg, idx = -EINVAL;
static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48};
static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48};
@@ -1212,15 +1222,10 @@ static int set_filter_clk(struct snd_soc_dapm_widget *w,
idx = rt5682_div_sel(rt5682, ref, div_f, ARRAY_SIZE(div_f));
- if (w->shift == RT5682_PWR_ADC_S1F_BIT) {
+ if (w->shift == RT5682_PWR_ADC_S1F_BIT)
reg = RT5682_PLL_TRACK_3;
- sft = RT5682_ADC_OSR_SFT;
- mask = RT5682_ADC_OSR_MASK;
- } else {
+ else
reg = RT5682_PLL_TRACK_2;
- sft = RT5682_DAC_OSR_SFT;
- mask = RT5682_DAC_OSR_MASK;
- }
snd_soc_component_update_bits(component, reg,
RT5682_FILTER_CLK_DIV_MASK, idx << RT5682_FILTER_CLK_DIV_SFT);
@@ -1232,7 +1237,8 @@ static int set_filter_clk(struct snd_soc_dapm_widget *w,
}
snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1,
- mask, idx << sft);
+ RT5682_ADC_OSR_MASK | RT5682_DAC_OSR_MASK,
+ (idx << RT5682_ADC_OSR_SFT) | (idx << RT5682_DAC_OSR_SFT));
return 0;
}
@@ -1591,8 +1597,6 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = {
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Vref1", RT5682_PWR_ANLG_1, RT5682_PWR_VREF1_BIT, 0,
rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_SUPPLY("Vref2", RT5682_PWR_ANLG_1, RT5682_PWR_VREF2_BIT, 0,
- rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
/* ASRC */
SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682_PLL_TRACK_1,
@@ -1627,9 +1631,6 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = {
SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM,
0, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("CBJ Power", RT5682_PWR_ANLG_3,
- RT5682_PWR_CBJ_BIT, 0, NULL, 0),
-
/* REC Mixer */
SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682_rec1_l_mix,
ARRAY_SIZE(rt5682_rec1_l_mix)),
@@ -1792,17 +1793,13 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = {
/*Vref*/
{"MICBIAS1", NULL, "Vref1"},
- {"MICBIAS1", NULL, "Vref2"},
{"MICBIAS2", NULL, "Vref1"},
- {"MICBIAS2", NULL, "Vref2"},
{"CLKDET SYS", NULL, "CLKDET"},
{"IN1P", NULL, "LDO2"},
{"BST1 CBJ", NULL, "IN1P"},
- {"BST1 CBJ", NULL, "CBJ Power"},
- {"CBJ Power", NULL, "Vref2"},
{"RECMIX1L", "CBJ Switch", "BST1 CBJ"},
{"RECMIX1L", NULL, "RECMIX1L Power"},
@@ -1912,9 +1909,7 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = {
{"HP Amp", NULL, "Capless"},
{"HP Amp", NULL, "Charge Pump"},
{"HP Amp", NULL, "CLKDET SYS"},
- {"HP Amp", NULL, "CBJ Power"},
{"HP Amp", NULL, "Vref1"},
- {"HP Amp", NULL, "Vref2"},
{"HPOL Playback", "Switch", "HP Amp"},
{"HPOR Playback", "Switch", "HP Amp"},
{"HPOL", NULL, "HPOL Playback"},
@@ -2303,16 +2298,13 @@ static int rt5682_set_bias_level(struct snd_soc_component *component,
switch (level) {
case SND_SOC_BIAS_PREPARE:
regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
- RT5682_PWR_MB | RT5682_PWR_BG,
- RT5682_PWR_MB | RT5682_PWR_BG);
+ RT5682_PWR_BG, RT5682_PWR_BG);
regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1,
RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO,
RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO);
break;
case SND_SOC_BIAS_STANDBY:
- regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
- RT5682_PWR_MB, RT5682_PWR_MB);
regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1,
RT5682_DIG_GATE_CTRL, RT5682_DIG_GATE_CTRL);
break;
@@ -2320,7 +2312,7 @@ static int rt5682_set_bias_level(struct snd_soc_component *component,
regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1,
RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, 0);
regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1,
- RT5682_PWR_MB | RT5682_PWR_BG, 0);
+ RT5682_PWR_BG, 0);
break;
default:
@@ -2363,6 +2355,8 @@ static int rt5682_resume(struct snd_soc_component *component)
regcache_cache_only(rt5682->regmap, false);
regcache_sync(rt5682->regmap);
+ rt5682_irq(0, rt5682);
+
return 0;
}
#else
diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c
index c07e8a80b4b7..351aa55c384e 100644
--- a/sound/soc/codecs/simple-amplifier.c
+++ b/sound/soc/codecs/simple-amplifier.c
@@ -89,7 +89,8 @@ static int simple_amp_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
- priv->gpiod_enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_enable)) {
err = PTR_ERR(priv->gpiod_enable);
if (err != -EPROBE_DEFER)
diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c
index e424499a8450..e0af21050078 100644
--- a/sound/soc/codecs/sirf-audio-codec.c
+++ b/sound/soc/codecs/sirf-audio-codec.c
@@ -461,9 +461,6 @@ static int sirf_audio_codec_driver_probe(struct platform_device *pdev)
struct sirf_audio_codec *sirf_audio_codec;
void __iomem *base;
struct resource *mem_res;
- const struct of_device_id *match;
-
- match = of_match_node(sirf_audio_codec_of_match, pdev->dev.of_node);
sirf_audio_codec = devm_kzalloc(&pdev->dev,
sizeof(struct sirf_audio_codec), GFP_KERNEL);
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index c544a1e35f5e..fa47bfcb93e9 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -25,6 +25,7 @@
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -89,6 +90,7 @@ static bool aic31xx_volatile(struct device *dev, unsigned int reg)
case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
case AIC31XX_INTRDACFLAG2:
case AIC31XX_INTRADCFLAG2:
+ case AIC31XX_HSDETECT:
return true;
}
return false;
@@ -163,6 +165,7 @@ struct aic31xx_priv {
struct aic31xx_pdata pdata;
struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
+ struct snd_soc_jack *jack;
unsigned int sysclk;
u8 p_div;
int rate_div_line;
@@ -1261,6 +1264,20 @@ static int aic31xx_set_bias_level(struct snd_soc_component *component,
return 0;
}
+int aic31xx_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
+
+ aic31xx->jack = jack;
+
+ /* Enable/Disable jack detection */
+ regmap_write(aic31xx->regmap, AIC31XX_HSDETECT,
+ jack ? AIC31XX_HSD_ENABLE : 0);
+
+ return 0;
+}
+
static int aic31xx_codec_probe(struct snd_soc_component *component)
{
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
@@ -1301,6 +1318,7 @@ static int aic31xx_codec_probe(struct snd_soc_component *component)
static const struct snd_soc_component_driver soc_codec_driver_aic31xx = {
.probe = aic31xx_codec_probe,
+ .set_jack = aic31xx_set_jack,
.set_bias_level = aic31xx_set_bias_level,
.controls = common31xx_snd_controls,
.num_controls = ARRAY_SIZE(common31xx_snd_controls),
@@ -1405,8 +1423,47 @@ static irqreturn_t aic31xx_irq(int irq, void *data)
dev_err(dev, "Short circuit on Left output is detected\n");
if (value & AIC31XX_HPRSCDETECT)
dev_err(dev, "Short circuit on Right output is detected\n");
+ if (value & (AIC31XX_HSPLUG | AIC31XX_BUTTONPRESS)) {
+ unsigned int val;
+ int status = 0;
+
+ ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG2,
+ &val);
+ if (ret) {
+ dev_err(dev, "Failed to read interrupt mask: %d\n",
+ ret);
+ goto exit;
+ }
+
+ if (val & AIC31XX_BUTTONPRESS)
+ status |= SND_JACK_BTN_0;
+
+ ret = regmap_read(aic31xx->regmap, AIC31XX_HSDETECT, &val);
+ if (ret) {
+ dev_err(dev, "Failed to read headset type: %d\n", ret);
+ goto exit;
+ }
+
+ switch ((val & AIC31XX_HSD_TYPE_MASK) >>
+ AIC31XX_HSD_TYPE_SHIFT) {
+ case AIC31XX_HSD_HP:
+ status |= SND_JACK_HEADPHONE;
+ break;
+ case AIC31XX_HSD_HS:
+ status |= SND_JACK_HEADSET;
+ break;
+ default:
+ break;
+ }
+
+ if (aic31xx->jack)
+ snd_soc_jack_report(aic31xx->jack, status,
+ AIC31XX_JACK_MASK);
+ }
if (value & ~(AIC31XX_HPLSCDETECT |
- AIC31XX_HPRSCDETECT))
+ AIC31XX_HPRSCDETECT |
+ AIC31XX_HSPLUG |
+ AIC31XX_BUTTONPRESS))
dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value);
read_overflow:
@@ -1518,6 +1575,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
AIC31XX_GPIO1_FUNC_SHIFT);
regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL,
+ AIC31XX_HSPLUGDET |
+ AIC31XX_BUTTONPRESSDET |
AIC31XX_SC |
AIC31XX_ENGINE);
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index 2636f2c6bc79..cb024955c978 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -20,6 +20,10 @@
#define AIC31XX_MINIDSP_BIT BIT(2)
#define DAC31XX_BIT BIT(3)
+#define AIC31XX_JACK_MASK (SND_JACK_HEADPHONE | \
+ SND_JACK_HEADSET | \
+ SND_JACK_BTN_0)
+
enum aic31xx_type {
AIC3100 = 0,
AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
@@ -220,6 +224,14 @@ struct aic31xx_pdata {
/* AIC31XX_DACMUTE */
#define AIC31XX_DACMUTE_MASK GENMASK(3, 2)
+/* AIC31XX_HSDETECT */
+#define AIC31XX_HSD_ENABLE BIT(7)
+#define AIC31XX_HSD_TYPE_MASK GENMASK(6, 5)
+#define AIC31XX_HSD_TYPE_SHIFT 5
+#define AIC31XX_HSD_NONE 0x00
+#define AIC31XX_HSD_HP 0x01
+#define AIC31XX_HSD_HS 0x03
+
/* AIC31XX_MICBIAS */
#define AIC31XX_MICBIAS_MASK GENMASK(1, 0)
#define AIC31XX_MICBIAS_SHIFT 0
diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c
new file mode 100644
index 000000000000..667ec2c03508
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic32x4-clk.c
@@ -0,0 +1,483 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Clock Tree for the Texas Instruments TLV320AIC32x4
+ *
+ * Copyright 2019 Annaliese McDermond
+ *
+ * Author: Annaliese McDermond <nh6z@nh6z.net>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+
+#include "tlv320aic32x4.h"
+
+#define to_clk_aic32x4(_hw) container_of(_hw, struct clk_aic32x4, hw)
+struct clk_aic32x4 {
+ struct clk_hw hw;
+ struct device *dev;
+ struct regmap *regmap;
+ unsigned int reg;
+};
+
+/*
+ * struct clk_aic32x4_pll_muldiv - Multiplier/divider settings
+ * @p: Divider
+ * @r: first multiplier
+ * @j: integer part of second multiplier
+ * @d: decimal part of second multiplier
+ */
+struct clk_aic32x4_pll_muldiv {
+ u8 p;
+ u16 r;
+ u8 j;
+ u16 d;
+};
+
+struct aic32x4_clkdesc {
+ const char *name;
+ const char * const *parent_names;
+ unsigned int num_parents;
+ const struct clk_ops *ops;
+ unsigned int reg;
+};
+
+static int clk_aic32x4_pll_prepare(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
+ AIC32X4_PLLEN, AIC32X4_PLLEN);
+}
+
+static void clk_aic32x4_pll_unprepare(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+
+ regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
+ AIC32X4_PLLEN, 0);
+}
+
+static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
+ if (ret < 0)
+ return ret;
+
+ return !!(val & AIC32X4_PLLEN);
+}
+
+static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll,
+ struct clk_aic32x4_pll_muldiv *settings)
+{
+ /* Change to use regmap_bulk_read? */
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
+ if (ret)
+ return ret;
+ settings->r = val & AIC32X4_PLL_R_MASK;
+ settings->p = (val & AIC32X4_PLL_P_MASK) >> AIC32X4_PLL_P_SHIFT;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLJ, &val);
+ if (ret < 0)
+ return ret;
+ settings->j = val;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLDMSB, &val);
+ if (ret < 0)
+ return ret;
+ settings->d = val << 8;
+
+ ret = regmap_read(pll->regmap, AIC32X4_PLLDLSB, &val);
+ if (ret < 0)
+ return ret;
+ settings->d |= val;
+
+ return 0;
+}
+
+static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll,
+ struct clk_aic32x4_pll_muldiv *settings)
+{
+ int ret;
+ /* Change to use regmap_bulk_write for some if not all? */
+
+ ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
+ AIC32X4_PLL_R_MASK, settings->r);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR,
+ AIC32X4_PLL_P_MASK,
+ settings->p << AIC32X4_PLL_P_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(pll->regmap, AIC32X4_PLLJ, settings->j);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(pll->regmap, AIC32X4_PLLDMSB, (settings->d >> 8));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(pll->regmap, AIC32X4_PLLDLSB, (settings->d & 0xff));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static unsigned long clk_aic32x4_pll_calc_rate(
+ struct clk_aic32x4_pll_muldiv *settings,
+ unsigned long parent_rate)
+{
+ u64 rate;
+ /*
+ * We scale j by 10000 to account for the decimal part of P and divide
+ * it back out later.
+ */
+ rate = (u64) parent_rate * settings->r *
+ ((settings->j * 10000) + settings->d);
+
+ return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000);
+}
+
+static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings,
+ unsigned long rate, unsigned long parent_rate)
+{
+ u64 multiplier;
+
+ settings->p = parent_rate / AIC32X4_MAX_PLL_CLKIN + 1;
+ if (settings->p > 8)
+ return -1;
+
+ /*
+ * We scale this figure by 10000 so that we can get the decimal part
+ * of the multiplier. This is because we can't do floating point
+ * math in the kernel.
+ */
+ multiplier = (u64) rate * settings->p * 10000;
+ do_div(multiplier, parent_rate);
+
+ /*
+ * J can't be over 64, so R can scale this.
+ * R can't be greater than 4.
+ */
+ settings->r = ((u32) multiplier / 640000) + 1;
+ if (settings->r > 4)
+ return -1;
+ do_div(multiplier, settings->r);
+
+ /*
+ * J can't be < 1.
+ */
+ if (multiplier < 10000)
+ return -1;
+
+ /* Figure out the integer part, J, and the fractional part, D. */
+ settings->j = (u32) multiplier / 10000;
+ settings->d = (u32) multiplier % 10000;
+
+ return 0;
+}
+
+static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+ struct clk_aic32x4_pll_muldiv settings;
+ int ret;
+
+ ret = clk_aic32x4_pll_get_muldiv(pll, &settings);
+ if (ret < 0)
+ return 0;
+
+ return clk_aic32x4_pll_calc_rate(&settings, parent_rate);
+}
+
+static long clk_aic32x4_pll_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_aic32x4_pll_muldiv settings;
+ int ret;
+
+ ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, *parent_rate);
+ if (ret < 0)
+ return 0;
+
+ return clk_aic32x4_pll_calc_rate(&settings, *parent_rate);
+}
+
+static int clk_aic32x4_pll_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+ struct clk_aic32x4_pll_muldiv settings;
+ int ret;
+
+ ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, parent_rate);
+ if (ret < 0)
+ return -EINVAL;
+
+ return clk_aic32x4_pll_set_muldiv(pll, &settings);
+}
+
+static int clk_aic32x4_pll_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(pll->regmap,
+ AIC32X4_CLKMUX,
+ AIC32X4_PLL_CLKIN_MASK,
+ index << AIC32X4_PLL_CLKIN_SHIFT);
+}
+
+static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *pll = to_clk_aic32x4(hw);
+ unsigned int val;
+
+ regmap_read(pll->regmap, AIC32X4_PLLPR, &val);
+
+ return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT;
+}
+
+
+static const struct clk_ops aic32x4_pll_ops = {
+ .prepare = clk_aic32x4_pll_prepare,
+ .unprepare = clk_aic32x4_pll_unprepare,
+ .is_prepared = clk_aic32x4_pll_is_prepared,
+ .recalc_rate = clk_aic32x4_pll_recalc_rate,
+ .round_rate = clk_aic32x4_pll_round_rate,
+ .set_rate = clk_aic32x4_pll_set_rate,
+ .set_parent = clk_aic32x4_pll_set_parent,
+ .get_parent = clk_aic32x4_pll_get_parent,
+};
+
+static int clk_aic32x4_codec_clkin_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(mux->regmap,
+ AIC32X4_CLKMUX,
+ AIC32X4_CODEC_CLKIN_MASK, index << AIC32X4_CODEC_CLKIN_SHIFT);
+}
+
+static u8 clk_aic32x4_codec_clkin_get_parent(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
+ unsigned int val;
+
+ regmap_read(mux->regmap, AIC32X4_CLKMUX, &val);
+
+ return (val & AIC32X4_CODEC_CLKIN_MASK) >> AIC32X4_CODEC_CLKIN_SHIFT;
+}
+
+static const struct clk_ops aic32x4_codec_clkin_ops = {
+ .set_parent = clk_aic32x4_codec_clkin_set_parent,
+ .get_parent = clk_aic32x4_codec_clkin_get_parent,
+};
+
+static int clk_aic32x4_div_prepare(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *div = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(div->regmap, div->reg,
+ AIC32X4_DIVEN, AIC32X4_DIVEN);
+}
+
+static void clk_aic32x4_div_unprepare(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *div = to_clk_aic32x4(hw);
+
+ regmap_update_bits(div->regmap, div->reg,
+ AIC32X4_DIVEN, 0);
+}
+
+static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_aic32x4 *div = to_clk_aic32x4(hw);
+ u8 divisor;
+
+ divisor = DIV_ROUND_UP(parent_rate, rate);
+ if (divisor > 128)
+ return -EINVAL;
+
+ return regmap_update_bits(div->regmap, div->reg,
+ AIC32X4_DIV_MASK, divisor);
+}
+
+static long clk_aic32x4_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long divisor;
+
+ divisor = DIV_ROUND_UP(*parent_rate, rate);
+ if (divisor > 128)
+ return -EINVAL;
+
+ return DIV_ROUND_UP(*parent_rate, divisor);
+}
+
+static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_aic32x4 *div = to_clk_aic32x4(hw);
+
+ unsigned int val;
+
+ regmap_read(div->regmap, div->reg, &val);
+
+ return DIV_ROUND_UP(parent_rate, val & AIC32X4_DIV_MASK);
+}
+
+static const struct clk_ops aic32x4_div_ops = {
+ .prepare = clk_aic32x4_div_prepare,
+ .unprepare = clk_aic32x4_div_unprepare,
+ .set_rate = clk_aic32x4_div_set_rate,
+ .round_rate = clk_aic32x4_div_round_rate,
+ .recalc_rate = clk_aic32x4_div_recalc_rate,
+};
+
+static int clk_aic32x4_bdiv_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
+
+ return regmap_update_bits(mux->regmap, AIC32X4_IFACE3,
+ AIC32X4_BDIVCLK_MASK, index);
+}
+
+static u8 clk_aic32x4_bdiv_get_parent(struct clk_hw *hw)
+{
+ struct clk_aic32x4 *mux = to_clk_aic32x4(hw);
+ unsigned int val;
+
+ regmap_read(mux->regmap, AIC32X4_IFACE3, &val);
+
+ return val & AIC32X4_BDIVCLK_MASK;
+}
+
+static const struct clk_ops aic32x4_bdiv_ops = {
+ .prepare = clk_aic32x4_div_prepare,
+ .unprepare = clk_aic32x4_div_unprepare,
+ .set_parent = clk_aic32x4_bdiv_set_parent,
+ .get_parent = clk_aic32x4_bdiv_get_parent,
+ .set_rate = clk_aic32x4_div_set_rate,
+ .round_rate = clk_aic32x4_div_round_rate,
+ .recalc_rate = clk_aic32x4_div_recalc_rate,
+};
+
+static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = {
+ {
+ .name = "pll",
+ .parent_names =
+ (const char* []) { "mclk", "bclk", "gpio", "din" },
+ .num_parents = 4,
+ .ops = &aic32x4_pll_ops,
+ .reg = 0,
+ },
+ {
+ .name = "codec_clkin",
+ .parent_names =
+ (const char *[]) { "mclk", "bclk", "gpio", "pll" },
+ .num_parents = 4,
+ .ops = &aic32x4_codec_clkin_ops,
+ .reg = 0,
+ },
+ {
+ .name = "ndac",
+ .parent_names = (const char * []) { "codec_clkin" },
+ .num_parents = 1,
+ .ops = &aic32x4_div_ops,
+ .reg = AIC32X4_NDAC,
+ },
+ {
+ .name = "mdac",
+ .parent_names = (const char * []) { "ndac" },
+ .num_parents = 1,
+ .ops = &aic32x4_div_ops,
+ .reg = AIC32X4_MDAC,
+ },
+ {
+ .name = "nadc",
+ .parent_names = (const char * []) { "codec_clkin" },
+ .num_parents = 1,
+ .ops = &aic32x4_div_ops,
+ .reg = AIC32X4_NADC,
+ },
+ {
+ .name = "madc",
+ .parent_names = (const char * []) { "nadc" },
+ .num_parents = 1,
+ .ops = &aic32x4_div_ops,
+ .reg = AIC32X4_MADC,
+ },
+ {
+ .name = "bdiv",
+ .parent_names =
+ (const char *[]) { "ndac", "mdac", "nadc", "madc" },
+ .num_parents = 4,
+ .ops = &aic32x4_bdiv_ops,
+ .reg = AIC32X4_BCLKN,
+ },
+};
+
+static struct clk *aic32x4_register_clk(struct device *dev,
+ struct aic32x4_clkdesc *desc)
+{
+ struct clk_init_data init;
+ struct clk_aic32x4 *priv;
+ const char *devname = dev_name(dev);
+
+ init.ops = desc->ops;
+ init.name = desc->name;
+ init.parent_names = desc->parent_names;
+ init.num_parents = desc->num_parents;
+ init.flags = 0;
+
+ priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL);
+ if (priv == NULL)
+ return (struct clk *) -ENOMEM;
+
+ priv->dev = dev;
+ priv->hw.init = &init;
+ priv->regmap = dev_get_regmap(dev, NULL);
+ priv->reg = desc->reg;
+
+ clk_hw_register_clkdev(&priv->hw, desc->name, devname);
+ return devm_clk_register(dev, &priv->hw);
+}
+
+int aic32x4_register_clocks(struct device *dev, const char *mclk_name)
+{
+ int i;
+
+ /*
+ * These lines are here to preserve the current functionality of
+ * the driver with regard to the DT. These should eventually be set
+ * by DT nodes so that the connections can be set up in configuration
+ * rather than code.
+ */
+ aic32x4_clkdesc_array[0].parent_names =
+ (const char* []) { mclk_name, "bclk", "gpio", "din" };
+ aic32x4_clkdesc_array[1].parent_names =
+ (const char *[]) { mclk_name, "bclk", "gpio", "pll" };
+
+ for (i = 0; i < ARRAY_SIZE(aic32x4_clkdesc_array); ++i)
+ aic32x4_register_clk(dev, &aic32x4_clkdesc_array[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aic32x4_register_clocks);
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 96f1526cb258..75443efeda69 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -14,7 +14,7 @@
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
@@ -33,6 +33,7 @@
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/clk.h>
+#include <linux/of_clk.h>
#include <linux/regulator/consumer.h>
#include <sound/tlv320aic32x4.h>
@@ -46,29 +47,13 @@
#include "tlv320aic32x4.h"
-struct aic32x4_rate_divs {
- u32 mclk;
- u32 rate;
- u8 p_val;
- u8 pll_j;
- u16 pll_d;
- u16 dosr;
- u8 ndac;
- u8 mdac;
- u8 aosr;
- u8 nadc;
- u8 madc;
- u8 blck_N;
-};
-
struct aic32x4_priv {
struct regmap *regmap;
- u32 sysclk;
u32 power_cfg;
u32 micpga_routing;
bool swapdacs;
int rstn_gpio;
- struct clk *mclk;
+ const char *mclk_name;
struct regulator *supply_ldo;
struct regulator *supply_iov;
@@ -305,38 +290,6 @@ static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
0, 0x0F, 0),
};
-static const struct aic32x4_rate_divs aic32x4_divs[] = {
- /* 8k rate */
- {12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24},
- {24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24},
- {25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24},
- /* 11.025k rate */
- {12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16},
- {24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16},
- /* 16k rate */
- {12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12},
- {24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12},
- {25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12},
- /* 22.05k rate */
- {12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8},
- {24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8},
- {25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8},
- /* 32k rate */
- {12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6},
- {24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6},
- /* 44.1k rate */
- {12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4},
- {24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4},
- {25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4},
- /* 48k rate */
- {12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
- {24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4},
- {25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4},
-
- /* 96k rate */
- {25000000, 96000, 2, 7, 8643, 64, 4, 4, 64, 4, 4, 1},
-};
-
static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0),
SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0),
@@ -391,7 +344,7 @@ static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = {
SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum),
};
-/* Right mixer pins */
+/* Right mixer pins */
static SOC_ENUM_SINGLE_DECL(in1r_rpga_p_enum, AIC32X4_RMICPGAPIN, 6, resistor_text);
static SOC_ENUM_SINGLE_DECL(in2r_rpga_p_enum, AIC32X4_RMICPGAPIN, 4, resistor_text);
static SOC_ENUM_SINGLE_DECL(in3r_rpga_p_enum, AIC32X4_RMICPGAPIN, 2, resistor_text);
@@ -593,7 +546,7 @@ static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
static const struct regmap_range_cfg aic32x4_regmap_pages[] = {
{
.selector_reg = 0,
- .selector_mask = 0xff,
+ .selector_mask = 0xff,
.window_start = 0,
.window_len = 128,
.range_min = 0,
@@ -608,35 +561,17 @@ const struct regmap_config aic32x4_regmap_config = {
};
EXPORT_SYMBOL(aic32x4_regmap_config);
-static inline int aic32x4_get_divs(int mclk, int rate)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) {
- if ((aic32x4_divs[i].rate == rate)
- && (aic32x4_divs[i].mclk == mclk)) {
- return i;
- }
- }
- printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n");
- return -EINVAL;
-}
-
static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_component *component = codec_dai->component;
- struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
+ struct clk *mclk;
+ struct clk *pll;
- switch (freq) {
- case 12000000:
- case 24000000:
- case 25000000:
- aic32x4->sysclk = freq;
- return 0;
- }
- printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n");
- return -EINVAL;
+ pll = devm_clk_get(component->dev, "pll");
+ mclk = clk_get_parent(pll);
+
+ return clk_set_rate(mclk, freq);
}
static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
@@ -686,103 +621,179 @@ static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
snd_soc_component_update_bits(component, AIC32X4_IFACE1,
- AIC32X4_IFACE1_DATATYPE_MASK |
- AIC32X4_IFACE1_MASTER_MASK, iface_reg_1);
+ AIC32X4_IFACE1_DATATYPE_MASK |
+ AIC32X4_IFACE1_MASTER_MASK, iface_reg_1);
snd_soc_component_update_bits(component, AIC32X4_IFACE2,
- AIC32X4_DATA_OFFSET_MASK, iface_reg_2);
+ AIC32X4_DATA_OFFSET_MASK, iface_reg_2);
snd_soc_component_update_bits(component, AIC32X4_IFACE3,
- AIC32X4_BCLKINV_MASK, iface_reg_3);
+ AIC32X4_BCLKINV_MASK, iface_reg_3);
return 0;
}
-static int aic32x4_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr)
{
- struct snd_soc_component *component = dai->component;
- struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
- u8 iface1_reg = 0;
- u8 dacsetup_reg = 0;
- int i;
-
- i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params));
- if (i < 0) {
- printk(KERN_ERR "aic32x4: sampling rate not supported\n");
- return i;
- }
+ return snd_soc_component_write(component, AIC32X4_AOSR, aosr);
+}
- /* MCLK as PLL_CLKIN */
- snd_soc_component_update_bits(component, AIC32X4_CLKMUX, AIC32X4_PLL_CLKIN_MASK,
- AIC32X4_PLL_CLKIN_MCLK << AIC32X4_PLL_CLKIN_SHIFT);
- /* PLL as CODEC_CLKIN */
- snd_soc_component_update_bits(component, AIC32X4_CLKMUX, AIC32X4_CODEC_CLKIN_MASK,
- AIC32X4_CODEC_CLKIN_PLL << AIC32X4_CODEC_CLKIN_SHIFT);
- /* DAC_MOD_CLK as BDIV_CLKIN */
- snd_soc_component_update_bits(component, AIC32X4_IFACE3, AIC32X4_BDIVCLK_MASK,
- AIC32X4_DACMOD2BCLK << AIC32X4_BDIVCLK_SHIFT);
+static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr)
+{
+ snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8);
+ snd_soc_component_write(component, AIC32X4_DOSRLSB,
+ (dosr & 0xff));
- /* We will fix R value to 1 and will make P & J=K.D as variable */
- snd_soc_component_update_bits(component, AIC32X4_PLLPR, AIC32X4_PLL_R_MASK, 0x01);
+ return 0;
+}
- /* PLL P value */
- snd_soc_component_update_bits(component, AIC32X4_PLLPR, AIC32X4_PLL_P_MASK,
- aic32x4_divs[i].p_val << AIC32X4_PLL_P_SHIFT);
+static int aic32x4_set_processing_blocks(struct snd_soc_component *component,
+ u8 r_block, u8 p_block)
+{
+ if (r_block > 18 || p_block > 25)
+ return -EINVAL;
- /* PLL J value */
- snd_soc_component_write(component, AIC32X4_PLLJ, aic32x4_divs[i].pll_j);
+ snd_soc_component_write(component, AIC32X4_ADCSPB, r_block);
+ snd_soc_component_write(component, AIC32X4_DACSPB, p_block);
- /* PLL D value */
- snd_soc_component_write(component, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8));
- snd_soc_component_write(component, AIC32X4_PLLDLSB, (aic32x4_divs[i].pll_d & 0xff));
+ return 0;
+}
- /* NDAC divider value */
- snd_soc_component_update_bits(component, AIC32X4_NDAC,
- AIC32X4_NDAC_MASK, aic32x4_divs[i].ndac);
+static int aic32x4_setup_clocks(struct snd_soc_component *component,
+ unsigned int sample_rate)
+{
+ u8 aosr;
+ u16 dosr;
+ u8 adc_resource_class, dac_resource_class;
+ u8 madc, nadc, mdac, ndac, max_nadc, min_mdac, max_ndac;
+ u8 dosr_increment;
+ u16 max_dosr, min_dosr;
+ unsigned long mclk_rate, adc_clock_rate, dac_clock_rate;
+ int ret;
+ struct clk *mclk;
- /* MDAC divider value */
- snd_soc_component_update_bits(component, AIC32X4_MDAC,
- AIC32X4_MDAC_MASK, aic32x4_divs[i].mdac);
+ struct clk_bulk_data clocks[] = {
+ { .id = "pll" },
+ { .id = "nadc" },
+ { .id = "madc" },
+ { .id = "ndac" },
+ { .id = "mdac" },
+ { .id = "bdiv" },
+ };
+ ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
+ if (ret)
+ return ret;
- /* DOSR MSB & LSB values */
- snd_soc_component_write(component, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8);
- snd_soc_component_write(component, AIC32X4_DOSRLSB, (aic32x4_divs[i].dosr & 0xff));
+ mclk = clk_get_parent(clocks[1].clk);
+ mclk_rate = clk_get_rate(mclk);
+
+ if (sample_rate <= 48000) {
+ aosr = 128;
+ adc_resource_class = 6;
+ dac_resource_class = 8;
+ dosr_increment = 8;
+ aic32x4_set_processing_blocks(component, 1, 1);
+ } else if (sample_rate <= 96000) {
+ aosr = 64;
+ adc_resource_class = 6;
+ dac_resource_class = 8;
+ dosr_increment = 4;
+ aic32x4_set_processing_blocks(component, 1, 9);
+ } else if (sample_rate == 192000) {
+ aosr = 32;
+ adc_resource_class = 3;
+ dac_resource_class = 4;
+ dosr_increment = 2;
+ aic32x4_set_processing_blocks(component, 13, 19);
+ } else {
+ dev_err(component->dev, "Sampling rate not supported\n");
+ return -EINVAL;
+ }
- /* NADC divider value */
- snd_soc_component_update_bits(component, AIC32X4_NADC,
- AIC32X4_NADC_MASK, aic32x4_divs[i].nadc);
+ madc = DIV_ROUND_UP((32 * adc_resource_class), aosr);
+ max_dosr = (AIC32X4_MAX_DOSR_FREQ / sample_rate / dosr_increment) *
+ dosr_increment;
+ min_dosr = (AIC32X4_MIN_DOSR_FREQ / sample_rate / dosr_increment) *
+ dosr_increment;
+ max_nadc = AIC32X4_MAX_CODEC_CLKIN_FREQ / (madc * aosr * sample_rate);
+
+ for (nadc = max_nadc; nadc > 0; --nadc) {
+ adc_clock_rate = nadc * madc * aosr * sample_rate;
+ for (dosr = max_dosr; dosr >= min_dosr;
+ dosr -= dosr_increment) {
+ min_mdac = DIV_ROUND_UP((32 * dac_resource_class), dosr);
+ max_ndac = AIC32X4_MAX_CODEC_CLKIN_FREQ /
+ (min_mdac * dosr * sample_rate);
+ for (mdac = min_mdac; mdac <= 128; ++mdac) {
+ for (ndac = max_ndac; ndac > 0; --ndac) {
+ dac_clock_rate = ndac * mdac * dosr *
+ sample_rate;
+ if (dac_clock_rate == adc_clock_rate) {
+ if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0)
+ continue;
+
+ clk_set_rate(clocks[0].clk,
+ dac_clock_rate);
+
+ clk_set_rate(clocks[1].clk,
+ sample_rate * aosr *
+ madc);
+ clk_set_rate(clocks[2].clk,
+ sample_rate * aosr);
+ aic32x4_set_aosr(component,
+ aosr);
+
+ clk_set_rate(clocks[3].clk,
+ sample_rate * dosr *
+ mdac);
+ clk_set_rate(clocks[4].clk,
+ sample_rate * dosr);
+ aic32x4_set_dosr(component,
+ dosr);
+
+ clk_set_rate(clocks[5].clk,
+ sample_rate * 32);
+ return 0;
+ }
+ }
+ }
+ }
+ }
- /* MADC divider value */
- snd_soc_component_update_bits(component, AIC32X4_MADC,
- AIC32X4_MADC_MASK, aic32x4_divs[i].madc);
+ dev_err(component->dev,
+ "Could not set clocks to support sample rate.\n");
+ return -EINVAL;
+}
- /* AOSR value */
- snd_soc_component_write(component, AIC32X4_AOSR, aic32x4_divs[i].aosr);
+static int aic32x4_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
+ u8 iface1_reg = 0;
+ u8 dacsetup_reg = 0;
- /* BCLK N divider */
- snd_soc_component_update_bits(component, AIC32X4_BCLKN,
- AIC32X4_BCLK_MASK, aic32x4_divs[i].blck_N);
+ aic32x4_setup_clocks(component, params_rate(params));
switch (params_width(params)) {
case 16:
iface1_reg |= (AIC32X4_WORD_LEN_16BITS <<
- AIC32X4_IFACE1_DATALEN_SHIFT);
+ AIC32X4_IFACE1_DATALEN_SHIFT);
break;
case 20:
iface1_reg |= (AIC32X4_WORD_LEN_20BITS <<
- AIC32X4_IFACE1_DATALEN_SHIFT);
+ AIC32X4_IFACE1_DATALEN_SHIFT);
break;
case 24:
iface1_reg |= (AIC32X4_WORD_LEN_24BITS <<
- AIC32X4_IFACE1_DATALEN_SHIFT);
+ AIC32X4_IFACE1_DATALEN_SHIFT);
break;
case 32:
iface1_reg |= (AIC32X4_WORD_LEN_32BITS <<
- AIC32X4_IFACE1_DATALEN_SHIFT);
+ AIC32X4_IFACE1_DATALEN_SHIFT);
break;
}
snd_soc_component_update_bits(component, AIC32X4_IFACE1,
- AIC32X4_IFACE1_DATALEN_MASK, iface1_reg);
+ AIC32X4_IFACE1_DATALEN_MASK, iface1_reg);
if (params_channels(params) == 1) {
dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN;
@@ -793,7 +804,7 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream,
dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN;
}
snd_soc_component_update_bits(component, AIC32X4_DACSETUP,
- AIC32X4_DAC_CHAN_MASK, dacsetup_reg);
+ AIC32X4_DAC_CHAN_MASK, dacsetup_reg);
return 0;
}
@@ -803,7 +814,7 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
struct snd_soc_component *component = dai->component;
snd_soc_component_update_bits(component, AIC32X4_DACMUTE,
- AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0);
+ AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0);
return 0;
}
@@ -811,41 +822,25 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
static int aic32x4_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
int ret;
+ struct clk_bulk_data clocks[] = {
+ { .id = "madc" },
+ { .id = "mdac" },
+ { .id = "bdiv" },
+ };
+
+ ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
+ if (ret)
+ return ret;
+
switch (level) {
case SND_SOC_BIAS_ON:
- /* Switch on master clock */
- ret = clk_prepare_enable(aic32x4->mclk);
+ ret = clk_bulk_prepare_enable(ARRAY_SIZE(clocks), clocks);
if (ret) {
- dev_err(component->dev, "Failed to enable master clock\n");
+ dev_err(component->dev, "Failed to enable clocks\n");
return ret;
}
-
- /* Switch on PLL */
- snd_soc_component_update_bits(component, AIC32X4_PLLPR,
- AIC32X4_PLLEN, AIC32X4_PLLEN);
-
- /* Switch on NDAC Divider */
- snd_soc_component_update_bits(component, AIC32X4_NDAC,
- AIC32X4_NDACEN, AIC32X4_NDACEN);
-
- /* Switch on MDAC Divider */
- snd_soc_component_update_bits(component, AIC32X4_MDAC,
- AIC32X4_MDACEN, AIC32X4_MDACEN);
-
- /* Switch on NADC Divider */
- snd_soc_component_update_bits(component, AIC32X4_NADC,
- AIC32X4_NADCEN, AIC32X4_NADCEN);
-
- /* Switch on MADC Divider */
- snd_soc_component_update_bits(component, AIC32X4_MADC,
- AIC32X4_MADCEN, AIC32X4_MADCEN);
-
- /* Switch on BCLK_N Divider */
- snd_soc_component_update_bits(component, AIC32X4_BCLKN,
- AIC32X4_BCLKEN, AIC32X4_BCLKEN);
break;
case SND_SOC_BIAS_PREPARE:
break;
@@ -854,32 +849,7 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component,
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
break;
- /* Switch off BCLK_N Divider */
- snd_soc_component_update_bits(component, AIC32X4_BCLKN,
- AIC32X4_BCLKEN, 0);
-
- /* Switch off MADC Divider */
- snd_soc_component_update_bits(component, AIC32X4_MADC,
- AIC32X4_MADCEN, 0);
-
- /* Switch off NADC Divider */
- snd_soc_component_update_bits(component, AIC32X4_NADC,
- AIC32X4_NADCEN, 0);
-
- /* Switch off MDAC Divider */
- snd_soc_component_update_bits(component, AIC32X4_MDAC,
- AIC32X4_MDACEN, 0);
-
- /* Switch off NDAC Divider */
- snd_soc_component_update_bits(component, AIC32X4_NDAC,
- AIC32X4_NDACEN, 0);
-
- /* Switch off PLL */
- snd_soc_component_update_bits(component, AIC32X4_PLLPR,
- AIC32X4_PLLEN, 0);
-
- /* Switch off master clock */
- clk_disable_unprepare(aic32x4->mclk);
+ clk_bulk_disable_unprepare(ARRAY_SIZE(clocks), clocks);
break;
case SND_SOC_BIAS_OFF:
break;
@@ -887,8 +857,8 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component,
return 0;
}
-#define AIC32X4_RATES SNDRV_PCM_RATE_8000_96000
-#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+#define AIC32X4_RATES SNDRV_PCM_RATE_8000_192000
+#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
| SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops aic32x4_ops = {
@@ -901,17 +871,17 @@ static const struct snd_soc_dai_ops aic32x4_ops = {
static struct snd_soc_dai_driver aic32x4_dai = {
.name = "tlv320aic32x4-hifi",
.playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AIC32X4_RATES,
- .formats = AIC32X4_FORMATS,},
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC32X4_RATES,
+ .formats = AIC32X4_FORMATS,},
.capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AIC32X4_RATES,
- .formats = AIC32X4_FORMATS,},
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AIC32X4_RATES,
+ .formats = AIC32X4_FORMATS,},
.ops = &aic32x4_ops,
.symmetric_rates = 1,
};
@@ -924,7 +894,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP1 */
if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_DINCTL,
- aic32x4->setup->gpio_func[0]);
+ aic32x4->setup->gpio_func[0]);
snd_soc_add_component_controls(component, aic32x4_mfp1,
ARRAY_SIZE(aic32x4_mfp1));
}
@@ -932,7 +902,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP2 */
if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_DOUTCTL,
- aic32x4->setup->gpio_func[1]);
+ aic32x4->setup->gpio_func[1]);
snd_soc_add_component_controls(component, aic32x4_mfp2,
ARRAY_SIZE(aic32x4_mfp2));
}
@@ -940,7 +910,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP3 */
if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_SCLKCTL,
- aic32x4->setup->gpio_func[2]);
+ aic32x4->setup->gpio_func[2]);
snd_soc_add_component_controls(component, aic32x4_mfp3,
ARRAY_SIZE(aic32x4_mfp3));
}
@@ -948,7 +918,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP4 */
if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_MISOCTL,
- aic32x4->setup->gpio_func[3]);
+ aic32x4->setup->gpio_func[3]);
snd_soc_add_component_controls(component, aic32x4_mfp4,
ARRAY_SIZE(aic32x4_mfp4));
}
@@ -956,7 +926,7 @@ static void aic32x4_setup_gpios(struct snd_soc_component *component)
/* MFP5 */
if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) {
snd_soc_component_write(component, AIC32X4_GPIOCTL,
- aic32x4->setup->gpio_func[4]);
+ aic32x4->setup->gpio_func[4]);
snd_soc_add_component_controls(component, aic32x4_mfp5,
ARRAY_SIZE(aic32x4_mfp5));
}
@@ -966,6 +936,18 @@ static int aic32x4_component_probe(struct snd_soc_component *component)
{
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
u32 tmp_reg;
+ int ret;
+
+ struct clk_bulk_data clocks[] = {
+ { .id = "codec_clkin" },
+ { .id = "pll" },
+ { .id = "bdiv" },
+ { .id = "mdac" },
+ };
+
+ ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
+ if (ret)
+ return ret;
if (gpio_is_valid(aic32x4->rstn_gpio)) {
ndelay(10);
@@ -978,10 +960,13 @@ static int aic32x4_component_probe(struct snd_soc_component *component)
if (aic32x4->setup)
aic32x4_setup_gpios(component);
+ clk_set_parent(clocks[0].clk, clocks[1].clk);
+ clk_set_parent(clocks[2].clk, clocks[3].clk);
+
/* Power platform configuration */
if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
- snd_soc_component_write(component, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
- AIC32X4_MICBIAS_2075V);
+ snd_soc_component_write(component, AIC32X4_MICBIAS,
+ AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V);
}
if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE)
snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);
@@ -1044,12 +1029,18 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
struct device_node *np)
{
struct aic32x4_setup_data *aic32x4_setup;
+ int ret;
aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup),
GFP_KERNEL);
if (!aic32x4_setup)
return -ENOMEM;
+ ret = of_property_match_string(np, "clock-names", "mclk");
+ if (ret < 0)
+ return -EINVAL;
+ aic32x4->mclk_name = of_clk_get_parent_name(np, ret);
+
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
@@ -1171,7 +1162,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
return PTR_ERR(regmap);
aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv),
- GFP_KERNEL);
+ GFP_KERNEL);
if (aic32x4 == NULL)
return -ENOMEM;
@@ -1183,6 +1174,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
aic32x4->swapdacs = pdata->swapdacs;
aic32x4->micpga_routing = pdata->micpga_routing;
aic32x4->rstn_gpio = pdata->rstn_gpio;
+ aic32x4->mclk_name = "mclk";
} else if (np) {
ret = aic32x4_parse_dt(aic32x4, np);
if (ret) {
@@ -1194,13 +1186,12 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
aic32x4->rstn_gpio = -1;
+ aic32x4->mclk_name = "mclk";
}
- aic32x4->mclk = devm_clk_get(dev, "mclk");
- if (IS_ERR(aic32x4->mclk)) {
- dev_err(dev, "Failed getting the mclk. The current implementation does not support the usage of this codec without mclk\n");
- return PTR_ERR(aic32x4->mclk);
- }
+ ret = aic32x4_register_clocks(dev, aic32x4->mclk_name);
+ if (ret)
+ return ret;
if (gpio_is_valid(aic32x4->rstn_gpio)) {
ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio,
diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h
index c2d74025bf4b..88205bc97198 100644
--- a/sound/soc/codecs/tlv320aic32x4.h
+++ b/sound/soc/codecs/tlv320aic32x4.h
@@ -16,6 +16,7 @@ struct regmap_config;
extern const struct regmap_config aic32x4_regmap_config;
int aic32x4_probe(struct device *dev, struct regmap *regmap);
int aic32x4_remove(struct device *dev);
+int aic32x4_register_clocks(struct device *dev, const char *mclk_name);
/* tlv320aic32x4 register space (in decimal to match datasheet) */
@@ -205,4 +206,14 @@ int aic32x4_remove(struct device *dev);
#define AIC32X4_RMICPGANIN_IN1L_10K 0x10
#define AIC32X4_RMICPGANIN_CM1R_10K 0x40
+/* Common mask and enable for all of the dividers */
+#define AIC32X4_DIVEN BIT(7)
+#define AIC32X4_DIV_MASK GENMASK(6, 0)
+
+/* Clock Limits */
+#define AIC32X4_MAX_DOSR_FREQ 6200000
+#define AIC32X4_MIN_DOSR_FREQ 2800000
+#define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000
+#define AIC32X4_MAX_PLL_CLKIN 20000000
+
#endif /* _TLV320AIC32X4_H */
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 283583d1db60..516d17cb2182 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1609,7 +1609,6 @@ static int aic3x_probe(struct snd_soc_component *component)
struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
int ret, i;
- INIT_LIST_HEAD(&aic3x->list);
aic3x->component = component;
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) {
@@ -1873,6 +1872,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
goto err_gpio;
+ INIT_LIST_HEAD(&aic3x->list);
list_add(&aic3x->list, &reset_list);
return 0;
@@ -1889,6 +1889,8 @@ static int aic3x_i2c_remove(struct i2c_client *client)
{
struct aic3x_priv *aic3x = i2c_get_clientdata(client);
+ list_del(&aic3x->list);
+
if (gpio_is_valid(aic3x->gpio_reset) &&
!aic3x_is_shared_reset(aic3x)) {
gpio_set_value(aic3x->gpio_reset, 0);
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 981f88a5f615..a04a7cedd99d 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -5188,6 +5188,7 @@ static int wcd9335_slim_status(struct slim_device *sdev,
wcd->slim = sdev;
wcd->slim_ifc_dev = of_slim_get_device(sdev->ctrl, ifc_dev_np);
+ of_node_put(ifc_dev_np);
if (!wcd->slim_ifc_dev) {
dev_err(dev, "Unable to get SLIM Interface device\n");
return -EINVAL;
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 4466e195b66d..b32e8313954d 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -646,6 +646,8 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
return ret;
}
}
+
+ wm_adsp2_set_dspclk(w, v);
break;
case SND_SOC_DAPM_POST_PMD:
@@ -659,7 +661,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
break;
}
- return wm_adsp2_early_event(w, kcontrol, event, v);
+ return wm_adsp_early_event(w, kcontrol, event);
}
static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index b25877fa529d..1f500cc8d96a 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -211,7 +211,9 @@ static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w,
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
- return wm_adsp2_early_event(w, kcontrol, event, v);
+ wm_adsp2_set_dspclk(w, v);
+
+ return wm_adsp_early_event(w, kcontrol, event);
}
static const struct reg_sequence wm5110_no_dre_left_enable[] = {
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index b93fdc8d2d6f..0aa62b26f61c 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -227,6 +227,89 @@
*/
#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001
+/*
+ * HALO system info
+ */
+#define HALO_AHBM_WINDOW_DEBUG_0 0x02040
+#define HALO_AHBM_WINDOW_DEBUG_1 0x02044
+
+/*
+ * HALO core
+ */
+#define HALO_SCRATCH1 0x005c0
+#define HALO_SCRATCH2 0x005c8
+#define HALO_SCRATCH3 0x005d0
+#define HALO_SCRATCH4 0x005d8
+#define HALO_CCM_CORE_CONTROL 0x41000
+#define HALO_CORE_SOFT_RESET 0x00010
+#define HALO_WDT_CONTROL 0x47000
+
+/*
+ * HALO MPU banks
+ */
+#define HALO_MPU_XMEM_ACCESS_0 0x43000
+#define HALO_MPU_YMEM_ACCESS_0 0x43004
+#define HALO_MPU_WINDOW_ACCESS_0 0x43008
+#define HALO_MPU_XREG_ACCESS_0 0x4300C
+#define HALO_MPU_YREG_ACCESS_0 0x43014
+#define HALO_MPU_XMEM_ACCESS_1 0x43018
+#define HALO_MPU_YMEM_ACCESS_1 0x4301C
+#define HALO_MPU_WINDOW_ACCESS_1 0x43020
+#define HALO_MPU_XREG_ACCESS_1 0x43024
+#define HALO_MPU_YREG_ACCESS_1 0x4302C
+#define HALO_MPU_XMEM_ACCESS_2 0x43030
+#define HALO_MPU_YMEM_ACCESS_2 0x43034
+#define HALO_MPU_WINDOW_ACCESS_2 0x43038
+#define HALO_MPU_XREG_ACCESS_2 0x4303C
+#define HALO_MPU_YREG_ACCESS_2 0x43044
+#define HALO_MPU_XMEM_ACCESS_3 0x43048
+#define HALO_MPU_YMEM_ACCESS_3 0x4304C
+#define HALO_MPU_WINDOW_ACCESS_3 0x43050
+#define HALO_MPU_XREG_ACCESS_3 0x43054
+#define HALO_MPU_YREG_ACCESS_3 0x4305C
+#define HALO_MPU_XM_VIO_ADDR 0x43100
+#define HALO_MPU_XM_VIO_STATUS 0x43104
+#define HALO_MPU_YM_VIO_ADDR 0x43108
+#define HALO_MPU_YM_VIO_STATUS 0x4310C
+#define HALO_MPU_PM_VIO_ADDR 0x43110
+#define HALO_MPU_PM_VIO_STATUS 0x43114
+#define HALO_MPU_LOCK_CONFIG 0x43140
+
+/*
+ * HALO_AHBM_WINDOW_DEBUG_1
+ */
+#define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00
+#define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8
+#define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff
+
+/*
+ * HALO_CCM_CORE_CONTROL
+ */
+#define HALO_CORE_EN 0x00000001
+
+/*
+ * HALO_CORE_SOFT_RESET
+ */
+#define HALO_CORE_SOFT_RESET_MASK 0x00000001
+
+/*
+ * HALO_WDT_CONTROL
+ */
+#define HALO_WDT_EN_MASK 0x00000001
+
+/*
+ * HALO_MPU_?M_VIO_STATUS
+ */
+#define HALO_MPU_VIO_STS_MASK 0x007e0000
+#define HALO_MPU_VIO_STS_SHIFT 17
+#define HALO_MPU_VIO_ERR_WR_MASK 0x00008000
+#define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff
+#define HALO_MPU_VIO_ERR_SRC_SHIFT 0
+
+static struct wm_adsp_ops wm_adsp1_ops;
+static struct wm_adsp_ops wm_adsp2_ops[];
+static struct wm_adsp_ops wm_halo_ops;
+
struct wm_adsp_buf {
struct list_head list;
void *buf;
@@ -306,6 +389,12 @@ struct wm_adsp_system_config_xm_hdr {
__be32 build_job_number;
};
+struct wm_halo_system_config_xm_hdr {
+ __be32 halo_heartbeat;
+ __be32 build_job_name[3];
+ __be32 build_job_number;
+};
+
struct wm_adsp_alg_xm_struct {
__be32 magic;
__be32 smoothing;
@@ -532,12 +621,18 @@ static const char *wm_adsp_mem_region_name(unsigned int type)
switch (type) {
case WMFW_ADSP1_PM:
return "PM";
+ case WMFW_HALO_PM_PACKED:
+ return "PM_PACKED";
case WMFW_ADSP1_DM:
return "DM";
case WMFW_ADSP2_XM:
return "XM";
+ case WMFW_HALO_XM_PACKED:
+ return "XM_PACKED";
case WMFW_ADSP2_YM:
return "YM";
+ case WMFW_HALO_YM_PACKED:
+ return "YM_PACKED";
case WMFW_ADSP1_ZM:
return "ZM";
default:
@@ -769,17 +864,12 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
unsigned int offset)
{
- if (WARN_ON(!mem))
- return offset;
switch (mem->type) {
case WMFW_ADSP1_PM:
return mem->base + (offset * 3);
case WMFW_ADSP1_DM:
- return mem->base + (offset * 2);
case WMFW_ADSP2_XM:
- return mem->base + (offset * 2);
case WMFW_ADSP2_YM:
- return mem->base + (offset * 2);
case WMFW_ADSP1_ZM:
return mem->base + (offset * 2);
default:
@@ -788,49 +878,72 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
}
}
-static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
+static unsigned int wm_halo_region_to_reg(struct wm_adsp_region const *mem,
+ unsigned int offset)
+{
+ switch (mem->type) {
+ case WMFW_ADSP2_XM:
+ case WMFW_ADSP2_YM:
+ return mem->base + (offset * 4);
+ case WMFW_HALO_XM_PACKED:
+ case WMFW_HALO_YM_PACKED:
+ return (mem->base + (offset * 3)) & ~0x3;
+ case WMFW_HALO_PM_PACKED:
+ return mem->base + (offset * 5);
+ default:
+ WARN(1, "Unknown memory region type");
+ return offset;
+ }
+}
+
+static void wm_adsp_read_fw_status(struct wm_adsp *dsp,
+ int noffs, unsigned int *offs)
{
- unsigned int scratch[4];
- unsigned int addr = dsp->base + ADSP2_SCRATCH0;
unsigned int i;
int ret;
- for (i = 0; i < ARRAY_SIZE(scratch); ++i) {
- ret = regmap_read(dsp->regmap, addr + i, &scratch[i]);
+ for (i = 0; i < noffs; ++i) {
+ ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]);
if (ret) {
adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret);
return;
}
}
+}
+
+static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
+{
+ unsigned int offs[] = {
+ ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3,
+ };
+
+ wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs);
adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
- scratch[0], scratch[1], scratch[2], scratch[3]);
+ offs[0], offs[1], offs[2], offs[3]);
}
static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp)
{
- unsigned int scratch[2];
- int ret;
+ unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 };
- ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1,
- &scratch[0]);
- if (ret) {
- adsp_err(dsp, "Failed to read SCRATCH0_1: %d\n", ret);
- return;
- }
+ wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs);
- ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH2_3,
- &scratch[1]);
- if (ret) {
- adsp_err(dsp, "Failed to read SCRATCH2_3: %d\n", ret);
- return;
- }
+ adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
+ offs[0] & 0xFFFF, offs[0] >> 16,
+ offs[1] & 0xFFFF, offs[1] >> 16);
+}
+
+static void wm_halo_show_fw_status(struct wm_adsp *dsp)
+{
+ unsigned int offs[] = {
+ HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4,
+ };
+
+ wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs);
adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
- scratch[0] & 0xFFFF,
- scratch[0] >> 16,
- scratch[1] & 0xFFFF,
- scratch[1] >> 16);
+ offs[0], offs[1], offs[2], offs[3]);
}
static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
@@ -851,7 +964,7 @@ static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg)
return -EINVAL;
}
- *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset);
+ *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset);
return 0;
}
@@ -1339,28 +1452,33 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
case 1:
snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x",
dsp->name, region_name, alg_region->alg);
+ subname = NULL; /* don't append subname */
break;
- default:
+ case 2:
ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
"%s%c %.12s %x", dsp->name, *region_name,
wm_adsp_fw_text[dsp->fw], alg_region->alg);
+ break;
+ default:
+ ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "%s %.12s %x", dsp->name,
+ wm_adsp_fw_text[dsp->fw], alg_region->alg);
+ break;
+ }
- /* Truncate the subname from the start if it is too long */
- if (subname) {
- int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
- int skip = 0;
+ if (subname) {
+ int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
+ int skip = 0;
- if (dsp->component->name_prefix)
- avail -= strlen(dsp->component->name_prefix) + 1;
+ if (dsp->component->name_prefix)
+ avail -= strlen(dsp->component->name_prefix) + 1;
- if (subname_len > avail)
- skip = subname_len - avail;
+ /* Truncate the subname from the start if it is too long */
+ if (subname_len > avail)
+ skip = subname_len - avail;
- snprintf(name + ret,
- SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
- subname_len - skip, subname + skip);
- }
- break;
+ snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
+ " %.*s", subname_len - skip, subname + skip);
}
list_for_each_entry(ctl, &dsp->ctl_list, list) {
@@ -1647,6 +1765,62 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
return 0;
}
+static unsigned int wm_adsp1_parse_sizes(struct wm_adsp *dsp,
+ const char * const file,
+ unsigned int pos,
+ const struct firmware *firmware)
+{
+ const struct wmfw_adsp1_sizes *adsp1_sizes;
+
+ adsp1_sizes = (void *)&firmware->data[pos];
+
+ adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file,
+ le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm),
+ le32_to_cpu(adsp1_sizes->zm));
+
+ return pos + sizeof(*adsp1_sizes);
+}
+
+static unsigned int wm_adsp2_parse_sizes(struct wm_adsp *dsp,
+ const char * const file,
+ unsigned int pos,
+ const struct firmware *firmware)
+{
+ const struct wmfw_adsp2_sizes *adsp2_sizes;
+
+ adsp2_sizes = (void *)&firmware->data[pos];
+
+ adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file,
+ le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym),
+ le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm));
+
+ return pos + sizeof(*adsp2_sizes);
+}
+
+static bool wm_adsp_validate_version(struct wm_adsp *dsp, unsigned int version)
+{
+ switch (version) {
+ case 0:
+ adsp_warn(dsp, "Deprecated file format %d\n", version);
+ return true;
+ case 1:
+ case 2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool wm_halo_validate_version(struct wm_adsp *dsp, unsigned int version)
+{
+ switch (version) {
+ case 3:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int wm_adsp_load(struct wm_adsp *dsp)
{
LIST_HEAD(buf_list);
@@ -1655,7 +1829,6 @@ static int wm_adsp_load(struct wm_adsp *dsp)
unsigned int pos = 0;
const struct wmfw_header *header;
const struct wmfw_adsp1_sizes *adsp1_sizes;
- const struct wmfw_adsp2_sizes *adsp2_sizes;
const struct wmfw_footer *footer;
const struct wmfw_region *region;
const struct wm_adsp_region *mem;
@@ -1664,7 +1837,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
struct wm_adsp_buf *buf;
unsigned int reg;
int regions = 0;
- int ret, offset, type, sizes;
+ int ret, offset, type;
file = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (file == NULL)
@@ -1695,15 +1868,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
goto out_fw;
}
- switch (header->ver) {
- case 0:
- adsp_warn(dsp, "%s: Depreciated file format %d\n",
- file, header->ver);
- break;
- case 1:
- case 2:
- break;
- default:
+ if (!dsp->ops->validate_version(dsp, header->ver)) {
adsp_err(dsp, "%s: unknown file format %d\n",
file, header->ver);
goto out_fw;
@@ -1718,39 +1883,13 @@ static int wm_adsp_load(struct wm_adsp *dsp)
goto out_fw;
}
- switch (dsp->type) {
- case WMFW_ADSP1:
- pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
- adsp1_sizes = (void *)&(header[1]);
- footer = (void *)&(adsp1_sizes[1]);
- sizes = sizeof(*adsp1_sizes);
-
- adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
- file, le32_to_cpu(adsp1_sizes->dm),
- le32_to_cpu(adsp1_sizes->pm),
- le32_to_cpu(adsp1_sizes->zm));
- break;
-
- case WMFW_ADSP2:
- pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
- adsp2_sizes = (void *)&(header[1]);
- footer = (void *)&(adsp2_sizes[1]);
- sizes = sizeof(*adsp2_sizes);
-
- adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
- file, le32_to_cpu(adsp2_sizes->xm),
- le32_to_cpu(adsp2_sizes->ym),
- le32_to_cpu(adsp2_sizes->pm),
- le32_to_cpu(adsp2_sizes->zm));
- break;
+ pos = sizeof(*header);
+ pos = dsp->ops->parse_sizes(dsp, file, pos, firmware);
- default:
- WARN(1, "Unknown DSP type");
- goto out_fw;
- }
+ footer = (void *)&firmware->data[pos];
+ pos += sizeof(*footer);
- if (le32_to_cpu(header->len) != sizeof(*header) +
- sizes + sizeof(*footer)) {
+ if (le32_to_cpu(header->len) != pos) {
adsp_err(dsp, "%s: unexpected header length %d\n",
file, le32_to_cpu(header->len));
goto out_fw;
@@ -1767,7 +1906,6 @@ static int wm_adsp_load(struct wm_adsp *dsp)
text = NULL;
offset = le32_to_cpu(region->offset) & 0xffffff;
type = be32_to_cpu(region->type) & 0xff;
- mem = wm_adsp_find_region(dsp, type);
switch (type) {
case WMFW_NAME_TEXT:
@@ -1795,8 +1933,17 @@ static int wm_adsp_load(struct wm_adsp *dsp)
case WMFW_ADSP2_XM:
case WMFW_ADSP2_YM:
case WMFW_ADSP1_ZM:
+ case WMFW_HALO_PM_PACKED:
+ case WMFW_HALO_XM_PACKED:
+ case WMFW_HALO_YM_PACKED:
+ mem = wm_adsp_find_region(dsp, type);
+ if (!mem) {
+ adsp_err(dsp, "No region of type: %x\n", type);
+ goto out_fw;
+ }
+
region_name = wm_adsp_mem_region_name(type);
- reg = wm_adsp_region_to_reg(mem, offset);
+ reg = dsp->ops->region_to_reg(mem, offset);
break;
default:
adsp_warn(dsp,
@@ -1909,7 +2056,7 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
}
/* Read the terminator first to validate the length */
- reg = wm_adsp_region_to_reg(mem, pos + len);
+ reg = dsp->ops->region_to_reg(mem, pos + len);
ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
if (ret != 0) {
@@ -1929,7 +2076,7 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
if (!alg)
return ERR_PTR(-ENOMEM);
- reg = wm_adsp_region_to_reg(mem, pos);
+ reg = dsp->ops->region_to_reg(mem, pos);
ret = regmap_raw_read(dsp->regmap, reg, alg, len);
if (ret != 0) {
@@ -1989,6 +2136,47 @@ static void wm_adsp_free_alg_regions(struct wm_adsp *dsp)
}
}
+static void wmfw_parse_id_header(struct wm_adsp *dsp,
+ struct wmfw_id_hdr *fw, int nalgs)
+{
+ dsp->fw_id = be32_to_cpu(fw->id);
+ dsp->fw_id_version = be32_to_cpu(fw->ver);
+
+ adsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n",
+ dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16,
+ (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff,
+ nalgs);
+}
+
+static void wmfw_v3_parse_id_header(struct wm_adsp *dsp,
+ struct wmfw_v3_id_hdr *fw, int nalgs)
+{
+ dsp->fw_id = be32_to_cpu(fw->id);
+ dsp->fw_id_version = be32_to_cpu(fw->ver);
+ dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id);
+
+ adsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n",
+ dsp->fw_id, dsp->fw_vendor_id,
+ (dsp->fw_id_version & 0xff0000) >> 16,
+ (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff,
+ nalgs);
+}
+
+static int wm_adsp_create_regions(struct wm_adsp *dsp, __be32 id, int nregions,
+ int *type, __be32 *base)
+{
+ struct wm_adsp_alg_region *alg_region;
+ int i;
+
+ for (i = 0; i < nregions; i++) {
+ alg_region = wm_adsp_create_region(dsp, type[i], id, base[i]);
+ if (IS_ERR(alg_region))
+ return PTR_ERR(alg_region);
+ }
+
+ return 0;
+}
+
static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
{
struct wmfw_adsp1_id_hdr adsp1_id;
@@ -2012,13 +2200,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
}
n_algs = be32_to_cpu(adsp1_id.n_algs);
- dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
- adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
- dsp->fw_id,
- (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
- (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
- be32_to_cpu(adsp1_id.fw.ver) & 0xff,
- n_algs);
+
+ wmfw_parse_id_header(dsp, &adsp1_id.fw, n_algs);
alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
adsp1_id.fw.id, adsp1_id.zm);
@@ -2118,14 +2301,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
}
n_algs = be32_to_cpu(adsp2_id.n_algs);
- dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
- dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver);
- adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
- dsp->fw_id,
- (dsp->fw_id_version & 0xff0000) >> 16,
- (dsp->fw_id_version & 0xff00) >> 8,
- dsp->fw_id_version & 0xff,
- n_algs);
+
+ wmfw_parse_id_header(dsp, &adsp2_id.fw, n_algs);
alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
adsp2_id.fw.id, adsp2_id.xm);
@@ -2230,6 +2407,78 @@ out:
return ret;
}
+static int wm_halo_create_regions(struct wm_adsp *dsp, __be32 id,
+ __be32 xm_base, __be32 ym_base)
+{
+ int types[] = {
+ WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED,
+ WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED
+ };
+ __be32 bases[] = { xm_base, xm_base, ym_base, ym_base };
+
+ return wm_adsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases);
+}
+
+static int wm_halo_setup_algs(struct wm_adsp *dsp)
+{
+ struct wmfw_halo_id_hdr halo_id;
+ struct wmfw_halo_alg_hdr *halo_alg;
+ const struct wm_adsp_region *mem;
+ unsigned int pos, len;
+ size_t n_algs;
+ int i, ret;
+
+ mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
+ if (WARN_ON(!mem))
+ return -EINVAL;
+
+ ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id,
+ sizeof(halo_id));
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm info: %d\n",
+ ret);
+ return ret;
+ }
+
+ n_algs = be32_to_cpu(halo_id.n_algs);
+
+ wmfw_v3_parse_id_header(dsp, &halo_id.fw, n_algs);
+
+ ret = wm_halo_create_regions(dsp, halo_id.fw.id,
+ halo_id.ym_base, halo_id.ym_base);
+ if (ret)
+ return ret;
+
+ /* Calculate offset and length in DSP words */
+ pos = sizeof(halo_id) / sizeof(u32);
+ len = (sizeof(*halo_alg) * n_algs) / sizeof(u32);
+
+ halo_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
+ if (IS_ERR(halo_alg))
+ return PTR_ERR(halo_alg);
+
+ for (i = 0; i < n_algs; i++) {
+ adsp_info(dsp,
+ "%d: ID %x v%d.%d.%d XM@%x YM@%x\n",
+ i, be32_to_cpu(halo_alg[i].alg.id),
+ (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8,
+ be32_to_cpu(halo_alg[i].alg.ver) & 0xff,
+ be32_to_cpu(halo_alg[i].xm_base),
+ be32_to_cpu(halo_alg[i].ym_base));
+
+ ret = wm_halo_create_regions(dsp, halo_alg[i].alg.id,
+ halo_alg[i].xm_base,
+ halo_alg[i].ym_base);
+ if (ret)
+ goto out;
+ }
+
+out:
+ kfree(halo_alg);
+ return ret;
+}
+
static int wm_adsp_load_coeff(struct wm_adsp *dsp)
{
LIST_HEAD(buf_list);
@@ -2324,7 +2573,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
adsp_err(dsp, "No ZM\n");
break;
}
- reg = wm_adsp_region_to_reg(mem, 0);
+ reg = dsp->ops->region_to_reg(mem, 0);
} else {
region_name = "register";
@@ -2336,6 +2585,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
case WMFW_ADSP1_ZM:
case WMFW_ADSP2_XM:
case WMFW_ADSP2_YM:
+ case WMFW_HALO_XM_PACKED:
+ case WMFW_HALO_YM_PACKED:
+ case WMFW_HALO_PM_PACKED:
adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
file, blocks, le32_to_cpu(blk->len),
type, le32_to_cpu(blk->id));
@@ -2350,7 +2602,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
le32_to_cpu(blk->id));
if (alg_region) {
reg = alg_region->base;
- reg = wm_adsp_region_to_reg(mem, reg);
+ reg = dsp->ops->region_to_reg(mem, reg);
reg += offset;
} else {
adsp_err(dsp, "No %x for algorithm %x\n",
@@ -2464,6 +2716,8 @@ static int wm_adsp_common_init(struct wm_adsp *dsp)
int wm_adsp1_init(struct wm_adsp *dsp)
{
+ dsp->ops = &wm_adsp1_ops;
+
return wm_adsp_common_init(dsp);
}
EXPORT_SYMBOL_GPL(wm_adsp1_init);
@@ -2583,23 +2837,11 @@ err_mutex:
}
EXPORT_SYMBOL_GPL(wm_adsp1_event);
-static int wm_adsp2_ena(struct wm_adsp *dsp)
+static int wm_adsp2v2_enable_core(struct wm_adsp *dsp)
{
unsigned int val;
int ret, count;
- switch (dsp->rev) {
- case 0:
- ret = regmap_update_bits_async(dsp->regmap,
- dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, ADSP2_SYS_ENA);
- if (ret != 0)
- return ret;
- break;
- default:
- break;
- }
-
/* Wait for the RAM to start, should be near instantaneous */
for (count = 0; count < 10; ++count) {
ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);
@@ -2622,7 +2864,78 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
return 0;
}
-static void wm_adsp2_boot_work(struct work_struct *work)
+static int wm_adsp2_enable_core(struct wm_adsp *dsp)
+{
+ int ret;
+
+ ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+ if (ret != 0)
+ return ret;
+
+ return wm_adsp2v2_enable_core(dsp);
+}
+
+static int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions)
+{
+ struct regmap *regmap = dsp->regmap;
+ unsigned int code0, code1, lock_reg;
+
+ if (!(lock_regions & WM_ADSP2_REGION_ALL))
+ return 0;
+
+ lock_regions &= WM_ADSP2_REGION_ALL;
+ lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0;
+
+ while (lock_regions) {
+ code0 = code1 = 0;
+ if (lock_regions & BIT(0)) {
+ code0 = ADSP2_LOCK_CODE_0;
+ code1 = ADSP2_LOCK_CODE_1;
+ }
+ if (lock_regions & BIT(1)) {
+ code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT;
+ code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT;
+ }
+ regmap_write(regmap, lock_reg, code0);
+ regmap_write(regmap, lock_reg, code1);
+ lock_regions >>= 2;
+ lock_reg += 2;
+ }
+
+ return 0;
+}
+
+static int wm_adsp2_enable_memory(struct wm_adsp *dsp)
+{
+ return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_MEM_ENA, ADSP2_MEM_ENA);
+}
+
+static void wm_adsp2_disable_memory(struct wm_adsp *dsp)
+{
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_MEM_ENA, 0);
+}
+
+static void wm_adsp2_disable_core(struct wm_adsp *dsp)
+{
+ regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, 0);
+}
+
+static void wm_adsp2v2_disable_core(struct wm_adsp *dsp)
+{
+ regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);
+}
+
+static void wm_adsp_boot_work(struct work_struct *work)
{
struct wm_adsp *dsp = container_of(work,
struct wm_adsp,
@@ -2631,20 +2944,23 @@ static void wm_adsp2_boot_work(struct work_struct *work)
mutex_lock(&dsp->pwr_lock);
- ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_MEM_ENA, ADSP2_MEM_ENA);
- if (ret != 0)
- goto err_mutex;
+ if (dsp->ops->enable_memory) {
+ ret = dsp->ops->enable_memory(dsp);
+ if (ret != 0)
+ goto err_mutex;
+ }
- ret = wm_adsp2_ena(dsp);
- if (ret != 0)
- goto err_mem;
+ if (dsp->ops->enable_core) {
+ ret = dsp->ops->enable_core(dsp);
+ if (ret != 0)
+ goto err_mem;
+ }
ret = wm_adsp_load(dsp);
if (ret != 0)
goto err_ena;
- ret = wm_adsp2_setup_algs(dsp);
+ ret = dsp->ops->setup_algs(dsp);
if (ret != 0)
goto err_ena;
@@ -2657,17 +2973,8 @@ static void wm_adsp2_boot_work(struct work_struct *work)
if (ret != 0)
goto err_ena;
- switch (dsp->rev) {
- case 0:
- /* Turn DSP back off until we are ready to run */
- ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, 0);
- if (ret != 0)
- goto err_ena;
- break;
- default:
- break;
- }
+ if (dsp->ops->disable_core)
+ dsp->ops->disable_core(dsp);
dsp->booted = true;
@@ -2676,35 +2983,62 @@ static void wm_adsp2_boot_work(struct work_struct *work)
return;
err_ena:
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+ if (dsp->ops->disable_core)
+ dsp->ops->disable_core(dsp);
err_mem:
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_MEM_ENA, 0);
+ if (dsp->ops->disable_memory)
+ dsp->ops->disable_memory(dsp);
err_mutex:
mutex_unlock(&dsp->pwr_lock);
}
-static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
+static int wm_halo_configure_mpu(struct wm_adsp *dsp, unsigned int lock_regions)
+{
+ struct reg_sequence config[] = {
+ { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 },
+ { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA },
+ { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF },
+ { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF },
+ { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions },
+ { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions },
+ { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions },
+ { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF },
+ { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF },
+ { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions },
+ { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions },
+ { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions },
+ { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF },
+ { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF },
+ { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions },
+ { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions },
+ { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions },
+ { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF },
+ { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF },
+ { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions },
+ { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions },
+ { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions },
+ { dsp->base + HALO_MPU_LOCK_CONFIG, 0 },
+ };
+
+ return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config));
+}
+
+int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq)
{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
+ struct wm_adsp *dsp = &dsps[w->shift];
int ret;
- switch (dsp->rev) {
- case 0:
- ret = regmap_update_bits_async(dsp->regmap,
- dsp->base + ADSP2_CLOCKING,
- ADSP2_CLK_SEL_MASK,
- freq << ADSP2_CLK_SEL_SHIFT);
- if (ret) {
- adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
- return;
- }
- break;
- default:
- /* clock is handled by parent codec driver */
- break;
- }
+ ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING,
+ ADSP2_CLK_SEL_MASK,
+ freq << ADSP2_CLK_SEL_SHIFT);
+ if (ret)
+ adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+
+ return ret;
}
+EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk);
int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -2751,19 +3085,18 @@ EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
static void wm_adsp_stop_watchdog(struct wm_adsp *dsp)
{
- switch (dsp->rev) {
- case 0:
- case 1:
- return;
- default:
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG,
- ADSP2_WDT_ENA_MASK, 0);
- }
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG,
+ ADSP2_WDT_ENA_MASK, 0);
}
-int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event,
- unsigned int freq)
+static void wm_halo_stop_watchdog(struct wm_adsp *dsp)
+{
+ regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL,
+ HALO_WDT_EN_MASK, 0);
+}
+
+int wm_adsp_early_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 wm_adsp *dsps = snd_soc_component_get_drvdata(component);
@@ -2772,7 +3105,6 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- wm_adsp2_set_dspclk(dsp, freq);
queue_work(system_unbound_wq, &dsp->boot_work);
break;
case SND_SOC_DAPM_PRE_PMD:
@@ -2785,8 +3117,8 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
dsp->booted = false;
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_MEM_ENA, 0);
+ if (dsp->ops->disable_memory)
+ dsp->ops->disable_memory(dsp);
list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0;
@@ -2803,10 +3135,23 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
return 0;
}
-EXPORT_SYMBOL_GPL(wm_adsp2_early_event);
+EXPORT_SYMBOL_GPL(wm_adsp_early_event);
+
+static int wm_adsp2_start_core(struct wm_adsp *dsp)
+{
+ return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_CORE_ENA | ADSP2_START,
+ ADSP2_CORE_ENA | ADSP2_START);
+}
+
+static void wm_adsp2_stop_core(struct wm_adsp *dsp)
+{
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_CORE_ENA | ADSP2_START, 0);
+}
-int wm_adsp2_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+int wm_adsp_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 wm_adsp *dsps = snd_soc_component_get_drvdata(component);
@@ -2824,23 +3169,31 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
goto err;
}
- ret = wm_adsp2_ena(dsp);
- if (ret != 0)
- goto err;
+ if (dsp->ops->enable_core) {
+ ret = dsp->ops->enable_core(dsp);
+ if (ret != 0)
+ goto err;
+ }
/* Sync set controls */
ret = wm_coeff_sync_controls(dsp);
if (ret != 0)
goto err;
- wm_adsp2_lock(dsp, dsp->lock_regions);
+ if (dsp->ops->lock_memory) {
+ ret = dsp->ops->lock_memory(dsp, dsp->lock_regions);
+ if (ret != 0) {
+ adsp_err(dsp, "Error configuring MPU: %d\n",
+ ret);
+ goto err;
+ }
+ }
- ret = regmap_update_bits(dsp->regmap,
- dsp->base + ADSP2_CONTROL,
- ADSP2_CORE_ENA | ADSP2_START,
- ADSP2_CORE_ENA | ADSP2_START);
- if (ret != 0)
- goto err;
+ if (dsp->ops->start_core) {
+ ret = dsp->ops->start_core(dsp);
+ if (ret != 0)
+ goto err;
+ }
if (wm_adsp_fw[dsp->fw].num_caps != 0) {
ret = wm_adsp_buffer_init(dsp);
@@ -2851,60 +3204,33 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
dsp->running = true;
mutex_unlock(&dsp->pwr_lock);
-
break;
case SND_SOC_DAPM_PRE_PMD:
/* Tell the firmware to cleanup */
wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
- wm_adsp_stop_watchdog(dsp);
+ if (dsp->ops->stop_watchdog)
+ dsp->ops->stop_watchdog(dsp);
/* Log firmware state, it can be useful for analysis */
- switch (dsp->rev) {
- case 0:
- wm_adsp2_show_fw_status(dsp);
- break;
- default:
- wm_adsp2v2_show_fw_status(dsp);
- break;
- }
+ if (dsp->ops->show_fw_status)
+ dsp->ops->show_fw_status(dsp);
mutex_lock(&dsp->pwr_lock);
dsp->running = false;
- regmap_update_bits(dsp->regmap,
- dsp->base + ADSP2_CONTROL,
- ADSP2_CORE_ENA | ADSP2_START, 0);
-
- /* Make sure DMAs are quiesced */
- switch (dsp->rev) {
- case 0:
- regmap_write(dsp->regmap,
- dsp->base + ADSP2_RDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap,
- dsp->base + ADSP2_WDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap,
- dsp->base + ADSP2_WDMA_CONFIG_2, 0);
-
- regmap_update_bits(dsp->regmap,
- dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, 0);
- break;
- default:
- regmap_write(dsp->regmap,
- dsp->base + ADSP2_RDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap,
- dsp->base + ADSP2_WDMA_CONFIG_1, 0);
- regmap_write(dsp->regmap,
- dsp->base + ADSP2V2_WDMA_CONFIG_2, 0);
- break;
- }
+ if (dsp->ops->stop_core)
+ dsp->ops->stop_core(dsp);
+ if (dsp->ops->disable_core)
+ dsp->ops->disable_core(dsp);
if (wm_adsp_fw[dsp->fw].num_caps != 0)
wm_adsp_buffer_free(dsp);
+ dsp->fatal_error = false;
+
mutex_unlock(&dsp->pwr_lock);
adsp_dbg(dsp, "Execution stopped\n");
@@ -2916,12 +3242,31 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
return 0;
err:
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+ if (dsp->ops->stop_core)
+ dsp->ops->stop_core(dsp);
+ if (dsp->ops->disable_core)
+ dsp->ops->disable_core(dsp);
mutex_unlock(&dsp->pwr_lock);
return ret;
}
-EXPORT_SYMBOL_GPL(wm_adsp2_event);
+EXPORT_SYMBOL_GPL(wm_adsp_event);
+
+static int wm_halo_start_core(struct wm_adsp *dsp)
+{
+ return regmap_update_bits(dsp->regmap,
+ dsp->base + HALO_CCM_CORE_CONTROL,
+ HALO_CORE_EN, HALO_CORE_EN);
+}
+
+static void wm_halo_stop_core(struct wm_adsp *dsp)
+{
+ regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL,
+ HALO_CORE_EN, 0);
+
+ /* reset halo core with CORE_SOFT_RESET */
+ regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET,
+ HALO_CORE_SOFT_RESET_MASK, 1);
+}
int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component)
{
@@ -2967,17 +3312,39 @@ int wm_adsp2_init(struct wm_adsp *dsp)
"Failed to clear memory retention: %d\n", ret);
return ret;
}
+
+ dsp->ops = &wm_adsp2_ops[0];
+ break;
+ case 1:
+ dsp->ops = &wm_adsp2_ops[1];
break;
default:
+ dsp->ops = &wm_adsp2_ops[2];
break;
}
- INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
+ INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);
+int wm_halo_init(struct wm_adsp *dsp)
+{
+ int ret;
+
+ ret = wm_adsp_common_init(dsp);
+ if (ret)
+ return ret;
+
+ dsp->ops = &wm_halo_ops;
+
+ INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_halo_init);
+
void wm_adsp2_remove(struct wm_adsp *dsp)
{
struct wm_coeff_ctl *ctl;
@@ -3000,6 +3367,9 @@ static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
{
struct wm_adsp_compr_buf *buf = NULL, *tmp;
+ if (compr->dsp->fatal_error)
+ return -EINVAL;
+
list_for_each_entry(tmp, &compr->dsp->buffer_list, list) {
if (!tmp->name || !strcmp(compr->name, tmp->name)) {
buf = tmp;
@@ -3219,7 +3589,7 @@ static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type,
if (!mem)
return -EINVAL;
- reg = wm_adsp_region_to_reg(mem, mem_addr);
+ reg = dsp->ops->region_to_reg(mem, mem_addr);
ret = regmap_raw_read(dsp->regmap, reg, data,
sizeof(*data) * num_words);
@@ -3247,7 +3617,7 @@ static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type,
if (!mem)
return -EINVAL;
- reg = wm_adsp_region_to_reg(mem, mem_addr);
+ reg = dsp->ops->region_to_reg(mem, mem_addr);
data = cpu_to_be32(data & 0x00ffffffu);
@@ -3358,7 +3728,7 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)
return -ENOMEM;
alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id);
- xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32);
+ xmalg = dsp->ops->sys_config_size / sizeof(__be32);
addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);
ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic);
@@ -3535,11 +3905,11 @@ static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
if (ret < 0) {
- adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret);
+ compr_err(buf, "Failed to check buffer error: %d\n", ret);
return ret;
}
if (buf->error != 0) {
- adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error);
+ compr_err(buf, "Buffer error occurred: %d\n", buf->error);
return -EIO;
}
@@ -3571,8 +3941,6 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
if (ret < 0)
break;
- wm_adsp_buffer_clear(compr->buf);
-
/* Trigger the IRQ at one fragment of data */
ret = wm_adsp_buffer_write(compr->buf,
HOST_BUFFER_FIELD(high_water_mark),
@@ -3584,6 +3952,7 @@ int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
}
break;
case SNDRV_PCM_TRIGGER_STOP:
+ wm_adsp_buffer_clear(compr->buf);
break;
default:
ret = -EINVAL;
@@ -3886,36 +4255,20 @@ int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
-int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions)
+static void wm_adsp_fatal_error(struct wm_adsp *dsp)
{
- struct regmap *regmap = dsp->regmap;
- unsigned int code0, code1, lock_reg;
-
- if (!(lock_regions & WM_ADSP2_REGION_ALL))
- return 0;
+ struct wm_adsp_compr *compr;
- lock_regions &= WM_ADSP2_REGION_ALL;
- lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0;
+ dsp->fatal_error = true;
- while (lock_regions) {
- code0 = code1 = 0;
- if (lock_regions & BIT(0)) {
- code0 = ADSP2_LOCK_CODE_0;
- code1 = ADSP2_LOCK_CODE_1;
- }
- if (lock_regions & BIT(1)) {
- code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT;
- code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT;
+ list_for_each_entry(compr, &dsp->compr_list, list) {
+ if (compr->stream) {
+ snd_compr_stop_error(compr->stream,
+ SNDRV_PCM_STATE_XRUN);
+ snd_compr_fragment_elapsed(compr->stream);
}
- regmap_write(regmap, lock_reg, code0);
- regmap_write(regmap, lock_reg, code1);
- lock_regions >>= 2;
- lock_reg += 2;
}
-
- return 0;
}
-EXPORT_SYMBOL_GPL(wm_adsp2_lock);
irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
{
@@ -3923,16 +4276,19 @@ irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
struct regmap *regmap = dsp->regmap;
int ret = 0;
+ mutex_lock(&dsp->pwr_lock);
+
ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val);
if (ret) {
adsp_err(dsp,
"Failed to read Region Lock Ctrl register: %d\n", ret);
- return IRQ_HANDLED;
+ goto error;
}
if (val & ADSP2_WDT_TIMEOUT_STS_MASK) {
adsp_err(dsp, "watchdog timeout error\n");
wm_adsp_stop_watchdog(dsp);
+ wm_adsp_fatal_error(dsp);
}
if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) {
@@ -3946,7 +4302,7 @@ irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
adsp_err(dsp,
"Failed to read Bus Err Addr register: %d\n",
ret);
- return IRQ_HANDLED;
+ goto error;
}
adsp_err(dsp, "bus error address = 0x%x\n",
@@ -3959,7 +4315,7 @@ irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
adsp_err(dsp,
"Failed to read Pmem Xmem Err Addr register: %d\n",
ret);
- return IRQ_HANDLED;
+ goto error;
}
adsp_err(dsp, "xmem error address = 0x%x\n",
@@ -3972,8 +4328,166 @@ irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL,
ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT);
+error:
+ mutex_unlock(&dsp->pwr_lock);
+
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
+irqreturn_t wm_halo_bus_error(struct wm_adsp *dsp)
+{
+ struct regmap *regmap = dsp->regmap;
+ unsigned int fault[6];
+ struct reg_sequence clear[] = {
+ { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 },
+ { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 },
+ { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 },
+ };
+ int ret;
+
+ mutex_lock(&dsp->pwr_lock);
+
+ ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1,
+ fault);
+ if (ret) {
+ adsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret);
+ goto exit_unlock;
+ }
+
+ adsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n",
+ *fault & HALO_AHBM_FLAGS_ERR_MASK,
+ (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >>
+ HALO_AHBM_CORE_ERR_ADDR_SHIFT);
+
+ ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0,
+ fault);
+ if (ret) {
+ adsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret);
+ goto exit_unlock;
+ }
+
+ adsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault);
+
+ ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR,
+ fault, ARRAY_SIZE(fault));
+ if (ret) {
+ adsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret);
+ goto exit_unlock;
+ }
+
+ adsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]);
+ adsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]);
+ adsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]);
+
+ ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear));
+ if (ret)
+ adsp_warn(dsp, "Failed to clear MPU status: %d\n", ret);
+
+exit_unlock:
+ mutex_unlock(&dsp->pwr_lock);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_halo_bus_error);
+
+irqreturn_t wm_halo_wdt_expire(int irq, void *data)
+{
+ struct wm_adsp *dsp = data;
+
+ mutex_lock(&dsp->pwr_lock);
+
+ adsp_warn(dsp, "WDT Expiry Fault\n");
+ wm_halo_stop_watchdog(dsp);
+ wm_adsp_fatal_error(dsp);
+
+ mutex_unlock(&dsp->pwr_lock);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_halo_wdt_expire);
+
+static struct wm_adsp_ops wm_adsp1_ops = {
+ .validate_version = wm_adsp_validate_version,
+ .parse_sizes = wm_adsp1_parse_sizes,
+ .region_to_reg = wm_adsp_region_to_reg,
+};
+
+static struct wm_adsp_ops wm_adsp2_ops[] = {
+ {
+ .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr),
+ .parse_sizes = wm_adsp2_parse_sizes,
+ .validate_version = wm_adsp_validate_version,
+ .setup_algs = wm_adsp2_setup_algs,
+ .region_to_reg = wm_adsp_region_to_reg,
+
+ .show_fw_status = wm_adsp2_show_fw_status,
+
+ .enable_memory = wm_adsp2_enable_memory,
+ .disable_memory = wm_adsp2_disable_memory,
+
+ .enable_core = wm_adsp2_enable_core,
+ .disable_core = wm_adsp2_disable_core,
+
+ .start_core = wm_adsp2_start_core,
+ .stop_core = wm_adsp2_stop_core,
+
+ },
+ {
+ .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr),
+ .parse_sizes = wm_adsp2_parse_sizes,
+ .validate_version = wm_adsp_validate_version,
+ .setup_algs = wm_adsp2_setup_algs,
+ .region_to_reg = wm_adsp_region_to_reg,
+
+ .show_fw_status = wm_adsp2v2_show_fw_status,
+
+ .enable_memory = wm_adsp2_enable_memory,
+ .disable_memory = wm_adsp2_disable_memory,
+ .lock_memory = wm_adsp2_lock,
+
+ .enable_core = wm_adsp2v2_enable_core,
+ .disable_core = wm_adsp2v2_disable_core,
+
+ .start_core = wm_adsp2_start_core,
+ .stop_core = wm_adsp2_stop_core,
+ },
+ {
+ .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr),
+ .parse_sizes = wm_adsp2_parse_sizes,
+ .validate_version = wm_adsp_validate_version,
+ .setup_algs = wm_adsp2_setup_algs,
+ .region_to_reg = wm_adsp_region_to_reg,
+
+ .show_fw_status = wm_adsp2v2_show_fw_status,
+ .stop_watchdog = wm_adsp_stop_watchdog,
+
+ .enable_memory = wm_adsp2_enable_memory,
+ .disable_memory = wm_adsp2_disable_memory,
+ .lock_memory = wm_adsp2_lock,
+
+ .enable_core = wm_adsp2v2_enable_core,
+ .disable_core = wm_adsp2v2_disable_core,
+
+ .start_core = wm_adsp2_start_core,
+ .stop_core = wm_adsp2_stop_core,
+ },
+};
+
+static struct wm_adsp_ops wm_halo_ops = {
+ .sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr),
+ .parse_sizes = wm_adsp2_parse_sizes,
+ .validate_version = wm_halo_validate_version,
+ .setup_algs = wm_halo_setup_algs,
+ .region_to_reg = wm_halo_region_to_reg,
+
+ .show_fw_status = wm_halo_show_fw_status,
+ .stop_watchdog = wm_halo_stop_watchdog,
+
+ .lock_memory = wm_halo_configure_mpu,
+
+ .start_core = wm_halo_start_core,
+ .stop_core = wm_halo_stop_core,
+};
+
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 59e07ad16329..3631c9200c5d 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -54,6 +54,7 @@ struct wm_adsp_alg_region {
struct wm_adsp_compr;
struct wm_adsp_compr_buf;
+struct wm_adsp_ops;
struct wm_adsp {
const char *part;
@@ -66,7 +67,10 @@ struct wm_adsp {
struct regmap *regmap;
struct snd_soc_component *component;
+ struct wm_adsp_ops *ops;
+
unsigned int base;
+ unsigned int base_sysinfo;
unsigned int sysclk_reg;
unsigned int sysclk_mask;
unsigned int sysclk_shift;
@@ -75,6 +79,7 @@ struct wm_adsp {
unsigned int fw_id;
unsigned int fw_id_version;
+ unsigned int fw_vendor_id;
const struct wm_adsp_region *mem;
int num_mems;
@@ -85,6 +90,7 @@ struct wm_adsp {
bool preloaded;
bool booted;
bool running;
+ bool fatal_error;
struct list_head ctl_list;
@@ -105,6 +111,32 @@ struct wm_adsp {
};
+struct wm_adsp_ops {
+ unsigned int sys_config_size;
+
+ bool (*validate_version)(struct wm_adsp *dsp, unsigned int version);
+ unsigned int (*parse_sizes)(struct wm_adsp *dsp,
+ const char * const file,
+ unsigned int pos,
+ const struct firmware *firmware);
+ int (*setup_algs)(struct wm_adsp *dsp);
+ unsigned int (*region_to_reg)(struct wm_adsp_region const *mem,
+ unsigned int offset);
+
+ void (*show_fw_status)(struct wm_adsp *dsp);
+ void (*stop_watchdog)(struct wm_adsp *dsp);
+
+ int (*enable_memory)(struct wm_adsp *dsp);
+ void (*disable_memory)(struct wm_adsp *dsp);
+ int (*lock_memory)(struct wm_adsp *dsp, unsigned int lock_regions);
+
+ int (*enable_core)(struct wm_adsp *dsp);
+ void (*disable_core)(struct wm_adsp *dsp);
+
+ int (*start_core)(struct wm_adsp *dsp);
+ void (*stop_core)(struct wm_adsp *dsp);
+};
+
#define WM_ADSP1(wname, num) \
SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
@@ -120,7 +152,7 @@ struct wm_adsp {
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, \
.subseq = 100, /* Ensure we run after SYSCLK supply widget */ }, \
{ .id = snd_soc_dapm_out_drv, .name = wname, \
- .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
+ .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp_event, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
#define WM_ADSP_FW_CONTROL(dspname, num) \
@@ -134,17 +166,22 @@ int wm_adsp2_init(struct wm_adsp *dsp);
void wm_adsp2_remove(struct wm_adsp *dsp);
int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component);
int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component);
+int wm_halo_init(struct wm_adsp *dsp);
+
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
-int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event,
- unsigned int freq);
-int wm_adsp2_lock(struct wm_adsp *adsp, unsigned int regions);
+int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
irqreturn_t wm_adsp2_bus_error(struct wm_adsp *adsp);
+irqreturn_t wm_halo_bus_error(struct wm_adsp *dsp);
+irqreturn_t wm_halo_wdt_expire(int irq, void *data);
-int wm_adsp2_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event);
+int wm_adsp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq);
int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
index 0c3f50acb8b1..14b2d1a2fc59 100644
--- a/sound/soc/codecs/wmfw.h
+++ b/sound/soc/codecs/wmfw.h
@@ -73,6 +73,14 @@ struct wmfw_id_hdr {
__be32 ver;
} __packed;
+struct wmfw_v3_id_hdr {
+ __be32 core_id;
+ __be32 block_rev;
+ __be32 vendor_id;
+ __be32 id;
+ __be32 ver;
+} __packed;
+
struct wmfw_adsp1_id_hdr {
struct wmfw_id_hdr fw;
__be32 zm;
@@ -88,6 +96,15 @@ struct wmfw_adsp2_id_hdr {
__be32 n_algs;
} __packed;
+struct wmfw_halo_id_hdr {
+ struct wmfw_v3_id_hdr fw;
+ __be32 xm_base;
+ __be32 xm_size;
+ __be32 ym_base;
+ __be32 ym_size;
+ __be32 n_algs;
+} __packed;
+
struct wmfw_alg_hdr {
__be32 id;
__be32 ver;
@@ -106,6 +123,14 @@ struct wmfw_adsp2_alg_hdr {
__be32 ym;
} __packed;
+struct wmfw_halo_alg_hdr {
+ struct wmfw_alg_hdr alg;
+ __be32 xm_base;
+ __be32 xm_size;
+ __be32 ym_base;
+ __be32 ym_size;
+} __packed;
+
struct wmfw_adsp_alg_data {
__le32 id;
u8 name[WMFW_MAX_ALG_NAME];
@@ -154,6 +179,7 @@ struct wmfw_coeff_item {
#define WMFW_ADSP1 1
#define WMFW_ADSP2 2
+#define WMFW_HALO 4
#define WMFW_ABSOLUTE 0xf0
#define WMFW_ALGORITHM_DATA 0xf2
@@ -169,4 +195,8 @@ struct wmfw_coeff_item {
#define WMFW_ADSP2_XM 5
#define WMFW_ADSP2_YM 6
+#define WMFW_HALO_PM_PACKED 0x10
+#define WMFW_HALO_XM_PACKED 0x11
+#define WMFW_HALO_YM_PACKED 0x12
+
#endif
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 7b1d9970be8b..d87c842806bd 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -24,6 +24,13 @@ config SND_SOC_FSL_SAI
This option is only useful for out-of-tree drivers since
in-tree drivers select it automatically.
+config SND_SOC_FSL_AUDMIX
+ tristate "Audio Mixer (AUDMIX) module support"
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add Audio Mixer (AUDMIX)
+ support for the NXP iMX CPUs.
+
config SND_SOC_FSL_SSI
tristate "Synchronous Serial Interface module (SSI) support"
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
@@ -296,6 +303,15 @@ config SND_SOC_FSL_ASOC_CARD
CS4271, CS4272 and SGTL5000.
Say Y if you want to add support for Freescale Generic ASoC Sound Card.
+config SND_SOC_IMX_AUDMIX
+ tristate "SoC Audio support for i.MX boards with AUDMIX"
+ select SND_SOC_FSL_AUDMIX
+ select SND_SOC_FSL_SAI
+ help
+ SoC Audio support for i.MX boards with Audio Mixer
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ an Audio Mixer.
+
endif # SND_IMX_SOC
endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 3c0ff315b971..c0dd04422fe9 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -12,6 +12,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
+snd-soc-fsl-audmix-objs := fsl_audmix.o
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-sai-objs := fsl_sai.o
@@ -22,6 +23,8 @@ snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-micfil-objs := fsl_micfil.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
+
+obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
@@ -59,6 +62,7 @@ snd-soc-imx-es8328-objs := imx-es8328.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-spdif-objs := imx-spdif.o
snd-soc-imx-mc13783-objs := imx-mc13783.o
+snd-soc-imx-audmix-objs := imx-audmix.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
@@ -68,3 +72,4 @@ obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
+obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index 191426a6d9ad..30a3d68b5c03 100644
--- a/sound/soc/fsl/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -118,13 +118,13 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev,
"fsl,mux-int-port node missing or invalid.\n");
- return ret;
+ goto err;
}
ret = of_property_read_u32(np, "fsl,mux-ext-port", &ext_port);
if (ret) {
dev_err(&pdev->dev,
"fsl,mux-ext-port node missing or invalid.\n");
- return ret;
+ goto err;
}
/*
diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c
new file mode 100644
index 000000000000..dabde0342c95
--- /dev/null
+++ b/sound/soc/fsl/fsl_audmix.c
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright 2017 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_audmix.h"
+
+#define SOC_ENUM_SINGLE_S(xreg, xshift, xtexts) \
+ SOC_ENUM_SINGLE(xreg, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+static const char
+ *tdm_sel[] = { "TDM1", "TDM2", },
+ *mode_sel[] = { "Disabled", "TDM1", "TDM2", "Mixed", },
+ *width_sel[] = { "16b", "18b", "20b", "24b", "32b", },
+ *endis_sel[] = { "Disabled", "Enabled", },
+ *updn_sel[] = { "Downward", "Upward", },
+ *mask_sel[] = { "Unmask", "Mask", };
+
+static const struct soc_enum fsl_audmix_enum[] = {
+/* FSL_AUDMIX_CTR enums */
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MIXCLK_SHIFT, tdm_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTSRC_SHIFT, mode_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_OUTWIDTH_SHIFT, width_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKRTDF_SHIFT, mask_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_MASKCKDF_SHIFT, mask_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCMODE_SHIFT, endis_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR, FSL_AUDMIX_CTR_SYNCSRC_SHIFT, tdm_sel),
+/* FSL_AUDMIX_ATCR0 enums */
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 0, endis_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0, 1, updn_sel),
+/* FSL_AUDMIX_ATCR1 enums */
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 0, endis_sel),
+SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1, 1, updn_sel),
+};
+
+struct fsl_audmix_state {
+ u8 tdms;
+ u8 clk;
+ char msg[64];
+};
+
+static const struct fsl_audmix_state prms[4][4] = {{
+ /* DIS->DIS, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" },
+ /* DIS->TDM1*/
+ { .tdms = 1, .clk = 1, .msg = "DIS->TDM1: TDM1 not started!\n" },
+ /* DIS->TDM2*/
+ { .tdms = 2, .clk = 2, .msg = "DIS->TDM2: TDM2 not started!\n" },
+ /* DIS->MIX */
+ { .tdms = 3, .clk = 0, .msg = "DIS->MIX: Please start both TDMs!\n" }
+}, { /* TDM1->DIS */
+ { .tdms = 1, .clk = 0, .msg = "TDM1->DIS: TDM1 not started!\n" },
+ /* TDM1->TDM1, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" },
+ /* TDM1->TDM2 */
+ { .tdms = 3, .clk = 2, .msg = "TDM1->TDM2: Please start both TDMs!\n" },
+ /* TDM1->MIX */
+ { .tdms = 3, .clk = 0, .msg = "TDM1->MIX: Please start both TDMs!\n" }
+}, { /* TDM2->DIS */
+ { .tdms = 2, .clk = 0, .msg = "TDM2->DIS: TDM2 not started!\n" },
+ /* TDM2->TDM1 */
+ { .tdms = 3, .clk = 1, .msg = "TDM2->TDM1: Please start both TDMs!\n" },
+ /* TDM2->TDM2, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" },
+ /* TDM2->MIX */
+ { .tdms = 3, .clk = 0, .msg = "TDM2->MIX: Please start both TDMs!\n" }
+}, { /* MIX->DIS */
+ { .tdms = 3, .clk = 0, .msg = "MIX->DIS: Please start both TDMs!\n" },
+ /* MIX->TDM1 */
+ { .tdms = 3, .clk = 1, .msg = "MIX->TDM1: Please start both TDMs!\n" },
+ /* MIX->TDM2 */
+ { .tdms = 3, .clk = 2, .msg = "MIX->TDM2: Please start both TDMs!\n" },
+ /* MIX->MIX, do nothing */
+ { .tdms = 0, .clk = 0, .msg = "" }
+}, };
+
+static int fsl_audmix_state_trans(struct snd_soc_component *comp,
+ unsigned int *mask, unsigned int *ctr,
+ const struct fsl_audmix_state prm)
+{
+ struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
+ /* Enforce all required TDMs are started */
+ if ((priv->tdms & prm.tdms) != prm.tdms) {
+ dev_dbg(comp->dev, "%s", prm.msg);
+ return -EINVAL;
+ }
+
+ switch (prm.clk) {
+ case 1:
+ case 2:
+ /* Set mix clock */
+ (*mask) |= FSL_AUDMIX_CTR_MIXCLK_MASK;
+ (*ctr) |= FSL_AUDMIX_CTR_MIXCLK(prm.clk - 1);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int reg_val, val, mix_clk;
+ int ret = 0;
+
+ /* Get current state */
+ ret = snd_soc_component_read(comp, FSL_AUDMIX_CTR, &reg_val);
+ if (ret)
+ return ret;
+
+ mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
+ >> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
+
+ /**
+ * Ensure the current selected mixer clock is available
+ * for configuration propagation
+ */
+ if (!(priv->tdms & BIT(mix_clk))) {
+ dev_err(comp->dev,
+ "Started TDM%d needed for config propagation!\n",
+ mix_clk + 1);
+ return -EINVAL;
+ }
+
+ if (!(priv->tdms & BIT(val))) {
+ dev_err(comp->dev,
+ "The selected clock source has no TDM%d enabled!\n",
+ val + 1);
+ return -EINVAL;
+ }
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ u32 out_src, mix_clk;
+ unsigned int reg_val, val, mask = 0, ctr = 0;
+ int ret = 0;
+
+ /* Get current state */
+ ret = snd_soc_component_read(comp, FSL_AUDMIX_CTR, &reg_val);
+ if (ret)
+ return ret;
+
+ /* "From" state */
+ out_src = ((reg_val & FSL_AUDMIX_CTR_OUTSRC_MASK)
+ >> FSL_AUDMIX_CTR_OUTSRC_SHIFT);
+ mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
+ >> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
+
+ /* "To" state */
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
+
+ /* Check if state is changing ... */
+ if (out_src == val)
+ return 0;
+ /**
+ * Ensure the current selected mixer clock is available
+ * for configuration propagation
+ */
+ if (!(priv->tdms & BIT(mix_clk))) {
+ dev_err(comp->dev,
+ "Started TDM%d needed for config propagation!\n",
+ mix_clk + 1);
+ return -EINVAL;
+ }
+
+ /* Check state transition constraints */
+ ret = fsl_audmix_state_trans(comp, &mask, &ctr, prms[out_src][val]);
+ if (ret)
+ return ret;
+
+ /* Complete transition to new state */
+ mask |= FSL_AUDMIX_CTR_OUTSRC_MASK;
+ ctr |= FSL_AUDMIX_CTR_OUTSRC(val);
+
+ return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
+}
+
+static const struct snd_kcontrol_new fsl_audmix_snd_controls[] = {
+ /* FSL_AUDMIX_CTR controls */
+ SOC_ENUM_EXT("Mixing Clock Source", fsl_audmix_enum[0],
+ snd_soc_get_enum_double, fsl_audmix_put_mix_clk_src),
+ SOC_ENUM_EXT("Output Source", fsl_audmix_enum[1],
+ snd_soc_get_enum_double, fsl_audmix_put_out_src),
+ SOC_ENUM("Output Width", fsl_audmix_enum[2]),
+ SOC_ENUM("Frame Rate Diff Error", fsl_audmix_enum[3]),
+ SOC_ENUM("Clock Freq Diff Error", fsl_audmix_enum[4]),
+ SOC_ENUM("Sync Mode Config", fsl_audmix_enum[5]),
+ SOC_ENUM("Sync Mode Clk Source", fsl_audmix_enum[6]),
+ /* TDM1 Attenuation controls */
+ SOC_ENUM("TDM1 Attenuation", fsl_audmix_enum[7]),
+ SOC_ENUM("TDM1 Attenuation Direction", fsl_audmix_enum[8]),
+ SOC_SINGLE("TDM1 Attenuation Step Divider", FSL_AUDMIX_ATCR0,
+ 2, 0x00fff, 0),
+ SOC_SINGLE("TDM1 Attenuation Initial Value", FSL_AUDMIX_ATIVAL0,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM1 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP0,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM1 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN0,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM1 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT0,
+ 0, 0x3ffff, 0),
+ /* TDM2 Attenuation controls */
+ SOC_ENUM("TDM2 Attenuation", fsl_audmix_enum[9]),
+ SOC_ENUM("TDM2 Attenuation Direction", fsl_audmix_enum[10]),
+ SOC_SINGLE("TDM2 Attenuation Step Divider", FSL_AUDMIX_ATCR1,
+ 2, 0x00fff, 0),
+ SOC_SINGLE("TDM2 Attenuation Initial Value", FSL_AUDMIX_ATIVAL1,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM2 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP1,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM2 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN1,
+ 0, 0x3ffff, 0),
+ SOC_SINGLE("TDM2 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT1,
+ 0, 0x3ffff, 0),
+};
+
+static int fsl_audmix_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *comp = dai->component;
+ u32 mask = 0, ctr = 0;
+
+ /* AUDMIX is working in DSP_A format only */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* For playback the AUDMIX is slave, and for record is master */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Output data will be written on positive edge of the clock */
+ ctr |= FSL_AUDMIX_CTR_OUTCKPOL(0);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ /* Output data will be written on negative edge of the clock */
+ ctr |= FSL_AUDMIX_CTR_OUTCKPOL(1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask |= FSL_AUDMIX_CTR_OUTCKPOL_MASK;
+
+ return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
+}
+
+static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
+
+ /* Capture stream shall not be handled */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ priv->tdms |= BIT(dai->driver->id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ priv->tdms &= ~BIT(dai->driver->id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_audmix_dai_ops = {
+ .set_fmt = fsl_audmix_dai_set_fmt,
+ .trigger = fsl_audmix_dai_trigger,
+};
+
+static struct snd_soc_dai_driver fsl_audmix_dai[] = {
+ {
+ .id = 0,
+ .name = "audmix-0",
+ .playback = {
+ .stream_name = "AUDMIX-Playback-0",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AUDMIX-Capture-0",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .ops = &fsl_audmix_dai_ops,
+ },
+ {
+ .id = 1,
+ .name = "audmix-1",
+ .playback = {
+ .stream_name = "AUDMIX-Playback-1",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AUDMIX-Capture-1",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = FSL_AUDMIX_FORMATS,
+ },
+ .ops = &fsl_audmix_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver fsl_audmix_component = {
+ .name = "fsl-audmix-dai",
+ .controls = fsl_audmix_snd_controls,
+ .num_controls = ARRAY_SIZE(fsl_audmix_snd_controls),
+};
+
+static bool fsl_audmix_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FSL_AUDMIX_CTR:
+ case FSL_AUDMIX_STR:
+ case FSL_AUDMIX_ATCR0:
+ case FSL_AUDMIX_ATIVAL0:
+ case FSL_AUDMIX_ATSTPUP0:
+ case FSL_AUDMIX_ATSTPDN0:
+ case FSL_AUDMIX_ATSTPTGT0:
+ case FSL_AUDMIX_ATTNVAL0:
+ case FSL_AUDMIX_ATSTP0:
+ case FSL_AUDMIX_ATCR1:
+ case FSL_AUDMIX_ATIVAL1:
+ case FSL_AUDMIX_ATSTPUP1:
+ case FSL_AUDMIX_ATSTPDN1:
+ case FSL_AUDMIX_ATSTPTGT1:
+ case FSL_AUDMIX_ATTNVAL1:
+ case FSL_AUDMIX_ATSTP1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_audmix_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FSL_AUDMIX_CTR:
+ case FSL_AUDMIX_ATCR0:
+ case FSL_AUDMIX_ATIVAL0:
+ case FSL_AUDMIX_ATSTPUP0:
+ case FSL_AUDMIX_ATSTPDN0:
+ case FSL_AUDMIX_ATSTPTGT0:
+ case FSL_AUDMIX_ATCR1:
+ case FSL_AUDMIX_ATIVAL1:
+ case FSL_AUDMIX_ATSTPUP1:
+ case FSL_AUDMIX_ATSTPDN1:
+ case FSL_AUDMIX_ATSTPTGT1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default fsl_audmix_reg[] = {
+ { FSL_AUDMIX_CTR, 0x00060 },
+ { FSL_AUDMIX_STR, 0x00003 },
+ { FSL_AUDMIX_ATCR0, 0x00000 },
+ { FSL_AUDMIX_ATIVAL0, 0x3FFFF },
+ { FSL_AUDMIX_ATSTPUP0, 0x2AAAA },
+ { FSL_AUDMIX_ATSTPDN0, 0x30000 },
+ { FSL_AUDMIX_ATSTPTGT0, 0x00010 },
+ { FSL_AUDMIX_ATTNVAL0, 0x00000 },
+ { FSL_AUDMIX_ATSTP0, 0x00000 },
+ { FSL_AUDMIX_ATCR1, 0x00000 },
+ { FSL_AUDMIX_ATIVAL1, 0x3FFFF },
+ { FSL_AUDMIX_ATSTPUP1, 0x2AAAA },
+ { FSL_AUDMIX_ATSTPDN1, 0x30000 },
+ { FSL_AUDMIX_ATSTPTGT1, 0x00010 },
+ { FSL_AUDMIX_ATTNVAL1, 0x00000 },
+ { FSL_AUDMIX_ATSTP1, 0x00000 },
+};
+
+static const struct regmap_config fsl_audmix_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = FSL_AUDMIX_ATSTP1,
+ .reg_defaults = fsl_audmix_reg,
+ .num_reg_defaults = ARRAY_SIZE(fsl_audmix_reg),
+ .readable_reg = fsl_audmix_readable_reg,
+ .writeable_reg = fsl_audmix_writeable_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int fsl_audmix_probe(struct platform_device *pdev)
+{
+ struct fsl_audmix *priv;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+ const char *sprop;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Get the addresses */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "ipg", regs,
+ &fsl_audmix_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap\n");
+ return PTR_ERR(priv->regmap);
+ }
+
+ priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(priv->ipg_clk)) {
+ dev_err(&pdev->dev, "failed to get ipg clock\n");
+ return PTR_ERR(priv->ipg_clk);
+ }
+
+ platform_set_drvdata(pdev, priv);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_audmix_component,
+ fsl_audmix_dai,
+ ARRAY_SIZE(fsl_audmix_dai));
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ASoC DAI\n");
+ return ret;
+ }
+
+ sprop = of_get_property(pdev->dev.of_node, "model", NULL);
+ if (sprop) {
+ priv->pdev = platform_device_register_data(&pdev->dev, sprop, 0,
+ NULL, 0);
+ if (IS_ERR(priv->pdev)) {
+ ret = PTR_ERR(priv->pdev);
+ dev_err(&pdev->dev,
+ "failed to register platform %s: %d\n", sprop,
+ ret);
+ }
+ } else {
+ dev_err(&pdev->dev, "[model] attribute missing.\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int fsl_audmix_remove(struct platform_device *pdev)
+{
+ struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev);
+
+ if (priv->pdev)
+ platform_device_unregister(priv->pdev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int fsl_audmix_runtime_resume(struct device *dev)
+{
+ struct fsl_audmix *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(priv->ipg_clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable IPG clock: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(priv->regmap, false);
+ regcache_mark_dirty(priv->regmap);
+
+ return regcache_sync(priv->regmap);
+}
+
+static int fsl_audmix_runtime_suspend(struct device *dev)
+{
+ struct fsl_audmix *priv = dev_get_drvdata(dev);
+
+ regcache_cache_only(priv->regmap, true);
+
+ clk_disable_unprepare(priv->ipg_clk);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops fsl_audmix_pm = {
+ SET_RUNTIME_PM_OPS(fsl_audmix_runtime_suspend,
+ fsl_audmix_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct of_device_id fsl_audmix_ids[] = {
+ { .compatible = "fsl,imx8qm-audmix", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_audmix_ids);
+
+static struct platform_driver fsl_audmix_driver = {
+ .probe = fsl_audmix_probe,
+ .remove = fsl_audmix_remove,
+ .driver = {
+ .name = "fsl-audmix",
+ .of_match_table = fsl_audmix_ids,
+ .pm = &fsl_audmix_pm,
+ },
+};
+module_platform_driver(fsl_audmix_driver);
+
+MODULE_DESCRIPTION("NXP AUDMIX ASoC DAI driver");
+MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
+MODULE_ALIAS("platform:fsl-audmix");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_audmix.h b/sound/soc/fsl/fsl_audmix.h
new file mode 100644
index 000000000000..7812ffec45c5
--- /dev/null
+++ b/sound/soc/fsl/fsl_audmix.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright 2017 NXP
+ */
+
+#ifndef __FSL_AUDMIX_H
+#define __FSL_AUDMIX_H
+
+#define FSL_AUDMIX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+/* AUDMIX Registers */
+#define FSL_AUDMIX_CTR 0x200 /* Control */
+#define FSL_AUDMIX_STR 0x204 /* Status */
+
+#define FSL_AUDMIX_ATCR0 0x208 /* Attenuation Control */
+#define FSL_AUDMIX_ATIVAL0 0x20c /* Attenuation Initial Value */
+#define FSL_AUDMIX_ATSTPUP0 0x210 /* Attenuation step up factor */
+#define FSL_AUDMIX_ATSTPDN0 0x214 /* Attenuation step down factor */
+#define FSL_AUDMIX_ATSTPTGT0 0x218 /* Attenuation step target */
+#define FSL_AUDMIX_ATTNVAL0 0x21c /* Attenuation Value */
+#define FSL_AUDMIX_ATSTP0 0x220 /* Attenuation step number */
+
+#define FSL_AUDMIX_ATCR1 0x228 /* Attenuation Control */
+#define FSL_AUDMIX_ATIVAL1 0x22c /* Attenuation Initial Value */
+#define FSL_AUDMIX_ATSTPUP1 0x230 /* Attenuation step up factor */
+#define FSL_AUDMIX_ATSTPDN1 0x234 /* Attenuation step down factor */
+#define FSL_AUDMIX_ATSTPTGT1 0x238 /* Attenuation step target */
+#define FSL_AUDMIX_ATTNVAL1 0x23c /* Attenuation Value */
+#define FSL_AUDMIX_ATSTP1 0x240 /* Attenuation step number */
+
+/* AUDMIX Control Register */
+#define FSL_AUDMIX_CTR_MIXCLK_SHIFT 0
+#define FSL_AUDMIX_CTR_MIXCLK_MASK BIT(FSL_AUDMIX_CTR_MIXCLK_SHIFT)
+#define FSL_AUDMIX_CTR_MIXCLK(i) ((i) << FSL_AUDMIX_CTR_MIXCLK_SHIFT)
+#define FSL_AUDMIX_CTR_OUTSRC_SHIFT 1
+#define FSL_AUDMIX_CTR_OUTSRC_MASK (0x3 << FSL_AUDMIX_CTR_OUTSRC_SHIFT)
+#define FSL_AUDMIX_CTR_OUTSRC(i) (((i) << FSL_AUDMIX_CTR_OUTSRC_SHIFT)\
+ & FSL_AUDMIX_CTR_OUTSRC_MASK)
+#define FSL_AUDMIX_CTR_OUTWIDTH_SHIFT 3
+#define FSL_AUDMIX_CTR_OUTWIDTH_MASK (0x7 << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)
+#define FSL_AUDMIX_CTR_OUTWIDTH(i) (((i) << FSL_AUDMIX_CTR_OUTWIDTH_SHIFT)\
+ & FSL_AUDMIX_CTR_OUTWIDTH_MASK)
+#define FSL_AUDMIX_CTR_OUTCKPOL_SHIFT 6
+#define FSL_AUDMIX_CTR_OUTCKPOL_MASK BIT(FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
+#define FSL_AUDMIX_CTR_OUTCKPOL(i) ((i) << FSL_AUDMIX_CTR_OUTCKPOL_SHIFT)
+#define FSL_AUDMIX_CTR_MASKRTDF_SHIFT 7
+#define FSL_AUDMIX_CTR_MASKRTDF_MASK BIT(FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
+#define FSL_AUDMIX_CTR_MASKRTDF(i) ((i) << FSL_AUDMIX_CTR_MASKRTDF_SHIFT)
+#define FSL_AUDMIX_CTR_MASKCKDF_SHIFT 8
+#define FSL_AUDMIX_CTR_MASKCKDF_MASK BIT(FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
+#define FSL_AUDMIX_CTR_MASKCKDF(i) ((i) << FSL_AUDMIX_CTR_MASKCKDF_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCMODE_SHIFT 9
+#define FSL_AUDMIX_CTR_SYNCMODE_MASK BIT(FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCMODE(i) ((i) << FSL_AUDMIX_CTR_SYNCMODE_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCSRC_SHIFT 10
+#define FSL_AUDMIX_CTR_SYNCSRC_MASK BIT(FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
+#define FSL_AUDMIX_CTR_SYNCSRC(i) ((i) << FSL_AUDMIX_CTR_SYNCSRC_SHIFT)
+
+/* AUDMIX Status Register */
+#define FSL_AUDMIX_STR_RATEDIFF BIT(0)
+#define FSL_AUDMIX_STR_CLKDIFF BIT(1)
+#define FSL_AUDMIX_STR_MIXSTAT_SHIFT 2
+#define FSL_AUDMIX_STR_MIXSTAT_MASK (0x3 << FSL_AUDMIX_STR_MIXSTAT_SHIFT)
+#define FSL_AUDMIX_STR_MIXSTAT(i) (((i) & FSL_AUDMIX_STR_MIXSTAT_MASK) \
+ >> FSL_AUDMIX_STR_MIXSTAT_SHIFT)
+/* AUDMIX Attenuation Control Register */
+#define FSL_AUDMIX_ATCR_AT_EN BIT(0)
+#define FSL_AUDMIX_ATCR_AT_UPDN BIT(1)
+#define FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT 2
+#define FSL_AUDMIX_ATCR_ATSTPDFI_MASK \
+ (0xfff << FSL_AUDMIX_ATCR_ATSTPDIF_SHIFT)
+
+/* AUDMIX Attenuation Initial Value Register */
+#define FSL_AUDMIX_ATIVAL_ATINVAL_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Up Factor Register */
+#define FSL_AUDMIX_ATSTPUP_ATSTEPUP_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Down Factor Register */
+#define FSL_AUDMIX_ATSTPDN_ATSTEPDN_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Target Register */
+#define FSL_AUDMIX_ATSTPTGT_ATSTPTG_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Value Register */
+#define FSL_AUDMIX_ATTNVAL_ATCURVAL_MASK 0x3FFFF
+
+/* AUDMIX Attenuation Step Number Register */
+#define FSL_AUDMIX_ATSTP_STPCTR_MASK 0x3FFFF
+
+#define FSL_AUDMIX_MAX_DAIS 2
+struct fsl_audmix {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct clk *ipg_clk;
+ u8 tdms;
+};
+
+#endif /* __FSL_AUDMIX_H */
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c
index 9981668ab590..040d06b89f00 100644
--- a/sound/soc/fsl/fsl_utils.c
+++ b/sound/soc/fsl/fsl_utils.c
@@ -71,6 +71,7 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
iprop = of_get_property(dma_np, "cell-index", NULL);
if (!iprop) {
of_node_put(dma_np);
+ of_node_put(dma_channel_np);
return -EINVAL;
}
*dma_id = be32_to_cpup(iprop);
diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c
new file mode 100644
index 000000000000..7983bd339c01
--- /dev/null
+++ b/sound/soc/fsl/imx-audmix.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017 NXP
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/pm_runtime.h>
+#include "fsl_sai.h"
+#include "fsl_audmix.h"
+
+struct imx_audmix {
+ struct platform_device *pdev;
+ struct snd_soc_card card;
+ struct platform_device *audmix_pdev;
+ struct platform_device *out_pdev;
+ struct clk *cpu_mclk;
+ int num_dai;
+ struct snd_soc_dai_link *dai;
+ int num_dai_conf;
+ struct snd_soc_codec_conf *dai_conf;
+ int num_dapm_routes;
+ struct snd_soc_dapm_route *dapm_routes;
+};
+
+static const u32 imx_audmix_rates[] = {
+ 8000, 12000, 16000, 24000, 32000, 48000, 64000, 96000,
+};
+
+static const struct snd_pcm_hw_constraint_list imx_audmix_rate_constraints = {
+ .count = ARRAY_SIZE(imx_audmix_rates),
+ .list = imx_audmix_rates,
+};
+
+static int imx_audmix_fe_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct imx_audmix *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = rtd->card->dev;
+ unsigned long clk_rate = clk_get_rate(priv->cpu_mclk);
+ int ret;
+
+ if (clk_rate % 24576000 == 0) {
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &imx_audmix_rate_constraints);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_warn(dev, "mclk may be not supported %lu\n", clk_rate);
+ }
+
+ ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
+ 1, 8);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+ FSL_AUDMIX_FORMATS);
+}
+
+static int imx_audmix_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct device *dev = rtd->card->dev;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF;
+ u32 channels = params_channels(params);
+ int ret, dir;
+
+ /* For playback the AUDMIX is slave, and for record is master */
+ fmt |= tx ? SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM;
+ dir = tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN;
+
+ /* set DAI configuration */
+ ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+ if (ret) {
+ dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, FSL_SAI_CLK_MAST1, 0, dir);
+ if (ret) {
+ dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Per datasheet, AUDMIX expects 8 slots and 32 bits
+ * for every slot in TDM mode.
+ */
+ ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, BIT(channels) - 1,
+ BIT(channels) - 1, 8, 32);
+ if (ret)
+ dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
+
+ return ret;
+}
+
+static int imx_audmix_be_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct device *dev = rtd->card->dev;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF;
+ int ret;
+
+ if (!tx)
+ return 0;
+
+ /* For playback the AUDMIX is slave */
+ fmt |= SND_SOC_DAIFMT_CBM_CFM;
+
+ /* set AUDMIX DAI configuration */
+ ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt);
+ if (ret)
+ dev_err(dev, "failed to set AUDMIX DAI fmt: %d\n", ret);
+
+ return ret;
+}
+
+static struct snd_soc_ops imx_audmix_fe_ops = {
+ .startup = imx_audmix_fe_startup,
+ .hw_params = imx_audmix_fe_hw_params,
+};
+
+static struct snd_soc_ops imx_audmix_be_ops = {
+ .hw_params = imx_audmix_be_hw_params,
+};
+
+static int imx_audmix_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *audmix_np = NULL, *out_cpu_np = NULL;
+ struct platform_device *audmix_pdev = NULL;
+ struct platform_device *cpu_pdev;
+ struct of_phandle_args args;
+ struct imx_audmix *priv;
+ int i, num_dai, ret;
+ const char *fe_name_pref = "HiFi-AUDMIX-FE-";
+ char *be_name, *be_pb, *be_cp, *dai_name, *capture_dai_name;
+
+ if (pdev->dev.parent) {
+ audmix_np = pdev->dev.parent->of_node;
+ } else {
+ dev_err(&pdev->dev, "Missing parent device.\n");
+ return -EINVAL;
+ }
+
+ if (!audmix_np) {
+ dev_err(&pdev->dev, "Missing DT node for parent device.\n");
+ return -EINVAL;
+ }
+
+ audmix_pdev = of_find_device_by_node(audmix_np);
+ if (!audmix_pdev) {
+ dev_err(&pdev->dev, "Missing AUDMIX platform device for %s\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ num_dai = of_count_phandle_with_args(audmix_np, "dais", NULL);
+ if (num_dai != FSL_AUDMIX_MAX_DAIS) {
+ dev_err(&pdev->dev, "Need 2 dais to be provided for %s\n",
+ audmix_np->full_name);
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->num_dai = 2 * num_dai;
+ priv->dai = devm_kzalloc(&pdev->dev, priv->num_dai *
+ sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (!priv->dai)
+ return -ENOMEM;
+
+ priv->num_dai_conf = num_dai;
+ priv->dai_conf = devm_kzalloc(&pdev->dev, priv->num_dai_conf *
+ sizeof(struct snd_soc_codec_conf),
+ GFP_KERNEL);
+ if (!priv->dai_conf)
+ return -ENOMEM;
+
+ priv->num_dapm_routes = 3 * num_dai;
+ priv->dapm_routes = devm_kzalloc(&pdev->dev, priv->num_dapm_routes *
+ sizeof(struct snd_soc_dapm_route),
+ GFP_KERNEL);
+ if (!priv->dapm_routes)
+ return -ENOMEM;
+
+ for (i = 0; i < num_dai; i++) {
+ ret = of_parse_phandle_with_args(audmix_np, "dais", NULL, i,
+ &args);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "of_parse_phandle_with_args failed\n");
+ return ret;
+ }
+
+ cpu_pdev = of_find_device_by_node(args.np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find SAI platform device\n");
+ return -EINVAL;
+ }
+
+ dai_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%s",
+ fe_name_pref, args.np->full_name + 1);
+
+ dev_info(pdev->dev.parent, "DAI FE name:%s\n", dai_name);
+
+ if (i == 0) {
+ out_cpu_np = args.np;
+ capture_dai_name =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
+ dai_name, "CPU-Capture");
+ }
+
+ priv->dai[i].name = dai_name;
+ priv->dai[i].stream_name = "HiFi-AUDMIX-FE";
+ priv->dai[i].codec_dai_name = "snd-soc-dummy-dai";
+ priv->dai[i].codec_name = "snd-soc-dummy";
+ priv->dai[i].cpu_of_node = args.np;
+ priv->dai[i].cpu_dai_name = dev_name(&cpu_pdev->dev);
+ priv->dai[i].platform_of_node = args.np;
+ priv->dai[i].dynamic = 1;
+ priv->dai[i].dpcm_playback = 1;
+ priv->dai[i].dpcm_capture = (i == 0 ? 1 : 0);
+ priv->dai[i].ignore_pmdown_time = 1;
+ priv->dai[i].ops = &imx_audmix_fe_ops;
+
+ /* Add AUDMIX Backend */
+ be_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "audmix-%d", i);
+ be_pb = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "AUDMIX-Playback-%d", i);
+ be_cp = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+ "AUDMIX-Capture-%d", i);
+
+ priv->dai[num_dai + i].name = be_name;
+ priv->dai[num_dai + i].codec_dai_name = "snd-soc-dummy-dai";
+ priv->dai[num_dai + i].codec_name = "snd-soc-dummy";
+ priv->dai[num_dai + i].cpu_of_node = audmix_np;
+ priv->dai[num_dai + i].cpu_dai_name = be_name;
+ priv->dai[num_dai + i].platform_name = "snd-soc-dummy";
+ priv->dai[num_dai + i].no_pcm = 1;
+ priv->dai[num_dai + i].dpcm_playback = 1;
+ priv->dai[num_dai + i].dpcm_capture = 1;
+ priv->dai[num_dai + i].ignore_pmdown_time = 1;
+ priv->dai[num_dai + i].ops = &imx_audmix_be_ops;
+
+ priv->dai_conf[i].of_node = args.np;
+ priv->dai_conf[i].name_prefix = dai_name;
+
+ priv->dapm_routes[i].source =
+ devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s",
+ dai_name, "CPU-Playback");
+ priv->dapm_routes[i].sink = be_pb;
+ priv->dapm_routes[num_dai + i].source = be_pb;
+ priv->dapm_routes[num_dai + i].sink = be_cp;
+ priv->dapm_routes[2 * num_dai + i].source = be_cp;
+ priv->dapm_routes[2 * num_dai + i].sink = capture_dai_name;
+ }
+
+ cpu_pdev = of_find_device_by_node(out_cpu_np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find SAI platform device\n");
+ return -EINVAL;
+ }
+ priv->cpu_mclk = devm_clk_get(&cpu_pdev->dev, "mclk1");
+ if (IS_ERR(priv->cpu_mclk)) {
+ ret = PTR_ERR(priv->cpu_mclk);
+ dev_err(&cpu_pdev->dev, "failed to get DAI mclk1: %d\n", ret);
+ return -EINVAL;
+ }
+
+ priv->audmix_pdev = audmix_pdev;
+ priv->out_pdev = cpu_pdev;
+
+ priv->card.dai_link = priv->dai;
+ priv->card.num_links = priv->num_dai;
+ priv->card.codec_conf = priv->dai_conf;
+ priv->card.num_configs = priv->num_dai_conf;
+ priv->card.dapm_routes = priv->dapm_routes;
+ priv->card.num_dapm_routes = priv->num_dapm_routes;
+ priv->card.dev = pdev->dev.parent;
+ priv->card.owner = THIS_MODULE;
+ priv->card.name = "imx-audmix";
+
+ platform_set_drvdata(pdev, &priv->card);
+ snd_soc_card_set_drvdata(&priv->card, priv);
+
+ ret = devm_snd_soc_register_card(pdev->dev.parent, &priv->card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct platform_driver imx_audmix_driver = {
+ .probe = imx_audmix_probe,
+ .driver = {
+ .name = "imx-audmix",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(imx_audmix_driver);
+
+MODULE_DESCRIPTION("NXP AUDMIX ASoC machine driver");
+MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
+MODULE_ALIAS("platform:imx-audmix");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index bb12351330e8..8114b5ea9319 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -20,37 +20,6 @@
#include <linux/string.h>
#include <sound/simple_card_utils.h>
-struct graph_priv {
- struct snd_soc_card snd_card;
- struct graph_dai_props {
- struct asoc_simple_dai *cpu_dai;
- struct asoc_simple_dai *codec_dai;
- struct snd_soc_dai_link_component codecs; /* single codec */
- struct snd_soc_dai_link_component platforms;
- struct asoc_simple_card_data adata;
- struct snd_soc_codec_conf *codec_conf;
- unsigned int mclk_fs;
- } *dai_props;
- struct asoc_simple_jack hp_jack;
- struct asoc_simple_jack mic_jack;
- struct snd_soc_dai_link *dai_link;
- struct asoc_simple_dai *dais;
- struct snd_soc_codec_conf *codec_conf;
- struct gpio_desc *pa_gpio;
-};
-
-struct link_info {
- int dais; /* number of dai */
- int link; /* number of link */
- int conf; /* number of codec_conf */
- int cpu; /* turn for CPU / Codec */
-};
-
-#define graph_priv_to_card(priv) (&(priv)->snd_card)
-#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
-#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
-#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i))
-
#define PREFIX "audio-graph-card,"
static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
@@ -58,7 +27,7 @@ static int graph_outdrv_event(struct snd_soc_dapm_widget *w,
int event)
{
struct snd_soc_dapm_context *dapm = w->dapm;
- struct graph_priv *priv = snd_soc_card_get_drvdata(dapm->card);
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(dapm->card);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -80,127 +49,156 @@ static const struct snd_soc_dapm_widget graph_dapm_widgets[] = {
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
};
-static int graph_startup(struct snd_pcm_substream *substream)
+static const struct snd_soc_ops graph_ops = {
+ .startup = asoc_simple_startup,
+ .shutdown = asoc_simple_shutdown,
+ .hw_params = asoc_simple_hw_params,
+};
+
+static int graph_get_dai_id(struct device_node *ep)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
+ struct device_node *node;
+ struct device_node *endpoint;
+ struct of_endpoint info;
+ int i, id;
int ret;
- ret = asoc_simple_card_clk_enable(dai_props->cpu_dai);
- if (ret)
+ /* use driver specified DAI ID if exist */
+ ret = snd_soc_get_dai_id(ep);
+ if (ret != -ENOTSUPP)
return ret;
- ret = asoc_simple_card_clk_enable(dai_props->codec_dai);
- if (ret)
- asoc_simple_card_clk_disable(dai_props->cpu_dai);
+ /* use endpoint/port reg if exist */
+ ret = of_graph_parse_endpoint(ep, &info);
+ if (ret == 0) {
+ /*
+ * Because it will count port/endpoint if it doesn't have "reg".
+ * But, we can't judge whether it has "no reg", or "reg = <0>"
+ * only of_graph_parse_endpoint().
+ * We need to check "reg" property
+ */
+ if (of_get_property(ep, "reg", NULL))
+ return info.id;
- return ret;
-}
+ node = of_get_parent(ep);
+ of_node_put(node);
+ if (of_get_property(node, "reg", NULL))
+ return info.port;
+ }
+ node = of_graph_get_port_parent(ep);
-static void graph_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
+ /*
+ * Non HDMI sound case, counting port/endpoint on its DT
+ * is enough. Let's count it.
+ */
+ i = 0;
+ id = -1;
+ for_each_endpoint_of_node(node, endpoint) {
+ if (endpoint == ep)
+ id = i;
+ i++;
+ }
- asoc_simple_card_clk_disable(dai_props->cpu_dai);
+ of_node_put(node);
+
+ if (id < 0)
+ return -ENODEV;
- asoc_simple_card_clk_disable(dai_props->codec_dai);
+ return id;
}
-static int graph_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static int asoc_simple_parse_dai(struct device_node *ep,
+ struct snd_soc_dai_link_component *dlc,
+ struct device_node **dai_of_node,
+ const char **dai_name,
+ int *is_single_link)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
- unsigned int mclk, mclk_fs = 0;
- int ret = 0;
-
- if (dai_props->mclk_fs)
- mclk_fs = dai_props->mclk_fs;
-
- if (mclk_fs) {
- mclk = params_rate(params) * mclk_fs;
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
- SND_SOC_CLOCK_IN);
- if (ret && ret != -ENOTSUPP)
- goto err;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
- SND_SOC_CLOCK_OUT);
- if (ret && ret != -ENOTSUPP)
- goto err;
+ struct device_node *node;
+ struct of_phandle_args args;
+ int ret;
+
+ /*
+ * Use snd_soc_dai_link_component instead of legacy style.
+ * It is only for codec, but cpu will be supported in the future.
+ * see
+ * soc-core.c :: snd_soc_init_multicodec()
+ */
+ if (dlc) {
+ dai_name = &dlc->dai_name;
+ dai_of_node = &dlc->of_node;
}
- return 0;
-err:
- return ret;
-}
-static const struct snd_soc_ops graph_ops = {
- .startup = graph_startup,
- .shutdown = graph_shutdown,
- .hw_params = graph_hw_params,
-};
+ if (!ep)
+ return 0;
+ if (!dai_name)
+ return 0;
-static int graph_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
- int ret = 0;
+ node = of_graph_get_port_parent(ep);
- ret = asoc_simple_card_init_dai(rtd->codec_dai,
- dai_props->codec_dai);
- if (ret < 0)
- return ret;
+ /* Get dai->name */
+ args.np = node;
+ args.args[0] = graph_get_dai_id(ep);
+ args.args_count = (of_graph_get_endpoint_count(node) > 1);
- ret = asoc_simple_card_init_dai(rtd->cpu_dai,
- dai_props->cpu_dai);
+ ret = snd_soc_get_dai_name(&args, dai_name);
if (ret < 0)
return ret;
- return 0;
-}
-
-static int graph_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
+ *dai_of_node = node;
- asoc_simple_card_convert_fixup(&dai_props->adata, params);
+ if (is_single_link)
+ *is_single_link = of_graph_get_endpoint_count(node) == 1;
return 0;
}
-static void graph_get_conversion(struct device *dev,
- struct device_node *ep,
- struct asoc_simple_card_data *adata)
+static void graph_parse_convert(struct device *dev,
+ struct device_node *ep,
+ struct asoc_simple_data *adata)
{
struct device_node *top = dev->of_node;
struct device_node *port = of_get_parent(ep);
struct device_node *ports = of_get_parent(port);
struct device_node *node = of_graph_get_port_parent(ep);
- asoc_simple_card_parse_convert(dev, top, NULL, adata);
- asoc_simple_card_parse_convert(dev, node, PREFIX, adata);
- asoc_simple_card_parse_convert(dev, ports, NULL, adata);
- asoc_simple_card_parse_convert(dev, port, NULL, adata);
- asoc_simple_card_parse_convert(dev, ep, NULL, adata);
+ asoc_simple_parse_convert(dev, top, NULL, adata);
+ asoc_simple_parse_convert(dev, node, PREFIX, adata);
+ asoc_simple_parse_convert(dev, ports, NULL, adata);
+ asoc_simple_parse_convert(dev, port, NULL, adata);
+ asoc_simple_parse_convert(dev, ep, NULL, adata);
+
+ of_node_put(port);
+ of_node_put(ports);
+ of_node_put(node);
}
-static int graph_dai_link_of_dpcm(struct graph_priv *priv,
+static void graph_parse_mclk_fs(struct device_node *top,
+ struct device_node *ep,
+ struct simple_dai_props *props)
+{
+ struct device_node *port = of_get_parent(ep);
+ struct device_node *ports = of_get_parent(port);
+ struct device_node *node = of_graph_get_port_parent(ep);
+
+ of_property_read_u32(top, "mclk-fs", &props->mclk_fs);
+ of_property_read_u32(ports, "mclk-fs", &props->mclk_fs);
+ of_property_read_u32(port, "mclk-fs", &props->mclk_fs);
+ of_property_read_u32(ep, "mclk-fs", &props->mclk_fs);
+
+ of_node_put(port);
+ of_node_put(ports);
+ of_node_put(node);
+}
+
+static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
struct device_node *cpu_ep,
struct device_node *codec_ep,
struct link_info *li,
int dup_codec)
{
- struct device *dev = graph_priv_to_dev(priv);
- struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link);
- struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link);
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
struct device_node *top = dev->of_node;
struct device_node *ep = li->cpu ? cpu_ep : codec_ep;
struct device_node *port;
@@ -222,18 +220,12 @@ static int graph_dai_link_of_dpcm(struct graph_priv *priv,
dev_dbg(dev, "link_of DPCM (%pOF)\n", ep);
- of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(port, "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs);
-
- graph_get_conversion(dev, ep, &dai_props->adata);
-
of_node_put(ports);
of_node_put(port);
of_node_put(node);
if (li->cpu) {
+ int is_single_links = 0;
/* BE is dummy */
codecs->of_node = NULL;
@@ -247,23 +239,22 @@ static int graph_dai_link_of_dpcm(struct graph_priv *priv,
dai =
dai_props->cpu_dai = &priv->dais[li->dais++];
- ret = asoc_simple_card_parse_graph_cpu(ep, dai_link);
+ ret = asoc_simple_parse_cpu(ep, dai_link, &is_single_links);
if (ret)
return ret;
- ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai);
+ ret = asoc_simple_parse_clk_cpu(dev, ep, dai_link, dai);
if (ret < 0)
return ret;
- ret = asoc_simple_card_set_dailink_name(dev, dai_link,
- "fe.%s",
- dai_link->cpu_dai_name);
+ ret = asoc_simple_set_dailink_name(dev, dai_link,
+ "fe.%s",
+ dai_link->cpu_dai_name);
if (ret < 0)
return ret;
/* card->num_links includes Codec */
- asoc_simple_card_canonicalize_cpu(dai_link,
- of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
+ asoc_simple_canonicalize_cpu(dai_link, is_single_links);
} else {
struct snd_soc_codec_conf *cconf;
@@ -274,7 +265,7 @@ static int graph_dai_link_of_dpcm(struct graph_priv *priv,
/* BE settings */
dai_link->no_pcm = 1;
- dai_link->be_hw_params_fixup = graph_be_hw_params_fixup;
+ dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
dai =
dai_props->codec_dai = &priv->dais[li->dais++];
@@ -282,17 +273,17 @@ static int graph_dai_link_of_dpcm(struct graph_priv *priv,
cconf =
dai_props->codec_conf = &priv->codec_conf[li->conf++];
- ret = asoc_simple_card_parse_graph_codec(ep, dai_link);
+ ret = asoc_simple_parse_codec(ep, dai_link);
if (ret < 0)
return ret;
- ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai);
+ ret = asoc_simple_parse_clk_codec(dev, ep, dai_link, dai);
if (ret < 0)
return ret;
- ret = asoc_simple_card_set_dailink_name(dev, dai_link,
- "be.%s",
- codecs->dai_name);
+ ret = asoc_simple_set_dailink_name(dev, dai_link,
+ "be.%s",
+ codecs->dai_name);
if (ret < 0)
return ret;
@@ -307,51 +298,45 @@ static int graph_dai_link_of_dpcm(struct graph_priv *priv,
"prefix");
}
- asoc_simple_card_canonicalize_platform(dai_link);
+ graph_parse_convert(dev, ep, &dai_props->adata);
+ graph_parse_mclk_fs(top, ep, dai_props);
- ret = asoc_simple_card_of_parse_tdm(ep, dai);
+ asoc_simple_canonicalize_platform(dai_link);
+
+ ret = asoc_simple_parse_tdm(ep, dai);
if (ret)
return ret;
- ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
- NULL, &dai_link->dai_fmt);
+ ret = asoc_simple_parse_daifmt(dev, cpu_ep, codec_ep,
+ NULL, &dai_link->dai_fmt);
if (ret < 0)
return ret;
dai_link->dpcm_playback = 1;
dai_link->dpcm_capture = 1;
dai_link->ops = &graph_ops;
- dai_link->init = graph_dai_init;
+ dai_link->init = asoc_simple_dai_init;
return 0;
}
-static int graph_dai_link_of(struct graph_priv *priv,
+static int graph_dai_link_of(struct asoc_simple_priv *priv,
struct device_node *cpu_ep,
struct device_node *codec_ep,
struct link_info *li)
{
- struct device *dev = graph_priv_to_dev(priv);
- struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link);
- struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link);
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
struct device_node *top = dev->of_node;
- struct device_node *cpu_port;
- struct device_node *cpu_ports;
- struct device_node *codec_port;
- struct device_node *codec_ports;
struct asoc_simple_dai *cpu_dai;
struct asoc_simple_dai *codec_dai;
- int ret;
+ int ret, single_cpu;
/* Do it only CPU turn */
if (!li->cpu)
return 0;
- cpu_port = of_get_parent(cpu_ep);
- cpu_ports = of_get_parent(cpu_port);
- codec_port = of_get_parent(codec_ep);
- codec_ports = of_get_parent(codec_port);
-
dev_dbg(dev, "link_of (%pOF)\n", cpu_ep);
li->link++;
@@ -362,84 +347,74 @@ static int graph_dai_link_of(struct graph_priv *priv,
dai_props->codec_dai = &priv->dais[li->dais++];
/* Factor to mclk, used in hw_params() */
- of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(cpu_ports, "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(codec_ports, "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(cpu_port, "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(codec_port, "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(cpu_ep, "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(codec_ep, "mclk-fs", &dai_props->mclk_fs);
- of_node_put(cpu_port);
- of_node_put(cpu_ports);
- of_node_put(codec_port);
- of_node_put(codec_ports);
-
- ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
- NULL, &dai_link->dai_fmt);
+ graph_parse_mclk_fs(top, cpu_ep, dai_props);
+ graph_parse_mclk_fs(top, codec_ep, dai_props);
+
+ ret = asoc_simple_parse_daifmt(dev, cpu_ep, codec_ep,
+ NULL, &dai_link->dai_fmt);
if (ret < 0)
return ret;
- ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link);
+ ret = asoc_simple_parse_cpu(cpu_ep, dai_link, &single_cpu);
if (ret < 0)
return ret;
- ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link);
+ ret = asoc_simple_parse_codec(codec_ep, dai_link);
if (ret < 0)
return ret;
- ret = asoc_simple_card_of_parse_tdm(cpu_ep, cpu_dai);
+ ret = asoc_simple_parse_tdm(cpu_ep, cpu_dai);
if (ret < 0)
return ret;
- ret = asoc_simple_card_of_parse_tdm(codec_ep, codec_dai);
+ ret = asoc_simple_parse_tdm(codec_ep, codec_dai);
if (ret < 0)
return ret;
- ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai);
+ ret = asoc_simple_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai);
if (ret < 0)
return ret;
- ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai);
+ ret = asoc_simple_parse_clk_codec(dev, codec_ep, dai_link, codec_dai);
if (ret < 0)
return ret;
- ret = asoc_simple_card_set_dailink_name(dev, dai_link,
- "%s-%s",
- dai_link->cpu_dai_name,
- dai_link->codecs->dai_name);
+ ret = asoc_simple_set_dailink_name(dev, dai_link,
+ "%s-%s",
+ dai_link->cpu_dai_name,
+ dai_link->codecs->dai_name);
if (ret < 0)
return ret;
dai_link->ops = &graph_ops;
- dai_link->init = graph_dai_init;
+ dai_link->init = asoc_simple_dai_init;
- asoc_simple_card_canonicalize_platform(dai_link);
- asoc_simple_card_canonicalize_cpu(dai_link,
- of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1);
+ asoc_simple_canonicalize_cpu(dai_link, single_cpu);
+ asoc_simple_canonicalize_platform(dai_link);
return 0;
}
-static int graph_for_each_link(struct graph_priv *priv,
+static int graph_for_each_link(struct asoc_simple_priv *priv,
struct link_info *li,
- int (*func_noml)(struct graph_priv *priv,
+ int (*func_noml)(struct asoc_simple_priv *priv,
struct device_node *cpu_ep,
struct device_node *codec_ep,
struct link_info *li),
- int (*func_dpcm)(struct graph_priv *priv,
+ int (*func_dpcm)(struct asoc_simple_priv *priv,
struct device_node *cpu_ep,
struct device_node *codec_ep,
struct link_info *li, int dup_codec))
{
struct of_phandle_iterator it;
- struct device *dev = graph_priv_to_dev(priv);
+ struct device *dev = simple_priv_to_dev(priv);
struct device_node *node = dev->of_node;
struct device_node *cpu_port;
struct device_node *cpu_ep;
struct device_node *codec_ep;
struct device_node *codec_port;
struct device_node *codec_port_old = NULL;
- struct asoc_simple_card_data adata;
+ struct asoc_simple_data adata;
int rc, ret;
/* loop for all listed CPU port */
@@ -462,8 +437,8 @@ static int graph_for_each_link(struct graph_priv *priv,
/* get convert-xxx property */
memset(&adata, 0, sizeof(adata));
- graph_get_conversion(dev, codec_ep, &adata);
- graph_get_conversion(dev, cpu_ep, &adata);
+ graph_parse_convert(dev, codec_ep, &adata);
+ graph_parse_convert(dev, cpu_ep, &adata);
/*
* It is DPCM
@@ -488,17 +463,17 @@ static int graph_for_each_link(struct graph_priv *priv,
return 0;
}
-static int graph_parse_of(struct graph_priv *priv)
+static int graph_parse_of(struct asoc_simple_priv *priv)
{
- struct snd_soc_card *card = graph_priv_to_card(priv);
+ struct snd_soc_card *card = simple_priv_to_card(priv);
struct link_info li;
int ret;
- ret = asoc_simple_card_of_parse_widgets(card, NULL);
+ ret = asoc_simple_parse_widgets(card, NULL);
if (ret < 0)
return ret;
- ret = asoc_simple_card_of_parse_routing(card, NULL);
+ ret = asoc_simple_parse_routing(card, NULL);
if (ret < 0)
return ret;
@@ -523,15 +498,15 @@ static int graph_parse_of(struct graph_priv *priv)
return ret;
}
- return asoc_simple_card_parse_card_name(card, NULL);
+ return asoc_simple_parse_card_name(card, NULL);
}
-static int graph_count_noml(struct graph_priv *priv,
+static int graph_count_noml(struct asoc_simple_priv *priv,
struct device_node *cpu_ep,
struct device_node *codec_ep,
struct link_info *li)
{
- struct device *dev = graph_priv_to_dev(priv);
+ struct device *dev = simple_priv_to_dev(priv);
li->link += 1; /* 1xCPU-Codec */
li->dais += 2; /* 1xCPU + 1xCodec */
@@ -541,13 +516,13 @@ static int graph_count_noml(struct graph_priv *priv,
return 0;
}
-static int graph_count_dpcm(struct graph_priv *priv,
+static int graph_count_dpcm(struct asoc_simple_priv *priv,
struct device_node *cpu_ep,
struct device_node *codec_ep,
struct link_info *li,
int dup_codec)
{
- struct device *dev = graph_priv_to_dev(priv);
+ struct device *dev = simple_priv_to_dev(priv);
li->link++; /* 1xCPU-dummy */
li->dais++; /* 1xCPU */
@@ -563,10 +538,10 @@ static int graph_count_dpcm(struct graph_priv *priv,
return 0;
}
-static void graph_get_dais_count(struct graph_priv *priv,
+static void graph_get_dais_count(struct asoc_simple_priv *priv,
struct link_info *li)
{
- struct device *dev = graph_priv_to_dev(priv);
+ struct device *dev = simple_priv_to_dev(priv);
/*
* link_num : number of links.
@@ -623,14 +598,14 @@ static void graph_get_dais_count(struct graph_priv *priv,
static int graph_card_probe(struct snd_soc_card *card)
{
- struct graph_priv *priv = snd_soc_card_get_drvdata(card);
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
int ret;
- ret = asoc_simple_card_init_hp(card, &priv->hp_jack, NULL);
+ ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL);
if (ret < 0)
return ret;
- ret = asoc_simple_card_init_mic(card, &priv->mic_jack, NULL);
+ ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL);
if (ret < 0)
return ret;
@@ -639,22 +614,18 @@ static int graph_card_probe(struct snd_soc_card *card)
static int graph_probe(struct platform_device *pdev)
{
- struct graph_priv *priv;
- struct snd_soc_dai_link *dai_link;
- struct graph_dai_props *dai_props;
- struct asoc_simple_dai *dais;
+ struct asoc_simple_priv *priv;
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
- struct snd_soc_codec_conf *cconf;
struct link_info li;
- int ret, i;
+ int ret;
/* Allocate the private data and the DAI link array */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- card = graph_priv_to_card(priv);
+ card = simple_priv_to_card(priv);
card->owner = THIS_MODULE;
card->dev = dev;
card->dapm_widgets = graph_dapm_widgets;
@@ -666,25 +637,9 @@ static int graph_probe(struct platform_device *pdev)
if (!li.link || !li.dais)
return -EINVAL;
- dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL);
- dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL);
- dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL);
- cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL);
- if (!dai_props || !dai_link || !dais)
- return -ENOMEM;
-
- /*
- * Use snd_soc_dai_link_component instead of legacy style
- * It is codec only. but cpu/platform will be supported in the future.
- * see
- * soc-core.c :: snd_soc_init_multicodec()
- */
- for (i = 0; i < li.link; i++) {
- dai_link[i].codecs = &dai_props[i].codecs;
- dai_link[i].num_codecs = 1;
- dai_link[i].platforms = &dai_props[i].platforms;
- dai_link[i].num_platforms = 1;
- }
+ ret = asoc_simple_init_priv(priv, &li);
+ if (ret < 0)
+ return ret;
priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
if (IS_ERR(priv->pa_gpio)) {
@@ -693,16 +648,6 @@ static int graph_probe(struct platform_device *pdev)
return ret;
}
- priv->dai_props = dai_props;
- priv->dai_link = dai_link;
- priv->dais = dais;
- priv->codec_conf = cconf;
-
- card->dai_link = dai_link;
- card->num_links = li.link;
- card->codec_conf = cconf;
- card->num_configs = li.conf;
-
ret = graph_parse_of(priv);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
@@ -712,13 +657,15 @@ static int graph_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, priv);
+ asoc_simple_debug_info(priv);
+
ret = devm_snd_soc_register_card(dev, card);
if (ret < 0)
goto err;
return 0;
err:
- asoc_simple_card_clean_reference(card);
+ asoc_simple_clean_reference(card);
return ret;
}
@@ -727,7 +674,7 @@ static int graph_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
- return asoc_simple_card_clean_reference(card);
+ return asoc_simple_clean_reference(card);
}
static const struct of_device_id graph_of_match[] = {
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 5c1424f03620..db1458a19985 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -14,8 +14,8 @@
#include <sound/jack.h>
#include <sound/simple_card_utils.h>
-void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
- struct snd_pcm_hw_params *params)
+void asoc_simple_convert_fixup(struct asoc_simple_data *data,
+ struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
@@ -30,12 +30,12 @@ void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data,
channels->min =
channels->max = data->convert_channels;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup);
+EXPORT_SYMBOL_GPL(asoc_simple_convert_fixup);
-void asoc_simple_card_parse_convert(struct device *dev,
- struct device_node *np,
- char *prefix,
- struct asoc_simple_card_data *data)
+void asoc_simple_parse_convert(struct device *dev,
+ struct device_node *np,
+ char *prefix,
+ struct asoc_simple_data *data)
{
char prop[128];
@@ -49,17 +49,14 @@ void asoc_simple_card_parse_convert(struct device *dev,
/* channels transfer */
snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels");
of_property_read_u32(np, prop, &data->convert_channels);
-
- dev_dbg(dev, "convert_rate %d\n", data->convert_rate);
- dev_dbg(dev, "convert_channels %d\n", data->convert_channels);
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_convert);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_convert);
-int asoc_simple_card_parse_daifmt(struct device *dev,
- struct device_node *node,
- struct device_node *codec,
- char *prefix,
- unsigned int *retfmt)
+int asoc_simple_parse_daifmt(struct device *dev,
+ struct device_node *node,
+ struct device_node *codec,
+ char *prefix,
+ unsigned int *retfmt)
{
struct device_node *bitclkmaster = NULL;
struct device_node *framemaster = NULL;
@@ -93,15 +90,13 @@ int asoc_simple_card_parse_daifmt(struct device *dev,
*retfmt = daifmt;
- dev_dbg(dev, "format : %04x\n", daifmt);
-
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_daifmt);
-int asoc_simple_card_set_dailink_name(struct device *dev,
- struct snd_soc_dai_link *dai_link,
- const char *fmt, ...)
+int asoc_simple_set_dailink_name(struct device *dev,
+ struct snd_soc_dai_link *dai_link,
+ const char *fmt, ...)
{
va_list ap;
char *name = NULL;
@@ -116,16 +111,14 @@ int asoc_simple_card_set_dailink_name(struct device *dev,
dai_link->name = name;
dai_link->stream_name = name;
-
- dev_dbg(dev, "name : %s\n", name);
}
return ret;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
+EXPORT_SYMBOL_GPL(asoc_simple_set_dailink_name);
-int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
- char *prefix)
+int asoc_simple_parse_card_name(struct snd_soc_card *card,
+ char *prefix)
{
int ret;
@@ -146,34 +139,30 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
if (!card->name && card->dai_link)
card->name = card->dai_link->name;
- dev_dbg(card->dev, "Card Name: %s\n", card->name ? card->name : "");
-
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_card_name);
-int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai)
+static int asoc_simple_clk_enable(struct asoc_simple_dai *dai)
{
if (dai)
return clk_prepare_enable(dai->clk);
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable);
-void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai)
+static void asoc_simple_clk_disable(struct asoc_simple_dai *dai)
{
if (dai)
clk_disable_unprepare(dai->clk);
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable);
-
-int asoc_simple_card_parse_clk(struct device *dev,
- struct device_node *node,
- struct device_node *dai_of_node,
- struct asoc_simple_dai *simple_dai,
- const char *dai_name,
- struct snd_soc_dai_link_component *dlc)
+
+int asoc_simple_parse_clk(struct device *dev,
+ struct device_node *node,
+ struct device_node *dai_of_node,
+ struct asoc_simple_dai *simple_dai,
+ const char *dai_name,
+ struct snd_soc_dai_link_component *dlc)
{
struct clk *clk;
u32 val;
@@ -184,10 +173,8 @@ int asoc_simple_card_parse_clk(struct device *dev,
* see
* soc-core.c :: snd_soc_init_multicodec()
*/
- if (dlc) {
+ if (dlc)
dai_of_node = dlc->of_node;
- dai_name = dlc->dai_name;
- }
/*
* Parse dai->sysclk come from "clocks = <&xxx>"
@@ -211,158 +198,113 @@ int asoc_simple_card_parse_clk(struct device *dev,
if (of_property_read_bool(node, "system-clock-direction-out"))
simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
- dev_dbg(dev, "%s : sysclk = %d, direction %d\n", dai_name,
- simple_dai->sysclk, simple_dai->clk_direction);
-
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk);
-
-int asoc_simple_card_parse_dai(struct device_node *node,
- struct snd_soc_dai_link_component *dlc,
- struct device_node **dai_of_node,
- const char **dai_name,
- const char *list_name,
- const char *cells_name,
- int *is_single_link)
+EXPORT_SYMBOL_GPL(asoc_simple_parse_clk);
+
+int asoc_simple_startup(struct snd_pcm_substream *substream)
{
- struct of_phandle_args args;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
int ret;
- if (!node)
- return 0;
-
- /*
- * Use snd_soc_dai_link_component instead of legacy style.
- * It is only for codec, but cpu will be supported in the future.
- * see
- * soc-core.c :: snd_soc_init_multicodec()
- */
- if (dlc) {
- dai_name = &dlc->dai_name;
- dai_of_node = &dlc->of_node;
- }
-
- /*
- * Get node via "sound-dai = <&phandle port>"
- * it will be used as xxx_of_node on soc_bind_dai_link()
- */
- ret = of_parse_phandle_with_args(node, list_name, cells_name, 0, &args);
+ ret = asoc_simple_clk_enable(dai_props->cpu_dai);
if (ret)
return ret;
- /* Get dai->name */
- if (dai_name) {
- ret = snd_soc_of_get_dai_name(node, dai_name);
- if (ret < 0)
- return ret;
- }
-
- *dai_of_node = args.np;
-
- if (is_single_link)
- *is_single_link = !args.args_count;
+ ret = asoc_simple_clk_enable(dai_props->codec_dai);
+ if (ret)
+ asoc_simple_clk_disable(dai_props->cpu_dai);
- return 0;
+ return ret;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
+EXPORT_SYMBOL_GPL(asoc_simple_startup);
-static int asoc_simple_card_get_dai_id(struct device_node *ep)
+void asoc_simple_shutdown(struct snd_pcm_substream *substream)
{
- struct device_node *node;
- struct device_node *endpoint;
- struct of_endpoint info;
- int i, id;
- int ret;
-
- /* use driver specified DAI ID if exist */
- ret = snd_soc_get_dai_id(ep);
- if (ret != -ENOTSUPP)
- return ret;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props =
+ simple_priv_to_props(priv, rtd->num);
- /* use endpoint/port reg if exist */
- ret = of_graph_parse_endpoint(ep, &info);
- if (ret == 0) {
- /*
- * Because it will count port/endpoint if it doesn't have "reg".
- * But, we can't judge whether it has "no reg", or "reg = <0>"
- * only of_graph_parse_endpoint().
- * We need to check "reg" property
- */
- if (of_get_property(ep, "reg", NULL))
- return info.id;
+ asoc_simple_clk_disable(dai_props->cpu_dai);
- node = of_get_parent(ep);
- of_node_put(node);
- if (of_get_property(node, "reg", NULL))
- return info.port;
- }
- node = of_graph_get_port_parent(ep);
+ asoc_simple_clk_disable(dai_props->codec_dai);
+}
+EXPORT_SYMBOL_GPL(asoc_simple_shutdown);
- /*
- * Non HDMI sound case, counting port/endpoint on its DT
- * is enough. Let's count it.
- */
- i = 0;
- id = -1;
- for_each_endpoint_of_node(node, endpoint) {
- if (endpoint == ep)
- id = i;
- i++;
- }
+static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
+ unsigned long rate)
+{
+ if (!simple_dai)
+ return 0;
- of_node_put(node);
+ if (!simple_dai->clk)
+ return 0;
- if (id < 0)
- return -ENODEV;
+ if (clk_get_rate(simple_dai->clk) == rate)
+ return 0;
- return id;
+ return clk_set_rate(simple_dai->clk, rate);
}
-int asoc_simple_card_parse_graph_dai(struct device_node *ep,
- struct snd_soc_dai_link_component *dlc,
- struct device_node **dai_of_node,
- const char **dai_name)
+int asoc_simple_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
- struct device_node *node;
- struct of_phandle_args args;
- int ret;
-
- /*
- * Use snd_soc_dai_link_component instead of legacy style.
- * It is only for codec, but cpu will be supported in the future.
- * see
- * soc-core.c :: snd_soc_init_multicodec()
- */
- if (dlc) {
- dai_name = &dlc->dai_name;
- dai_of_node = &dlc->of_node;
- }
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props =
+ simple_priv_to_props(priv, rtd->num);
+ unsigned int mclk, mclk_fs = 0;
+ int ret = 0;
+
+ if (dai_props->mclk_fs)
+ mclk_fs = dai_props->mclk_fs;
+
+ if (mclk_fs) {
+ mclk = params_rate(params) * mclk_fs;
+
+ ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk);
+ if (ret < 0)
+ return ret;
- if (!ep)
- return 0;
- if (!dai_name)
- return 0;
+ ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk);
+ if (ret < 0)
+ return ret;
- node = of_graph_get_port_parent(ep);
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (ret && ret != -ENOTSUPP)
+ goto err;
- /* Get dai->name */
- args.np = node;
- args.args[0] = asoc_simple_card_get_dai_id(ep);
- args.args_count = (of_graph_get_endpoint_count(node) > 1);
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
+ SND_SOC_CLOCK_OUT);
+ if (ret && ret != -ENOTSUPP)
+ goto err;
+ }
+ return 0;
+err:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_hw_params);
- ret = snd_soc_get_dai_name(&args, dai_name);
- if (ret < 0)
- return ret;
+int asoc_simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
- *dai_of_node = node;
+ asoc_simple_convert_fixup(&dai_props->adata, params);
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai);
+EXPORT_SYMBOL_GPL(asoc_simple_be_hw_params_fixup);
-int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
- struct asoc_simple_dai *simple_dai)
+static int asoc_simple_init_dai(struct snd_soc_dai *dai,
+ struct asoc_simple_dai *simple_dai)
{
int ret;
@@ -392,18 +334,37 @@ int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai);
-void asoc_simple_card_canonicalize_platform(struct snd_soc_dai_link *dai_link)
+int asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
+ int ret;
+
+ ret = asoc_simple_init_dai(rtd->codec_dai,
+ dai_props->codec_dai);
+ if (ret < 0)
+ return ret;
+
+ ret = asoc_simple_init_dai(rtd->cpu_dai,
+ dai_props->cpu_dai);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_dai_init);
+
+void asoc_simple_canonicalize_platform(struct snd_soc_dai_link *dai_link)
{
/* Assumes platform == cpu */
if (!dai_link->platforms->of_node)
dai_link->platforms->of_node = dai_link->cpu_of_node;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_platform);
+EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
-void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
- int is_single_links)
+void asoc_simple_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
+ int is_single_links)
{
/*
* In soc_bind_dai_link() will check cpu name after
@@ -417,9 +378,9 @@ void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
if (is_single_links)
dai_link->cpu_dai_name = NULL;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_cpu);
+EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_cpu);
-int asoc_simple_card_clean_reference(struct snd_soc_card *card)
+int asoc_simple_clean_reference(struct snd_soc_card *card)
{
struct snd_soc_dai_link *dai_link;
int i;
@@ -430,10 +391,10 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card)
}
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference);
+EXPORT_SYMBOL_GPL(asoc_simple_clean_reference);
-int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
- char *prefix)
+int asoc_simple_parse_routing(struct snd_soc_card *card,
+ char *prefix)
{
struct device_node *node = card->dev->of_node;
char prop[128];
@@ -448,10 +409,10 @@ int asoc_simple_card_of_parse_routing(struct snd_soc_card *card,
return snd_soc_of_parse_audio_routing(card, prop);
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_routing);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_routing);
-int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
- char *prefix)
+int asoc_simple_parse_widgets(struct snd_soc_card *card,
+ char *prefix)
{
struct device_node *node = card->dev->of_node;
char prop[128];
@@ -467,11 +428,11 @@ int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card,
/* no widgets is not error */
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_of_parse_widgets);
+EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets);
-int asoc_simple_card_init_jack(struct snd_soc_card *card,
- struct asoc_simple_jack *sjack,
- int is_hp, char *prefix)
+int asoc_simple_init_jack(struct snd_soc_card *card,
+ struct asoc_simple_jack *sjack,
+ int is_hp, char *prefix)
{
struct device *dev = card->dev;
enum of_gpio_flags flags;
@@ -522,7 +483,61 @@ int asoc_simple_card_init_jack(struct snd_soc_card *card,
return 0;
}
-EXPORT_SYMBOL_GPL(asoc_simple_card_init_jack);
+EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
+
+int asoc_simple_init_priv(struct asoc_simple_priv *priv,
+ struct link_info *li)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link;
+ struct simple_dai_props *dai_props;
+ struct asoc_simple_dai *dais;
+ struct snd_soc_codec_conf *cconf = NULL;
+ int i;
+
+ dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
+ dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL);
+ dais = devm_kcalloc(dev, li->dais, sizeof(*dais), GFP_KERNEL);
+ if (!dai_props || !dai_link || !dais)
+ return -ENOMEM;
+
+ if (li->conf) {
+ cconf = devm_kcalloc(dev, li->conf, sizeof(*cconf), GFP_KERNEL);
+ if (!cconf)
+ return -ENOMEM;
+ }
+
+ /*
+ * Use snd_soc_dai_link_component instead of legacy style
+ * It is codec only. but cpu/platform will be supported in the future.
+ * see
+ * soc-core.c :: snd_soc_init_multicodec()
+ *
+ * "platform" might be removed
+ * see
+ * simple-card-utils.c :: asoc_simple_canonicalize_platform()
+ */
+ for (i = 0; i < li->link; i++) {
+ dai_link[i].codecs = &dai_props[i].codecs;
+ dai_link[i].num_codecs = 1;
+ dai_link[i].platforms = &dai_props[i].platforms;
+ dai_link[i].num_platforms = 1;
+ }
+
+ priv->dai_props = dai_props;
+ priv->dai_link = dai_link;
+ priv->dais = dais;
+ priv->codec_conf = cconf;
+
+ card->dai_link = priv->dai_link;
+ card->num_links = li->link;
+ card->codec_conf = cconf;
+ card->num_configs = li->conf;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_init_priv);
/* Module information */
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 7147bba45a2a..dd9ac60d61f1 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -15,179 +15,98 @@
#include <sound/soc-dai.h>
#include <sound/soc.h>
-struct simple_priv {
- struct snd_soc_card snd_card;
- struct simple_dai_props {
- struct asoc_simple_dai *cpu_dai;
- struct asoc_simple_dai *codec_dai;
- struct snd_soc_dai_link_component codecs; /* single codec */
- struct snd_soc_dai_link_component platforms;
- struct asoc_simple_card_data adata;
- struct snd_soc_codec_conf *codec_conf;
- unsigned int mclk_fs;
- } *dai_props;
- struct asoc_simple_jack hp_jack;
- struct asoc_simple_jack mic_jack;
- struct snd_soc_dai_link *dai_link;
- struct asoc_simple_dai *dais;
- struct snd_soc_codec_conf *codec_conf;
-};
-
-struct link_info {
- int dais; /* number of dai */
- int link; /* number of link */
- int conf; /* number of codec_conf */
- int cpu; /* turn for CPU / Codec */
-};
-
-#define simple_priv_to_card(priv) (&(priv)->snd_card)
-#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
-#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
-#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
-
#define DAI "sound-dai"
#define CELL "#sound-dai-cells"
#define PREFIX "simple-audio-card,"
-static int simple_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct simple_dai_props *dai_props =
- simple_priv_to_props(priv, rtd->num);
- int ret;
-
- ret = asoc_simple_card_clk_enable(dai_props->cpu_dai);
- if (ret)
- return ret;
-
- ret = asoc_simple_card_clk_enable(dai_props->codec_dai);
- if (ret)
- asoc_simple_card_clk_disable(dai_props->cpu_dai);
-
- return ret;
-}
-
-static void simple_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct simple_dai_props *dai_props =
- simple_priv_to_props(priv, rtd->num);
-
- asoc_simple_card_clk_disable(dai_props->cpu_dai);
-
- asoc_simple_card_clk_disable(dai_props->codec_dai);
-}
+static const struct snd_soc_ops simple_ops = {
+ .startup = asoc_simple_startup,
+ .shutdown = asoc_simple_shutdown,
+ .hw_params = asoc_simple_hw_params,
+};
-static int simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
- unsigned long rate)
+static int asoc_simple_parse_dai(struct device_node *node,
+ struct snd_soc_dai_link_component *dlc,
+ struct device_node **dai_of_node,
+ const char **dai_name,
+ int *is_single_link)
{
- if (!simple_dai)
- return 0;
-
- if (!simple_dai->clk)
- return 0;
+ struct of_phandle_args args;
+ int ret;
- if (clk_get_rate(simple_dai->clk) == rate)
+ if (!node)
return 0;
- return clk_set_rate(simple_dai->clk, rate);
-}
-
-static int simple_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct simple_dai_props *dai_props =
- simple_priv_to_props(priv, rtd->num);
- unsigned int mclk, mclk_fs = 0;
- int ret = 0;
-
- if (dai_props->mclk_fs)
- mclk_fs = dai_props->mclk_fs;
-
- if (mclk_fs) {
- mclk = params_rate(params) * mclk_fs;
+ /*
+ * Use snd_soc_dai_link_component instead of legacy style.
+ * It is only for codec, but cpu will be supported in the future.
+ * see
+ * soc-core.c :: snd_soc_init_multicodec()
+ */
+ if (dlc) {
+ dai_name = &dlc->dai_name;
+ dai_of_node = &dlc->of_node;
+ }
- ret = simple_set_clk_rate(dai_props->codec_dai, mclk);
- if (ret < 0)
- return ret;
+ /*
+ * Get node via "sound-dai = <&phandle port>"
+ * it will be used as xxx_of_node on soc_bind_dai_link()
+ */
+ ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
+ if (ret)
+ return ret;
- ret = simple_set_clk_rate(dai_props->cpu_dai, mclk);
+ /* Get dai->name */
+ if (dai_name) {
+ ret = snd_soc_of_get_dai_name(node, dai_name);
if (ret < 0)
return ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
- SND_SOC_CLOCK_IN);
- if (ret && ret != -ENOTSUPP)
- goto err;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
- SND_SOC_CLOCK_OUT);
- if (ret && ret != -ENOTSUPP)
- goto err;
}
- return 0;
-err:
- return ret;
-}
-
-static const struct snd_soc_ops simple_ops = {
- .startup = simple_startup,
- .shutdown = simple_shutdown,
- .hw_params = simple_hw_params,
-};
-
-static int simple_dai_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
- int ret;
- ret = asoc_simple_card_init_dai(rtd->codec_dai,
- dai_props->codec_dai);
- if (ret < 0)
- return ret;
+ *dai_of_node = args.np;
- ret = asoc_simple_card_init_dai(rtd->cpu_dai,
- dai_props->cpu_dai);
- if (ret < 0)
- return ret;
+ if (is_single_link)
+ *is_single_link = !args.args_count;
return 0;
}
-static int simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
+static void simple_parse_convert(struct device *dev,
+ struct device_node *np,
+ struct asoc_simple_data *adata)
{
- struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
+ struct device_node *top = dev->of_node;
+ struct device_node *node = of_get_parent(np);
- asoc_simple_card_convert_fixup(&dai_props->adata, params);
+ asoc_simple_parse_convert(dev, top, PREFIX, adata);
+ asoc_simple_parse_convert(dev, node, PREFIX, adata);
+ asoc_simple_parse_convert(dev, node, NULL, adata);
+ asoc_simple_parse_convert(dev, np, NULL, adata);
- return 0;
+ of_node_put(node);
}
-static void simple_get_conversion(struct device *dev,
- struct device_node *np,
- struct asoc_simple_card_data *adata)
+static void simple_parse_mclk_fs(struct device_node *top,
+ struct device_node *cpu,
+ struct device_node *codec,
+ struct simple_dai_props *props,
+ char *prefix)
{
- struct device_node *top = dev->of_node;
- struct device_node *node = of_get_parent(np);
+ struct device_node *node = of_get_parent(cpu);
+ char prop[128];
+
+ snprintf(prop, sizeof(prop), "%smclk-fs", PREFIX);
+ of_property_read_u32(top, prop, &props->mclk_fs);
- asoc_simple_card_parse_convert(dev, top, PREFIX, adata);
- asoc_simple_card_parse_convert(dev, node, PREFIX, adata);
- asoc_simple_card_parse_convert(dev, node, NULL, adata);
- asoc_simple_card_parse_convert(dev, np, NULL, adata);
+ snprintf(prop, sizeof(prop), "%smclk-fs", prefix);
+ of_property_read_u32(node, prop, &props->mclk_fs);
+ of_property_read_u32(cpu, prop, &props->mclk_fs);
+ of_property_read_u32(codec, prop, &props->mclk_fs);
of_node_put(node);
}
-static int simple_dai_link_of_dpcm(struct simple_priv *priv,
+static int simple_dai_link_of_dpcm(struct asoc_simple_priv *priv,
struct device_node *np,
struct device_node *codec,
struct link_info *li,
@@ -200,7 +119,6 @@ static int simple_dai_link_of_dpcm(struct simple_priv *priv,
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
struct device_node *top = dev->of_node;
struct device_node *node = of_get_parent(np);
- char prop[128];
char *prefix = "";
int ret;
@@ -238,22 +156,21 @@ static int simple_dai_link_of_dpcm(struct simple_priv *priv,
dai =
dai_props->cpu_dai = &priv->dais[li->dais++];
- ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
- &is_single_links);
+ ret = asoc_simple_parse_cpu(np, dai_link, &is_single_links);
if (ret)
return ret;
- ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai);
+ ret = asoc_simple_parse_clk_cpu(dev, np, dai_link, dai);
if (ret < 0)
return ret;
- ret = asoc_simple_card_set_dailink_name(dev, dai_link,
- "fe.%s",
- dai_link->cpu_dai_name);
+ ret = asoc_simple_set_dailink_name(dev, dai_link,
+ "fe.%s",
+ dai_link->cpu_dai_name);
if (ret < 0)
return ret;
- asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
+ asoc_simple_canonicalize_cpu(dai_link, is_single_links);
} else {
struct snd_soc_codec_conf *cconf;
@@ -264,7 +181,7 @@ static int simple_dai_link_of_dpcm(struct simple_priv *priv,
/* BE settings */
dai_link->no_pcm = 1;
- dai_link->be_hw_params_fixup = simple_be_hw_params_fixup;
+ dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup;
dai =
dai_props->codec_dai = &priv->dais[li->dais++];
@@ -272,17 +189,17 @@ static int simple_dai_link_of_dpcm(struct simple_priv *priv,
cconf =
dai_props->codec_conf = &priv->codec_conf[li->conf++];
- ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
+ ret = asoc_simple_parse_codec(np, dai_link);
if (ret < 0)
return ret;
- ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai);
+ ret = asoc_simple_parse_clk_codec(dev, np, dai_link, dai);
if (ret < 0)
return ret;
- ret = asoc_simple_card_set_dailink_name(dev, dai_link,
- "be.%s",
- codecs->dai_name);
+ ret = asoc_simple_set_dailink_name(dev, dai_link,
+ "be.%s",
+ codecs->dai_name);
if (ret < 0)
return ret;
@@ -295,33 +212,29 @@ static int simple_dai_link_of_dpcm(struct simple_priv *priv,
"prefix");
}
- simple_get_conversion(dev, np, &dai_props->adata);
+ simple_parse_convert(dev, np, &dai_props->adata);
+ simple_parse_mclk_fs(top, np, codec, dai_props, prefix);
- asoc_simple_card_canonicalize_platform(dai_link);
+ asoc_simple_canonicalize_platform(dai_link);
- ret = asoc_simple_card_of_parse_tdm(np, dai);
+ ret = asoc_simple_parse_tdm(np, dai);
if (ret)
return ret;
- snprintf(prop, sizeof(prop), "%smclk-fs", prefix);
- of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(node, prop, &dai_props->mclk_fs);
- of_property_read_u32(np, prop, &dai_props->mclk_fs);
-
- ret = asoc_simple_card_parse_daifmt(dev, node, codec,
- prefix, &dai_link->dai_fmt);
+ ret = asoc_simple_parse_daifmt(dev, node, codec,
+ prefix, &dai_link->dai_fmt);
if (ret < 0)
return ret;
dai_link->dpcm_playback = 1;
dai_link->dpcm_capture = 1;
dai_link->ops = &simple_ops;
- dai_link->init = simple_dai_init;
+ dai_link->init = asoc_simple_dai_init;
return 0;
}
-static int simple_dai_link_of(struct simple_priv *priv,
+static int simple_dai_link_of(struct asoc_simple_priv *priv,
struct device_node *np,
struct device_node *codec,
struct link_info *li,
@@ -367,58 +280,53 @@ static int simple_dai_link_of(struct simple_priv *priv,
codec_dai =
dai_props->codec_dai = &priv->dais[li->dais++];
- ret = asoc_simple_card_parse_daifmt(dev, node, codec,
- prefix, &dai_link->dai_fmt);
+ ret = asoc_simple_parse_daifmt(dev, node, codec,
+ prefix, &dai_link->dai_fmt);
if (ret < 0)
goto dai_link_of_err;
- snprintf(prop, sizeof(prop), "%smclk-fs", prefix);
- of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs);
- of_property_read_u32(node, prop, &dai_props->mclk_fs);
- of_property_read_u32(cpu, prop, &dai_props->mclk_fs);
- of_property_read_u32(codec, prop, &dai_props->mclk_fs);
+ simple_parse_mclk_fs(top, cpu, codec, dai_props, prefix);
- ret = asoc_simple_card_parse_cpu(cpu, dai_link,
- DAI, CELL, &single_cpu);
+ ret = asoc_simple_parse_cpu(cpu, dai_link, &single_cpu);
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL);
+ ret = asoc_simple_parse_codec(codec, dai_link);
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL);
+ ret = asoc_simple_parse_platform(plat, dai_link);
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_card_of_parse_tdm(cpu, cpu_dai);
+ ret = asoc_simple_parse_tdm(cpu, cpu_dai);
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_card_of_parse_tdm(codec, codec_dai);
+ ret = asoc_simple_parse_tdm(codec, codec_dai);
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
+ ret = asoc_simple_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);
+ ret = asoc_simple_parse_clk_codec(dev, codec, dai_link, codec_dai);
if (ret < 0)
goto dai_link_of_err;
- ret = asoc_simple_card_set_dailink_name(dev, dai_link,
- "%s-%s",
- dai_link->cpu_dai_name,
- dai_link->codecs->dai_name);
+ ret = asoc_simple_set_dailink_name(dev, dai_link,
+ "%s-%s",
+ dai_link->cpu_dai_name,
+ dai_link->codecs->dai_name);
if (ret < 0)
goto dai_link_of_err;
dai_link->ops = &simple_ops;
- dai_link->init = simple_dai_init;
+ dai_link->init = asoc_simple_dai_init;
- asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
- asoc_simple_card_canonicalize_platform(dai_link);
+ asoc_simple_canonicalize_cpu(dai_link, single_cpu);
+ asoc_simple_canonicalize_platform(dai_link);
dai_link_of_err:
of_node_put(plat);
@@ -427,13 +335,13 @@ dai_link_of_err:
return ret;
}
-static int simple_for_each_link(struct simple_priv *priv,
+static int simple_for_each_link(struct asoc_simple_priv *priv,
struct link_info *li,
- int (*func_noml)(struct simple_priv *priv,
+ int (*func_noml)(struct asoc_simple_priv *priv,
struct device_node *np,
struct device_node *codec,
struct link_info *li, bool is_top),
- int (*func_dpcm)(struct simple_priv *priv,
+ int (*func_dpcm)(struct asoc_simple_priv *priv,
struct device_node *np,
struct device_node *codec,
struct link_info *li, bool is_top))
@@ -453,7 +361,7 @@ static int simple_for_each_link(struct simple_priv *priv,
/* loop for all dai-link */
do {
- struct asoc_simple_card_data adata;
+ struct asoc_simple_data adata;
struct device_node *codec;
struct device_node *np;
int num = of_get_child_count(node);
@@ -471,7 +379,7 @@ static int simple_for_each_link(struct simple_priv *priv,
/* get convert-xxx property */
memset(&adata, 0, sizeof(adata));
for_each_child_of_node(node, np)
- simple_get_conversion(dev, np, &adata);
+ simple_parse_convert(dev, np, &adata);
/* loop for all CPU/Codec node */
for_each_child_of_node(node, np) {
@@ -502,7 +410,7 @@ static int simple_for_each_link(struct simple_priv *priv,
}
static int simple_parse_aux_devs(struct device_node *node,
- struct simple_priv *priv)
+ struct asoc_simple_priv *priv)
{
struct device *dev = simple_priv_to_dev(priv);
struct device_node *aux_node;
@@ -532,7 +440,7 @@ static int simple_parse_aux_devs(struct device_node *node,
return 0;
}
-static int simple_parse_of(struct simple_priv *priv)
+static int simple_parse_of(struct asoc_simple_priv *priv)
{
struct device *dev = simple_priv_to_dev(priv);
struct device_node *top = dev->of_node;
@@ -543,11 +451,11 @@ static int simple_parse_of(struct simple_priv *priv)
if (!top)
return -EINVAL;
- ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
+ ret = asoc_simple_parse_widgets(card, PREFIX);
if (ret < 0)
return ret;
- ret = asoc_simple_card_of_parse_routing(card, PREFIX);
+ ret = asoc_simple_parse_routing(card, PREFIX);
if (ret < 0)
return ret;
@@ -573,7 +481,7 @@ static int simple_parse_of(struct simple_priv *priv)
return ret;
}
- ret = asoc_simple_card_parse_card_name(card, PREFIX);
+ ret = asoc_simple_parse_card_name(card, PREFIX);
if (ret < 0)
return ret;
@@ -582,7 +490,7 @@ static int simple_parse_of(struct simple_priv *priv)
return ret;
}
-static int simple_count_noml(struct simple_priv *priv,
+static int simple_count_noml(struct asoc_simple_priv *priv,
struct device_node *np,
struct device_node *codec,
struct link_info *li, bool is_top)
@@ -594,7 +502,7 @@ static int simple_count_noml(struct simple_priv *priv,
return 0;
}
-static int simple_count_dpcm(struct simple_priv *priv,
+static int simple_count_dpcm(struct asoc_simple_priv *priv,
struct device_node *np,
struct device_node *codec,
struct link_info *li, bool is_top)
@@ -607,7 +515,7 @@ static int simple_count_dpcm(struct simple_priv *priv,
return 0;
}
-static void simple_get_dais_count(struct simple_priv *priv,
+static void simple_get_dais_count(struct asoc_simple_priv *priv,
struct link_info *li)
{
struct device *dev = simple_priv_to_dev(priv);
@@ -676,14 +584,14 @@ static void simple_get_dais_count(struct simple_priv *priv,
static int simple_soc_probe(struct snd_soc_card *card)
{
- struct simple_priv *priv = snd_soc_card_get_drvdata(card);
+ struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
int ret;
- ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX);
+ ret = asoc_simple_init_hp(card, &priv->hp_jack, PREFIX);
if (ret < 0)
return ret;
- ret = asoc_simple_card_init_mic(card, &priv->mic_jack, PREFIX);
+ ret = asoc_simple_init_mic(card, &priv->mic_jack, PREFIX);
if (ret < 0)
return ret;
@@ -692,16 +600,12 @@ static int simple_soc_probe(struct snd_soc_card *card)
static int simple_probe(struct platform_device *pdev)
{
- struct simple_priv *priv;
- struct snd_soc_dai_link *dai_link;
- struct simple_dai_props *dai_props;
- struct asoc_simple_dai *dais;
+ struct asoc_simple_priv *priv;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct snd_soc_card *card;
- struct snd_soc_codec_conf *cconf;
struct link_info li;
- int ret, i;
+ int ret;
/* Allocate the private data and the DAI link array */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -718,35 +622,9 @@ static int simple_probe(struct platform_device *pdev)
if (!li.link || !li.dais)
return -EINVAL;
- dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL);
- dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL);
- dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL);
- cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL);
- if (!dai_props || !dai_link || !dais)
- return -ENOMEM;
-
- /*
- * Use snd_soc_dai_link_component instead of legacy style
- * It is codec only. but cpu/platform will be supported in the future.
- * see
- * soc-core.c :: snd_soc_init_multicodec()
- */
- for (i = 0; i < li.link; i++) {
- dai_link[i].codecs = &dai_props[i].codecs;
- dai_link[i].num_codecs = 1;
- dai_link[i].platforms = &dai_props[i].platforms;
- dai_link[i].num_platforms = 1;
- }
-
- priv->dai_props = dai_props;
- priv->dai_link = dai_link;
- priv->dais = dais;
- priv->codec_conf = cconf;
-
- card->dai_link = priv->dai_link;
- card->num_links = li.link;
- card->codec_conf = cconf;
- card->num_configs = li.conf;
+ ret = asoc_simple_init_priv(priv, &li);
+ if (ret < 0)
+ return ret;
if (np && of_device_is_available(np)) {
@@ -761,6 +639,9 @@ static int simple_probe(struct platform_device *pdev)
struct asoc_simple_card_info *cinfo;
struct snd_soc_dai_link_component *codecs;
struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dai_link = priv->dai_link;
+ struct simple_dai_props *dai_props = priv->dai_props;
+
int dai_idx = 0;
cinfo = dev->platform_data;
@@ -793,22 +674,24 @@ static int simple_probe(struct platform_device *pdev)
dai_link->stream_name = cinfo->name;
dai_link->cpu_dai_name = cinfo->cpu_dai.name;
dai_link->dai_fmt = cinfo->daifmt;
- dai_link->init = simple_dai_init;
- memcpy(priv->dai_props->cpu_dai, &cinfo->cpu_dai,
- sizeof(*priv->dai_props->cpu_dai));
- memcpy(priv->dai_props->codec_dai, &cinfo->codec_dai,
- sizeof(*priv->dai_props->codec_dai));
+ dai_link->init = asoc_simple_dai_init;
+ memcpy(dai_props->cpu_dai, &cinfo->cpu_dai,
+ sizeof(*dai_props->cpu_dai));
+ memcpy(dai_props->codec_dai, &cinfo->codec_dai,
+ sizeof(*dai_props->codec_dai));
}
snd_soc_card_set_drvdata(card, priv);
+ asoc_simple_debug_info(priv);
+
ret = devm_snd_soc_register_card(dev, card);
if (ret < 0)
goto err;
return 0;
err:
- asoc_simple_card_clean_reference(card);
+ asoc_simple_clean_reference(card);
return ret;
}
@@ -817,7 +700,7 @@ static int simple_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
- return asoc_simple_card_clean_reference(card);
+ return asoc_simple_clean_reference(card);
}
static const struct of_device_id simple_of_match[] = {
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index b744add01d12..21c6675abd19 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -79,14 +79,15 @@ enum {
#define BYT_RT5651_SSP0_AIF2 BIT(21)
#define BYT_RT5651_HP_LR_SWAPPED BIT(22)
#define BYT_RT5651_MONO_SPEAKER BIT(23)
+#define BYT_RT5651_JD_NOT_INV BIT(24)
#define BYT_RT5651_DEFAULT_QUIRKS (BYT_RT5651_MCLK_EN | \
BYT_RT5651_JD1_1 | \
BYT_RT5651_OVCD_TH_2000UA | \
BYT_RT5651_OVCD_SF_0P75)
-/* jack-detect-source + dmic-en + ovcd-th + -sf + terminating empty entry */
-#define MAX_NO_PROPS 5
+/* jack-detect-source + inv + dmic-en + ovcd-th + -sf + terminating entry */
+#define MAX_NO_PROPS 6
struct byt_rt5651_private {
struct clk *mclk;
@@ -137,6 +138,8 @@ static void log_quirks(struct device *dev)
dev_info(dev, "quirk SSP0_AIF2 enabled\n");
if (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER)
dev_info(dev, "quirk MONO_SPEAKER enabled\n");
+ if (byt_rt5651_quirk & BYT_RT5651_JD_NOT_INV)
+ dev_info(dev, "quirk JD_NOT_INV enabled\n");
}
#define BYT_CODEC_DAI1 "rt5651-aif1"
@@ -415,6 +418,18 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = {
BYT_RT5651_MONO_SPEAKER),
},
{
+ /* Complet Electro Serv MY8307 */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Complet Electro Serv"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MY8307"),
+ },
+ .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS |
+ BYT_RT5651_IN2_MAP |
+ BYT_RT5651_MONO_SPEAKER |
+ BYT_RT5651_JD_NOT_INV),
+ },
+ {
/* I.T.Works TW701, Ployer Momo7w and Trekstor ST70416-6
* (these all use the same mainboard) */
.callback = byt_rt5651_quirk_cb,
@@ -525,6 +540,9 @@ static int byt_rt5651_add_codec_device_props(struct device *i2c_dev)
if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN)
props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,dmic-en");
+ if (byt_rt5651_quirk & BYT_RT5651_JD_NOT_INV)
+ props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,jack-detect-not-inverted");
+
return device_add_properties(i2c_dev, props);
}
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 1ae83f4ccc36..56099db8f86d 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -181,6 +181,7 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params)
struct hdac_stream *hstream;
struct hdac_ext_stream *stream;
struct hdac_ext_link *link;
+ unsigned char stream_tag;
hstream = snd_hdac_get_stream(bus, params->stream,
params->link_dma_id + 1);
@@ -199,10 +200,13 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params)
snd_hdac_ext_link_stream_setup(stream, format_val);
- list_for_each_entry(link, &bus->hlink_list, list) {
- if (link->index == params->link_index)
- snd_hdac_ext_link_set_stream_id(link,
- hstream->stream_tag);
+ stream_tag = hstream->stream_tag;
+ if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ list_for_each_entry(link, &bus->hlink_list, list) {
+ if (link->index == params->link_index)
+ snd_hdac_ext_link_set_stream_id(link,
+ stream_tag);
+ }
}
stream->link_prepared = 1;
@@ -645,6 +649,7 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream,
struct hdac_ext_stream *link_dev =
snd_soc_dai_get_dma_data(dai, substream);
struct hdac_ext_link *link;
+ unsigned char stream_tag;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -654,7 +659,11 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream,
if (!link)
return -EINVAL;
- snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_dev)->stream_tag);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ stream_tag = hdac_stream(link_dev)->stream_tag;
+ snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ }
+
snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
return 0;
}
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
index 1a354a6b6e87..b3f9c41b4319 100644
--- a/sound/soc/jz4740/Kconfig
+++ b/sound/soc/jz4740/Kconfig
@@ -1,6 +1,6 @@
config SND_JZ4740_SOC
tristate "SoC Audio for Ingenic JZ4740 SoC"
- depends on MACH_JZ4740 || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for codecs attached to
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index b35410e4020e..874404bcccfd 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -116,6 +116,32 @@ config SND_SOC_MT8183
Select Y if you have such device.
If unsure select "N".
+config SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A
+ tristate "ASoC Audio driver for MT8183 with MT6358 TS3A227E MAX98357A codec"
+ depends on SND_SOC_MT8183
+ select SND_SOC_MT6358
+ select SND_SOC_MAX98357A
+ select SND_SOC_BT_SCO
+ select SND_SOC_TS3A227E
+ help
+ This adds ASoC driver for Mediatek MT8183 boards
+ with the MT6358 TS3A227E MAX98357A audio codec.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8183_DA7219_MAX98357A
+ tristate "ASoC Audio driver for MT8183 with DA7219 MAX98357A codec"
+ depends on SND_SOC_MT8183
+ select SND_SOC_MT6358
+ select SND_SOC_MAX98357A
+ select SND_SOC_DA7219
+ select SND_SOC_BT_SCO
+ help
+ This adds ASoC driver for Mediatek MT8183 boards
+ with the DA7219 MAX98357A audio codec.
+ Select Y if you have such device.
+ If unsure select "N".
+
config SND_SOC_MTK_BTCVSD
tristate "ALSA BT SCO CVSD/MSBC Driver"
help
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
index cf4978be062f..fded11d14cde 100644
--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -18,11 +18,11 @@
static int mtk_regmap_update_bits(struct regmap *map, int reg,
unsigned int mask,
- unsigned int val)
+ unsigned int val, int shift)
{
- if (reg < 0)
+ if (reg < 0 || WARN_ON_ONCE(shift < 0))
return 0;
- return regmap_update_bits(map, reg, mask, val);
+ return regmap_update_bits(map, reg, mask << shift, val << shift);
}
static int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
@@ -49,8 +49,7 @@ int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
/* enable agent */
mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
- 1 << memif->data->agent_disable_shift,
- 0 << memif->data->agent_disable_shift);
+ 1, 0, memif->data->agent_disable_shift);
snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
@@ -105,8 +104,7 @@ void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
irq_id = memif->irq_usage;
mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
- 1 << memif->data->agent_disable_shift,
- 1 << memif->data->agent_disable_shift);
+ 1, 1, memif->data->agent_disable_shift);
if (!memif->const_irq) {
mtk_dynamic_irq_release(afe, irq_id);
@@ -144,16 +142,14 @@ int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
/* set MSB to 33-bit */
mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
- 1 << memif->data->msb_shift,
- msb_at_bit33 << memif->data->msb_shift);
+ 1, msb_at_bit33, memif->data->msb_shift);
/* set channel */
if (memif->data->mono_shift >= 0) {
unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
- 1 << memif->data->mono_shift,
- mono << memif->data->mono_shift);
+ 1, mono, memif->data->mono_shift);
}
/* set rate */
@@ -166,8 +162,8 @@ int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
- memif->data->fs_maskbit << memif->data->fs_shift,
- fs << memif->data->fs_shift);
+ memif->data->fs_maskbit, fs,
+ memif->data->fs_shift);
return 0;
}
@@ -197,17 +193,14 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- if (memif->data->enable_shift >= 0)
- mtk_regmap_update_bits(afe->regmap,
- memif->data->enable_reg,
- 1 << memif->data->enable_shift,
- 1 << memif->data->enable_shift);
+ mtk_regmap_update_bits(afe->regmap,
+ memif->data->enable_reg,
+ 1, 1, memif->data->enable_shift);
/* set irq counter */
mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
- irq_data->irq_cnt_maskbit
- << irq_data->irq_cnt_shift,
- counter << irq_data->irq_cnt_shift);
+ irq_data->irq_cnt_maskbit, counter,
+ irq_data->irq_cnt_shift);
/* set irq fs */
fs = afe->irq_fs(substream, runtime->rate);
@@ -216,24 +209,21 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
return -EINVAL;
mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
- irq_data->irq_fs_maskbit
- << irq_data->irq_fs_shift,
- fs << irq_data->irq_fs_shift);
+ irq_data->irq_fs_maskbit, fs,
+ irq_data->irq_fs_shift);
/* enable interrupt */
mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
- 1 << irq_data->irq_en_shift,
- 1 << irq_data->irq_en_shift);
+ 1, 1, irq_data->irq_en_shift);
return 0;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
- 1 << memif->data->enable_shift, 0);
+ 1, 0, memif->data->enable_shift);
/* disable interrupt */
mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
- 1 << irq_data->irq_en_shift,
- 0 << irq_data->irq_en_shift);
+ 1, 0, irq_data->irq_en_shift);
/* and clear pending IRQ */
mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
1 << irq_data->irq_clr_shift);
@@ -270,8 +260,7 @@ int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
}
mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
- 1 << memif->data->hd_shift,
- hd_audio << memif->data->hd_shift);
+ 1, hd_audio, memif->data->hd_shift);
return 0;
}
diff --git a/sound/soc/mediatek/common/mtk-btcvsd.c b/sound/soc/mediatek/common/mtk-btcvsd.c
index 1b8bcdaf02d1..9a163d7064d1 100644
--- a/sound/soc/mediatek/common/mtk-btcvsd.c
+++ b/sound/soc/mediatek/common/mtk-btcvsd.c
@@ -49,6 +49,7 @@ enum bt_sco_state {
BT_SCO_STATE_IDLE,
BT_SCO_STATE_RUNNING,
BT_SCO_STATE_ENDING,
+ BT_SCO_STATE_LOOPBACK,
};
enum bt_sco_direct {
@@ -486,7 +487,8 @@ static irqreturn_t mtk_btcvsd_snd_irq_handler(int irq_id, void *dev)
if (bt->rx->state != BT_SCO_STATE_RUNNING &&
bt->rx->state != BT_SCO_STATE_ENDING &&
bt->tx->state != BT_SCO_STATE_RUNNING &&
- bt->tx->state != BT_SCO_STATE_ENDING) {
+ bt->tx->state != BT_SCO_STATE_ENDING &&
+ bt->tx->state != BT_SCO_STATE_LOOPBACK) {
dev_warn(bt->dev, "%s(), in idle state: rx->state: %d, tx->state: %d\n",
__func__, bt->rx->state, bt->tx->state);
goto irq_handler_exit;
@@ -512,6 +514,42 @@ static irqreturn_t mtk_btcvsd_snd_irq_handler(int irq_id, void *dev)
buf_cnt_tx = btsco_packet_info[packet_type][2];
buf_cnt_rx = btsco_packet_info[packet_type][3];
+ if (bt->tx->state == BT_SCO_STATE_LOOPBACK) {
+ u8 *src, *dst;
+ unsigned long connsys_addr_rx, ap_addr_rx;
+ unsigned long connsys_addr_tx, ap_addr_tx;
+
+ connsys_addr_rx = *bt->bt_reg_pkt_r;
+ ap_addr_rx = (unsigned long)bt->bt_sram_bank2_base +
+ (connsys_addr_rx & 0xFFFF);
+
+ connsys_addr_tx = *bt->bt_reg_pkt_w;
+ ap_addr_tx = (unsigned long)bt->bt_sram_bank2_base +
+ (connsys_addr_tx & 0xFFFF);
+
+ if (connsys_addr_tx == 0xdeadfeed ||
+ connsys_addr_rx == 0xdeadfeed) {
+ /* bt return 0xdeadfeed if read reg during bt sleep */
+ dev_warn(bt->dev, "%s(), connsys_addr_tx == 0xdeadfeed\n",
+ __func__);
+ goto irq_handler_exit;
+ }
+
+ src = (u8 *)ap_addr_rx;
+ dst = (u8 *)ap_addr_tx;
+
+ mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_BT2ARM, src,
+ bt->tx->temp_packet_buf,
+ packet_length,
+ packet_num);
+ mtk_btcvsd_snd_data_transfer(BT_SCO_DIRECT_ARM2BT,
+ bt->tx->temp_packet_buf, dst,
+ packet_length,
+ packet_num);
+ bt->rx->rw_cnt++;
+ bt->tx->rw_cnt++;
+ }
+
if (bt->rx->state == BT_SCO_STATE_RUNNING ||
bt->rx->state == BT_SCO_STATE_ENDING) {
if (bt->rx->xrun) {
@@ -1067,6 +1105,33 @@ static int btcvsd_band_set(struct snd_kcontrol *kcontrol,
return 0;
}
+static int btcvsd_loopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+ bool lpbk_en = bt->tx->state == BT_SCO_STATE_LOOPBACK;
+
+ ucontrol->value.integer.value[0] = lpbk_en;
+ return 0;
+}
+
+static int btcvsd_loopback_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(cmpnt);
+
+ if (ucontrol->value.integer.value[0]) {
+ mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_LOOPBACK);
+ mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_LOOPBACK);
+ } else {
+ mtk_btcvsd_snd_set_state(bt, bt->tx, BT_SCO_STATE_RUNNING);
+ mtk_btcvsd_snd_set_state(bt, bt->rx, BT_SCO_STATE_RUNNING);
+ }
+ return 0;
+}
+
static int btcvsd_tx_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1202,6 +1267,8 @@ static int btcvsd_tx_timestamp_get(struct snd_kcontrol *kcontrol,
static const struct snd_kcontrol_new mtk_btcvsd_snd_controls[] = {
SOC_ENUM_EXT("BTCVSD Band", btcvsd_enum[0],
btcvsd_band_get, btcvsd_band_set),
+ SOC_SINGLE_BOOL_EXT("BTCVSD Loopback Switch", 0,
+ btcvsd_loopback_get, btcvsd_loopback_set),
SOC_SINGLE_BOOL_EXT("BTCVSD Tx Mute Switch", 0,
btcvsd_tx_mute_get, btcvsd_tx_mute_set),
SOC_SINGLE_BOOL_EXT("BTCVSD Tx Irq Received Switch", 0,
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index 968fba4d7533..7064a9fd6f74 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -994,7 +994,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 6,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "DL2",
@@ -1013,7 +1012,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 7,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "DL3",
@@ -1032,7 +1030,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 8,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "DL4",
@@ -1051,7 +1048,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 9,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "DL5",
@@ -1070,7 +1066,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 10,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "DLM",
@@ -1089,7 +1084,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 12,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "UL1",
@@ -1108,7 +1102,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 0,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "UL2",
@@ -1127,7 +1120,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 1,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "UL3",
@@ -1146,7 +1138,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 2,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "UL4",
@@ -1165,7 +1156,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 3,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "UL5",
@@ -1184,7 +1174,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 4,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "DLBT",
@@ -1203,7 +1192,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 13,
.msb_reg = -1,
- .msb_shift = -1,
},
{
.name = "ULBT",
@@ -1222,7 +1210,6 @@ static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
.agent_disable_reg = AUDIO_TOP_CON5,
.agent_disable_shift = 16,
.msb_reg = -1,
- .msb_shift = -1,
},
};
diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
index bff7d71d0742..08a6532da322 100644
--- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
@@ -401,9 +401,7 @@ static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = DL1_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT6797_MEMIF_DL2] = {
.name = "DL2",
@@ -420,9 +418,7 @@ static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = DL2_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT6797_MEMIF_DL3] = {
.name = "DL3",
@@ -439,9 +435,7 @@ static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = DL3_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT6797_MEMIF_VUL] = {
.name = "VUL",
@@ -458,9 +452,7 @@ static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = VUL_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT6797_MEMIF_AWB] = {
.name = "AWB",
@@ -477,9 +469,7 @@ static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = AWB_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT6797_MEMIF_VUL12] = {
.name = "VUL12",
@@ -496,9 +486,7 @@ static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = VUL_DATA2_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT6797_MEMIF_DAI] = {
.name = "DAI",
@@ -515,9 +503,7 @@ static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = DAI_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT6797_MEMIF_MOD_DAI] = {
.name = "MOD_DAI",
@@ -534,9 +520,7 @@ static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = MOD_DAI_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
};
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 166aed28330d..0382896c162e 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -714,13 +714,11 @@ static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
.mono_reg = AFE_DAC_CON1,
.mono_shift = 21,
.hd_reg = -1,
- .hd_shift = -1,
.enable_reg = AFE_DAC_CON0,
.enable_shift = 1,
.msb_reg = AFE_MEMIF_MSB,
.msb_shift = 0,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
}, {
.name = "DL2",
.id = MT8173_AFE_MEMIF_DL2,
@@ -732,13 +730,11 @@ static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
.mono_reg = AFE_DAC_CON1,
.mono_shift = 22,
.hd_reg = -1,
- .hd_shift = -1,
.enable_reg = AFE_DAC_CON0,
.enable_shift = 2,
.msb_reg = AFE_MEMIF_MSB,
.msb_shift = 1,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
}, {
.name = "VUL",
.id = MT8173_AFE_MEMIF_VUL,
@@ -750,13 +746,11 @@ static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
.mono_reg = AFE_DAC_CON1,
.mono_shift = 27,
.hd_reg = -1,
- .hd_shift = -1,
.enable_reg = AFE_DAC_CON0,
.enable_shift = 3,
.msb_reg = AFE_MEMIF_MSB,
.msb_shift = 6,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
}, {
.name = "DAI",
.id = MT8173_AFE_MEMIF_DAI,
@@ -768,13 +762,11 @@ static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
.mono_reg = -1,
.mono_shift = -1,
.hd_reg = -1,
- .hd_shift = -1,
.enable_reg = AFE_DAC_CON0,
.enable_shift = 4,
.msb_reg = AFE_MEMIF_MSB,
.msb_shift = 5,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
}, {
.name = "AWB",
.id = MT8173_AFE_MEMIF_AWB,
@@ -786,13 +778,11 @@ static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
.mono_reg = AFE_DAC_CON1,
.mono_shift = 24,
.hd_reg = -1,
- .hd_shift = -1,
.enable_reg = AFE_DAC_CON0,
.enable_shift = 6,
.msb_reg = AFE_MEMIF_MSB,
.msb_shift = 3,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
}, {
.name = "MOD_DAI",
.id = MT8173_AFE_MEMIF_MOD_DAI,
@@ -804,13 +794,11 @@ static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
.mono_reg = AFE_DAC_CON1,
.mono_shift = 30,
.hd_reg = -1,
- .hd_shift = -1,
.enable_reg = AFE_DAC_CON0,
.enable_shift = 7,
.msb_reg = AFE_MEMIF_MSB,
.msb_shift = 4,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
}, {
.name = "HDMI",
.id = MT8173_AFE_MEMIF_HDMI,
@@ -822,13 +810,10 @@ static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
.mono_reg = -1,
.mono_shift = -1,
.hd_reg = -1,
- .hd_shift = -1,
.enable_reg = -1,
- .enable_shift = -1,
.msb_reg = AFE_MEMIF_MSB,
.msb_shift = 8,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
},
};
@@ -914,7 +899,6 @@ static const struct mtk_base_irq_data irq_data[MT8173_AFE_IRQ_NUM] = {
.irq_en_reg = AFE_IRQ_MCU_CON,
.irq_en_shift = 12,
.irq_fs_reg = -1,
- .irq_fs_shift = -1,
.irq_fs_maskbit = -1,
.irq_clr_reg = AFE_IRQ_CLR,
.irq_clr_shift = 4,
diff --git a/sound/soc/mediatek/mt8183/Makefile b/sound/soc/mediatek/mt8183/Makefile
index f3ee6ac98fe8..c0a3bbc2c1f6 100644
--- a/sound/soc/mediatek/mt8183/Makefile
+++ b/sound/soc/mediatek/mt8183/Makefile
@@ -11,3 +11,5 @@ snd-soc-mt8183-afe-objs := \
mt8183-dai-adda.o
obj-$(CONFIG_SND_SOC_MT8183) += snd-soc-mt8183-afe.o
+obj-$(CONFIG_SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A) += mt8183-mt6358-ts3a227-max98357.o
+obj-$(CONFIG_SND_SOC_MT8183_DA7219_MAX98357A) += mt8183-da7219-max98357.o
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-clk.c b/sound/soc/mediatek/mt8183/mt8183-afe-clk.c
index f523ad103acc..48e81c5d52fc 100644
--- a/sound/soc/mediatek/mt8183/mt8183-afe-clk.c
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-clk.c
@@ -605,6 +605,10 @@ void mt8183_mck_disable(struct mtk_base_afe *afe, int mck_id)
int m_sel_id = mck_div[mck_id].m_sel_id;
int div_clk_id = mck_div[mck_id].div_clk_id;
+ /* i2s5 mck not support */
+ if (mck_id == MT8183_I2S5_MCK)
+ return;
+
clk_disable_unprepare(afe_priv->clk[div_clk_id]);
if (m_sel_id >= 0)
clk_disable_unprepare(afe_priv->clk[m_sel_id]);
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
index 4e045dd305a7..43be51bf0329 100644
--- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
@@ -421,9 +421,7 @@ static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = DL1_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT8183_MEMIF_DL2] = {
.name = "DL2",
@@ -440,9 +438,7 @@ static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = DL2_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT8183_MEMIF_DL3] = {
.name = "DL3",
@@ -459,9 +455,7 @@ static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = DL3_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT8183_MEMIF_VUL2] = {
.name = "VUL2",
@@ -478,9 +472,7 @@ static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = VUL2_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT8183_MEMIF_AWB] = {
.name = "AWB",
@@ -497,9 +489,7 @@ static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = AWB_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT8183_MEMIF_AWB2] = {
.name = "AWB2",
@@ -516,9 +506,7 @@ static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = AWB2_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT8183_MEMIF_VUL12] = {
.name = "VUL12",
@@ -535,9 +523,7 @@ static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = VUL12_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT8183_MEMIF_MOD_DAI] = {
.name = "MOD_DAI",
@@ -554,9 +540,7 @@ static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = MOD_DAI_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
[MT8183_MEMIF_HDMI] = {
.name = "HDMI",
@@ -569,13 +553,10 @@ static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
.mono_reg = -1,
.mono_shift = -1,
.enable_reg = -1, /* control in tdm for sync start */
- .enable_shift = -1,
.hd_reg = AFE_MEMIF_HD_MODE,
.hd_shift = HDMI_HD_SFT,
.agent_disable_reg = -1,
- .agent_disable_shift = -1,
.msb_reg = -1,
- .msb_shift = -1,
},
};
@@ -690,7 +671,6 @@ static const struct mtk_base_irq_data irq_data[MT8183_IRQ_NUM] = {
.irq_cnt_shift = 0,
.irq_cnt_maskbit = 0x3ffff,
.irq_fs_reg = -1,
- .irq_fs_shift = -1,
.irq_fs_maskbit = -1,
.irq_en_reg = AFE_IRQ_MCU_CON0,
.irq_en_shift = IRQ8_MCU_ON_SFT,
diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
new file mode 100644
index 000000000000..93536659a859
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8183-da7219-max98357.c
+// -- MT8183-DA7219-MAX98357 ALSA SoC machine driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: Shunli Wang <shunli.wang@mediatek.com>
+
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/pinctrl/consumer.h>
+
+#include "mt8183-afe-common.h"
+#include "../../codecs/da7219-aad.h"
+#include "../../codecs/da7219.h"
+
+static struct snd_soc_jack headset_jack;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin headset_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_dai_link_component
+mt8183_da7219_max98357_external_codecs[] = {
+ {
+ .name = "max98357a",
+ .dai_name = "HiFi",
+ },
+ {
+ .name = "da7219.5-001a",
+ .dai_name = "da7219-hifi",
+ },
+};
+
+static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+
+ return snd_soc_dai_set_sysclk(rtd->cpu_dai,
+ 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8183_mt6358_i2s_ops = {
+ .hw_params = mt8183_mt6358_i2s_hw_params,
+};
+
+static int mt8183_da7219_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 256;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ unsigned int freq;
+ int ret = 0, j;
+
+ ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0,
+ mclk_fs, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ dev_err(rtd->dev, "failed to set cpu dai sysclk\n");
+
+ for (j = 0; j < rtd->num_codecs; j++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
+
+ if (!strcmp(codec_dai->component->name, "da7219.5-001a")) {
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ DA7219_CLKSRC_MCLK,
+ mclk_fs,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "failed to set sysclk\n");
+
+ if ((rate % 8000) == 0)
+ freq = DA7219_PLL_FREQ_OUT_98304;
+ else
+ freq = DA7219_PLL_FREQ_OUT_90316;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7219_SYSCLK_PLL_SRM,
+ 0, freq);
+ if (ret)
+ dev_err(rtd->dev, "failed to start PLL: %d\n",
+ ret);
+ }
+ }
+
+ return ret;
+}
+
+static int mt8183_da7219_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int ret = 0, j;
+
+ for (j = 0; j < rtd->num_codecs; j++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
+
+ if (!strcmp(codec_dai->component->name, "da7219.5-001a")) {
+ ret = snd_soc_dai_set_pll(codec_dai,
+ 0, DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(rtd->dev, "failed to stop PLL: %d\n",
+ ret);
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops mt8183_da7219_i2s_ops = {
+ .hw_params = mt8183_da7219_i2s_hw_params,
+ .hw_free = mt8183_da7219_hw_free,
+};
+
+static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to 32bit, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget
+mt8183_da7219_max98357_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("IT6505_8CH"),
+};
+
+static const struct snd_soc_dapm_route mt8183_da7219_max98357_dapm_routes[] = {
+ {"IT6505_8CH", NULL, "TDM"},
+};
+
+static struct snd_soc_dai_link mt8183_da7219_max98357_dai_links[] = {
+ /* FE */
+ {
+ .name = "Playback_1",
+ .stream_name = "Playback_1",
+ .cpu_dai_name = "DL1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Playback_2",
+ .stream_name = "Playback_2",
+ .cpu_dai_name = "DL2",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Playback_3",
+ .stream_name = "Playback_3",
+ .cpu_dai_name = "DL3",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Capture_1",
+ .stream_name = "Capture_1",
+ .cpu_dai_name = "UL1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Capture_2",
+ .stream_name = "Capture_2",
+ .cpu_dai_name = "UL2",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Capture_3",
+ .stream_name = "Capture_3",
+ .cpu_dai_name = "UL3",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Capture_Mono_1",
+ .stream_name = "Capture_Mono_1",
+ .cpu_dai_name = "UL_MONO_1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Playback_HDMI",
+ .stream_name = "Playback_HDMI",
+ .cpu_dai_name = "HDMI",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ /* BE */
+ {
+ .name = "Primary Codec",
+ .cpu_dai_name = "ADDA",
+ .codec_dai_name = "mt6358-snd-codec-aif1",
+ .codec_name = "mt6358-sound",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "PCM 1",
+ .cpu_dai_name = "PCM 1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "PCM 2",
+ .cpu_dai_name = "PCM 2",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "I2S0",
+ .cpu_dai_name = "I2S0",
+ .codec_dai_name = "bt-sco-pcm",
+ .codec_name = "bt-sco",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ },
+ {
+ .name = "I2S1",
+ .cpu_dai_name = "I2S1",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ },
+ {
+ .name = "I2S2",
+ .cpu_dai_name = "I2S2",
+ .codec_dai_name = "da7219-hifi",
+ .codec_name = "da7219.5-001a",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_da7219_i2s_ops,
+ },
+ {
+ .name = "I2S3",
+ .cpu_dai_name = "I2S3",
+ .codecs = mt8183_da7219_max98357_external_codecs,
+ .num_codecs =
+ ARRAY_SIZE(mt8183_da7219_max98357_external_codecs),
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_da7219_i2s_ops,
+ },
+ {
+ .name = "I2S5",
+ .cpu_dai_name = "I2S5",
+ .codec_dai_name = "bt-sco-pcm",
+ .codec_name = "bt-sco",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ },
+ {
+ .name = "TDM",
+ .cpu_dai_name = "TDM",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static int
+mt8183_da7219_max98357_headset_init(struct snd_soc_component *component);
+
+static struct snd_soc_aux_dev mt8183_da7219_max98357_headset_dev = {
+ .name = "Headset Chip",
+ .init = mt8183_da7219_max98357_headset_init,
+};
+
+static struct snd_soc_codec_conf mt6358_codec_conf[] = {
+ {
+ .dev_name = "mt6358-sound",
+ .name_prefix = "Mt6358",
+ },
+};
+
+static struct snd_soc_card mt8183_da7219_max98357_card = {
+ .name = "mt8183_da7219_max98357",
+ .owner = THIS_MODULE,
+ .dai_link = mt8183_da7219_max98357_dai_links,
+ .num_links = ARRAY_SIZE(mt8183_da7219_max98357_dai_links),
+ .aux_dev = &mt8183_da7219_max98357_headset_dev,
+ .num_aux_devs = 1,
+ .codec_conf = mt6358_codec_conf,
+ .num_configs = ARRAY_SIZE(mt6358_codec_conf),
+};
+
+static int
+mt8183_da7219_max98357_headset_init(struct snd_soc_component *component)
+{
+ int ret;
+
+ /* Enable Headset and 4 Buttons Jack detection */
+ ret = snd_soc_card_jack_new(&mt8183_da7219_max98357_card,
+ "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
+ if (ret)
+ return ret;
+
+ da7219_aad_jack_det(component, &headset_jack);
+
+ return ret;
+}
+
+static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8183_da7219_max98357_card;
+ struct device_node *platform_node;
+ struct snd_soc_dai_link *dai_link;
+ struct pinctrl *default_pins;
+ int ret, i;
+
+ card->dev = &pdev->dev;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ /* In the alsa soc-core, the "platform" will be
+ * allocated by devm_kzalloc if null.
+ * There is a special case that registerring
+ * sound card is failed at the first time, but
+ * the "platform" will not null when probe is trying
+ * again. It's not expected normally.
+ */
+ dai_link->platform = NULL;
+
+ if (dai_link->platform_name)
+ continue;
+ dai_link->platform_of_node = platform_node;
+ }
+
+ mt8183_da7219_max98357_headset_dev.codec_of_node =
+ of_parse_phandle(pdev->dev.of_node,
+ "mediatek,headset-codec", 0);
+ if (!mt8183_da7219_max98357_headset_dev.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'mediatek,headset-codec' missing/invalid\n");
+ return -EINVAL;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ default_pins =
+ devm_pinctrl_get_select(&pdev->dev, PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(default_pins)) {
+ dev_err(&pdev->dev, "%s set pins failed\n",
+ __func__);
+ return PTR_ERR(default_pins);
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt8183_da7219_max98357_dt_match[] = {
+ {.compatible = "mediatek,mt8183_da7219_max98357",},
+ {}
+};
+#endif
+
+static struct platform_driver mt8183_da7219_max98357_driver = {
+ .driver = {
+ .name = "mt8183_da7219_max98357",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = mt8183_da7219_max98357_dt_match,
+#endif
+ },
+ .probe = mt8183_da7219_max98357_dev_probe,
+};
+
+module_platform_driver(mt8183_da7219_max98357_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8183-DA7219-MAX98357 ALSA SoC machine driver");
+MODULE_AUTHOR("Shunli Wang <shunli.wang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt8183_da7219_max98357 soc card");
+
diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
new file mode 100644
index 000000000000..2da56232a9e1
--- /dev/null
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt8183-mt6358.c --
+// MT8183-MT6358-TS3A227-MAX98357 ALSA SoC machine driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: Shunli Wang <shunli.wang@mediatek.com>
+
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/pinctrl/consumer.h>
+
+#include "mt8183-afe-common.h"
+#include "../../codecs/ts3a227e.h"
+
+static struct snd_soc_jack headset_jack;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin headset_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+
+};
+
+static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 128;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+
+ return snd_soc_dai_set_sysclk(rtd->cpu_dai,
+ 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8183_mt6358_i2s_ops = {
+ .hw_params = mt8183_mt6358_i2s_hw_params,
+};
+
+static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__);
+
+ /* fix BE i2s format to 32bit, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget
+mt8183_mt6358_ts3a227_max98357_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("IT6505_8CH"),
+};
+
+static const struct snd_soc_dapm_route
+mt8183_mt6358_ts3a227_max98357_dapm_routes[] = {
+ {"IT6505_8CH", NULL, "TDM"},
+};
+
+static struct snd_soc_dai_link
+mt8183_mt6358_ts3a227_max98357_dai_links[] = {
+ /* FE */
+ {
+ .name = "Playback_1",
+ .stream_name = "Playback_1",
+ .cpu_dai_name = "DL1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Playback_2",
+ .stream_name = "Playback_2",
+ .cpu_dai_name = "DL2",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Playback_3",
+ .stream_name = "Playback_3",
+ .cpu_dai_name = "DL3",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Capture_1",
+ .stream_name = "Capture_1",
+ .cpu_dai_name = "UL1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Capture_2",
+ .stream_name = "Capture_2",
+ .cpu_dai_name = "UL2",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Capture_3",
+ .stream_name = "Capture_3",
+ .cpu_dai_name = "UL3",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Capture_Mono_1",
+ .stream_name = "Capture_Mono_1",
+ .cpu_dai_name = "UL_MONO_1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Playback_HDMI",
+ .stream_name = "Playback_HDMI",
+ .cpu_dai_name = "HDMI",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ /* BE */
+ {
+ .name = "Primary Codec",
+ .cpu_dai_name = "ADDA",
+ .codec_dai_name = "mt6358-snd-codec-aif1",
+ .codec_name = "mt6358-sound",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "PCM 1",
+ .cpu_dai_name = "PCM 1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "PCM 2",
+ .cpu_dai_name = "PCM 2",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "I2S0",
+ .cpu_dai_name = "I2S0",
+ .codec_dai_name = "bt-sco-pcm",
+ .codec_name = "bt-sco",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ },
+ {
+ .name = "I2S1",
+ .cpu_dai_name = "I2S1",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ },
+ {
+ .name = "I2S2",
+ .cpu_dai_name = "I2S2",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ },
+ {
+ .name = "I2S3",
+ .cpu_dai_name = "I2S3",
+ .codec_dai_name = "HiFi",
+ .codec_name = "max98357a",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ },
+ {
+ .name = "I2S5",
+ .cpu_dai_name = "I2S5",
+ .codec_dai_name = "bt-sco-pcm",
+ .codec_name = "bt-sco",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+ .ops = &mt8183_mt6358_i2s_ops,
+ },
+ {
+ .name = "TDM",
+ .cpu_dai_name = "TDM",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static int
+mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *cpnt);
+
+static struct snd_soc_aux_dev mt8183_mt6358_ts3a227_max98357_headset_dev = {
+ .name = "Headset Chip",
+ .init = mt8183_mt6358_ts3a227_max98357_headset_init,
+};
+
+static struct snd_soc_card mt8183_mt6358_ts3a227_max98357_card = {
+ .name = "mt8183_mt6358_ts3a227_max98357",
+ .owner = THIS_MODULE,
+ .dai_link = mt8183_mt6358_ts3a227_max98357_dai_links,
+ .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dai_links),
+ .aux_dev = &mt8183_mt6358_ts3a227_max98357_headset_dev,
+ .num_aux_devs = 1,
+};
+
+static int
+mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *component)
+{
+ int ret;
+
+ /* Enable Headset and 4 Buttons Jack detection */
+ ret = snd_soc_card_jack_new(&mt8183_mt6358_ts3a227_max98357_card,
+ "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &headset_jack,
+ headset_jack_pins,
+ ARRAY_SIZE(headset_jack_pins));
+ if (ret)
+ return ret;
+
+ ret = ts3a227e_enable_jack_detect(component, &headset_jack);
+
+ return ret;
+}
+
+static int
+mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8183_mt6358_ts3a227_max98357_card;
+ struct device_node *platform_node;
+ struct snd_soc_dai_link *dai_link;
+ struct pinctrl *default_pins;
+ int ret, i;
+
+ card->dev = &pdev->dev;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ /* In the alsa soc-core, the "platform" will be
+ * allocated by devm_kzalloc if null.
+ * There is a special case that registerring
+ * sound card is failed at the first time, but
+ * the "platform" will not null when probe is trying
+ * again. It's not expected normally.
+ */
+ dai_link->platform = NULL;
+
+ if (dai_link->platform_name)
+ continue;
+ dai_link->platform_of_node = platform_node;
+ }
+
+ mt8183_mt6358_ts3a227_max98357_headset_dev.codec_of_node =
+ of_parse_phandle(pdev->dev.of_node,
+ "mediatek,headset-codec", 0);
+ if (!mt8183_mt6358_ts3a227_max98357_headset_dev.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'mediatek,headset-codec' missing/invalid\n");
+ return -EINVAL;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+
+ default_pins =
+ devm_pinctrl_get_select(&pdev->dev, PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(default_pins)) {
+ dev_err(&pdev->dev, "%s set pins failed\n",
+ __func__);
+ return PTR_ERR(default_pins);
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt8183_mt6358_ts3a227_max98357_dt_match[] = {
+ {.compatible = "mediatek,mt8183_mt6358_ts3a227_max98357",},
+ {}
+};
+#endif
+
+static struct platform_driver mt8183_mt6358_ts3a227_max98357_driver = {
+ .driver = {
+ .name = "mt8183_mt6358_ts3a227_max98357",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = mt8183_mt6358_ts3a227_max98357_dt_match,
+#endif
+ },
+ .probe = mt8183_mt6358_ts3a227_max98357_dev_probe,
+};
+
+module_platform_driver(mt8183_mt6358_ts3a227_max98357_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8183-MT6358-TS3A227-MAX98357 ALSA SoC machine driver");
+MODULE_AUTHOR("Shunli Wang <shunli.wang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt8183_mt6358_ts3a227_max98357 soc card");
+
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 75ceb04d8bf0..b1764af858ba 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -98,7 +98,7 @@ config SND_SOC_MSM8996
config SND_SOC_SDM845
tristate "SoC Machine driver for SDM845 boards"
- depends on QCOM_APR && MFD_CROS_EC
+ depends on QCOM_APR && MFD_CROS_EC && I2C
select SND_SOC_QDSP6
select SND_SOC_QCOM_COMMON
select SND_SOC_RT5663
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 4231001226f4..9722940da6a4 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -88,12 +88,6 @@ struct samsung_i2s_priv {
struct platform_device *pdev;
struct platform_device *pdev_sec;
- /* Memory mapped SFR region */
- void __iomem *addr;
-
- /* Spinlock protecting access to the device's registers */
- spinlock_t lock;
-
/* Lock for cross interface checks */
spinlock_t pcm_lock;
@@ -122,6 +116,15 @@ struct samsung_i2s_priv {
/* The clock provider's data */
struct clk *clk_table[3];
struct clk_onecell_data clk_data;
+
+ /* Spinlock protecting member fields below */
+ spinlock_t lock;
+
+ /* Memory mapped SFR region */
+ void __iomem *addr;
+
+ /* A flag indicating the I2S slave mode operation */
+ bool slave_mode;
};
/* Returns true if this is the 'overlay' stereo DAI */
@@ -130,15 +133,6 @@ static inline bool is_secondary(struct i2s_dai *i2s)
return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY;
}
-/* If operating in SoC-Slave mode */
-static inline bool is_slave(struct i2s_dai *i2s)
-{
- struct samsung_i2s_priv *priv = i2s->priv;
-
- u32 mod = readl(priv->addr + I2SMOD);
- return (mod & (1 << priv->variant_regs->mss_off)) ? true : false;
-}
-
/* If this interface of the controller is transmitting data */
static inline bool tx_active(struct i2s_dai *i2s)
{
@@ -715,6 +709,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
mod &= ~(sdf_mask | lrp_rlow | mod_slave);
mod |= tmp;
writel(mod, priv->addr + I2SMOD);
+ priv->slave_mode = (mod & mod_slave);
spin_unlock_irqrestore(&priv->lock, flags);
pm_runtime_put(dai->dev);
@@ -917,7 +912,7 @@ static int config_setup(struct i2s_dai *i2s)
set_rfs(i2s, rfs);
/* Don't bother with PSR in Slave mode */
- if (is_slave(i2s))
+ if (priv->slave_mode)
return 0;
if (!(priv->quirks & QUIRK_NO_MUXPSR)) {
@@ -1130,11 +1125,11 @@ static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = {
};
static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = {
- { "Playback Mixer", NULL, "Primary" },
- { "Playback Mixer", NULL, "Secondary" },
+ { "Playback Mixer", NULL, "Primary Playback" },
+ { "Playback Mixer", NULL, "Secondary Playback" },
{ "Mixer DAI TX", NULL, "Playback Mixer" },
- { "Playback Mixer", NULL, "Mixer DAI RX" },
+ { "Primary Capture", NULL, "Mixer DAI RX" },
};
static const struct snd_soc_component_driver samsung_i2s_component = {
@@ -1155,7 +1150,8 @@ static int i2s_alloc_dais(struct samsung_i2s_priv *priv,
int num_dais)
{
static const char *dai_names[] = { "samsung-i2s", "samsung-i2s-sec" };
- static const char *stream_names[] = { "Primary", "Secondary" };
+ static const char *stream_names[] = { "Primary Playback",
+ "Secondary Playback" };
struct snd_soc_dai_driver *dai_drv;
struct i2s_dai *dai;
int i;
@@ -1201,6 +1197,7 @@ static int i2s_alloc_dais(struct samsung_i2s_priv *priv,
dai_drv->capture.channels_max = 2;
dai_drv->capture.rates = i2s_dai_data->pcm_rates;
dai_drv->capture.formats = SAMSUNG_I2S_FMTS;
+ dai_drv->capture.stream_name = "Primary Capture";
return 0;
}
diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c
index 694512f980fd..1dc54c4206f0 100644
--- a/sound/soc/samsung/odroid.c
+++ b/sound/soc/samsung/odroid.c
@@ -91,11 +91,11 @@ static int odroid_card_be_hw_params(struct snd_pcm_substream *substream,
return ret;
/*
- * We add 1 to the rclk_freq value in order to avoid too low clock
+ * We add 2 to the rclk_freq value in order to avoid too low clock
* frequency values due to the EPLL output frequency not being exact
* multiple of the audio sampling rate.
*/
- rclk_freq = params_rate(params) * rfs + 1;
+ rclk_freq = params_rate(params) * rfs + 2;
ret = clk_set_rate(priv->sclk_i2s, rclk_freq);
if (ret < 0)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 022996d2db13..4fe83e611c01 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -110,6 +110,8 @@ static const struct of_device_id rsnd_of_match[] = {
{ .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
{ .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 },
{ .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN3 },
+ /* Special Handling */
+ { .compatible = "renesas,rcar_sound-r8a77990", .data = (void *)(RSND_GEN3 | RSND_SOC_E) },
{},
};
MODULE_DEVICE_TABLE(of, rsnd_of_match);
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 90625c57847b..0e6ef4e18400 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -607,6 +607,8 @@ struct rsnd_priv {
#define RSND_GEN1 (1 << 0)
#define RSND_GEN2 (2 << 0)
#define RSND_GEN3 (3 << 0)
+#define RSND_SOC_MASK (0xFF << 4)
+#define RSND_SOC_E (1 << 4) /* E1/E2/E3 */
/*
* below value will be filled on rsnd_gen_probe()
@@ -679,6 +681,9 @@ struct rsnd_priv {
#define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1)
#define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2)
#define rsnd_is_gen3(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN3)
+#define rsnd_is_e3(priv) (((priv)->flags & \
+ (RSND_GEN_MASK | RSND_SOC_MASK)) == \
+ (RSND_GEN3 | RSND_SOC_E))
#define rsnd_flags_has(p, f) ((p)->flags & (f))
#define rsnd_flags_set(p, f) ((p)->flags |= (f))
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index db81e066b92e..585ffba0244b 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -14,7 +14,6 @@
*/
#include "rsnd.h"
-#include <linux/sys_soc.h>
#define SRC_NAME "src"
@@ -135,7 +134,7 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
return rate;
}
-const static u32 bsdsr_table_pattern1[] = {
+static const u32 bsdsr_table_pattern1[] = {
0x01800000, /* 6 - 1/6 */
0x01000000, /* 6 - 1/4 */
0x00c00000, /* 6 - 1/3 */
@@ -144,7 +143,7 @@ const static u32 bsdsr_table_pattern1[] = {
0x00400000, /* 6 - 1 */
};
-const static u32 bsdsr_table_pattern2[] = {
+static const u32 bsdsr_table_pattern2[] = {
0x02400000, /* 6 - 1/6 */
0x01800000, /* 6 - 1/4 */
0x01200000, /* 6 - 1/3 */
@@ -153,7 +152,7 @@ const static u32 bsdsr_table_pattern2[] = {
0x00600000, /* 6 - 1 */
};
-const static u32 bsisr_table[] = {
+static const u32 bsisr_table[] = {
0x00100060, /* 6 - 1/6 */
0x00100040, /* 6 - 1/4 */
0x00100030, /* 6 - 1/3 */
@@ -162,7 +161,7 @@ const static u32 bsisr_table[] = {
0x00100020, /* 6 - 1 */
};
-const static u32 chan288888[] = {
+static const u32 chan288888[] = {
0x00000006, /* 1 to 2 */
0x000001fe, /* 1 to 8 */
0x000001fe, /* 1 to 8 */
@@ -171,7 +170,7 @@ const static u32 chan288888[] = {
0x000001fe, /* 1 to 8 */
};
-const static u32 chan244888[] = {
+static const u32 chan244888[] = {
0x00000006, /* 1 to 2 */
0x0000001e, /* 1 to 4 */
0x0000001e, /* 1 to 4 */
@@ -180,7 +179,7 @@ const static u32 chan244888[] = {
0x000001fe, /* 1 to 8 */
};
-const static u32 chan222222[] = {
+static const u32 chan222222[] = {
0x00000006, /* 1 to 2 */
0x00000006, /* 1 to 2 */
0x00000006, /* 1 to 2 */
@@ -189,18 +188,12 @@ const static u32 chan222222[] = {
0x00000006, /* 1 to 2 */
};
-static const struct soc_device_attribute ov_soc[] = {
- { .soc_id = "r8a77990" }, /* E3 */
- { /* sentinel */ }
-};
-
static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- const struct soc_device_attribute *soc = soc_device_match(ov_soc);
int is_play = rsnd_io_is_play(io);
int use_src = 0;
u32 fin, fout;
@@ -307,7 +300,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
/*
* E3 need to overwrite
*/
- if (soc)
+ if (rsnd_is_e3(priv))
switch (rsnd_mod_id(mod)) {
case 0:
case 4:
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 93d316d5bf8e..6f4842977b8d 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1974,10 +1974,13 @@ static void soc_check_tplg_fes(struct snd_soc_card *card)
continue;
/* for this machine ? */
+ if (!strcmp(component->driver->ignore_machine,
+ card->dev->driver->name))
+ goto match;
if (strcmp(component->driver->ignore_machine,
- card->dev->driver->name))
+ dev_name(card->dev)))
continue;
-
+match:
/* machine matches, so override the rtd data */
for_each_card_prelinks(card, i, dai_link) {
@@ -2797,6 +2800,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
ret = soc_init_dai_link(card, link);
if (ret) {
+ soc_cleanup_platform(card);
dev_err(card->dev, "ASoC: failed to init link %s\n",
link->name);
mutex_unlock(&client_mutex);
@@ -2819,6 +2823,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
+ spin_lock_init(&card->dpcm_lock);
return snd_soc_bind_card(card);
}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 1ec06ef6d161..74a6f5705f66 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -4038,7 +4038,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dapm_widget template;
struct snd_soc_dapm_widget *w;
const char **w_param_text;
- unsigned long private_value;
+ unsigned long private_value = 0;
char *link_name;
int ret;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 0d5ec68a1e50..7fe5321000e8 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -954,10 +954,13 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
codec_params = *params;
/* fixup params based on TDM slot masks */
- if (codec_dai->tx_mask)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ codec_dai->tx_mask)
soc_pcm_codec_params_fixup(&codec_params,
codec_dai->tx_mask);
- if (codec_dai->rx_mask)
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ codec_dai->rx_mask)
soc_pcm_codec_params_fixup(&codec_params,
codec_dai->rx_mask);
@@ -1213,6 +1216,7 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
struct snd_soc_pcm_runtime *be, int stream)
{
struct snd_soc_dpcm *dpcm;
+ unsigned long flags;
/* only add new dpcms */
for_each_dpcm_be(fe, stream, dpcm) {
@@ -1228,8 +1232,10 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
dpcm->fe = fe;
be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
+ spin_lock_irqsave(&fe->card->dpcm_lock, flags);
list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
+ spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n",
stream ? "capture" : "playback", fe->dai_link->name,
@@ -1275,6 +1281,7 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm, *d;
+ unsigned long flags;
for_each_dpcm_be_safe(fe, stream, dpcm, d) {
dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n",
@@ -1294,8 +1301,10 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
#ifdef CONFIG_DEBUG_FS
debugfs_remove(dpcm->debugfs_state);
#endif
+ spin_lock_irqsave(&fe->card->dpcm_lock, flags);
list_del(&dpcm->list_be);
list_del(&dpcm->list_fe);
+ spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
kfree(dpcm);
}
}
@@ -1547,10 +1556,13 @@ int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
+ unsigned long flags;
+ spin_lock_irqsave(&fe->card->dpcm_lock, flags);
for_each_dpcm_be(fe, stream, dpcm)
dpcm->be->dpcm[stream].runtime_update =
SND_SOC_DPCM_UPDATE_NO;
+ spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
}
static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe,
@@ -2571,6 +2583,7 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
struct snd_soc_dpcm *dpcm;
enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
int ret;
+ unsigned long flags;
dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n",
stream ? "capture" : "playback", fe->dai_link->name);
@@ -2640,11 +2653,13 @@ close:
dpcm_be_dai_shutdown(fe, stream);
disconnect:
/* disconnect any non started BEs */
+ spin_lock_irqsave(&fe->card->dpcm_lock, flags);
for_each_dpcm_be(fe, stream, dpcm) {
struct snd_soc_pcm_runtime *be = dpcm->be;
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
}
+ spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
return ret;
}
@@ -3221,7 +3236,10 @@ int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
{
struct snd_soc_dpcm *dpcm;
int state;
+ int ret = 1;
+ unsigned long flags;
+ spin_lock_irqsave(&fe->card->dpcm_lock, flags);
for_each_dpcm_fe(be, stream, dpcm) {
if (dpcm->fe == fe)
@@ -3230,12 +3248,15 @@ int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
state = dpcm->fe->dpcm[stream].state;
if (state == SND_SOC_DPCM_STATE_START ||
state == SND_SOC_DPCM_STATE_PAUSED ||
- state == SND_SOC_DPCM_STATE_SUSPEND)
- return 0;
+ state == SND_SOC_DPCM_STATE_SUSPEND) {
+ ret = 0;
+ break;
+ }
}
+ spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
/* it's safe to free/stop this BE DAI */
- return 1;
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_free_stop);
@@ -3248,7 +3269,10 @@ int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
{
struct snd_soc_dpcm *dpcm;
int state;
+ int ret = 1;
+ unsigned long flags;
+ spin_lock_irqsave(&fe->card->dpcm_lock, flags);
for_each_dpcm_fe(be, stream, dpcm) {
if (dpcm->fe == fe)
@@ -3258,12 +3282,15 @@ int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
if (state == SND_SOC_DPCM_STATE_START ||
state == SND_SOC_DPCM_STATE_PAUSED ||
state == SND_SOC_DPCM_STATE_SUSPEND ||
- state == SND_SOC_DPCM_STATE_PREPARE)
- return 0;
+ state == SND_SOC_DPCM_STATE_PREPARE) {
+ ret = 0;
+ break;
+ }
}
+ spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
/* it's safe to change hw_params */
- return 1;
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
@@ -3302,6 +3329,7 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
struct snd_soc_dpcm *dpcm;
ssize_t offset = 0;
+ unsigned long flags;
/* FE state */
offset += snprintf(buf + offset, size - offset,
@@ -3329,6 +3357,7 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
goto out;
}
+ spin_lock_irqsave(&fe->card->dpcm_lock, flags);
for_each_dpcm_be(fe, stream, dpcm) {
struct snd_soc_pcm_runtime *be = dpcm->be;
params = &dpcm->hw_params;
@@ -3349,7 +3378,7 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
params_channels(params),
params_rate(params));
}
-
+ spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
out:
return offset;
}
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 25fca7055464..472f7705da93 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -535,6 +535,8 @@ static void remove_dai(struct snd_soc_component *comp,
if (dai->driver == dai_drv)
dai->driver = NULL;
+ kfree(dai_drv->playback.stream_name);
+ kfree(dai_drv->capture.stream_name);
kfree(dai_drv->name);
list_del(&dobj->list);
kfree(dai_drv);
@@ -894,19 +896,20 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
continue;
}
+ /* create any TLV data */
+ soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
+
/* pass control to driver for optional further init */
err = soc_tplg_init_kcontrol(tplg, &kc,
(struct snd_soc_tplg_ctl_hdr *) mc);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
mc->hdr.name);
+ soc_tplg_free_tlv(tplg, &kc);
kfree(sm);
continue;
}
- /* create any TLV data */
- soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
-
/* register control here */
err = soc_tplg_add_kcontrol(tplg, &kc,
&sm->dobj.control.kcontrol);
@@ -993,8 +996,6 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
for (i = 0; i < count; i++) {
ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
- tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
- ec->priv.size);
/* validate kcontrol */
if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
@@ -1005,6 +1006,9 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count,
if (se == NULL)
return -ENOMEM;
+ tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+ ec->priv.size);
+
dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n",
ec->hdr.name, ec->items);
@@ -1281,14 +1285,14 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
if (sm == NULL)
goto err;
- tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
- mc->priv.size);
-
/* validate kcontrol */
if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
goto err_str;
+ tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) +
+ mc->priv.size);
+
dev_dbg(tplg->dev, " adding DAPM widget mixer control %s at %d\n",
mc->hdr.name, i);
@@ -1324,18 +1328,19 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
continue;
}
+ /* create any TLV data */
+ soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
+
/* pass control to driver for optional further init */
err = soc_tplg_init_kcontrol(tplg, &kc[i],
(struct snd_soc_tplg_ctl_hdr *)mc);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
mc->hdr.name);
+ soc_tplg_free_tlv(tplg, &kc[i]);
kfree(sm);
continue;
}
-
- /* create any TLV data */
- soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
}
return kc;
@@ -1373,6 +1378,9 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
if (se == NULL)
goto err;
+ tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+ ec->priv.size);
+
dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
ec->hdr.name);
@@ -1437,9 +1445,6 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
ec->hdr.name);
goto err_se;
}
-
- tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
- ec->priv.size);
}
return kc;
@@ -1806,6 +1811,9 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL);
if (ret < 0) {
dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
+ kfree(dai_drv->playback.stream_name);
+ kfree(dai_drv->capture.stream_name);
+ kfree(dai_drv->name);
kfree(dai_drv);
return ret;
}
@@ -1876,6 +1884,9 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
ret = soc_tplg_dai_link_load(tplg, link, NULL);
if (ret < 0) {
dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n");
+ kfree(link->name);
+ kfree(link->stream_name);
+ kfree(link->cpu_dai_name);
kfree(link);
return ret;
}
diff --git a/sound/soc/sprd/Kconfig b/sound/soc/sprd/Kconfig
index 43ece7daf0e9..3b1eb320dab4 100644
--- a/sound/soc/sprd/Kconfig
+++ b/sound/soc/sprd/Kconfig
@@ -1,6 +1,7 @@
config SND_SOC_SPRD
tristate "SoC Audio for the Spreadtrum SoC chips"
depends on ARCH_SPRD || COMPILE_TEST
+ select SND_SOC_COMPRESS
help
Say Y or M if you want to add support for codecs attached to
the Spreadtrum SoCs' Audio interfaces.
diff --git a/sound/soc/sprd/Makefile b/sound/soc/sprd/Makefile
index 47620e57a9f2..fa1d5476b8da 100644
--- a/sound/soc/sprd/Makefile
+++ b/sound/soc/sprd/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
# Spreadtrum Audio Support
-obj-$(CONFIG_SND_SOC_SPRD) += sprd-pcm-dma.o
+obj-$(CONFIG_SND_SOC_SPRD) += sprd-pcm-dma.o sprd-pcm-compress.o
diff --git a/sound/soc/sprd/sprd-pcm-compress.c b/sound/soc/sprd/sprd-pcm-compress.c
new file mode 100644
index 000000000000..dc84257cffc2
--- /dev/null
+++ b/sound/soc/sprd/sprd-pcm-compress.c
@@ -0,0 +1,675 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2019 Spreadtrum Communications Inc.
+
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dma/sprd-dma.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+
+#include "sprd-pcm-dma.h"
+
+#define SPRD_COMPR_DMA_CHANS 2
+
+/* Default values if userspace does not set */
+#define SPRD_COMPR_MIN_FRAGMENT_SIZE SZ_8K
+#define SPRD_COMPR_MAX_FRAGMENT_SIZE SZ_128K
+#define SPRD_COMPR_MIN_NUM_FRAGMENTS 4
+#define SPRD_COMPR_MAX_NUM_FRAGMENTS 64
+
+/* DSP FIFO size */
+#define SPRD_COMPR_MCDT_EMPTY_WMK 0
+#define SPRD_COMPR_MCDT_FIFO_SIZE 512
+
+/* Stage 0 IRAM buffer size definition */
+#define SPRD_COMPR_IRAM_BUF_SIZE SZ_32K
+#define SPRD_COMPR_IRAM_INFO_SIZE (sizeof(struct sprd_compr_playinfo))
+#define SPRD_COMPR_IRAM_LINKLIST_SIZE (1024 - SPRD_COMPR_IRAM_INFO_SIZE)
+#define SPRD_COMPR_IRAM_SIZE (SPRD_COMPR_IRAM_BUF_SIZE + \
+ SPRD_COMPR_IRAM_INFO_SIZE + \
+ SPRD_COMPR_IRAM_LINKLIST_SIZE)
+
+/* Stage 1 DDR buffer size definition */
+#define SPRD_COMPR_AREA_BUF_SIZE SZ_2M
+#define SPRD_COMPR_AREA_LINKLIST_SIZE 1024
+#define SPRD_COMPR_AREA_SIZE (SPRD_COMPR_AREA_BUF_SIZE + \
+ SPRD_COMPR_AREA_LINKLIST_SIZE)
+
+struct sprd_compr_dma {
+ struct dma_chan *chan;
+ struct dma_async_tx_descriptor *desc;
+ dma_cookie_t cookie;
+ dma_addr_t phys;
+ void *virt;
+ int trans_len;
+};
+
+/*
+ * The Spreadtrum Audio compress offload mode will use 2-stage DMA transfer to
+ * save power. That means we can request 2 dma channels, one for source channel,
+ * and another one for destination channel. Once the source channel's transaction
+ * is done, it will trigger the destination channel's transaction automatically
+ * by hardware signal.
+ *
+ * For 2-stage DMA transfer, we can allocate 2 buffers: IRAM buffer (always
+ * power-on) and DDR buffer. The source channel will transfer data from IRAM
+ * buffer to the DSP fifo to decoding/encoding, once IRAM buffer is empty by
+ * transferring done, the destination channel will start to transfer data from
+ * DDR buffer to IRAM buffer.
+ *
+ * Since the DSP fifo is only 512B, IRAM buffer is allocated by 32K, and DDR
+ * buffer is larger to 2M. That means only the IRAM 32k data is transferred
+ * done, we can wake up the AP system to transfer data from DDR to IRAM, and
+ * other time the AP system can be suspended to save power.
+ */
+struct sprd_compr_stream {
+ struct snd_compr_stream *cstream;
+ struct sprd_compr_ops *compr_ops;
+ struct sprd_compr_dma dma[SPRD_COMPR_DMA_CHANS];
+
+ /* DMA engine channel number */
+ int num_channels;
+
+ /* Stage 0 IRAM buffer */
+ struct snd_dma_buffer iram_buffer;
+ /* Stage 1 DDR buffer */
+ struct snd_dma_buffer compr_buffer;
+
+ /* DSP play information IRAM buffer */
+ dma_addr_t info_phys;
+ void *info_area;
+ int info_size;
+
+ /* Data size copied to IRAM buffer */
+ int copied_total;
+ /* Total received data size from userspace */
+ int received_total;
+ /* Stage 0 IRAM buffer received data size */
+ int received_stage0;
+ /* Stage 1 DDR buffer received data size */
+ int received_stage1;
+ /* Stage 1 DDR buffer pointer */
+ int stage1_pointer;
+};
+
+static int sprd_platform_compr_trigger(struct snd_compr_stream *cstream,
+ int cmd);
+
+static void sprd_platform_compr_drain_notify(void *arg)
+{
+ struct snd_compr_stream *cstream = arg;
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sprd_compr_stream *stream = runtime->private_data;
+
+ memset(stream->info_area, 0, sizeof(struct sprd_compr_playinfo));
+
+ if (cstream)
+ snd_compr_drain_notify(cstream);
+}
+
+static void sprd_platform_compr_dma_complete(void *data)
+{
+ struct snd_compr_stream *cstream = data;
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sprd_compr_stream *stream = runtime->private_data;
+ struct sprd_compr_dma *dma = &stream->dma[1];
+
+ /* Update data size copied to IRAM buffer */
+ stream->copied_total += dma->trans_len;
+ if (stream->copied_total > stream->received_total)
+ stream->copied_total = stream->received_total;
+
+ snd_compr_fragment_elapsed(cstream);
+}
+
+static int sprd_platform_compr_dma_config(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params,
+ int channel)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sprd_compr_stream *stream = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ struct device *dev = component->dev;
+ struct sprd_compr_data *data = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct sprd_pcm_dma_params *dma_params = data->dma_params;
+ struct sprd_compr_dma *dma = &stream->dma[channel];
+ struct dma_slave_config config = { };
+ struct sprd_dma_linklist link = { };
+ enum dma_transfer_direction dir;
+ struct scatterlist *sg, *sgt;
+ enum dma_slave_buswidth bus_width;
+ int period, period_cnt, sg_num = 2;
+ dma_addr_t src_addr, dst_addr;
+ unsigned long flags;
+ int ret, j;
+
+ if (!dma_params) {
+ dev_err(dev, "no dma parameters setting\n");
+ return -EINVAL;
+ }
+
+ dma->chan = dma_request_slave_channel(dev,
+ dma_params->chan_name[channel]);
+ if (!dma->chan) {
+ dev_err(dev, "failed to request dma channel\n");
+ return -ENODEV;
+ }
+
+ sgt = sg = devm_kcalloc(dev, sg_num, sizeof(*sg), GFP_KERNEL);
+ if (!sg) {
+ ret = -ENOMEM;
+ goto sg_err;
+ }
+
+ switch (channel) {
+ case 0:
+ bus_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ period = (SPRD_COMPR_MCDT_FIFO_SIZE - SPRD_COMPR_MCDT_EMPTY_WMK) * 4;
+ period_cnt = params->buffer.fragment_size / period;
+ src_addr = stream->iram_buffer.addr;
+ dst_addr = dma_params->dev_phys[channel];
+ flags = SPRD_DMA_FLAGS(SPRD_DMA_SRC_CHN1,
+ SPRD_DMA_TRANS_DONE_TRG,
+ SPRD_DMA_FRAG_REQ,
+ SPRD_DMA_TRANS_INT);
+ break;
+
+ case 1:
+ bus_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ period = params->buffer.fragment_size;
+ period_cnt = params->buffer.fragments;
+ src_addr = stream->compr_buffer.addr;
+ dst_addr = stream->iram_buffer.addr;
+ flags = SPRD_DMA_FLAGS(SPRD_DMA_DST_CHN1,
+ SPRD_DMA_TRANS_DONE_TRG,
+ SPRD_DMA_FRAG_REQ,
+ SPRD_DMA_TRANS_INT);
+ break;
+
+ default:
+ ret = -EINVAL;
+ goto config_err;
+ }
+
+ dma->trans_len = period * period_cnt;
+
+ config.src_maxburst = period;
+ config.src_addr_width = bus_width;
+ config.dst_addr_width = bus_width;
+ if (cstream->direction == SND_COMPRESS_PLAYBACK) {
+ config.src_addr = src_addr;
+ config.dst_addr = dst_addr;
+ dir = DMA_MEM_TO_DEV;
+ } else {
+ config.src_addr = dst_addr;
+ config.dst_addr = src_addr;
+ dir = DMA_DEV_TO_MEM;
+ }
+
+ sg_init_table(sgt, sg_num);
+ for (j = 0; j < sg_num; j++, sgt++) {
+ sg_dma_len(sgt) = dma->trans_len;
+ sg_dma_address(sgt) = dst_addr;
+ }
+
+ /*
+ * Configure the link-list address for the DMA engine link-list
+ * mode.
+ */
+ link.virt_addr = (unsigned long)dma->virt;
+ link.phy_addr = dma->phys;
+
+ ret = dmaengine_slave_config(dma->chan, &config);
+ if (ret) {
+ dev_err(dev,
+ "failed to set slave configuration: %d\n", ret);
+ goto config_err;
+ }
+
+ /*
+ * We configure the DMA request mode, interrupt mode, channel
+ * mode and channel trigger mode by the flags.
+ */
+ dma->desc = dma->chan->device->device_prep_slave_sg(dma->chan, sg,
+ sg_num, dir,
+ flags, &link);
+ if (!dma->desc) {
+ dev_err(dev, "failed to prepare slave sg\n");
+ ret = -ENOMEM;
+ goto config_err;
+ }
+
+ /* Only channel 1 transfer can wake up the AP system. */
+ if (!params->no_wake_mode && channel == 1) {
+ dma->desc->callback = sprd_platform_compr_dma_complete;
+ dma->desc->callback_param = cstream;
+ }
+
+ devm_kfree(dev, sg);
+
+ return 0;
+
+config_err:
+ devm_kfree(dev, sg);
+sg_err:
+ dma_release_channel(dma->chan);
+ return ret;
+}
+
+static int sprd_platform_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sprd_compr_stream *stream = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ struct device *dev = component->dev;
+ struct sprd_compr_params compr_params = { };
+ int ret;
+
+ /*
+ * Configure the DMA engine 2-stage transfer mode. Channel 1 set as the
+ * destination channel, and channel 0 set as the source channel, that
+ * means once the source channel's transaction is done, it will trigger
+ * the destination channel's transaction automatically.
+ */
+ ret = sprd_platform_compr_dma_config(cstream, params, 1);
+ if (ret) {
+ dev_err(dev, "faied to config stage 1 DMA: %d\n", ret);
+ return ret;
+ }
+
+ ret = sprd_platform_compr_dma_config(cstream, params, 0);
+ if (ret) {
+ dev_err(dev, "faied to config stage 0 DMA: %d\n", ret);
+ goto config_err;
+ }
+
+ compr_params.direction = cstream->direction;
+ compr_params.sample_rate = params->codec.sample_rate;
+ compr_params.channels = stream->num_channels;
+ compr_params.info_phys = stream->info_phys;
+ compr_params.info_size = stream->info_size;
+ compr_params.rate = params->codec.bit_rate;
+ compr_params.format = params->codec.id;
+
+ ret = stream->compr_ops->set_params(cstream->direction, &compr_params);
+ if (ret) {
+ dev_err(dev, "failed to set parameters: %d\n", ret);
+ goto params_err;
+ }
+
+ return 0;
+
+params_err:
+ dma_release_channel(stream->dma[0].chan);
+config_err:
+ dma_release_channel(stream->dma[1].chan);
+ return ret;
+}
+
+static int sprd_platform_compr_open(struct snd_compr_stream *cstream)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ struct device *dev = component->dev;
+ struct sprd_compr_data *data = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct sprd_compr_stream *stream;
+ struct sprd_compr_callback cb;
+ int stream_id = cstream->direction, ret;
+
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ stream = devm_kzalloc(dev, sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->cstream = cstream;
+ stream->num_channels = 2;
+ stream->compr_ops = data->ops;
+
+ /*
+ * Allocate the stage 0 IRAM buffer size, including the DMA 0
+ * link-list size and play information of DSP address size.
+ */
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_IRAM, dev,
+ SPRD_COMPR_IRAM_SIZE, &stream->iram_buffer);
+ if (ret < 0)
+ goto err_iram;
+
+ /* Use to save link-list configuration for DMA 0. */
+ stream->dma[0].virt = stream->iram_buffer.area + SPRD_COMPR_IRAM_SIZE;
+ stream->dma[0].phys = stream->iram_buffer.addr + SPRD_COMPR_IRAM_SIZE;
+
+ /* Use to update the current data offset of DSP. */
+ stream->info_phys = stream->iram_buffer.addr + SPRD_COMPR_IRAM_SIZE +
+ SPRD_COMPR_IRAM_LINKLIST_SIZE;
+ stream->info_area = stream->iram_buffer.area + SPRD_COMPR_IRAM_SIZE +
+ SPRD_COMPR_IRAM_LINKLIST_SIZE;
+ stream->info_size = SPRD_COMPR_IRAM_INFO_SIZE;
+
+ /*
+ * Allocate the stage 1 DDR buffer size, including the DMA 1 link-list
+ * size.
+ */
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev,
+ SPRD_COMPR_AREA_SIZE, &stream->compr_buffer);
+ if (ret < 0)
+ goto err_compr;
+
+ /* Use to save link-list configuration for DMA 1. */
+ stream->dma[1].virt = stream->compr_buffer.area + SPRD_COMPR_AREA_SIZE;
+ stream->dma[1].phys = stream->compr_buffer.addr + SPRD_COMPR_AREA_SIZE;
+
+ cb.drain_notify = sprd_platform_compr_drain_notify;
+ cb.drain_data = cstream;
+ ret = stream->compr_ops->open(stream_id, &cb);
+ if (ret) {
+ dev_err(dev, "failed to open compress platform: %d\n", ret);
+ goto err_open;
+ }
+
+ runtime->private_data = stream;
+ return 0;
+
+err_open:
+ snd_dma_free_pages(&stream->compr_buffer);
+err_compr:
+ snd_dma_free_pages(&stream->iram_buffer);
+err_iram:
+ devm_kfree(dev, stream);
+
+ return ret;
+}
+
+static int sprd_platform_compr_free(struct snd_compr_stream *cstream)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sprd_compr_stream *stream = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ struct device *dev = component->dev;
+ int stream_id = cstream->direction, i;
+
+ for (i = 0; i < stream->num_channels; i++) {
+ struct sprd_compr_dma *dma = &stream->dma[i];
+
+ if (dma->chan) {
+ dma_release_channel(dma->chan);
+ dma->chan = NULL;
+ }
+ }
+
+ snd_dma_free_pages(&stream->compr_buffer);
+ snd_dma_free_pages(&stream->iram_buffer);
+
+ stream->compr_ops->close(stream_id);
+
+ devm_kfree(dev, stream);
+ return 0;
+}
+
+static int sprd_platform_compr_trigger(struct snd_compr_stream *cstream,
+ int cmd)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sprd_compr_stream *stream = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_component *component =
+ snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ struct device *dev = component->dev;
+ int channels = stream->num_channels, ret = 0, i;
+ int stream_id = cstream->direction;
+
+ if (cstream->direction != SND_COMPRESS_PLAYBACK) {
+ dev_err(dev, "unsupported compress direction\n");
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ for (i = channels - 1; i >= 0; i--) {
+ struct sprd_compr_dma *dma = &stream->dma[i];
+
+ if (!dma->desc)
+ continue;
+
+ dma->cookie = dmaengine_submit(dma->desc);
+ ret = dma_submit_error(dma->cookie);
+ if (ret) {
+ dev_err(dev, "failed to submit request: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ for (i = channels - 1; i >= 0; i--) {
+ struct sprd_compr_dma *dma = &stream->dma[i];
+
+ if (dma->chan)
+ dma_async_issue_pending(dma->chan);
+ }
+
+ ret = stream->compr_ops->start(stream_id);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ for (i = channels - 1; i >= 0; i--) {
+ struct sprd_compr_dma *dma = &stream->dma[i];
+
+ if (dma->chan)
+ dmaengine_terminate_async(dma->chan);
+ }
+
+ stream->copied_total = 0;
+ stream->stage1_pointer = 0;
+ stream->received_total = 0;
+ stream->received_stage0 = 0;
+ stream->received_stage1 = 0;
+
+ ret = stream->compr_ops->stop(stream_id);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ for (i = channels - 1; i >= 0; i--) {
+ struct sprd_compr_dma *dma = &stream->dma[i];
+
+ if (dma->chan)
+ dmaengine_pause(dma->chan);
+ }
+
+ ret = stream->compr_ops->pause(stream_id);
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ for (i = channels - 1; i >= 0; i--) {
+ struct sprd_compr_dma *dma = &stream->dma[i];
+
+ if (dma->chan)
+ dmaengine_resume(dma->chan);
+ }
+
+ ret = stream->compr_ops->pause_release(stream_id);
+ break;
+
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ case SND_COMPR_TRIGGER_DRAIN:
+ ret = stream->compr_ops->drain(stream->received_total);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int sprd_platform_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sprd_compr_stream *stream = runtime->private_data;
+ struct sprd_compr_playinfo *info =
+ (struct sprd_compr_playinfo *)stream->info_area;
+
+ tstamp->copied_total = stream->copied_total;
+ tstamp->pcm_io_frames = info->current_data_offset;
+
+ return 0;
+}
+
+static int sprd_platform_compr_copy(struct snd_compr_stream *cstream,
+ char __user *buf, size_t count)
+{
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sprd_compr_stream *stream = runtime->private_data;
+ int avail_bytes, data_count = count;
+ void *dst;
+
+ /*
+ * We usually set fragment size as 32K, and the stage 0 IRAM buffer
+ * size is 32K too. So if now the received data size of the stage 0
+ * IRAM buffer is less than 32K, that means we have some available
+ * spaces for the stage 0 IRAM buffer.
+ */
+ if (stream->received_stage0 < runtime->fragment_size) {
+ avail_bytes = runtime->fragment_size - stream->received_stage0;
+ dst = stream->iram_buffer.area + stream->received_stage0;
+
+ if (avail_bytes >= data_count) {
+ /*
+ * Copy data to the stage 0 IRAM buffer directly if
+ * spaces are enough.
+ */
+ if (copy_from_user(dst, buf, data_count))
+ return -EFAULT;
+
+ stream->received_stage0 += data_count;
+ stream->copied_total += data_count;
+ goto copy_done;
+ } else {
+ /*
+ * If the data count is larger than the available spaces
+ * of the the stage 0 IRAM buffer, we should copy one
+ * partial data to the stage 0 IRAM buffer, and copy
+ * the left to the stage 1 DDR buffer.
+ */
+ if (copy_from_user(dst, buf, avail_bytes))
+ return -EFAULT;
+
+ data_count -= avail_bytes;
+ stream->received_stage0 += avail_bytes;
+ stream->copied_total += avail_bytes;
+ buf += avail_bytes;
+ }
+ }
+
+ /*
+ * Copy data to the stage 1 DDR buffer if no spaces for the stage 0 IRAM
+ * buffer.
+ */
+ dst = stream->compr_buffer.area + stream->stage1_pointer;
+ if (data_count < stream->compr_buffer.bytes - stream->stage1_pointer) {
+ if (copy_from_user(dst, buf, data_count))
+ return -EFAULT;
+
+ stream->stage1_pointer += data_count;
+ } else {
+ avail_bytes = stream->compr_buffer.bytes - stream->stage1_pointer;
+
+ if (copy_from_user(dst, buf, avail_bytes))
+ return -EFAULT;
+
+ if (copy_from_user(stream->compr_buffer.area, buf + avail_bytes,
+ data_count - avail_bytes))
+ return -EFAULT;
+
+ stream->stage1_pointer = data_count - avail_bytes;
+ }
+
+ stream->received_stage1 += data_count;
+
+copy_done:
+ /* Update the copied data size. */
+ stream->received_total += count;
+ return count;
+}
+
+static int sprd_platform_compr_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ caps->direction = cstream->direction;
+ caps->min_fragment_size = SPRD_COMPR_MIN_FRAGMENT_SIZE;
+ caps->max_fragment_size = SPRD_COMPR_MAX_FRAGMENT_SIZE;
+ caps->min_fragments = SPRD_COMPR_MIN_NUM_FRAGMENTS;
+ caps->max_fragments = SPRD_COMPR_MAX_NUM_FRAGMENTS;
+ caps->num_codecs = 2;
+ caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_AAC;
+
+ return 0;
+}
+
+static int
+sprd_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
+{
+ switch (codec->codec) {
+ case SND_AUDIOCODEC_MP3:
+ codec->num_descriptors = 2;
+ codec->descriptor[0].max_ch = 2;
+ codec->descriptor[0].bit_rate[0] = 320;
+ codec->descriptor[0].bit_rate[1] = 128;
+ codec->descriptor[0].num_bitrates = 2;
+ codec->descriptor[0].profiles = 0;
+ codec->descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO;
+ codec->descriptor[0].formats = 0;
+ break;
+
+ case SND_AUDIOCODEC_AAC:
+ codec->num_descriptors = 2;
+ codec->descriptor[1].max_ch = 2;
+ codec->descriptor[1].bit_rate[0] = 320;
+ codec->descriptor[1].bit_rate[1] = 128;
+ codec->descriptor[1].num_bitrates = 2;
+ codec->descriptor[1].profiles = 0;
+ codec->descriptor[1].modes = 0;
+ codec->descriptor[1].formats = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const struct snd_compr_ops sprd_platform_compr_ops = {
+ .open = sprd_platform_compr_open,
+ .free = sprd_platform_compr_free,
+ .set_params = sprd_platform_compr_set_params,
+ .trigger = sprd_platform_compr_trigger,
+ .pointer = sprd_platform_compr_pointer,
+ .copy = sprd_platform_compr_copy,
+ .get_caps = sprd_platform_compr_get_caps,
+ .get_codec_caps = sprd_platform_compr_get_codec_caps,
+};
+
+MODULE_DESCRIPTION("Spreadtrum ASoC Compress Platform Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:compress-platform");
diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c
index cbb27c4abeba..9be6d4b2bf74 100644
--- a/sound/soc/sprd/sprd-pcm-dma.c
+++ b/sound/soc/sprd/sprd-pcm-dma.c
@@ -13,7 +13,6 @@
#include "sprd-pcm-dma.h"
-#define DRV_NAME "sprd_pcm_dma"
#define SPRD_PCM_DMA_LINKLIST_SIZE 64
#define SPRD_PCM_DMA_BRUST_LEN 640
@@ -524,6 +523,7 @@ static void sprd_pcm_free(struct snd_pcm *pcm)
static const struct snd_soc_component_driver sprd_soc_component = {
.name = DRV_NAME,
.ops = &sprd_pcm_ops,
+ .compr_ops = &sprd_platform_compr_ops,
.pcm_new = sprd_pcm_new,
.pcm_free = sprd_pcm_free,
};
diff --git a/sound/soc/sprd/sprd-pcm-dma.h b/sound/soc/sprd/sprd-pcm-dma.h
index d85a34f1461d..08e9fdba82f1 100644
--- a/sound/soc/sprd/sprd-pcm-dma.h
+++ b/sound/soc/sprd/sprd-pcm-dma.h
@@ -3,8 +3,11 @@
#ifndef __SPRD_PCM_DMA_H
#define __SPRD_PCM_DMA_H
+#define DRV_NAME "sprd_pcm_dma"
#define SPRD_PCM_CHANNEL_MAX 2
+extern const struct snd_compr_ops sprd_platform_compr_ops;
+
struct sprd_pcm_dma_params {
dma_addr_t dev_phys[SPRD_PCM_CHANNEL_MAX];
u32 datawidth[SPRD_PCM_CHANNEL_MAX];
@@ -12,4 +15,44 @@ struct sprd_pcm_dma_params {
const char *chan_name[SPRD_PCM_CHANNEL_MAX];
};
+struct sprd_compr_playinfo {
+ int total_time;
+ int current_time;
+ int total_data_length;
+ int current_data_offset;
+};
+
+struct sprd_compr_params {
+ u32 direction;
+ u32 rate;
+ u32 sample_rate;
+ u32 channels;
+ u32 format;
+ u32 period;
+ u32 periods;
+ u32 info_phys;
+ u32 info_size;
+};
+
+struct sprd_compr_callback {
+ void (*drain_notify)(void *data);
+ void *drain_data;
+};
+
+struct sprd_compr_ops {
+ int (*open)(int str_id, struct sprd_compr_callback *cb);
+ int (*close)(int str_id);
+ int (*start)(int str_id);
+ int (*stop)(int str_id);
+ int (*pause)(int str_id);
+ int (*pause_release)(int str_id);
+ int (*drain)(int received_total);
+ int (*set_params)(int str_id, struct sprd_compr_params *params);
+};
+
+struct sprd_compr_data {
+ struct sprd_compr_ops *ops;
+ struct sprd_pcm_dma_params *dma_params;
+};
+
#endif /* __SPRD_PCM_DMA_H */
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
index 47901983a6ff..1f20ff1ccb57 100644
--- a/sound/soc/stm/stm32_adfsdm.c
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -41,7 +41,7 @@ struct stm32_adfsdm_priv {
static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_PAUSE,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rate_min = 8000,
diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c
index 47c334de6b09..952634258b02 100644
--- a/sound/soc/stm/stm32_i2s.c
+++ b/sound/soc/stm/stm32_i2s.c
@@ -179,7 +179,6 @@ enum i2s_datlen {
I2S_I2SMOD_DATLEN_32,
};
-#define STM32_I2S_DAI_NAME_SIZE 20
#define STM32_I2S_FIFO_SIZE 16
#define STM32_I2S_IS_MASTER(x) ((x)->ms_flg == I2S_MS_MASTER)
@@ -202,7 +201,6 @@ enum i2s_datlen {
* @phys_addr: I2S registers physical base address
* @lock_fd: lock to manage race conditions in full duplex mode
* @irq_lock: prevent race condition with IRQ
- * @dais_name: DAI name
* @mclk_rate: master clock frequency (Hz)
* @fmt: DAI protocol
* @refcount: keep count of opened streams on I2S
@@ -224,7 +222,6 @@ struct stm32_i2s_data {
dma_addr_t phys_addr;
spinlock_t lock_fd; /* Manage race conditions for full duplex */
spinlock_t irq_lock; /* used to prevent race condition with IRQ */
- char dais_name[STM32_I2S_DAI_NAME_SIZE];
unsigned int mclk_rate;
unsigned int fmt;
int refcount;
@@ -281,7 +278,6 @@ static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg)
case STM32_I2S_CFG2_REG:
case STM32_I2S_IER_REG:
case STM32_I2S_SR_REG:
- case STM32_I2S_TXDR_REG:
case STM32_I2S_RXDR_REG:
case STM32_I2S_CGFR_REG:
return true;
@@ -293,7 +289,7 @@ static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg)
static bool stm32_i2s_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case STM32_I2S_TXDR_REG:
+ case STM32_I2S_SR_REG:
case STM32_I2S_RXDR_REG:
return true;
default:
@@ -496,12 +492,6 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai,
unsigned int fthlv;
int ret;
- if ((params_channels(params) == 1) &&
- ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A)) {
- dev_err(cpu_dai->dev, "Mono mode supported only by DSP_A\n");
- return -EINVAL;
- }
-
switch (format) {
case 16:
cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16);
@@ -551,6 +541,10 @@ static int stm32_i2s_startup(struct snd_pcm_substream *substream,
i2s->substream = substream;
spin_unlock_irqrestore(&i2s->irq_lock, flags);
+ if ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A)
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+
ret = clk_prepare_enable(i2s->i2sclk);
if (ret < 0) {
dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret);
@@ -593,7 +587,8 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* Enable i2s */
- dev_dbg(cpu_dai->dev, "start I2S\n");
+ dev_dbg(cpu_dai->dev, "start I2S %s\n",
+ playback_flg ? "playback" : "capture");
cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN;
regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG,
@@ -638,6 +633,9 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dev_dbg(cpu_dai->dev, "stop I2S %s\n",
+ playback_flg ? "playback" : "capture");
+
if (playback_flg)
regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG,
I2S_IER_UDRIE,
@@ -654,8 +652,6 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
break;
}
- dev_dbg(cpu_dai->dev, "stop I2S\n");
-
ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
I2S_CR1_SPE, 0);
if (ret < 0) {
@@ -771,12 +767,8 @@ static int stm32_i2s_dais_init(struct platform_device *pdev,
if (!dai_ptr)
return -ENOMEM;
- snprintf(i2s->dais_name, STM32_I2S_DAI_NAME_SIZE,
- "%s", dev_name(&pdev->dev));
-
dai_ptr->probe = stm32_i2s_dai_probe;
dai_ptr->ops = &stm32_i2s_pcm_dai_ops;
- dai_ptr->name = i2s->dais_name;
dai_ptr->id = 1;
stm32_i2s_dai_init(&dai_ptr->playback, "playback");
stm32_i2s_dai_init(&dai_ptr->capture, "capture");
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
index 14c9591aae42..7550d5f08be3 100644
--- a/sound/soc/stm/stm32_sai.c
+++ b/sound/soc/stm/stm32_sai.c
@@ -21,6 +21,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
@@ -44,20 +45,41 @@ static const struct of_device_id stm32_sai_ids[] = {
{}
};
-static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
+static int stm32_sai_pclk_disable(struct device *dev)
+{
+ struct stm32_sai_data *sai = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(sai->pclk);
+
+ return 0;
+}
+
+static int stm32_sai_pclk_enable(struct device *dev)
{
+ struct stm32_sai_data *sai = dev_get_drvdata(dev);
int ret;
- /* Enable peripheral clock to allow GCR register access */
ret = clk_prepare_enable(sai->pclk);
if (ret) {
dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret);
return ret;
}
+ return 0;
+}
+
+static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
+{
+ int ret;
+
+ /* Enable peripheral clock to allow GCR register access */
+ ret = stm32_sai_pclk_enable(&sai->pdev->dev);
+ if (ret)
+ return ret;
+
writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base);
- clk_disable_unprepare(sai->pclk);
+ stm32_sai_pclk_disable(&sai->pdev->dev);
return 0;
}
@@ -68,11 +90,9 @@ static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
int ret;
/* Enable peripheral clock to allow GCR register access */
- ret = clk_prepare_enable(sai->pclk);
- if (ret) {
- dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret);
+ ret = stm32_sai_pclk_enable(&sai->pdev->dev);
+ if (ret)
return ret;
- }
dev_dbg(&sai->pdev->dev, "Set %pOFn%s as synchro provider\n",
sai->pdev->dev.of_node,
@@ -83,13 +103,13 @@ static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n",
sai->pdev->dev.of_node,
prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B");
- clk_disable_unprepare(sai->pclk);
+ stm32_sai_pclk_disable(&sai->pdev->dev);
return -EINVAL;
}
writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base);
- clk_disable_unprepare(sai->pclk);
+ stm32_sai_pclk_disable(&sai->pdev->dev);
return 0;
}
@@ -105,6 +125,7 @@ static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
if (!pdev) {
dev_err(&sai_client->pdev->dev,
"Device not found for node %pOFn\n", np_provider);
+ of_node_put(np_provider);
return -ENODEV;
}
@@ -113,19 +134,20 @@ static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
dev_err(&sai_client->pdev->dev,
"SAI sync provider data not found\n");
ret = -EINVAL;
- goto out_put_dev;
+ goto error;
}
/* Configure sync client */
ret = stm32_sai_sync_conf_client(sai_client, synci);
if (ret < 0)
- goto out_put_dev;
+ goto error;
/* Configure sync provider */
ret = stm32_sai_sync_conf_provider(sai_provider, synco);
-out_put_dev:
+error:
put_device(&pdev->dev);
+ of_node_put(np_provider);
return ret;
}
@@ -193,12 +215,54 @@ static int stm32_sai_probe(struct platform_device *pdev)
return devm_of_platform_populate(&pdev->dev);
}
+#ifdef CONFIG_PM_SLEEP
+/*
+ * When pins are shared by two sai sub instances, pins have to be defined
+ * in sai parent node. In this case, pins state is not managed by alsa fw.
+ * These pins are managed in suspend/resume callbacks.
+ */
+static int stm32_sai_suspend(struct device *dev)
+{
+ struct stm32_sai_data *sai = dev_get_drvdata(dev);
+ int ret;
+
+ ret = stm32_sai_pclk_enable(dev);
+ if (ret)
+ return ret;
+
+ sai->gcr = readl_relaxed(sai->base);
+ stm32_sai_pclk_disable(dev);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int stm32_sai_resume(struct device *dev)
+{
+ struct stm32_sai_data *sai = dev_get_drvdata(dev);
+ int ret;
+
+ ret = stm32_sai_pclk_enable(dev);
+ if (ret)
+ return ret;
+
+ writel_relaxed(sai->gcr, sai->base);
+ stm32_sai_pclk_disable(dev);
+
+ return pinctrl_pm_select_default_state(dev);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops stm32_sai_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume)
+};
+
MODULE_DEVICE_TABLE(of, stm32_sai_ids);
static struct platform_driver stm32_sai_driver = {
.driver = {
.name = "st,stm32-sai",
.of_match_table = stm32_sai_ids,
+ .pm = &stm32_sai_pm_ops,
},
.probe = stm32_sai_probe,
};
diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h
index 08de899c766b..9c36a393ab7b 100644
--- a/sound/soc/stm/stm32_sai.h
+++ b/sound/soc/stm/stm32_sai.h
@@ -268,6 +268,7 @@ struct stm32_sai_conf {
* @version: SOC version
* @irq: SAI interrupt line
* @set_sync: pointer to synchro mode configuration callback
+ * @gcr: SAI Global Configuration Register
*/
struct stm32_sai_data {
struct platform_device *pdev;
@@ -279,4 +280,5 @@ struct stm32_sai_data {
int irq;
int (*set_sync)(struct stm32_sai_data *sai,
struct device_node *np_provider, int synco, int synci);
+ u32 gcr;
};
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index f9297228c41c..2a90641cf164 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -100,8 +100,9 @@
* @slot_mask: rx or tx active slots mask. set at init or at runtime
* @data_size: PCM data width. corresponds to PCM substream width.
* @spdif_frm_cnt: S/PDIF playback frame counter
- * @snd_aes_iec958: iec958 data
+ * @iec958: iec958 data
* @ctrl_lock: control lock
+ * @irq_lock: prevent race condition with IRQ
*/
struct stm32_sai_sub_data {
struct platform_device *pdev;
@@ -133,6 +134,7 @@ struct stm32_sai_sub_data {
unsigned int spdif_frm_cnt;
struct snd_aes_iec958 iec958;
struct mutex ctrl_lock; /* protect resources accessed by controls */
+ spinlock_t irq_lock; /* used to prevent race condition with IRQ */
};
enum stm32_sai_fifo_th {
@@ -166,6 +168,7 @@ static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case STM_SAI_DR_REGX:
+ case STM_SAI_SR_REGX:
return true;
default:
return false;
@@ -180,7 +183,6 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)
case STM_SAI_FRCR_REGX:
case STM_SAI_SLOTR_REGX:
case STM_SAI_IMR_REGX:
- case STM_SAI_SR_REGX:
case STM_SAI_CLRFR_REGX:
case STM_SAI_DR_REGX:
case STM_SAI_PDMCR_REGX:
@@ -200,6 +202,7 @@ static const struct regmap_config stm32_sai_sub_regmap_config_f4 = {
.volatile_reg = stm32_sai_sub_volatile_reg,
.writeable_reg = stm32_sai_sub_writeable_reg,
.fast_io = true,
+ .cache_type = REGCACHE_FLAT,
};
static const struct regmap_config stm32_sai_sub_regmap_config_h7 = {
@@ -211,6 +214,7 @@ static const struct regmap_config stm32_sai_sub_regmap_config_h7 = {
.volatile_reg = stm32_sai_sub_volatile_reg,
.writeable_reg = stm32_sai_sub_writeable_reg,
.fast_io = true,
+ .cache_type = REGCACHE_FLAT,
};
static int snd_pcm_iec958_info(struct snd_kcontrol *kcontrol,
@@ -439,8 +443,8 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid)
if (!flags)
return IRQ_NONE;
- regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
- SAI_XCLRFR_MASK);
+ regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
+ SAI_XCLRFR_MASK);
if (!sai->substream) {
dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr);
@@ -474,8 +478,10 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid)
status = SNDRV_PCM_STATE_XRUN;
}
- if (status != SNDRV_PCM_STATE_RUNNING)
+ spin_lock(&sai->irq_lock);
+ if (status != SNDRV_PCM_STATE_RUNNING && sai->substream)
snd_pcm_stop_xrun(sai->substream);
+ spin_unlock(&sai->irq_lock);
return IRQ_HANDLED;
}
@@ -679,8 +685,19 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream,
{
struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
int imr, cr2, ret;
+ unsigned long flags;
+ spin_lock_irqsave(&sai->irq_lock, flags);
sai->substream = substream;
+ spin_unlock_irqrestore(&sai->irq_lock, flags);
+
+ if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) {
+ snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_FMTBIT_S32_LE);
+ snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ }
ret = clk_prepare_enable(sai->sai_ck);
if (ret < 0) {
@@ -689,9 +706,8 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream,
}
/* Enable ITs */
-
- regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX,
- SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
+ regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX,
+ SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
imr = SAI_XIMR_OVRUDRIE;
if (STM_SAI_IS_CAPTURE(sai)) {
@@ -723,10 +739,10 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,
* SAI fifo threshold is set to half fifo, to keep enough space
* for DMA incoming bursts.
*/
- regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX,
- SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
- SAI_XCR2_FFLUSH |
- SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF));
+ regmap_write_bits(sai->regmap, STM_SAI_CR2_REGX,
+ SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
+ SAI_XCR2_FFLUSH |
+ SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF));
/* DS bits in CR1 not set for SPDIF (size forced to 24 bits).*/
if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) {
@@ -898,7 +914,7 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
struct snd_pcm_hw_params *params)
{
struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
- int div = 0;
+ int div = 0, cr1 = 0;
int sai_clk_rate, mclk_ratio, den;
unsigned int rate = params_rate(params);
@@ -943,13 +959,19 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
} else {
if (sai->mclk_rate) {
mclk_ratio = sai->mclk_rate / rate;
- if ((mclk_ratio != 512) &&
- (mclk_ratio != 256)) {
+ if (mclk_ratio == 512) {
+ cr1 = SAI_XCR1_OSR;
+ } else if (mclk_ratio != 256) {
dev_err(cpu_dai->dev,
"Wrong mclk ratio %d\n",
mclk_ratio);
return -EINVAL;
}
+
+ regmap_update_bits(sai->regmap,
+ STM_SAI_CR1_REGX,
+ SAI_XCR1_OSR, cr1);
+
div = stm32_sai_get_clk_div(sai, sai_clk_rate,
sai->mclk_rate);
if (div < 0)
@@ -1051,6 +1073,7 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned long flags;
regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0);
@@ -1061,18 +1084,21 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
clk_rate_exclusive_put(sai->sai_mclk);
+ spin_lock_irqsave(&sai->irq_lock, flags);
sai->substream = NULL;
+ spin_unlock_irqrestore(&sai->irq_lock, flags);
}
static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *cpu_dai)
{
struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev);
+ struct snd_kcontrol_new knew = iec958_ctls;
if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) {
dev_dbg(&sai->pdev->dev, "%s: register iec controls", __func__);
- return snd_ctl_add(rtd->pcm->card,
- snd_ctl_new1(&iec958_ctls, sai));
+ knew.device = rtd->pcm->device;
+ return snd_ctl_add(rtd->pcm->card, snd_ctl_new1(&knew, sai));
}
return 0;
@@ -1081,7 +1107,7 @@ static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd,
static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
{
struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev);
- int cr1 = 0, cr1_mask;
+ int cr1 = 0, cr1_mask, ret;
sai->cpu_dai = cpu_dai;
@@ -1111,8 +1137,10 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
/* Configure synchronization */
if (sai->sync == SAI_SYNC_EXTERNAL) {
/* Configure synchro client and provider */
- sai->pdata->set_sync(sai->pdata, sai->np_sync_provider,
- sai->synco, sai->synci);
+ ret = sai->pdata->set_sync(sai->pdata, sai->np_sync_provider,
+ sai->synco, sai->synci);
+ if (ret)
+ return ret;
}
cr1_mask |= SAI_XCR1_SYNCEN_MASK;
@@ -1424,6 +1452,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
sai->pdev = pdev;
mutex_init(&sai->ctrl_lock);
+ spin_lock_init(&sai->irq_lock);
platform_set_drvdata(pdev, sai);
sai->pdata = dev_get_drvdata(pdev->dev.parent);
@@ -1464,10 +1493,34 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int stm32_sai_sub_suspend(struct device *dev)
+{
+ struct stm32_sai_sub_data *sai = dev_get_drvdata(dev);
+
+ regcache_cache_only(sai->regmap, true);
+ regcache_mark_dirty(sai->regmap);
+ return 0;
+}
+
+static int stm32_sai_sub_resume(struct device *dev)
+{
+ struct stm32_sai_sub_data *sai = dev_get_drvdata(dev);
+
+ regcache_cache_only(sai->regmap, false);
+ return regcache_sync(sai->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops stm32_sai_sub_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_sub_suspend, stm32_sai_sub_resume)
+};
+
static struct platform_driver stm32_sai_sub_driver = {
.driver = {
.name = "st,stm32-sai-sub",
.of_match_table = stm32_sai_sub_ids,
+ .pm = &stm32_sai_sub_pm_ops,
},
.probe = stm32_sai_sub_probe,
};
diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c
index 373df4f24be1..b4c3d983e195 100644
--- a/sound/soc/stm/stm32_spdifrx.c
+++ b/sound/soc/stm/stm32_spdifrx.c
@@ -21,6 +21,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/regmap.h>
#include <linux/reset.h>
@@ -471,6 +472,8 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx)
memset(spdifrx->cs, 0, SPDIFRX_CS_BYTES_NB);
memset(spdifrx->ub, 0, SPDIFRX_UB_BYTES_NB);
+ pinctrl_pm_select_default_state(&spdifrx->pdev->dev);
+
ret = stm32_spdifrx_dma_ctrl_start(spdifrx);
if (ret < 0)
return ret;
@@ -502,6 +505,7 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx)
end:
clk_disable_unprepare(spdifrx->kclk);
+ pinctrl_pm_select_sleep_state(&spdifrx->pdev->dev);
return ret;
}
@@ -611,10 +615,15 @@ static bool stm32_spdifrx_readable_reg(struct device *dev, unsigned int reg)
static bool stm32_spdifrx_volatile_reg(struct device *dev, unsigned int reg)
{
- if (reg == STM32_SPDIFRX_DR)
+ switch (reg) {
+ case STM32_SPDIFRX_DR:
+ case STM32_SPDIFRX_CSR:
+ case STM32_SPDIFRX_SR:
+ case STM32_SPDIFRX_DIR:
return true;
-
- return false;
+ default:
+ return false;
+ }
}
static bool stm32_spdifrx_writeable_reg(struct device *dev, unsigned int reg)
@@ -638,6 +647,7 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = {
.volatile_reg = stm32_spdifrx_volatile_reg,
.writeable_reg = stm32_spdifrx_writeable_reg,
.fast_io = true,
+ .cache_type = REGCACHE_FLAT,
};
static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
@@ -983,10 +993,36 @@ static int stm32_spdifrx_remove(struct platform_device *pdev)
MODULE_DEVICE_TABLE(of, stm32_spdifrx_ids);
+#ifdef CONFIG_PM_SLEEP
+static int stm32_spdifrx_suspend(struct device *dev)
+{
+ struct stm32_spdifrx_data *spdifrx = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdifrx->regmap, true);
+ regcache_mark_dirty(spdifrx->regmap);
+
+ return 0;
+}
+
+static int stm32_spdifrx_resume(struct device *dev)
+{
+ struct stm32_spdifrx_data *spdifrx = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdifrx->regmap, false);
+
+ return regcache_sync(spdifrx->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops stm32_spdifrx_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_spdifrx_suspend, stm32_spdifrx_resume)
+};
+
static struct platform_driver stm32_spdifrx_driver = {
.driver = {
.name = "st,stm32-spdifrx",
.of_match_table = stm32_spdifrx_ids,
+ .pm = &stm32_spdifrx_pm_ops,
},
.probe = stm32_spdifrx_probe,
.remove = stm32_spdifrx_remove,
diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig
index 4bf3c15d4e51..ee7c202c69b7 100644
--- a/sound/soc/ti/Kconfig
+++ b/sound/soc/ti/Kconfig
@@ -21,8 +21,8 @@ config SND_SOC_DAVINCI_ASP
config SND_SOC_DAVINCI_MCASP
tristate "Multichannel Audio Serial Port (McASP) support"
- select SND_SOC_TI_EDMA_PCM if TI_EDMA
- select SND_SOC_TI_SDMA_PCM if DMA_OMAP
+ select SND_SOC_TI_EDMA_PCM
+ select SND_SOC_TI_SDMA_PCM
help
Say Y or M here if you want to have support for McASP IP found in
various Texas Instruments SoCs like:
diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c
index 4dce494dfbd3..b9611db14c86 100644
--- a/sound/soc/ti/ams-delta.c
+++ b/sound/soc/ti/ams-delta.c
@@ -200,7 +200,7 @@ static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol,
return 0;
}
-static const SOC_ENUM_SINGLE_EXT_DECL(ams_delta_audio_enum,
+static SOC_ENUM_SINGLE_EXT_DECL(ams_delta_audio_enum,
ams_delta_audio_mode);
static const struct snd_kcontrol_new ams_delta_audio_controls[] = {
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index a3a67a8f0f54..9fbc759fdefe 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -45,6 +45,7 @@
#define MCASP_MAX_AFIFO_DEPTH 64
+#ifdef CONFIG_PM
static u32 context_regs[] = {
DAVINCI_MCASP_TXFMCTL_REG,
DAVINCI_MCASP_RXFMCTL_REG,
@@ -68,6 +69,7 @@ struct davinci_mcasp_context {
u32 *xrsr_regs; /* for serializer configuration */
bool pm_state;
};
+#endif
struct davinci_mcasp_ruledata {
struct davinci_mcasp *mcasp;
diff --git a/sound/soc/ti/edma-pcm.c b/sound/soc/ti/edma-pcm.c
index 59e588abe54b..fdffb801b185 100644
--- a/sound/soc/ti/edma-pcm.c
+++ b/sound/soc/ti/edma-pcm.c
@@ -23,7 +23,6 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
-#include <linux/edma.h>
#include "edma-pcm.h"
@@ -43,14 +42,12 @@ static const struct snd_pcm_hardware edma_pcm_hardware = {
static const struct snd_dmaengine_pcm_config edma_dmaengine_pcm_config = {
.pcm_hardware = &edma_pcm_hardware,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
- .compat_filter_fn = edma_filter_fn,
.prealloc_buffer_size = 128 * 1024,
};
int edma_pcm_platform_register(struct device *dev)
{
- return devm_snd_dmaengine_pcm_register(dev, &edma_dmaengine_pcm_config,
- SND_DMAENGINE_PCM_FLAG_COMPAT);
+ return devm_snd_dmaengine_pcm_register(dev, &edma_dmaengine_pcm_config, 0);
}
EXPORT_SYMBOL_GPL(edma_pcm_platform_register);
diff --git a/sound/soc/ti/sdma-pcm.c b/sound/soc/ti/sdma-pcm.c
index 21a9c2499d48..a236350beb10 100644
--- a/sound/soc/ti/sdma-pcm.c
+++ b/sound/soc/ti/sdma-pcm.c
@@ -11,7 +11,6 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
-#include <linux/omap-dmaengine.h>
#include "sdma-pcm.h"
@@ -31,7 +30,6 @@ static const struct snd_pcm_hardware sdma_pcm_hardware = {
static const struct snd_dmaengine_pcm_config sdma_dmaengine_pcm_config = {
.pcm_hardware = &sdma_pcm_hardware,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
- .compat_filter_fn = omap_dma_filter_fn,
.prealloc_buffer_size = 128 * 1024,
};
@@ -39,13 +37,12 @@ int sdma_pcm_platform_register(struct device *dev,
char *txdmachan, char *rxdmachan)
{
struct snd_dmaengine_pcm_config *config;
- unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT;
+ unsigned int flags = 0;
/* Standard names for the directions: 'tx' and 'rx' */
if (!txdmachan && !rxdmachan)
return devm_snd_dmaengine_pcm_register(dev,
- &sdma_dmaengine_pcm_config,
- flags);
+ &sdma_dmaengine_pcm_config, 0);
config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL);
if (!config)
@@ -65,7 +62,7 @@ int sdma_pcm_platform_register(struct device *dev,
config->chan_names[0] = txdmachan;
config->chan_names[1] = rxdmachan;
- return devm_snd_dmaengine_pcm_register(dev, config, flags);
+ return devm_snd_dmaengine_pcm_register(dev, config, 0);
}
EXPORT_SYMBOL_GPL(sdma_pcm_platform_register);