[alsa-devel] [PATCH 0/7] asoc: Add audio for sti platforms
version 1: - First patches submission
Set of patches concerns the audio part of the STi platform
Hardware is split in two main blocks: Unipheriperal IPs and codec IPs
Uniperipheral IPs: Ips in charge of the I2S buses Uniperipheral IPs are in charge the PCM rendering/capturing on/from I2S. - 4 uniperipheral player IPs receive the PCM audio data or audio encoded bit stream data from the memory (via FDMA)and performs optional IEC-60958 or IEC-61937 formatting on the audio data and sends them out on the I2S output. - 2 uniperipheral reader IPS receive the PCM audio or encoded bit stream on the I2S input interface and writes the processed data in memory (via FDMA)
Codec IPs: - HDMI_TX: format and generate HDMI signal. - SPDIF: format and generate biphase signal for SPDIF output - DAC: format and generate stereo analogic signal - HDMI_RX: receive HDMI signal from external source.
In addition 2 I2S/TDM interfaces are available to connect external ICs - PCM_IN to external capture codec (ADC, BT...) - PCM_OUT for external player codec (DAC, BT..)
On Sti platform audio driver is splitted in 2 part to support Hw architecture - sti_audio_platform for uniperipheral IPs mmannagement - sti-sas codec for codec IP management
+-----------------------+ +-----------------+ | +-----------------+ | | | | | +---------+ | | | +---------+ | | | |Uniperif | | | | | HDMI | | +---+ | | |player 0 +---------------> TX +-------> | | | +---------+ | | | +---------+ | +---+ | | +---------+ | | | | | | |Uniperif | | | | | +---+ | | |player 1 +---------------------------------> | | | +---------+ | | | | +---+ | | +---------+ | | | +---------+ | +---+ | | |Uniperif | | | | | DAC | | | | | | |player 2 +---------------> +------^----+ | | +---------+ | | | +---------+ | | | +---------+ | | | +---------+ | | | |Uniperif | | | | | SPDIF | | +---+ | | |player 3 +---------------> +-------> | | | +---------+ | | | +---------+ | +---+ | | +---------+ | | | | | | |Uniperif | | | | | +---+ | | |reader 0 <---------------------------------+ | | | +---------+ | | | | +---+ | | +---------+ | | | +---------+ | | | |Uniperif | | | | | HDMIF | | +---+ | | |reader 1 <---------------+ RX <-------+ | | | +---------+ | | | +---------+ | +---+ | | | | | | | | CPU DAI | | | | | +-----------------+ | | | | STI platform | | SAS CODEC | +-----------------------+ +-----------------+
In the first version of the driver only SPDIF and DAC are supported. in future HDMI TX, HDMI_RX and support for TDM mode should be added.
Arnaud Pouliquen (7): ASoC: sti: add binding for ASoc driver Asoc: sti: add uniperipheral header file Asoc: sti: add CPU DAI driver for playback Asoc: sti: add CPU DAI driver for capture Asoc: sti: Add platform driver ASoc: Add ability to build sti drivers ASoc: Codec: add sti platform codec
.../devicetree/bindings/sound/st,sti-asoc-card.txt | 176 +++ sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/sti-sas.c | 663 ++++++++++ sound/soc/sti/Kconfig | 17 + sound/soc/sti/Makefile | 5 + sound/soc/sti/sti_platform.c | 643 +++++++++ sound/soc/sti/uniperif.h | 1236 ++++++++++++++++++ sound/soc/sti/uniperif_player.c | 1367 ++++++++++++++++++++ sound/soc/sti/uniperif_reader.c | 493 +++++++ 12 files changed, 4608 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt create mode 100644 sound/soc/codecs/sti-sas.c create mode 100644 sound/soc/sti/Kconfig create mode 100644 sound/soc/sti/Makefile create mode 100644 sound/soc/sti/sti_platform.c create mode 100644 sound/soc/sti/uniperif.h create mode 100644 sound/soc/sti/uniperif_player.c create mode 100644 sound/soc/sti/uniperif_reader.c
Add Asoc driver bindings documentation. Describe the required properties for each of the hardware IPs drivers.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@st.com --- .../devicetree/bindings/sound/st,sti-asoc-card.txt | 176 +++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt
diff --git a/Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt b/Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt new file mode 100644 index 0000000..aafc3b3 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt @@ -0,0 +1,176 @@ +STMicroelectronics sti ASOC cards + +The sti ASoC Sound Card can be used, for all sti SoCs using internal sti-sas +codec or external codecs. + +sti sound drivers allows to expose sti SOC audio interface through the +generic ASOC simple card. For details about sound card declaration please refer to +Documentation/devicetree/bindings/sound/simple-card.txt. + +-sti-asoc-platform: audio platform engine. +Required properties: + - compatible: "st,sti-audio-platform". + - st,syscfg: phandle to boot-device system configuration registers + - clocks: CPU_DAI IP clock source, listed in the same order than the + CPU_DAI properties. + - clock-names: name of the clocks listed in clocks property in the same order + - reg: CPU DAI IP Base address and size entries, listed in same + order than the CPU_DAI properties. + - reg-names: names of the mapped memory regions listed in regs property in + the same order. + - interrupts: CPU_DAI interrupt line, listed in the same order than the + CPU_DAI properties. + - dma: CPU_DAI DMA controller phandle and DMA request line, listed in the same + order than the CPU_DAI properties. + - dma-names: identifier string for each DMA request line in the dmas property. + +- cpu-dai: sti-asoc-platform subnode that describes properties for a CPU-DAI instance. +Required properties: + - dai-name: DAI name that describes the IP. + - dai-type: DAI functionality. + "uni-player" for playback. + "uni-reader" for capture. + - uniperiph-id: internal SOC IP instance ID. + - version: IP version integrated in SOC. + - IP mode: IP working mode depending on associated codec. + "HDMI" connected to HDMI codec IP and IEC HDMI formats. + "SPDIF"connected to SPDIF codec and support SPDIF formats. + "PCM" PCM standard mode for I2S or TDM bus. + +Optional properties: + - standby: standby mode activation deactivation. this mode is specific to + Uni player IP and allows to transmit bus clock and a pattern data + while audio stream is stopped or paused. + - auto-suspend-delay: runtime auto suspend delay (in ms) before deactivating + stanby mode. Equivalent to the DAPM mechanism but for + CPU DAI. + - pinctrl-0: defined for CPU_DAI@1 and CPU_DAI@4 to describe I2S PIOs for + external codecs connection. + - pinctrl-names: should contain only one value - "default". + +- sti-sas-codec: internal audio codec IPs driver +Required properties: + - compatible: "st,sti<chip>-sas-codec" . + Should be chip "st,sti416-sas-codec" or "st,sti417-sas-codec" + - st,syscfg: phandle to boot-device system configuration registers. + - pinctrl-0: SPDIF PIO description. + - pinctrl-names: should contain only one value - "default". + +audio_controller: sti-asoc-platform { + #sound-dai-cells = <1>; + compatible = "st,sti-audio-platform"; + st,syscfg = <&syscfg_core>; + clocks = <&clk_s_d0_flexgen CLK_PCM_0>, /*UPP 0*/ + <&clk_s_d0_flexgen CLK_PCM_1>, /*UPP 1*/ + <&clk_s_d0_flexgen CLK_PCM_2>, /*UPP 2*/ + <&clk_s_d0_flexgen CLK_SPDIFF>, /*UPP 3 */ + <0>, /*UPR 0: no clock*/ + <0>; /*UPR 1: no clock */ + clock-names = "uni_p0", "uni_p1", "uni_p2", "uni_p3", + "no_clock", "no_clock"; + reg = <0x8D80000 0x158>, <0x8D81000 0x158>, + <0x8D82000 0x158>, <0x8D85000 0x158>, + <0x8D83000 0x158>, <0x8D84000 0x158>; + interrupts = <GIC_SPI 84 IRQ_TYPE_NONE>, + <GIC_SPI 85 IRQ_TYPE_NONE>, + <GIC_SPI 86 IRQ_TYPE_NONE>, + <GIC_SPI 89 IRQ_TYPE_NONE>, + <GIC_SPI 87 IRQ_TYPE_NONE>, + <GIC_SPI 88 IRQ_TYPE_NONE>; + + dmas = <&fdma0 2 0 1>, <&fdma0 3 0 1>, + <&fdma0 4 0 1>, <&fdma0 7 0 1>, + <&fdma0 5 0 1>, <&fdma0 6 0 1>; + + dma-names = "tx-0", "tx-1", + "tx-2", "tx-3", + "rx-4", "rx-5"; + cpu-dai@0 { + dai-name = "Uni Player #0 (HDMI)"; + dai-type = "uni-player"; + uniperiph-id = <0>; + version = <5>; + mode = "HDMI"; + standby = <1>; + auto-suspend-delay = <5000>; + }; + + cpu-dai@1 { + dai-name = "Uni Player #1 (PIO)"; + dai-type = "uni-player"; + uniperiph-id = <1>; + version = <5>; + mode = "PCM"; + standby = <1>; + auto-suspend-delay = <5000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2s_8ch_out>; + }; + cpu-dai@2 { + dai-name = "Uni Player #2 (DAC)"; + dai-type = "uni-player"; + uniperiph-id = <2>; + version = <5>; + mode = "PCM"; + standby = <1>; + auto-suspend-delay = <5000>; + }; + cpu-dai@3 { + dai-name = "Uni Player #3 (SPDIF)"; + dai-type = "uni-player"; + uniperiph-id = <3>; + version = <5>; + mode = "SPDIF"; + standby = <1>; + auto-suspend-delay = <5000>; + }; + cpu-dai@4 { + dai-name = "Uni Reader #0 (PCM IN)"; + dai-type = "uni-reader"; + version = <3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2s_8ch_in>; + }; + cpu-dai@5 { + dai-name = "Uni Reader #1 (HDMI)"; + dai-type = "uni-reader"; + version = <3>; + channels = <8>; + }; + }; + + sti_sas_codec: sti-sas-codec { + compatible = "st,stih407-sas-codec"; + #sound-dai-cells = <1>; + st,reg_audio = <&syscfg_core>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif_out >; + }; + +Example of audio card declaration: + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "sti audio card"; + status = "okay"; + + simple-audio-card,dai-link@0 { /* DAC */ + format = "i2s"; + cpu { + sound-dai = <&audio_controller 2>; + }; + + codec { + sound-dai = <&sti_sasg_codec 1>; + }; + }; + simple-audio-card,dai-link@1 { /* SPDIF */ + format = "left_j"; + cpu { + sound-dai = <&audio_controller 3>; + }; + + codec { + sound-dai = <&sti_sasg_codec 0>; + }; + }; + };
On Tue, Apr 14, 2015 at 03:35:25PM +0200, Arnaud Pouliquen wrote:
Add Asoc driver bindings documentation.
ASoC.
+- cpu-dai: sti-asoc-platform subnode that describes properties for a CPU-DAI instance. +Required properties:
- dai-name: DAI name that describes the IP.
- dai-type: DAI functionality.
- "uni-player" for playback.
- "uni-reader" for capture.
It would be more idiomatic to do this with eithe ra commpatible string or a specifically named node.
+Optional properties:
- standby: standby mode activation deactivation. this mode is specific to
Uni player IP and allows to transmit bus clock and a pattern data
while audio stream is stopped or paused.
I'm not entirely sure what the above means but it sounds like some software configuration rather than a property of the hardware? Or do some variants of the IP lack this functionality?
- auto-suspend-delay: runtime auto suspend delay (in ms) before deactivating
stanby mode. Equivalent to the DAPM mechanism but for
CPU DAI.
DAPM works perfectly well for on SoC components, please use it.
Add the Uniperipheral header file for uniperipheral IPs registers definition.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@st.com --- sound/soc/sti/uniperif.h | 1099 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1099 insertions(+) create mode 100644 sound/soc/sti/uniperif.h
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h new file mode 100644 index 0000000..043853e --- /dev/null +++ b/sound/soc/sti/uniperif.h @@ -0,0 +1,1099 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen arnaud.pouliquen@st.com + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef __SND_ST_AUD_UNIPERIF_H +#define __SND_ST_AUD_UNIPERIF_H + +#include <linux/regmap.h> + +/* + * Register access macros + */ + +#define GET_UNIPERIF_REG(ip, offset, shift, mask) \ + ((readl_relaxed(ip->base + offset) >> shift) & mask) +#define SET_UNIPERIF_REG(ip, offset, shift, mask, value) \ + writel_relaxed(((readl_relaxed(ip->base + offset) & \ + ~(mask << shift)) | (((value) & mask) << shift)), ip->base + offset) +#define SET_UNIPERIF_BIT_REG(ip, offset, shift, mask, value) \ + writel_relaxed((((value) & mask) << shift), ip->base + offset) + +/* + * AUD_UNIPERIF_SOFT_RST reg + */ + +#define UNIPERIF_SOFT_RST_OFFSET(ip) 0x0000 +#define GET_UNIPERIF_SOFT_RST(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + readl_relaxed(ip->base + UNIPERIF_SOFT_RST_OFFSET(ip)) : 0) +#define SET_UNIPERIF_SOFT_RST(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_SOFT_RST_OFFSET(ip)) + +/* SOFT_RST */ +#define UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip) 0x0 +#define UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip) 0x1 +#define SET_UNIPERIF_SOFT_RST_SOFT_RST(ip) \ + SET_UNIPERIF_BIT_REG(ip, \ + UNIPERIF_SOFT_RST_OFFSET(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip), 1) +#define GET_UNIPERIF_SOFT_RST_SOFT_RST(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_SOFT_RST_OFFSET(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip)) + +/* + * AUD_UNIPERIF_FIFO_DATA reg + */ + +#define UNIPERIF_FIFO_DATA_OFFSET(ip) 0x0004 +#define SET_UNIPERIF_DATA(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_FIFO_DATA_OFFSET(ip)) + +/* + * AUD_UNIPERIF_CHANNEL_STA_REGN reg + */ + +#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n)) +#define GET_UNIPERIF_CHANNEL_STA_REGN(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REGN(ip, n)) +#define SET_UNIPERIF_CHANNEL_STA_REGN(ip, n, value) \ + writel_relaxed(value, ip->base + \ + UNIPERIF_CHANNEL_STA_REGN(ip, n)) + +#define UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip) 0x0060 +#define GET_UNIPERIF_CHANNEL_STA_REG0(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG0(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip) 0x0064 +#define GET_UNIPERIF_CHANNEL_STA_REG1(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG1(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip) 0x0068 +#define GET_UNIPERIF_CHANNEL_STA_REG2(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG2(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip) 0x006C +#define GET_UNIPERIF_CHANNEL_STA_REG3(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG3(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip) 0x0070 +#define GET_UNIPERIF_CHANNEL_STA_REG4(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG4(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip) 0x0074 +#define GET_UNIPERIF_CHANNEL_STA_REG5(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG5(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip)) + +/* + * AUD_UNIPERIF_ITS reg + */ + +#define UNIPERIF_ITS_OFFSET(ip) 0x000C +#define GET_UNIPERIF_ITS(ip) \ + readl_relaxed(ip->base + UNIPERIF_ITS_OFFSET(ip)) + +/* MEM_BLK_READ */ +#define UNIPERIF_ITS_MEM_BLK_READ_SHIFT(ip) 5 +#define UNIPERIF_ITS_MEM_BLK_READ_MASK(ip) \ + (BIT(UNIPERIF_ITS_MEM_BLK_READ_SHIFT(ip))) + +/* FIFO_ERROR */ +#define UNIPERIF_ITS_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITS_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITS_FIFO_ERROR_SHIFT(ip))) + +/* DMA_ERROR */ +#define UNIPERIF_ITS_DMA_ERROR_SHIFT(ip) 9 +#define UNIPERIF_ITS_DMA_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITS_DMA_ERROR_SHIFT(ip))) + +/* UNDERFLOW_REC_DONE */ +#define UNIPERIF_ITS_UNDERFLOW_REC_DONE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12) +#define UNIPERIF_ITS_UNDERFLOW_REC_DONE_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_DONE_SHIFT(ip)))) + +/* UNDERFLOW_REC_FAILED */ +#define UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13) +#define UNIPERIF_ITS_UNDERFLOW_REC_FAILED_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip)))) + +/* + * AUD_UNIPERIF_ITS_BCLR reg + */ + +/* FIFO_ERROR */ +#define UNIPERIF_ITS_BCLR_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITS_BCLR_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITS_BCLR_FIFO_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITS_BCLR_FIFO_ERROR(ip) \ + SET_UNIPERIF_ITS_BCLR(ip, \ + UNIPERIF_ITS_BCLR_FIFO_ERROR_MASK(ip)) + +#define UNIPERIF_ITS_BCLR_OFFSET(ip) 0x0010 +#define SET_UNIPERIF_ITS_BCLR(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_ITS_BCLR_OFFSET(ip)) + +/* + * AUD_UNIPERIF_ITM reg + */ + +#define UNIPERIF_ITM_OFFSET(ip) 0x0018 +#define GET_UNIPERIF_ITM(ip) \ + readl_relaxed(ip->base + UNIPERIF_ITM_OFFSET(ip)) + +/* FIFO_ERROR */ +#define UNIPERIF_ITM_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITM_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_FIFO_ERROR_SHIFT(ip))) + +/* UNDERFLOW_REC_DONE */ +#define UNIPERIF_ITM_UNDERFLOW_REC_DONE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12) +#define UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_DONE_SHIFT(ip)))) + +/* UNDERFLOW_REC_FAILED */ +#define UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13) +#define UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip)))) + +/* + * AUD_UNIPERIF_ITM_BCLR reg + */ + +#define UNIPERIF_ITM_BCLR_OFFSET(ip) 0x001c +#define SET_UNIPERIF_ITM_BCLR(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_ITM_BCLR_OFFSET(ip)) + +/* FIFO_ERROR */ +#define UNIPERIF_ITM_BCLR_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITM_BCLR_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BCLR_FIFO_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(ip) \ + SET_UNIPERIF_ITM_BCLR(ip, \ + UNIPERIF_ITM_BCLR_FIFO_ERROR_MASK(ip)) + +/* DMA_ERROR */ +#define UNIPERIF_ITM_BCLR_DMA_ERROR_SHIFT(ip) 9 +#define UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BCLR_DMA_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BCLR_DMA_ERROR(ip) \ + SET_UNIPERIF_ITM_BCLR(ip, \ + UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip)) + +/* + * AUD_UNIPERIF_ITM_BSET reg + */ + +#define UNIPERIF_ITM_BSET_OFFSET(ip) 0x0020 +#define SET_UNIPERIF_ITM_BSET(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_ITM_BSET_OFFSET(ip)) + +/* FIFO_ERROR */ +#define UNIPERIF_ITM_BSET_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITM_BSET_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BSET_FIFO_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BSET_FIFO_ERROR(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_FIFO_ERROR_MASK(ip)) + +/* MEM_BLK_READ */ +#define UNIPERIF_ITM_BSET_MEM_BLK_READ_SHIFT(ip) 5 +#define UNIPERIF_ITM_BSET_MEM_BLK_READ_MASK(ip) \ + (BIT(UNIPERIF_ITM_BSET_MEM_BLK_READ_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_MEM_BLK_READ_MASK(ip)) + +/* DMA_ERROR */ +#define UNIPERIF_ITM_BSET_DMA_ERROR_SHIFT(ip) 9 +#define UNIPERIF_ITM_BSET_DMA_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BSET_DMA_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BSET_DMA_ERROR(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_DMA_ERROR_MASK(ip)) + +/* UNDERFLOW_REC_DONE */ +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12) +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_SHIFT(ip)))) +#define SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_MASK(ip)) + +/* UNDERFLOW_REC_FAILED */ +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13) +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_SHIFT(ip)))) +#define SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_MASK(ip)) + +/* + * UNIPERIF_CONFIG reg + */ + +#define UNIPERIF_CONFIG_OFFSET(ip) 0x0040 +#define GET_UNIPERIF_CONFIG(ip) \ + readl_relaxed(ip->base + UNIPERIF_CONFIG_OFFSET(ip)) +#define SET_UNIPERIF_CONFIG(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CONFIG_OFFSET(ip)) + +/* PARITY_CNTR */ +#define UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip) 0 +#define UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_PARITY_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip), 1) + +/* CHANNEL_STA_CNTR */ +#define UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip) 1 +#define UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip), 1) + +/* USER_DAT_CNTR */ +#define UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip) 2 +#define UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_USER_DAT_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip), 0) + +/* VALIDITY_DAT_CNTR */ +#define UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip) 3 +#define UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip), 1) + +/* ONE_BIT_AUD_SUPPORT */ +#define UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip) 4 +#define UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_ONE_BIT_AUD(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip)) +#define SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_ONE_BIT_AUD_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip), 1) + +/* MEMORY_FMT */ +#define UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip) 5 +#define UNIPERIF_CONFIG_MEM_FMT_MASK(ip) 0x1 +#define VALUE_UNIPERIF_CONFIG_MEM_FMT_16_0(ip) 0 +#define VALUE_UNIPERIF_CONFIG_MEM_FMT_16_16(ip) 1 +#define GET_UNIPERIF_CONFIG_MEM_FMT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip), \ + UNIPERIF_CONFIG_MEM_FMT_MASK(ip)) +#define SET_UNIPERIF_CONFIG_MEM_FMT(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip), \ + UNIPERIF_CONFIG_MEM_FMT_MASK(ip), value) +#define SET_UNIPERIF_CONFIG_MEM_FMT_16_0(ip) \ + SET_UNIPERIF_CONFIG_MEM_FMT(ip, \ + VALUE_UNIPERIF_CONFIG_MEM_FMT_16_0(ip)) +#define SET_UNIPERIF_CONFIG_MEM_FMT_16_16(ip) \ + SET_UNIPERIF_CONFIG_MEM_FMT(ip, \ + VALUE_UNIPERIF_CONFIG_MEM_FMT_16_16(ip)) + +/* REPEAT_CHL_STS */ +#define UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip) 6 +#define UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_REPEAT_CHL_STS(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip)) +#define SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip), 1) + +/* BACK_STALL_REQ */ +#define UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 7 : -1) +#define UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_BACK_STALL_REQ(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip)) +#define SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_BACK_STALL_REQ_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip), 1) + +/* FDMA_TRIGGER_LIMIT */ +#define UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip) 8 +#define UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip) 0x7F +#define GET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip)) +#define SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip), value) + +/* CHL_STS_UPDATE */ +#define UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 16 : -1) +#define UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_CHL_STS_UPDATE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip)) +#define SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip), 1) + +/* IDLE_MOD */ +#define UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip) 18 +#define UNIPERIF_CONFIG_IDLE_MOD_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_IDLE_MOD(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_MASK(ip)) +#define SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_IDLE_MOD_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_MASK(ip), 1) + +/* SUBFRAME_SELECTION */ +#define UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip) 19 +#define UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_SUBFRAME_SEL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip)) +#define SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF1_SUBF0(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF0_SUBF1(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip), 0) + +/* FULL_SW_CONTROL */ +#define UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip) 20 +#define UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_SPDIF_SW_CTRL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip)) +#define SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip), 0) + +/* MASTER_CLKEDGE */ +#define UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 24 : -1) +#define UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_MSTR_CLKEDGE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip)) +#define SET_UNIPERIF_CONFIG_MSTR_CLKEDGE_FALLING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_MSTR_CLKEDGE_RISING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip), 0) + +/* + * UNIPERIF_CTRL reg + */ + +#define UNIPERIF_CTRL_OFFSET(ip) 0x0044 +#define GET_UNIPERIF_CTRL(ip) \ + readl_relaxed(ip->base + UNIPERIF_CTRL_OFFSET(ip)) +#define SET_UNIPERIF_CTRL(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CTRL_OFFSET(ip)) + +/* OPERATION */ +#define UNIPERIF_CTRL_OPERATION_SHIFT(ip) 0 +#define UNIPERIF_CTRL_OPERATION_MASK(ip) 0x7 +#define GET_UNIPERIF_CTRL_OPERATION(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_OFF(ip) 0 +#define SET_UNIPERIF_CTRL_OPERATION_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_OFF(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 1 : -1) +#define SET_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 2 : -1) +#define SET_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip) 3 +#define SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip)) +/* This is the same as above! */ +#define VALUE_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip) 3 +#define SET_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip) 4 +#define SET_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_CD_DATA(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 5 : -1) +#define SET_UNIPERIF_CTRL_OPERATION_CD_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_CD_DATA(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_STANDBY(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 7) +#define SET_UNIPERIF_CTRL_OPERATION_STANDBY(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_STANDBY(ip)) + +/* EXIT_STBY_ON_EOBLOCK */ +#define UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 3) +#define UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip)) +#define SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip), 1) + +/* ROUNDING */ +#define UNIPERIF_CTRL_ROUNDING_SHIFT(ip) 4 +#define UNIPERIF_CTRL_ROUNDING_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_ROUNDING(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \ + UNIPERIF_CTRL_ROUNDING_MASK(ip)) +#define SET_UNIPERIF_CTRL_ROUNDING_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \ + UNIPERIF_CTRL_ROUNDING_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_ROUNDING_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \ + UNIPERIF_CTRL_ROUNDING_MASK(ip), 1) + +/* DIVIDER */ +#define UNIPERIF_CTRL_DIVIDER_SHIFT(ip) 5 +#define UNIPERIF_CTRL_DIVIDER_MASK(ip) 0xff +#define GET_UNIPERIF_CTRL_DIVIDER(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_DIVIDER_SHIFT(ip), \ + UNIPERIF_CTRL_DIVIDER_MASK(ip)) +#define SET_UNIPERIF_CTRL_DIVIDER(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_DIVIDER_SHIFT(ip), \ + UNIPERIF_CTRL_DIVIDER_MASK(ip), value) + +/* BYTE_SWAP */ +#define UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 13 : -1) +#define UNIPERIF_CTRL_BYTE_SWP_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_BYTE_SWP(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \ + UNIPERIF_CTRL_BYTE_SWP_MASK(ip)) +#define SET_UNIPERIF_CTRL_BYTE_SWP_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \ + UNIPERIF_CTRL_BYTE_SWP_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_BYTE_SWP_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \ + UNIPERIF_CTRL_BYTE_SWP_MASK(ip), 1) + +/* ZERO_STUFFING_HW_SW */ +#define UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 14 : -1) +#define UNIPERIF_CTRL_ZERO_STUFF_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_ZERO_STUFF(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_MASK(ip)) +#define SET_UNIPERIF_CTRL_ZERO_STUFF_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_MASK(ip), 1) +#define SET_UNIPERIF_CTRL_ZERO_STUFF_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_MASK(ip), 0) + +/* SPDIF_LAT */ +#define UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 16 : -1) +#define UNIPERIF_CTRL_SPDIF_LAT_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_SPDIF_LAT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_MASK(ip)) +#define SET_UNIPERIF_CTRL_SPDIF_LAT_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_MASK(ip), 1) +#define SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_MASK(ip), 0) + +/* EN_SPDIF_FORMATTING */ +#define UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip) 17 +#define UNIPERIF_CTRL_SPDIF_FMT_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_SPDIF_FMT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_MASK(ip)) +#define SET_UNIPERIF_CTRL_SPDIF_FMT_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_MASK(ip), 1) +#define SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_MASK(ip), 0) + +/* READER_OUT_SELECT */ +#define UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 18 : -1) +#define UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_READER_OUT_SEL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip)) +#define SET_UNIPERIF_CTRL_READER_OUT_SEL_IN_MEM(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_READER_OUT_SEL_ON_I2S_LINE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \ + CORAUD_UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1) + +/* UNDERFLOW_REC_WINDOW */ +#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip) 20 +#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip) 0xff +#define GET_UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip)) +#define SET_UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip), value) + +/* + * UNIPERIF_I2S_FMT a.k.a UNIPERIF_FORMAT reg + */ + +#define UNIPERIF_I2S_FMT_OFFSET(ip) 0x0048 +#define GET_UNIPERIF_I2S_FMT(ip) \ + readl_relaxed(ip->base + UNIPERIF_I2S_FMT_OFFSET(ip)) +#define SET_UNIPERIF_I2S_FMT(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_I2S_FMT_OFFSET(ip)) + +/* NBIT */ +#define UNIPERIF_I2S_FMT_NBIT_SHIFT(ip) 0 +#define UNIPERIF_I2S_FMT_NBIT_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_NBIT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NBIT_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_NBIT_32(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NBIT_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_NBIT_16(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NBIT_MASK(ip), 1) + +/* DATA_SIZE */ +#define UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip) 1 +#define UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip) 0x7 +#define GET_UNIPERIF_I2S_FMT_DATA_SIZE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_18(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 1) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_20(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 2) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 3) +#define SET_UNIPERIF_I2S_FMTL_DATA_SIZE_28(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 4) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 5) + +/* LR_POL */ +#define UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip) 4 +#define UNIPERIF_I2S_FMT_LR_POL_MASK(ip) 0x1 +#define VALUE_UNIPERIF_I2S_FMT_LR_POL_LOW(ip) 0x0 +#define VALUE_UNIPERIF_I2S_FMT_LR_POL_HIG(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_LR_POL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip), \ + UNIPERIF_I2S_FMT_LR_POL_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_LR_POL(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip), \ + UNIPERIF_I2S_FMT_LR_POL_MASK(ip), value) +#define SET_UNIPERIF_I2S_FMT_LR_POL_LOW(ip) \ + SET_UNIPERIF_I2S_FMT_LR_POL(ip, \ + VALUE_UNIPERIF_I2S_FMT_LR_POL_LOW(ip)) +#define SET_UNIPERIF_I2S_FMT_LR_POL_HIG(ip) \ + SET_UNIPERIF_I2S_FMT_LR_POL(ip, \ + VALUE_UNIPERIF_I2S_FMT_LR_POL_HIG(ip)) + +/* SCLK_EDGE */ +#define UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip) 5 +#define UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_SCLK_EDGE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip), 1) + +/* PADDING */ +#define UNIPERIF_I2S_FMT_PADDING_SHIFT(ip) 6 +#define UNIPERIF_I2S_FMT_PADDING_MASK(ip) 0x1 +#define UNIPERIF_I2S_FMT_PADDING_MASK(ip) 0x1 +#define VALUE_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip) 0x0 +#define VALUE_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_PADDING(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_PADDING_SHIFT(ip), \ + UNIPERIF_I2S_FMT_PADDING_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_PADDING(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_PADDING_SHIFT(ip), \ + UNIPERIF_I2S_FMT_PADDING_MASK(ip), value) +#define SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip) \ + SET_UNIPERIF_I2S_FMT_PADDING(ip, \ + VALUE_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip)) +#define SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip) \ + SET_UNIPERIF_I2S_FMT_PADDING(ip, \ + VALUE_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip)) + +/* ALIGN */ +#define UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip) 7 +#define UNIPERIF_I2S_FMT_ALIGN_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_ALIGN(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ALIGN_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ALIGN_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ALIGN_MASK(ip), 1) + +/* ORDER */ +#define UNIPERIF_I2S_FMT_ORDER_SHIFT(ip) 8 +#define UNIPERIF_I2S_FMT_ORDER_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_ORDER(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ORDER_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_ORDER_LSB(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ORDER_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_ORDER_MSB(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ORDER_MASK(ip), 1) + +/* NUM_CH */ +#define UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip) 9 +#define UNIPERIF_I2S_FMT_NUM_CH_MASK(ip) 0x7 +#define GET_UNIPERIF_I2S_FMT_NUM_CH(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_NUM_CH(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_MASK(ip), value) + +/* NO_OF_SAMPLES_TO_READ */ +#define UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip) 12 +#define UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip) 0xfffff +#define GET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip), value) + +/* + * UNIPERIF_BIT_CONTROL reg + */ + +#define UNIPERIF_BIT_CONTROL_OFFSET(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0x004c) +#define GET_UNIPERIF_BIT_CONTROL(ip) \ + readl_relaxed(ip->base + UNIPERIF_BIT_CONTROL_OFFSET(ip)) +#define SET_UNIPERIF_BIT_CONTROL(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_BIT_CONTROL_OFFSET(ip)) + +/* CLR_UNDERFLOW_DURATION */ +#define UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip) 0 +#define UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip) 0x1 +#define GET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip)) +#define SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip), 1) + +/* CHL_STS_UPDATE */ +#define UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip) 1 +#define UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip) 0x1 +#define GET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip)) +#define SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(ip) \ + SET_UNIPERIF_BIT_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip), 1) + +/* + * UNIPERIF_STATUS_1 reg + */ + +#define UNIPERIF_STATUS_1_OFFSET(ip) 0x0050 +#define GET_UNIPERIF_STATUS_1(ip) \ + readl_relaxed(ip->base + UNIPERIF_STATUS_1_OFFSET(ip)) +#define SET_UNIPERIF_STATUS_1(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_STATUS_1_OFFSET(ip)) + +/* UNDERFLOW_DURATION */ +#define UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0) +#define UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip) 0xff +#define GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_STATUS_1_OFFSET(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip)) +#define SET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_STATUS_1_OFFSET(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip), value) + +/* + * AUD_UNIPERIF_CHANNEL_STA_REGN reg + */ + +#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n)) +#define GET_UNIPERIF_CHANNEL_STA_REGN(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REGN(ip, n)) +#define SET_UNIPERIF_CHANNEL_STA_REGN(ip, n, value) \ + writel_relaxed(value, ip->base + \ + UNIPERIF_CHANNEL_STA_REGN(ip, n)) + +/* + * AUD_UNIPERIF_USER_VALIDITY reg + */ + +#define UNIPERIF_USER_VALIDITY_OFFSET(ip) 0x0090 +#define GET_UNIPERIF_USER_VALIDITY(ip) \ + readl_relaxed(ip->base + UNIPERIF_USER_VALIDITY_OFFSET(ip)) +#define SET_UNIPERIF_USER_VALIDITY(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_USER_VALIDITY_OFFSET(ip)) + +/* VALIDITY_LEFT_AND_RIGHT */ +#define UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip) 0 +#define UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip) 0x3 +#define GET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_USER_VALIDITY_OFFSET(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip)) +#define SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_USER_VALIDITY_OFFSET(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip), \ + value ? 0x3 : 0) + +/* + * UNIPERIF_DBG_STANDBY_LEFT_SP reg + */ +#define UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip) 0x0150 +#define UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0) +#define UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 0xFFFFFF) +#define GET_UNIPERIF_DBG_STANDBY_LEFT_SP(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip)) +#define SET_UNIPERIF_DBG_STANDBY_LEFT_SP(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip), value)
The patch
ASoC: sti: Add uniperipheral header file
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From e1ecace6a68518a6751987ab2032b0ec1b3bd5fe Mon Sep 17 00:00:00 2001
From: Arnaud Pouliquen arnaud.pouliquen@st.com Date: Mon, 22 Jun 2015 16:31:06 +0200 Subject: [PATCH] ASoC: sti: Add uniperipheral header file
Add the Uniperipheral header file for uniperipheral IPs registers definition.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@st.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/sti/uniperif.h | 1099 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1099 insertions(+) create mode 100644 sound/soc/sti/uniperif.h
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h new file mode 100644 index 000000000000..043853e093a5 --- /dev/null +++ b/sound/soc/sti/uniperif.h @@ -0,0 +1,1099 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen arnaud.pouliquen@st.com + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef __SND_ST_AUD_UNIPERIF_H +#define __SND_ST_AUD_UNIPERIF_H + +#include <linux/regmap.h> + +/* + * Register access macros + */ + +#define GET_UNIPERIF_REG(ip, offset, shift, mask) \ + ((readl_relaxed(ip->base + offset) >> shift) & mask) +#define SET_UNIPERIF_REG(ip, offset, shift, mask, value) \ + writel_relaxed(((readl_relaxed(ip->base + offset) & \ + ~(mask << shift)) | (((value) & mask) << shift)), ip->base + offset) +#define SET_UNIPERIF_BIT_REG(ip, offset, shift, mask, value) \ + writel_relaxed((((value) & mask) << shift), ip->base + offset) + +/* + * AUD_UNIPERIF_SOFT_RST reg + */ + +#define UNIPERIF_SOFT_RST_OFFSET(ip) 0x0000 +#define GET_UNIPERIF_SOFT_RST(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + readl_relaxed(ip->base + UNIPERIF_SOFT_RST_OFFSET(ip)) : 0) +#define SET_UNIPERIF_SOFT_RST(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_SOFT_RST_OFFSET(ip)) + +/* SOFT_RST */ +#define UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip) 0x0 +#define UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip) 0x1 +#define SET_UNIPERIF_SOFT_RST_SOFT_RST(ip) \ + SET_UNIPERIF_BIT_REG(ip, \ + UNIPERIF_SOFT_RST_OFFSET(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip), 1) +#define GET_UNIPERIF_SOFT_RST_SOFT_RST(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_SOFT_RST_OFFSET(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_SHIFT(ip), \ + UNIPERIF_SOFT_RST_SOFT_RST_MASK(ip)) + +/* + * AUD_UNIPERIF_FIFO_DATA reg + */ + +#define UNIPERIF_FIFO_DATA_OFFSET(ip) 0x0004 +#define SET_UNIPERIF_DATA(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_FIFO_DATA_OFFSET(ip)) + +/* + * AUD_UNIPERIF_CHANNEL_STA_REGN reg + */ + +#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n)) +#define GET_UNIPERIF_CHANNEL_STA_REGN(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REGN(ip, n)) +#define SET_UNIPERIF_CHANNEL_STA_REGN(ip, n, value) \ + writel_relaxed(value, ip->base + \ + UNIPERIF_CHANNEL_STA_REGN(ip, n)) + +#define UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip) 0x0060 +#define GET_UNIPERIF_CHANNEL_STA_REG0(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG0(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG0_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip) 0x0064 +#define GET_UNIPERIF_CHANNEL_STA_REG1(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG1(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG1_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip) 0x0068 +#define GET_UNIPERIF_CHANNEL_STA_REG2(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG2(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG2_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip) 0x006C +#define GET_UNIPERIF_CHANNEL_STA_REG3(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG3(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG3_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip) 0x0070 +#define GET_UNIPERIF_CHANNEL_STA_REG4(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG4(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG4_OFFSET(ip)) + +#define UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip) 0x0074 +#define GET_UNIPERIF_CHANNEL_STA_REG5(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip)) +#define SET_UNIPERIF_CHANNEL_STA_REG5(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CHANNEL_STA_REG5_OFFSET(ip)) + +/* + * AUD_UNIPERIF_ITS reg + */ + +#define UNIPERIF_ITS_OFFSET(ip) 0x000C +#define GET_UNIPERIF_ITS(ip) \ + readl_relaxed(ip->base + UNIPERIF_ITS_OFFSET(ip)) + +/* MEM_BLK_READ */ +#define UNIPERIF_ITS_MEM_BLK_READ_SHIFT(ip) 5 +#define UNIPERIF_ITS_MEM_BLK_READ_MASK(ip) \ + (BIT(UNIPERIF_ITS_MEM_BLK_READ_SHIFT(ip))) + +/* FIFO_ERROR */ +#define UNIPERIF_ITS_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITS_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITS_FIFO_ERROR_SHIFT(ip))) + +/* DMA_ERROR */ +#define UNIPERIF_ITS_DMA_ERROR_SHIFT(ip) 9 +#define UNIPERIF_ITS_DMA_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITS_DMA_ERROR_SHIFT(ip))) + +/* UNDERFLOW_REC_DONE */ +#define UNIPERIF_ITS_UNDERFLOW_REC_DONE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12) +#define UNIPERIF_ITS_UNDERFLOW_REC_DONE_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_DONE_SHIFT(ip)))) + +/* UNDERFLOW_REC_FAILED */ +#define UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13) +#define UNIPERIF_ITS_UNDERFLOW_REC_FAILED_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITS_UNDERFLOW_REC_FAILED_SHIFT(ip)))) + +/* + * AUD_UNIPERIF_ITS_BCLR reg + */ + +/* FIFO_ERROR */ +#define UNIPERIF_ITS_BCLR_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITS_BCLR_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITS_BCLR_FIFO_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITS_BCLR_FIFO_ERROR(ip) \ + SET_UNIPERIF_ITS_BCLR(ip, \ + UNIPERIF_ITS_BCLR_FIFO_ERROR_MASK(ip)) + +#define UNIPERIF_ITS_BCLR_OFFSET(ip) 0x0010 +#define SET_UNIPERIF_ITS_BCLR(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_ITS_BCLR_OFFSET(ip)) + +/* + * AUD_UNIPERIF_ITM reg + */ + +#define UNIPERIF_ITM_OFFSET(ip) 0x0018 +#define GET_UNIPERIF_ITM(ip) \ + readl_relaxed(ip->base + UNIPERIF_ITM_OFFSET(ip)) + +/* FIFO_ERROR */ +#define UNIPERIF_ITM_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITM_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_FIFO_ERROR_SHIFT(ip))) + +/* UNDERFLOW_REC_DONE */ +#define UNIPERIF_ITM_UNDERFLOW_REC_DONE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12) +#define UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_DONE_SHIFT(ip)))) + +/* UNDERFLOW_REC_FAILED */ +#define UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13) +#define UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_UNDERFLOW_REC_FAILED_SHIFT(ip)))) + +/* + * AUD_UNIPERIF_ITM_BCLR reg + */ + +#define UNIPERIF_ITM_BCLR_OFFSET(ip) 0x001c +#define SET_UNIPERIF_ITM_BCLR(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_ITM_BCLR_OFFSET(ip)) + +/* FIFO_ERROR */ +#define UNIPERIF_ITM_BCLR_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITM_BCLR_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BCLR_FIFO_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(ip) \ + SET_UNIPERIF_ITM_BCLR(ip, \ + UNIPERIF_ITM_BCLR_FIFO_ERROR_MASK(ip)) + +/* DMA_ERROR */ +#define UNIPERIF_ITM_BCLR_DMA_ERROR_SHIFT(ip) 9 +#define UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BCLR_DMA_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BCLR_DMA_ERROR(ip) \ + SET_UNIPERIF_ITM_BCLR(ip, \ + UNIPERIF_ITM_BCLR_DMA_ERROR_MASK(ip)) + +/* + * AUD_UNIPERIF_ITM_BSET reg + */ + +#define UNIPERIF_ITM_BSET_OFFSET(ip) 0x0020 +#define SET_UNIPERIF_ITM_BSET(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_ITM_BSET_OFFSET(ip)) + +/* FIFO_ERROR */ +#define UNIPERIF_ITM_BSET_FIFO_ERROR_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 8) +#define UNIPERIF_ITM_BSET_FIFO_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BSET_FIFO_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BSET_FIFO_ERROR(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_FIFO_ERROR_MASK(ip)) + +/* MEM_BLK_READ */ +#define UNIPERIF_ITM_BSET_MEM_BLK_READ_SHIFT(ip) 5 +#define UNIPERIF_ITM_BSET_MEM_BLK_READ_MASK(ip) \ + (BIT(UNIPERIF_ITM_BSET_MEM_BLK_READ_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_MEM_BLK_READ_MASK(ip)) + +/* DMA_ERROR */ +#define UNIPERIF_ITM_BSET_DMA_ERROR_SHIFT(ip) 9 +#define UNIPERIF_ITM_BSET_DMA_ERROR_MASK(ip) \ + (BIT(UNIPERIF_ITM_BSET_DMA_ERROR_SHIFT(ip))) +#define SET_UNIPERIF_ITM_BSET_DMA_ERROR(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_DMA_ERROR_MASK(ip)) + +/* UNDERFLOW_REC_DONE */ +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 12) +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_SHIFT(ip)))) +#define SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE_MASK(ip)) + +/* UNDERFLOW_REC_FAILED */ +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 13) +#define UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? \ + 0 : (BIT(UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_SHIFT(ip)))) +#define SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(ip) \ + SET_UNIPERIF_ITM_BSET(ip, \ + UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED_MASK(ip)) + +/* + * UNIPERIF_CONFIG reg + */ + +#define UNIPERIF_CONFIG_OFFSET(ip) 0x0040 +#define GET_UNIPERIF_CONFIG(ip) \ + readl_relaxed(ip->base + UNIPERIF_CONFIG_OFFSET(ip)) +#define SET_UNIPERIF_CONFIG(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CONFIG_OFFSET(ip)) + +/* PARITY_CNTR */ +#define UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip) 0 +#define UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_PARITY_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_PARITY_CNTR_MASK(ip), 1) + +/* CHANNEL_STA_CNTR */ +#define UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip) 1 +#define UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_CHANNEL_STA_CNTR_MASK(ip), 1) + +/* USER_DAT_CNTR */ +#define UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip) 2 +#define UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_USER_DAT_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_USER_DAT_CNTR_MASK(ip), 0) + +/* VALIDITY_DAT_CNTR */ +#define UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip) 3 +#define UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip)) +#define SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_SHIFT(ip), \ + UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_MASK(ip), 1) + +/* ONE_BIT_AUD_SUPPORT */ +#define UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip) 4 +#define UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_ONE_BIT_AUD(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip)) +#define SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_ONE_BIT_AUD_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_SHIFT(ip), \ + UNIPERIF_CONFIG_ONE_BIT_AUD_MASK(ip), 1) + +/* MEMORY_FMT */ +#define UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip) 5 +#define UNIPERIF_CONFIG_MEM_FMT_MASK(ip) 0x1 +#define VALUE_UNIPERIF_CONFIG_MEM_FMT_16_0(ip) 0 +#define VALUE_UNIPERIF_CONFIG_MEM_FMT_16_16(ip) 1 +#define GET_UNIPERIF_CONFIG_MEM_FMT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip), \ + UNIPERIF_CONFIG_MEM_FMT_MASK(ip)) +#define SET_UNIPERIF_CONFIG_MEM_FMT(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MEM_FMT_SHIFT(ip), \ + UNIPERIF_CONFIG_MEM_FMT_MASK(ip), value) +#define SET_UNIPERIF_CONFIG_MEM_FMT_16_0(ip) \ + SET_UNIPERIF_CONFIG_MEM_FMT(ip, \ + VALUE_UNIPERIF_CONFIG_MEM_FMT_16_0(ip)) +#define SET_UNIPERIF_CONFIG_MEM_FMT_16_16(ip) \ + SET_UNIPERIF_CONFIG_MEM_FMT(ip, \ + VALUE_UNIPERIF_CONFIG_MEM_FMT_16_16(ip)) + +/* REPEAT_CHL_STS */ +#define UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip) 6 +#define UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_REPEAT_CHL_STS(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip)) +#define SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_SHIFT(ip), \ + UNIPERIF_CONFIG_REPEAT_CHL_STS_MASK(ip), 1) + +/* BACK_STALL_REQ */ +#define UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 7 : -1) +#define UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_BACK_STALL_REQ(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip)) +#define SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_BACK_STALL_REQ_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_SHIFT(ip), \ + UNIPERIF_CONFIG_BACK_STALL_REQ_MASK(ip), 1) + +/* FDMA_TRIGGER_LIMIT */ +#define UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip) 8 +#define UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip) 0x7F +#define GET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip)) +#define SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_SHIFT(ip), \ + UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(ip), value) + +/* CHL_STS_UPDATE */ +#define UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 16 : -1) +#define UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_CHL_STS_UPDATE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip)) +#define SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_CONFIG_CHL_STS_UPDATE_MASK(ip), 1) + +/* IDLE_MOD */ +#define UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip) 18 +#define UNIPERIF_CONFIG_IDLE_MOD_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_IDLE_MOD(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_MASK(ip)) +#define SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_MASK(ip), 0) +#define SET_UNIPERIF_CONFIG_IDLE_MOD_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_SHIFT(ip), \ + UNIPERIF_CONFIG_IDLE_MOD_MASK(ip), 1) + +/* SUBFRAME_SELECTION */ +#define UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip) 19 +#define UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_SUBFRAME_SEL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip)) +#define SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF1_SUBF0(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF0_SUBF1(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_SHIFT(ip), \ + UNIPERIF_CONFIG_SUBFRAME_SEL_MASK(ip), 0) + +/* FULL_SW_CONTROL */ +#define UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip) 20 +#define UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_SPDIF_SW_CTRL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip)) +#define SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_ENABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_DISABLE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_SHIFT(ip), \ + UNIPERIF_CONFIG_SPDIF_SW_CTRL_MASK(ip), 0) + +/* MASTER_CLKEDGE */ +#define UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 24 : -1) +#define UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip) 0x1 +#define GET_UNIPERIF_CONFIG_MSTR_CLKEDGE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip)) +#define SET_UNIPERIF_CONFIG_MSTR_CLKEDGE_FALLING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip), 1) +#define SET_UNIPERIF_CONFIG_MSTR_CLKEDGE_RISING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CONFIG_OFFSET(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_SHIFT(ip), \ + UNIPERIF_CONFIG_MSTR_CLKEDGE_MASK(ip), 0) + +/* + * UNIPERIF_CTRL reg + */ + +#define UNIPERIF_CTRL_OFFSET(ip) 0x0044 +#define GET_UNIPERIF_CTRL(ip) \ + readl_relaxed(ip->base + UNIPERIF_CTRL_OFFSET(ip)) +#define SET_UNIPERIF_CTRL(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_CTRL_OFFSET(ip)) + +/* OPERATION */ +#define UNIPERIF_CTRL_OPERATION_SHIFT(ip) 0 +#define UNIPERIF_CTRL_OPERATION_MASK(ip) 0x7 +#define GET_UNIPERIF_CTRL_OPERATION(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_OFF(ip) 0 +#define SET_UNIPERIF_CTRL_OPERATION_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_OFF(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 1 : -1) +#define SET_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PCM_NULL(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 2 : -1) +#define SET_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_MUTE_PAUSE_BURST(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip) 3 +#define SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_PCM_DATA(ip)) +/* This is the same as above! */ +#define VALUE_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip) 3 +#define SET_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip) 4 +#define SET_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_ENC_DATA(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_CD_DATA(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 5 : -1) +#define SET_UNIPERIF_CTRL_OPERATION_CD_DATA(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_CD_DATA(ip)) +#define VALUE_UNIPERIF_CTRL_OPERATION_STANDBY(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 7) +#define SET_UNIPERIF_CTRL_OPERATION_STANDBY(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_OPERATION_SHIFT(ip), \ + UNIPERIF_CTRL_OPERATION_MASK(ip), \ + VALUE_UNIPERIF_CTRL_OPERATION_STANDBY(ip)) + +/* EXIT_STBY_ON_EOBLOCK */ +#define UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 3) +#define UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip)) +#define SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_SHIFT(ip), \ + UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_MASK(ip), 1) + +/* ROUNDING */ +#define UNIPERIF_CTRL_ROUNDING_SHIFT(ip) 4 +#define UNIPERIF_CTRL_ROUNDING_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_ROUNDING(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \ + UNIPERIF_CTRL_ROUNDING_MASK(ip)) +#define SET_UNIPERIF_CTRL_ROUNDING_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \ + UNIPERIF_CTRL_ROUNDING_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_ROUNDING_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ROUNDING_SHIFT(ip), \ + UNIPERIF_CTRL_ROUNDING_MASK(ip), 1) + +/* DIVIDER */ +#define UNIPERIF_CTRL_DIVIDER_SHIFT(ip) 5 +#define UNIPERIF_CTRL_DIVIDER_MASK(ip) 0xff +#define GET_UNIPERIF_CTRL_DIVIDER(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_DIVIDER_SHIFT(ip), \ + UNIPERIF_CTRL_DIVIDER_MASK(ip)) +#define SET_UNIPERIF_CTRL_DIVIDER(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_DIVIDER_SHIFT(ip), \ + UNIPERIF_CTRL_DIVIDER_MASK(ip), value) + +/* BYTE_SWAP */ +#define UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 13 : -1) +#define UNIPERIF_CTRL_BYTE_SWP_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_BYTE_SWP(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \ + UNIPERIF_CTRL_BYTE_SWP_MASK(ip)) +#define SET_UNIPERIF_CTRL_BYTE_SWP_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \ + UNIPERIF_CTRL_BYTE_SWP_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_BYTE_SWP_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_BYTE_SWP_SHIFT(ip), \ + UNIPERIF_CTRL_BYTE_SWP_MASK(ip), 1) + +/* ZERO_STUFFING_HW_SW */ +#define UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 14 : -1) +#define UNIPERIF_CTRL_ZERO_STUFF_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_ZERO_STUFF(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_MASK(ip)) +#define SET_UNIPERIF_CTRL_ZERO_STUFF_HW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_MASK(ip), 1) +#define SET_UNIPERIF_CTRL_ZERO_STUFF_SW(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_SHIFT(ip), \ + UNIPERIF_CTRL_ZERO_STUFF_MASK(ip), 0) + +/* SPDIF_LAT */ +#define UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 16 : -1) +#define UNIPERIF_CTRL_SPDIF_LAT_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_SPDIF_LAT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_MASK(ip)) +#define SET_UNIPERIF_CTRL_SPDIF_LAT_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_MASK(ip), 1) +#define SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_LAT_MASK(ip), 0) + +/* EN_SPDIF_FORMATTING */ +#define UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip) 17 +#define UNIPERIF_CTRL_SPDIF_FMT_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_SPDIF_FMT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_MASK(ip)) +#define SET_UNIPERIF_CTRL_SPDIF_FMT_ON(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_MASK(ip), 1) +#define SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_SHIFT(ip), \ + UNIPERIF_CTRL_SPDIF_FMT_MASK(ip), 0) + +/* READER_OUT_SELECT */ +#define UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 18 : -1) +#define UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip) 0x1 +#define GET_UNIPERIF_CTRL_READER_OUT_SEL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip)) +#define SET_UNIPERIF_CTRL_READER_OUT_SEL_IN_MEM(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 0) +#define SET_UNIPERIF_CTRL_READER_OUT_SEL_ON_I2S_LINE(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_READER_OUT_SEL_SHIFT(ip), \ + CORAUD_UNIPERIF_CTRL_READER_OUT_SEL_MASK(ip), 1) + +/* UNDERFLOW_REC_WINDOW */ +#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip) 20 +#define UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip) 0xff +#define GET_UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip)) +#define SET_UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_CTRL_OFFSET(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_SHIFT(ip), \ + UNIPERIF_CTRL_UNDERFLOW_REC_WINDOW_MASK(ip), value) + +/* + * UNIPERIF_I2S_FMT a.k.a UNIPERIF_FORMAT reg + */ + +#define UNIPERIF_I2S_FMT_OFFSET(ip) 0x0048 +#define GET_UNIPERIF_I2S_FMT(ip) \ + readl_relaxed(ip->base + UNIPERIF_I2S_FMT_OFFSET(ip)) +#define SET_UNIPERIF_I2S_FMT(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_I2S_FMT_OFFSET(ip)) + +/* NBIT */ +#define UNIPERIF_I2S_FMT_NBIT_SHIFT(ip) 0 +#define UNIPERIF_I2S_FMT_NBIT_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_NBIT(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NBIT_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_NBIT_32(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NBIT_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_NBIT_16(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NBIT_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NBIT_MASK(ip), 1) + +/* DATA_SIZE */ +#define UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip) 1 +#define UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip) 0x7 +#define GET_UNIPERIF_I2S_FMT_DATA_SIZE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_18(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 1) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_20(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 2) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 3) +#define SET_UNIPERIF_I2S_FMTL_DATA_SIZE_28(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 4) +#define SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_DATA_SIZE_MASK(ip), 5) + +/* LR_POL */ +#define UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip) 4 +#define UNIPERIF_I2S_FMT_LR_POL_MASK(ip) 0x1 +#define VALUE_UNIPERIF_I2S_FMT_LR_POL_LOW(ip) 0x0 +#define VALUE_UNIPERIF_I2S_FMT_LR_POL_HIG(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_LR_POL(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip), \ + UNIPERIF_I2S_FMT_LR_POL_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_LR_POL(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_LR_POL_SHIFT(ip), \ + UNIPERIF_I2S_FMT_LR_POL_MASK(ip), value) +#define SET_UNIPERIF_I2S_FMT_LR_POL_LOW(ip) \ + SET_UNIPERIF_I2S_FMT_LR_POL(ip, \ + VALUE_UNIPERIF_I2S_FMT_LR_POL_LOW(ip)) +#define SET_UNIPERIF_I2S_FMT_LR_POL_HIG(ip) \ + SET_UNIPERIF_I2S_FMT_LR_POL(ip, \ + VALUE_UNIPERIF_I2S_FMT_LR_POL_HIG(ip)) + +/* SCLK_EDGE */ +#define UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip) 5 +#define UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_SCLK_EDGE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_SHIFT(ip), \ + UNIPERIF_I2S_FMT_SCLK_EDGE_MASK(ip), 1) + +/* PADDING */ +#define UNIPERIF_I2S_FMT_PADDING_SHIFT(ip) 6 +#define UNIPERIF_I2S_FMT_PADDING_MASK(ip) 0x1 +#define UNIPERIF_I2S_FMT_PADDING_MASK(ip) 0x1 +#define VALUE_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip) 0x0 +#define VALUE_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_PADDING(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_PADDING_SHIFT(ip), \ + UNIPERIF_I2S_FMT_PADDING_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_PADDING(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_PADDING_SHIFT(ip), \ + UNIPERIF_I2S_FMT_PADDING_MASK(ip), value) +#define SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip) \ + SET_UNIPERIF_I2S_FMT_PADDING(ip, \ + VALUE_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(ip)) +#define SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip) \ + SET_UNIPERIF_I2S_FMT_PADDING(ip, \ + VALUE_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(ip)) + +/* ALIGN */ +#define UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip) 7 +#define UNIPERIF_I2S_FMT_ALIGN_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_ALIGN(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ALIGN_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ALIGN_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ALIGN_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ALIGN_MASK(ip), 1) + +/* ORDER */ +#define UNIPERIF_I2S_FMT_ORDER_SHIFT(ip) 8 +#define UNIPERIF_I2S_FMT_ORDER_MASK(ip) 0x1 +#define GET_UNIPERIF_I2S_FMT_ORDER(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ORDER_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_ORDER_LSB(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ORDER_MASK(ip), 0) +#define SET_UNIPERIF_I2S_FMT_ORDER_MSB(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_ORDER_SHIFT(ip), \ + UNIPERIF_I2S_FMT_ORDER_MASK(ip), 1) + +/* NUM_CH */ +#define UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip) 9 +#define UNIPERIF_I2S_FMT_NUM_CH_MASK(ip) 0x7 +#define GET_UNIPERIF_I2S_FMT_NUM_CH(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_NUM_CH(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NUM_CH_MASK(ip), value) + +/* NO_OF_SAMPLES_TO_READ */ +#define UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip) 12 +#define UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip) 0xfffff +#define GET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip)) +#define SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_I2S_FMT_OFFSET(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_SHIFT(ip), \ + UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ_MASK(ip), value) + +/* + * UNIPERIF_BIT_CONTROL reg + */ + +#define UNIPERIF_BIT_CONTROL_OFFSET(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0x004c) +#define GET_UNIPERIF_BIT_CONTROL(ip) \ + readl_relaxed(ip->base + UNIPERIF_BIT_CONTROL_OFFSET(ip)) +#define SET_UNIPERIF_BIT_CONTROL(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_BIT_CONTROL_OFFSET(ip)) + +/* CLR_UNDERFLOW_DURATION */ +#define UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip) 0 +#define UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip) 0x1 +#define GET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip)) +#define SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(ip) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION_MASK(ip), 1) + +/* CHL_STS_UPDATE */ +#define UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip) 1 +#define UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip) 0x1 +#define GET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip)) +#define SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(ip) \ + SET_UNIPERIF_BIT_REG(ip, \ + UNIPERIF_BIT_CONTROL_OFFSET(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_SHIFT(ip), \ + UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE_MASK(ip), 1) + +/* + * UNIPERIF_STATUS_1 reg + */ + +#define UNIPERIF_STATUS_1_OFFSET(ip) 0x0050 +#define GET_UNIPERIF_STATUS_1(ip) \ + readl_relaxed(ip->base + UNIPERIF_STATUS_1_OFFSET(ip)) +#define SET_UNIPERIF_STATUS_1(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_STATUS_1_OFFSET(ip)) + +/* UNDERFLOW_DURATION */ +#define UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0) +#define UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip) 0xff +#define GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_STATUS_1_OFFSET(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip)) +#define SET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_STATUS_1_OFFSET(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_SHIFT(ip), \ + UNIPERIF_STATUS_1_UNDERFLOW_DURATION_MASK(ip), value) + +/* + * AUD_UNIPERIF_CHANNEL_STA_REGN reg + */ + +#define UNIPERIF_CHANNEL_STA_REGN(ip, n) (0x0060 + (4 * n)) +#define GET_UNIPERIF_CHANNEL_STA_REGN(ip) \ + readl_relaxed(ip->base + UNIPERIF_CHANNEL_STA_REGN(ip, n)) +#define SET_UNIPERIF_CHANNEL_STA_REGN(ip, n, value) \ + writel_relaxed(value, ip->base + \ + UNIPERIF_CHANNEL_STA_REGN(ip, n)) + +/* + * AUD_UNIPERIF_USER_VALIDITY reg + */ + +#define UNIPERIF_USER_VALIDITY_OFFSET(ip) 0x0090 +#define GET_UNIPERIF_USER_VALIDITY(ip) \ + readl_relaxed(ip->base + UNIPERIF_USER_VALIDITY_OFFSET(ip)) +#define SET_UNIPERIF_USER_VALIDITY(ip, value) \ + writel_relaxed(value, ip->base + UNIPERIF_USER_VALIDITY_OFFSET(ip)) + +/* VALIDITY_LEFT_AND_RIGHT */ +#define UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip) 0 +#define UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip) 0x3 +#define GET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_USER_VALIDITY_OFFSET(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip)) +#define SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_USER_VALIDITY_OFFSET(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_SHIFT(ip), \ + UNIPERIF_USER_VALIDITY_VALIDITY_LR_MASK(ip), \ + value ? 0x3 : 0) + +/* + * UNIPERIF_DBG_STANDBY_LEFT_SP reg + */ +#define UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip) 0x0150 +#define UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? -1 : 0) +#define UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip) \ + ((ip)->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 ? 0 : 0xFFFFFF) +#define GET_UNIPERIF_DBG_STANDBY_LEFT_SP(ip) \ + GET_UNIPERIF_REG(ip, \ + UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip)) +#define SET_UNIPERIF_DBG_STANDBY_LEFT_SP(ip, value) \ + SET_UNIPERIF_REG(ip, \ + UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \ + UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip), value)
Add code to manage Uniperipheral player IP instances. These DAIs are dedicated to playback and support I2S and IEC mode.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@st.com --- sound/soc/sti/uniperif.h | 133 ++++ sound/soc/sti/uniperif_player.c | 1367 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1500 insertions(+) create mode 100644 sound/soc/sti/uniperif_player.c
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h index 043853e..02ac9a8 100644 --- a/sound/soc/sti/uniperif.h +++ b/sound/soc/sti/uniperif.h @@ -1097,3 +1097,136 @@ UNIPERIF_DBG_STANDBY_LEFT_SP_OFFSET(ip), \ UNIPERIF_DBG_STANDBY_LEFT_SP_SHIFT(ip), \ UNIPERIF_DBG_STANDBY_LEFT_SP_MASK(ip), value) + +/* + * uniperipheral IP capabilities + */ + +#define UNIPERIF_FIFO_SIZE 70 /* FIFO is 70 cells deep */ +#define UNIPERIF_FIFO_FRAMES 4 /* FDMA trigger limit in frames */ + +#define UNIPERIF_MIN_RATE 8000 +#define UNIPERIF_MAX_RATE 192000 + +#define UNIPERIF_MIN_CHANNELS 2 +#define UNIPERIF_MAX_CHANNELS 8 + +#define UNIPERIF_PERIODS_BYTES_MIN 128 +#define UNIPERIF_PERIODS_BYTES_MAX (64 * PAGE_SIZE) +#define UNIPERIF_PERIODS_MIN 2 +#define UNIPERIF_PERIODS_MAX 48 +#define UNIPERIF_BUFFER_BYTES_MAX (2048 * PAGE_SIZE) + +/* + * Uniperipheral IP revisions + */ +enum uniperif_version { + SND_ST_UNIPERIF_VERSION_UNKNOWN, + /* SASG1 (Orly), Newman */ + SND_ST_UNIPERIF_VERSION_C6AUD0_UNI_1_0, + /* SASC1, SASG2 (Orly2) */ + SND_ST_UNIPERIF_VERSION_UNI_PLR_1_0, + /* SASC1, SASG2 (Orly2), TELSS, Cannes */ + SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0, + /* TELSS (SASC1) */ + SND_ST_UNIPERIF_VERSION_TDM_PLR_1_0, + /* Cannes/Monaco */ + SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 +}; + +enum uniperif_type { + SND_ST_UNIPERIF_PLAYER_TYPE_NONE, + SND_ST_UNIPERIF_PLAYER_TYPE_HDMI, + SND_ST_UNIPERIF_PLAYER_TYPE_PCM, + SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF +}; + +enum uniperif_state { + UNIPERIF_STATE_STOPPED, + UNIPERIF_STATE_STARTED, + UNIPERIF_STATE_STANDBY, + UNIPERIF_STATE_UNDERFLOW, + UNIPERIF_STATE_OVERFLOW = UNIPERIF_STATE_UNDERFLOW, + UNIPERIF_STATE_XRUN +}; + +enum uniperif_iec958_encoding_mode { + UNIPERIF_IEC958_ENCODING_MODE_PCM, + UNIPERIF_IEC958_ENCODING_MODE_ENCODED +}; + +struct uniperif_info { + int ver; + int uni_num; /* instance value of the uniperipheral IP */ + enum uniperif_type player_type; + int underflow_enabled; /* Underflow recovery mode */ + int standby_enabled; + unsigned int suspend_delay; +}; + +struct uniperif_iec958_settings { + enum uniperif_iec958_encoding_mode encoding_mode; + struct snd_aes_iec958 iec958; +}; + +struct uniperif; + +struct uniperif_ops { + int (*open)(struct uniperif *player); + void (*close)(struct uniperif *player); + int (*prepare)(struct uniperif *player, + struct snd_pcm_runtime *runtime); + int (*trigger)(struct uniperif *player, int cmd); +}; + +struct uniperif { + /* System information */ + struct uniperif_info *info; + struct device *dev; + int ver; /* IP version, used by register access macros */ + struct regmap_field *clk_sel; + + /* capabilities */ + const struct snd_pcm_hardware *hw; + + /* Resources */ + struct resource *mem_region; + void *base; + unsigned long fifo_phys_address; + int irq; + + /* Clocks */ + struct clk *clk; + int clk_div; + int clk_adj; + int rate; + + /* Runtime data */ + enum uniperif_state state; + + struct snd_pcm_substream *substream; + + /* Specific to IEC958 player */ + struct uniperif_iec958_settings stream_settings; + spinlock_t lock; /* lock on resource updated by alsa controls */ + + /*alsa ctrl*/ + struct snd_kcontrol_new *snd_ctrls; + int num_ctrls; + + /* dai properties */ + unsigned int daifmt; + + /* DAI callbacks */ + const struct uniperif_ops *ops; + + /* work struct */ + struct delayed_work delayed_work; +}; + +/* uniperiph player*/ +int uni_player_init(struct platform_device *pdev, struct device_node *node, + struct uniperif **uni_player, int idx); +int uni_player_remove(struct platform_device *pdev); + +#endif diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c new file mode 100644 index 0000000..702bcd5 --- /dev/null +++ b/sound/soc/sti/uniperif_player.c @@ -0,0 +1,1367 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen arnaud.pouliquen@st.com + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> + +#include <sound/asoundef.h> +#include <sound/soc.h> + +#include "uniperif.h" + +/* + * Some hardware-related definitions + */ + +#define DEFAULT_OVERSAMPLING 128 /* make all ip's running at same rate*/ + +#define MIN_IEC958_SAMPLE_RATE 32000 + +/* sys config registers definitions */ +#define SYS_CFG_AUDIO_GLUE 0xA4 +#define SYS_CFG_AUDI0_GLUE_PCM_CLKX 8 + +/* + * Driver specific types. + */ + +#define UNIPERIF_PLAYER_TYPE_IS_HDMI(p) \ + ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_HDMI) +#define UNIPERIF_PLAYER_TYPE_IS_PCM(p) \ + ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_PCM) +#define UNIPERIF_PLAYER_TYPE_IS_SPDIF(p) \ + ((p)->info->player_type == SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF) +#define UNIPERIF_PLAYER_TYPE_IS_IEC958(p) \ + (UNIPERIF_PLAYER_TYPE_IS_HDMI(p) || \ + UNIPERIF_PLAYER_TYPE_IS_SPDIF(p)) + +#define DUMP_REGISTER(r) \ + snd_iprintf(buffer, "AUD_UNIPERIF_%-23s (offset 0x%04x) = " \ + "0x%08x\n", __stringify(r), \ + UNIPERIF_##r##_OFFSET(player), \ + GET_UNIPERIF_##r(player)) + +#define UNIPERIF_PLAYER_CLK_ADJ_MIN -999999 +#define UNIPERIF_PLAYER_CLK_ADJ_MAX 1000000 +#define UNIPERIF_PLAYER_CLK_ADJ_STEP 1 + +#define MIN_AUTOSUPEND_DELAY_MS 100 + +static int uni_player_suspend(struct uniperif *player); + +static struct workqueue_struct *uni_player_wq; + +const struct snd_pcm_hardware uni_player_pcm_hw = { + .info = ( + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S16_LE), + + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = UNIPERIF_MIN_RATE, + .rate_max = UNIPERIF_MAX_RATE, + + .channels_min = UNIPERIF_MIN_CHANNELS, + .channels_max = UNIPERIF_MAX_CHANNELS, + + .periods_min = UNIPERIF_PERIODS_MIN, + .periods_max = UNIPERIF_PERIODS_MAX, + + .period_bytes_min = UNIPERIF_PERIODS_BYTES_MIN, + .period_bytes_max = UNIPERIF_PERIODS_BYTES_MAX, + .buffer_bytes_max = UNIPERIF_BUFFER_BYTES_MAX +}; + +static inline int reset_player(struct uniperif *player) +{ + int count = 10; + + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + while (GET_UNIPERIF_SOFT_RST_SOFT_RST(player) && count) { + udelay(5); + count--; + } + + if (!count) { + dev_err(player->dev, "Failed to reset uniperif"); + return -EIO; + } + + return 0; +} + +static inline int get_property_hdl(struct device *dev, struct device_node *np, + const char *prop, int idx) +{ + int sz = 0; + const __be32 *phandle; + + phandle = of_get_property(np, prop, &sz); + + if (!phandle) { + dev_err(dev, "%s: ERROR: DT-property '%s' missing or invalid!", + __func__, prop); + return -EINVAL; + } + + if (idx >= sz) { + dev_err(dev, "%s: ERROR: Array-index (%u) >= array-size (%u)!", + __func__, idx, sz); + return -EINVAL; + } + + return be32_to_cpup(phandle + idx); +} + +/* + * Uniperipheral player implementation + */ + +static void uni_player_work(struct work_struct *work) +{ + struct uniperif *player = + container_of(work, struct uniperif, delayed_work.work); + + spin_lock(&player->lock); + if (uni_player_suspend(player)) + dev_err(player->dev, "%s: failed to suspend player", __func__); + spin_unlock(&player->lock); +} + +static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) +{ + irqreturn_t ret = IRQ_NONE; + struct uniperif *player = dev_id; + unsigned int status; + unsigned int tmp; + + /* Get interrupt status & clear them immediately */ + preempt_disable(); + status = GET_UNIPERIF_ITS(player); + SET_UNIPERIF_ITS_BCLR(player, status); + preempt_enable(); + + if ((player->state == UNIPERIF_STATE_STANDBY) || + (player->state == UNIPERIF_STATE_STOPPED)) { + /* unexpected IRQ: do nothing */ + dev_warn(player->dev, "unexpected IRQ: status flag: %#x", + status); + return IRQ_HANDLED; + }; + + snd_pcm_stream_lock(player->substream); + /* Check for fifo error (under run) */ + if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) { + dev_err(player->dev, "FIFO underflow error detected"); + + /* Interrupt is just for information when underflow recovery */ + if (player->info->underflow_enabled) { + /* Update state to underflow */ + player->state = UNIPERIF_STATE_UNDERFLOW; + + } else { + /* Disable interrupt so doesn't continually fire */ + SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player); + + /* Stop the player */ + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + } + + ret = IRQ_HANDLED; + } + + /* Check for dma error (over run) */ + if (unlikely(status & UNIPERIF_ITS_DMA_ERROR_MASK(player))) { + dev_err(player->dev, "DMA error detected"); + + /* Disable interrupt so doesn't continually fire */ + SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); + + /* Stop the player */ + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + + ret = IRQ_HANDLED; + } + + /* Check for underflow recovery done */ + if (unlikely(status & + UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) { + if (!player->info->underflow_enabled) { + dev_err(player->dev, "unexpected Underflow recovering"); + snd_pcm_stream_unlock(player->substream); + return -EPERM; + } + /* Read the underflow recovery duration */ + tmp = GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(player); + + /* Clear the underflow recovery duration */ + SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(player); + + /* Update state to started */ + player->state = UNIPERIF_STATE_STARTED; + + ret = IRQ_HANDLED; + } + + if (unlikely(status & + UNIPERIF_ITS_MEM_BLK_READ_MASK(player))) { + ret = IRQ_HANDLED; + } + + /* Checkf or underflow recovery failed */ + if (unlikely(status & + UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(player))) { + dev_err(player->dev, "Underflow recovery failed"); + + /* Stop the player */ + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); + + ret = IRQ_HANDLED; + } + + /* error on unhandled interrupt */ + if (ret != IRQ_HANDLED) + dev_err(player->dev, "IRQ status : %#x", status); + + snd_pcm_stream_unlock(player->substream); + return ret; +} + +int uni_player_clk_set_rate(struct uniperif *player, unsigned long rate) +{ + int rate_adjusted, rate_achieved, delta; + int adjustment = player->clk_adj; + + /* + * a + * F = f + --------- * f = f + d + * 1000000 + * + * a + * d = --------- * f + * 1000000 + * + * where: + * f - nominal rate + * a - adjustment in ppm (parts per milion) + * F - rate to be set in synthesizer + * d - delta (difference) between f and F + */ + if (adjustment < 0) { + /* div64_64 operates on unsigned values... */ + delta = -1; + adjustment = -adjustment; + } else { + delta = 1; + } + /* 500000 ppm is 0.5, which is used to round up values */ + delta *= (int)div64_u64((uint64_t)rate * + (uint64_t)adjustment + 500000, 1000000); + rate_adjusted = rate + delta; + + /* adjusted rate should never be == 0 */ + if (!rate_adjusted) + return -EINVAL; + + if (clk_set_rate(player->clk, rate_adjusted) < 0) { + dev_err(player->dev, "Failed to clk set rate %d !\n", + rate_adjusted); + return -EINVAL; + } + + rate_achieved = clk_get_rate(player->clk); + if (!rate_achieved) + return -EINVAL; + + /* + * using ALSA's adjustment control, we can modify the rate to be up + * to twice as much as requested, but no more + */ + delta = rate_achieved - rate; + if (delta < 0) { + /* div64_64 operates on unsigned values... */ + delta = -delta; + adjustment = -1; + } else { + adjustment = 1; + } + /* frequency/2 is added to round up result */ + adjustment *= (int)div64_u64((uint64_t)delta * 1000000 + rate / 2, + rate); + player->clk_adj = adjustment; + return 0; +} + +static void uni_player_set_channel_status(struct uniperif *player, + struct snd_pcm_runtime *runtime) +{ + int n; + unsigned int status; + + /* + * Some AVRs and TVs require the channel status to contain a correct + * sampling frequency. If no sample rate is already specified, then + * set one. + */ + spin_lock(&player->lock); + if (runtime && (player->stream_settings.iec958.status[3] + == IEC958_AES3_CON_FS_NOTID)) { + switch (runtime->rate) { + case 22050: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_22050; + break; + case 44100: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_44100; + break; + case 88200: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_88200; + break; + case 176400: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_176400; + break; + case 24000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_24000; + break; + case 48000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_48000; + break; + case 96000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_96000; + break; + case 192000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_192000; + break; + case 32000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_32000; + break; + case 768000: + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_768000; + break; + default: + /* Mark as sampling frequency not indicated */ + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_NOTID; + break; + } + } + + /* audio mode + * use audio mode status to select PCM or encoded mode + */ + if (player->stream_settings.iec958.status[0] & IEC958_AES0_NONAUDIO) + player->stream_settings.encoding_mode = + UNIPERIF_IEC958_ENCODING_MODE_ENCODED; + else + player->stream_settings.encoding_mode = + UNIPERIF_IEC958_ENCODING_MODE_PCM; + + if (player->stream_settings.encoding_mode == + UNIPERIF_IEC958_ENCODING_MODE_PCM) + /* Clear user validity bits */ + SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0); + else + /* Set user validity bits */ + SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 1); + + /* Program the new channel status */ + for (n = 0; n < 6; ++n) { + status = + player->stream_settings.iec958.status[0 + (n * 4)] & 0xf; + status |= + player->stream_settings.iec958.status[1 + (n * 4)] << 8; + status |= + player->stream_settings.iec958.status[2 + (n * 4)] << 16; + status |= + player->stream_settings.iec958.status[3 + (n * 4)] << 24; + SET_UNIPERIF_CHANNEL_STA_REGN(player, n, status); + } + spin_unlock(&player->lock); + + /* Update the channel status */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(player); + else + SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(player); +} + +static int uni_player_prepare_iec958(struct uniperif *player, + struct snd_pcm_runtime *runtime) +{ + if (!runtime) { + dev_err(player->dev, "%s: invalid pointer(s)", __func__); + return -EINVAL; + } + + /* Oversampling must be multiple of 128 as iec958 frame is 32-bits */ + if ((player->clk_div % 128) || + (player->clk_div <= 0)) { + dev_err(player->dev, "%s: invalid clk_div %d", + __func__, player->clk_div); + return -EINVAL; + } + + /* No sample rates below 32kHz are supported for iec958 */ + if (runtime->rate < MIN_IEC958_SAMPLE_RATE) { + dev_err(player->dev, "Invalid rate (%d)", runtime->rate); + return -EINVAL; + } + + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + /* 16/16 memory format */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_16(player); + /* 16-bits per sub-frame */ + SET_UNIPERIF_I2S_FMT_NBIT_32(player); + /* Set 16-bit sample precision */ + SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player); + break; + case SNDRV_PCM_FORMAT_S32_LE: + /* 16/0 memory format */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player); + /* 32-bits per sub-frame */ + SET_UNIPERIF_I2S_FMT_NBIT_32(player); + /* Set 24-bit sample precision */ + SET_UNIPERIF_I2S_FMT_DATA_SIZE_24(player); + break; + default: + dev_err(player->dev, "format not supported"); + return -EINVAL; + } + + /* Set parity to be calculated by the hardware */ + SET_UNIPERIF_CONFIG_PARITY_CNTR_BY_HW(player); + + /* Set channel status bits to be inserted by the hardware */ + SET_UNIPERIF_CONFIG_CHANNEL_STA_CNTR_BY_HW(player); + + /* Set user data bits to be inserted by the hardware */ + SET_UNIPERIF_CONFIG_USER_DAT_CNTR_BY_HW(player); + + /* Set validity bits to be inserted by the hardware */ + SET_UNIPERIF_CONFIG_VALIDITY_DAT_CNTR_BY_HW(player); + + /* Set full software control to disabled */ + SET_UNIPERIF_CONFIG_SPDIF_SW_CTRL_DISABLE(player); + + SET_UNIPERIF_CTRL_ZERO_STUFF_HW(player); + + /* Update the channel status */ + uni_player_set_channel_status(player, runtime); + + /* Clear the user validity user bits */ + SET_UNIPERIF_USER_VALIDITY_VALIDITY_LR(player, 0); + + /* Disable one-bit audio mode */ + SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player); + + /* Enable consecutive frames repetition of Z preamble (not for HBRA) */ + SET_UNIPERIF_CONFIG_REPEAT_CHL_STS_ENABLE(player); + + /* Change to SUF0_SUBF1 and left/right channels swap! */ + SET_UNIPERIF_CONFIG_SUBFRAME_SEL_SUBF1_SUBF0(player); + + /* Set lr clock polarity and i2s mode using platform configuration */ + /* Set data output on rising edge */ + SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(player); + + /* Set data aligned to left with respect to left-right clock polarity */ + SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player); + + /* Set data output as MSB first */ + SET_UNIPERIF_I2S_FMT_ORDER_MSB(player); + + /* Set the number of channels (maximum supported by spdif is 2) */ + if (UNIPERIF_PLAYER_TYPE_IS_SPDIF(player) && + (runtime->channels != 2)) { + dev_err(player->dev, "invalid nb of channels"); + return -EINVAL; + } + + SET_UNIPERIF_I2S_FMT_NUM_CH(player, runtime->channels / 2); + + /* Set rounding to off */ + SET_UNIPERIF_CTRL_ROUNDING_OFF(player); + + /* Set clock divisor */ + SET_UNIPERIF_CTRL_DIVIDER(player, + player->clk_div / 128); + + /* Set the spdif latency to not wait before starting player */ + SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player); + + /* + * Ensure iec958 formatting is off. It will be enabled in function + * uni_player_start() at the same time as the operation + * mode is set to work around a silicon issue. + */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player); + else + SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player); + + return 0; +} + +static int uni_player_prepare_pcm(struct uniperif *player, + struct snd_pcm_runtime *runtime) +{ + int output_frame_size, slot_width; + + if (!runtime) { + dev_err(player->dev, "%s: invalid pointer(s)", __func__); + return -EINVAL; + } + + /* force slot width to 32 in I2S mode (HW constraint) */ + if ((player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == + SND_SOC_DAIFMT_I2S) { + slot_width = 32; + } else { + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + slot_width = 16; + break; + default: + slot_width = 32; + break; + } + } + output_frame_size = slot_width * runtime->channels; + + if (player->clk_div <= 0) { + dev_err(player->dev, "%s: invalid clk_div", __func__); + return -EINVAL; + } + + /* For 32 bits subframe clk_div must be a multiple of 128, + * for 16 bits - of 64 */ + if ((slot_width == 32) && (player->clk_div % 128)) { + dev_err(player->dev, "%s: invalid clk_div", __func__); + return -EINVAL; + } + + if ((slot_width == 16) && (player->clk_div % 64)) { + dev_err(player->dev, "%s: invalid clk_div", __func__); + return -EINVAL; + } + + /* Number of bits per subframe (which is one channel sample) + * on output - Transfer 16 or 32 bits from FIFO + */ + switch (slot_width) { + case 32: + SET_UNIPERIF_I2S_FMT_NBIT_32(player); + SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(player); + break; + case 16: + SET_UNIPERIF_I2S_FMT_NBIT_16(player); + SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(player); + break; + default: + dev_err(player->dev, "subframe format not supported"); + return -EINVAL; + } + + /* Configure data memory format */ + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + /* One data word contains two samples */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_16(player); + break; + + case SNDRV_PCM_FORMAT_S32_LE: + /* Actually "16 bits/0 bits" means "32/28/24/20/18/16 bits + * on the left than zeros (if less than 32 bytes)"... ;-) */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_0(player); + break; + + default: + dev_err(player->dev, "format not supported"); + return -EINVAL; + } + + /* Set rounding to off */ + SET_UNIPERIF_CTRL_ROUNDING_OFF(player); + + /* Set clock divisor */ + SET_UNIPERIF_CTRL_DIVIDER(player, + player->clk_div / (2 * output_frame_size)); + + /* Number of channels... */ + + if ((runtime->channels % 2) || (runtime->channels < 2) || + (runtime->channels > 10)) { + dev_err(player->dev, "%s: invalid nb of channels", __func__); + return -EINVAL; + } + + SET_UNIPERIF_I2S_FMT_NUM_CH(player, runtime->channels / 2); + + /* Set 1-bit audio format to disabled */ + SET_UNIPERIF_CONFIG_ONE_BIT_AUD_DISABLE(player); + + SET_UNIPERIF_I2S_FMT_ORDER_MSB(player); + SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player); + + /* No iec958 formatting as outputting to DAC */ + SET_UNIPERIF_CTRL_SPDIF_FMT_OFF(player); + + return 0; +} + +/* + * ALSA uniperipheral iec958 controls + */ +static int uni_player_ctl_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int uni_player_ctl_iec958_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uniperif *player = snd_kcontrol_chip(kcontrol); + struct snd_aes_iec958 *iec958 = &player->stream_settings.iec958; + + spin_lock(&player->lock); + ucontrol->value.iec958.status[0] = iec958->status[0]; + ucontrol->value.iec958.status[1] = iec958->status[1]; + ucontrol->value.iec958.status[2] = iec958->status[2]; + ucontrol->value.iec958.status[3] = iec958->status[3]; + spin_unlock(&player->lock); + return 0; +} + +static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uniperif *player = snd_kcontrol_chip(kcontrol); + struct snd_aes_iec958 *iec958 = &player->stream_settings.iec958; + + spin_lock(&player->lock); + iec958->status[0] = ucontrol->value.iec958.status[0]; + iec958->status[1] = ucontrol->value.iec958.status[1]; + iec958->status[2] = ucontrol->value.iec958.status[2]; + iec958->status[3] = ucontrol->value.iec958.status[3]; + spin_unlock(&player->lock); + + uni_player_set_channel_status(player, NULL); + + return 0; +} + +static struct snd_kcontrol_new uni_player_iec958_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = uni_player_ctl_iec958_info, + .get = uni_player_ctl_iec958_get, + .put = uni_player_ctl_iec958_put, +}; + +/* + * uniperif rate adjustement control + */ +static int snd_sti_clk_adjustment_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = UNIPERIF_PLAYER_CLK_ADJ_MIN; + uinfo->value.integer.max = UNIPERIF_PLAYER_CLK_ADJ_MAX; + uinfo->value.integer.step = UNIPERIF_PLAYER_CLK_ADJ_STEP; + + return 0; +} + +static int snd_sti_clk_adjustment_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uniperif *player = snd_kcontrol_chip(kcontrol); + + spin_lock(&player->lock); + ucontrol->value.integer.value[0] = player->clk_adj; + spin_unlock(&player->lock); + + return 0; +} + +static int snd_sti_clk_adjustment_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uniperif *player = snd_kcontrol_chip(kcontrol); + int ret = 0; + + if ((ucontrol->value.integer.value[0] < UNIPERIF_PLAYER_CLK_ADJ_MIN) || + (ucontrol->value.integer.value[0] > UNIPERIF_PLAYER_CLK_ADJ_MAX)) + return -EINVAL; + + spin_lock(&player->lock); + player->clk_adj = ucontrol->value.integer.value[0]; + + if (player->rate) + ret = uni_player_clk_set_rate(player, + player->rate * player->clk_div); + spin_unlock(&player->lock); + + return ret; +} + +static struct snd_kcontrol_new uni_player_clk_adj_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Playback Oversampling Freq. Adjustment", + .info = snd_sti_clk_adjustment_info, + .get = snd_sti_clk_adjustment_get, + .put = snd_sti_clk_adjustment_put, +}; + +static struct snd_kcontrol_new *snd_sti_pcm_ctl[] = { + &uni_player_clk_adj_ctl, +}; + +static struct snd_kcontrol_new *snd_sti_iec_ctl[] = { + &uni_player_iec958_ctl, + &uni_player_clk_adj_ctl, +}; + +static int uni_player_open(struct uniperif *player) +{ + /* cancel pending delayed work (suspend) */ + if (!cancel_delayed_work(&player->delayed_work)) + flush_workqueue(uni_player_wq); + + spin_lock(&player->lock); + player->rate = 0; + player->clk_adj = 0; + spin_unlock(&player->lock); + + return 0; +} + +static int uni_player_prepare(struct uniperif *player, + struct snd_pcm_runtime *runtime) +{ + int transfer_size, trigger_limit; + int ret; + + /* The player should be stopped or in standby */ + if ((player->state != UNIPERIF_STATE_STOPPED) && + (player->state != UNIPERIF_STATE_STANDBY)) { + dev_err(player->dev, "%s: invalid player state %d", __func__, + player->state); + return -EINVAL; + } + + /* Calculate transfer size (in fifo cells and bytes) for frame count */ + transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES; + + /* Calculate number of empty cells available before asserting DREQ */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size; + else + /* Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 + * FDMA_TRIGGER_LIMIT also controls when the state switches + * from OFF or STANDBY to AUDIO DATA.*/ + trigger_limit = transfer_size; + + /* Trigger limit must be an even number */ + if ((!trigger_limit % 2) || (trigger_limit != 1 && transfer_size % 2) || + (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(player))) { + dev_err(player->dev, "invalid trigger limit %d", trigger_limit); + return -EINVAL; + } + + SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit); + + /* Uniperipheral setup is depend on player type */ + switch (player->info->player_type) { + case SND_ST_UNIPERIF_PLAYER_TYPE_HDMI: + ret = uni_player_prepare_iec958(player, runtime); + break; + case SND_ST_UNIPERIF_PLAYER_TYPE_PCM: + ret = uni_player_prepare_pcm(player, runtime); + break; + case SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF: + ret = uni_player_prepare_iec958(player, runtime); + break; + default: + dev_err(player->dev, "invalid player type"); + return -EINVAL; + } + + if (ret) + return ret; + + switch (player->daifmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + case SND_SOC_DAIFMT_NB_IF: + SET_UNIPERIF_I2S_FMT_LR_POL_HIG(player); + break; + default: + SET_UNIPERIF_I2S_FMT_LR_POL_LOW(player); + } + + switch (player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player); + SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(player); + break; + case SND_SOC_DAIFMT_LEFT_J: + SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player); + SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player); + break; + case SND_SOC_DAIFMT_RIGHT_J: + SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(player); + SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player); + break; + default: + dev_err(player->dev, "format not supported"); + return -EINVAL; + } + + /* Set the interrupt mask */ + SET_UNIPERIF_ITM_BSET_DMA_ERROR(player); + SET_UNIPERIF_ITM_BSET_FIFO_ERROR(player); + + /* Enable underflow recovery interrupts */ + if (player->info->underflow_enabled) { + SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(player); + SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(player); + } + + /* Set clock rate */ + spin_lock(&player->lock); + ret = uni_player_clk_set_rate(player, + runtime->rate * player->clk_div); + if (ret) { + dev_err(player->dev, "Failed to set clock rate"); + spin_unlock(&player->lock); + return ret; + } + + player->rate = runtime->rate; + spin_unlock(&player->lock); + + if (player->state == UNIPERIF_STATE_STANDBY) { + /* We are moving from standby to started + *Synchronise transition out of standby + */ + if ((UNIPERIF_PLAYER_TYPE_IS_IEC958(player)) && + (player->stream_settings.encoding_mode == + UNIPERIF_IEC958_ENCODING_MODE_ENCODED)) + SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_ON(player); + else + SET_UNIPERIF_CTRL_EXIT_STBY_ON_EOBLOCK_OFF( + player); + return 0; + } + + SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(player, 0); + + /* Reset uniperipheral player */ + SET_UNIPERIF_SOFT_RST_SOFT_RST(player); + + return reset_player(player); +} + +static int uni_player_start(struct uniperif *player) +{ + int ret; + + /* The player should be stopped ot in standby */ + if ((player->state != UNIPERIF_STATE_STOPPED) && + (player->state != UNIPERIF_STATE_STANDBY)) { + dev_err(player->dev, "%s: invalid player state", __func__); + return -EINVAL; + } + + /* pinctrl: switch pinstate to default */ + ret = pinctrl_pm_select_default_state(player->dev); + if (ret) { + dev_err(player->dev, + "%s: failed to select default pinctrl state", + __func__); + return ret; + } + + /* Check if we are moving from standby state to started */ + if (player->state == UNIPERIF_STATE_STANDBY) { + /* Set the player to audio/pcm data mode */ + SET_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(player); + + /* Clear any pending interrupts */ + SET_UNIPERIF_ITS_BCLR(player, GET_UNIPERIF_ITS(player)); + + /* Enable player interrupts */ + enable_irq(player->irq); + + /* Update state to started and return */ + player->state = UNIPERIF_STATE_STARTED; + return 0; + } + + ret = clk_prepare_enable(player->clk); + if (ret) { + dev_err(player->dev, "%s: Failed to enable clock", __func__); + return ret; + } + + /* Clear any pending interrupts */ + SET_UNIPERIF_ITS_BCLR(player, GET_UNIPERIF_ITS(player)); + + /* Enable player interrupts */ + enable_irq(player->irq); + + /* Reset uniperipheral player */ + SET_UNIPERIF_SOFT_RST_SOFT_RST(player); + + ret = reset_player(player); + if (ret < 0) + return ret; + + /* + * Does not use IEC61937 features of the uniperipheral hardware. + * Instead it performs IEC61937 in software and inserts it directly + * into the audio data stream. As such, when encoded mode is selected, + * linear pcm mode is still used, but with the differences of the + * channel status bits set for encoded mode and the validity bits set. + */ + + SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(player); + + /* + * If iec958 formatting is required for hdmi or spdif, then it must be + * enabled after the operation mode is set. If set prior to this, it + * will not take affect and hang the player. + */ + + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player)) + SET_UNIPERIF_CTRL_SPDIF_FMT_ON(player); + + /* Force channel status update (no update if clk disable) */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + SET_UNIPERIF_CONFIG_CHL_STS_UPDATE(player); + else + SET_UNIPERIF_BIT_CONTROL_CHL_STS_UPDATE(player); + + /* Update state to started */ + player->state = UNIPERIF_STATE_STARTED; + + return 0; +} + +static int uni_player_stop(struct uniperif *player) +{ + int ret; + + /* The player should not be in stopped state */ + if (player->state == UNIPERIF_STATE_STOPPED) { + dev_err(player->dev, "%s: invalid player state", __func__); + return -EINVAL; + } + + /* Turn the player off */ + SET_UNIPERIF_CTRL_OPERATION_OFF(player); + + /* Soft reset the player */ + SET_UNIPERIF_SOFT_RST_SOFT_RST(player); + + ret = reset_player(player); + if (ret < 0) + return ret; + + if (player->state != UNIPERIF_STATE_STANDBY) { + /* Disable interrupts */ + SET_UNIPERIF_ITM_BCLR(player, GET_UNIPERIF_ITM(player)); + disable_irq_nosync(player->irq); + } + + /* Disable clock */ + clk_disable_unprepare(player->clk); + + /* Update state to stopped and return */ + player->state = UNIPERIF_STATE_STOPPED; + + /* pinctrl: switch pinstate to sleep */ + ret = pinctrl_pm_select_sleep_state(player->dev); + if (ret) { + dev_err(player->dev, + "%s: failed to select sleep pinctrl state", + __func__); + return ret; + } + + return 0; +} + +static int uni_player_standby(struct uniperif *player) +{ + /* Check if we should enable standby mode */ + if ((player->state == UNIPERIF_STATE_STARTED) && + (player->info->standby_enabled == 1)) { + /* Disable interrupts */ + SET_UNIPERIF_ITM_BCLR(player, GET_UNIPERIF_ITM(player)); + disable_irq_nosync(player->irq); + + /* Set standby mode */ + SET_UNIPERIF_CTRL_OPERATION_STANDBY(player); + + /* Update state to standby and return */ + player->state = UNIPERIF_STATE_STANDBY; + return 0; + } + + return uni_player_stop(player); +} + +static int uni_player_suspend(struct uniperif *player) +{ + /* The player should be in stopped or standby state */ + if ((player->state != UNIPERIF_STATE_STOPPED) && + (player->state != UNIPERIF_STATE_STANDBY)) { + dev_err(player->dev, "%s: invalid player state( %d)", + __func__, (int)player->state); + return -EBUSY; + } + if (player->state == UNIPERIF_STATE_STANDBY) + return uni_player_stop(player); + + return 0; +} + +static int uni_player_resume(struct uniperif *player) +{ + int ret; + + /* select the frequency synthesizer clock */ + if (player->clk_sel) { + ret = regmap_field_write(player->clk_sel, 1); + if (ret) { + dev_err(player->dev, + "%s: Failed to select freq synth clock", + __func__); + return ret; + } + } + + /* cancel pending delayed work (suspend) */ + if (!cancel_delayed_work(&player->delayed_work)) + flush_workqueue(uni_player_wq); + + SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player); + SET_UNIPERIF_CTRL_ROUNDING_OFF(player); + SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player); + SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player); + + return 0; +} + +static int uni_player_trigger(struct uniperif *player, int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + return uni_player_start(player); + case SNDRV_PCM_TRIGGER_STOP: + return uni_player_standby(player); + case SNDRV_PCM_TRIGGER_SUSPEND: + return uni_player_suspend(player); + case SNDRV_PCM_TRIGGER_RESUME: + return uni_player_resume(player); + default: + return -EINVAL; + } +} + +static void uni_player_close(struct uniperif *player) +{ + if (player->state == UNIPERIF_STATE_STANDBY) { + /* Keep uni player in standby and trigger delayed suspend */ + if (player->info->suspend_delay) + queue_delayed_work(uni_player_wq, + &player->delayed_work, + msecs_to_jiffies(player->info->suspend_delay)); + return; + } + + if (player->state != UNIPERIF_STATE_STOPPED) { + /* Stop the player */ + uni_player_stop(player); + } +} + +/* + * Platform driver routines + */ + +static int uni_player_parse_dt_clk_glue(struct platform_device *pdev, + struct uniperif *player) +{ + int bit_offset; + struct device_node *node = pdev->dev.of_node; + struct regmap *regmap; + + bit_offset = SYS_CFG_AUDI0_GLUE_PCM_CLKX + player->info->uni_num; + + regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg"); + + if (regmap) { + struct reg_field regfield = + REG_FIELD(SYS_CFG_AUDIO_GLUE, bit_offset, bit_offset); + + player->clk_sel = regmap_field_alloc(regmap, regfield); + } else { + dev_err(&pdev->dev, "sti-audio-clk-glue syscf not found\n"); + return -EINVAL; + } + + return 0; +} + +static int uni_player_parse_dt(struct platform_device *pdev, + struct device_node *pnode, + struct uniperif *player) +{ + struct uniperif_info *info; + struct device *dev = &pdev->dev; + const char *mode; + + /* Allocate memory for the info structure */ + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (!pnode) { + dev_err(dev, "%s: invalid pnode", __func__); + return -EINVAL; + } + + of_property_read_u32(pnode, "version", &info->ver); + + of_property_read_u32(pnode, "uniperiph-id", &info->uni_num); + + /* Read the device mode property */ + of_property_read_string(pnode, "mode", &mode); + + if (strcasecmp(mode, "hdmi") == 0) + info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_HDMI; + else if (strcasecmp(mode, "pcm") == 0) + info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_PCM; + else if (strcasecmp(mode, "spdif") == 0) + info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_SPDIF; + else + info->player_type = SND_ST_UNIPERIF_PLAYER_TYPE_NONE; + + /* Read the device features properties */ + of_property_read_u32(pnode, "underflow", &info->underflow_enabled); + of_property_read_u32(pnode, "standby", &info->standby_enabled); + + /* Save the info structure */ + player->info = info; + + /* get the PCM_CLK_SEL bit from audio-glue-ctrl SoC register */ + if (uni_player_parse_dt_clk_glue(pdev, player)) + return -EINVAL; + + of_property_read_u32(pnode, "auto-suspend-delay", + &info->suspend_delay); + if (info->suspend_delay) + if (info->suspend_delay < MIN_AUTOSUPEND_DELAY_MS) { + info->suspend_delay = MIN_AUTOSUPEND_DELAY_MS; + dev_info(dev, + "%s: auto-suspend-delay set to min: %d ms", + __func__, info->suspend_delay); + } + + return 0; +} + +const struct uniperif_ops uni_player_ops = { + .open = uni_player_open, + .close = uni_player_close, + .prepare = uni_player_prepare, + .trigger = uni_player_trigger +}; + +int uni_player_init(struct platform_device *pdev, struct device_node *node, + struct uniperif **uni_player, int idx) +{ + struct uniperif *player; + int ret = 0; + + player = devm_kzalloc(&pdev->dev, sizeof(*player), GFP_KERNEL); + + if (!player) + return -ENOMEM; + + player->dev = &pdev->dev; + player->state = UNIPERIF_STATE_STOPPED; + player->hw = &uni_player_pcm_hw; + player->ops = &uni_player_ops; + + ret = uni_player_parse_dt(pdev, node, player); + + if (ret < 0) { + dev_err(player->dev, "Failed to parse DeviceTree"); + return ret; + } + + /* get uniperif resource */ + player->clk = of_clk_get(pdev->dev.of_node, idx); + + if (IS_ERR(player->clk)) { + ret = (int)PTR_ERR(player->clk); + dev_err(player->dev, "%s: ERROR: clk_get failed (%d)!\n", + __func__, ret); + return -EINVAL; + } + + /* select the frequency synthesizer clock */ + if (player->clk_sel) { + ret = regmap_field_write(player->clk_sel, 1); + if (ret) { + dev_err(player->dev, + "%s: Failed to select freq synth clock", + __func__); + return ret; + } + } + + if (player->info->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) { + dev_err(player->dev, "Unknown uniperipheral version "); + return -EINVAL; + } + player->ver = player->info->ver; + + /* Check for underflow recovery being enabled */ + if (player->info->underflow_enabled) + /* Underflow recovery is only supported on later ip revisions */ + if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) { + dev_err(player->dev, + "underflow recovery not supported"); + player->info->underflow_enabled = 0; + } + + /* Get resources */ + + player->mem_region = platform_get_resource(pdev, IORESOURCE_MEM, idx); + + if (!player->mem_region) { + dev_err(&pdev->dev, "Failed to get memory resource"); + return -ENODEV; + } + + player->base = devm_ioremap_resource(&pdev->dev, + player->mem_region); + + if (!player->base) { + dev_err(&pdev->dev, "Failed to ioremap memory region"); + return -ENXIO; + } + + player->fifo_phys_address = player->mem_region->start + + UNIPERIF_FIFO_DATA_OFFSET(player); + + player->irq = platform_get_irq(pdev, idx); + + if (player->irq < 0) { + dev_err(&pdev->dev, "Failed to get IRQ resource"); + return -ENXIO; + } + + ret = devm_request_irq(&pdev->dev, player->irq, + uni_player_irq_handler, IRQF_SHARED, + dev_name(&pdev->dev), player); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request IRQ"); + return -EBUSY; + } + + /* request_irq() enables the interrupt immediately; as it is + * lethal in concurrent audio environment, we want to have + * it disabled for most of the time... + */ + disable_irq(player->irq); + + player->clk_div = DEFAULT_OVERSAMPLING; + + *uni_player = player; + + spin_lock_init(&player->lock); + + /* ensure that disabled by default */ + SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player); + SET_UNIPERIF_CTRL_ROUNDING_OFF(player); + SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(player); + SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(player); + + if (UNIPERIF_PLAYER_TYPE_IS_IEC958(player)) { + /* Set default iec958 status bits */ + + /* Consumer, PCM, copyright, 2ch, mode 0 */ + player->stream_settings.iec958.status[0] = 0x00; + /* Broadcast reception category */ + player->stream_settings.iec958.status[1] = + IEC958_AES1_CON_GENERAL; + /* Do not take into account source or channel number */ + player->stream_settings.iec958.status[2] = + IEC958_AES2_CON_SOURCE_UNSPEC; + /* Sampling frequency not indicated */ + player->stream_settings.iec958.status[3] = + IEC958_AES3_CON_FS_NOTID; + /* Max sample word 24-bit, sample word length not indicated */ + player->stream_settings.iec958.status[4] = + IEC958_AES4_CON_MAX_WORDLEN_24 | + IEC958_AES4_CON_WORDLEN_24_20; + + player->num_ctrls = ARRAY_SIZE(snd_sti_iec_ctl); + player->snd_ctrls = snd_sti_iec_ctl[0]; + } else { + player->num_ctrls = ARRAY_SIZE(snd_sti_pcm_ctl); + player->snd_ctrls = snd_sti_pcm_ctl[0]; + } + + uni_player_wq = create_workqueue("uni_player_workqueue"); + if (!uni_player_wq) + return -ENOMEM; + + INIT_DELAYED_WORK(&player->delayed_work, uni_player_work); + + return 0; +} +EXPORT_SYMBOL_GPL(uni_player_init); + +int uni_player_remove(struct platform_device *pdev) +{ + struct uniperif *player = platform_get_drvdata(pdev); + + if (player->clk) + clk_put(player->clk); + + if (uni_player_wq) + destroy_workqueue(uni_player_wq); + + return 0; +} +EXPORT_SYMBOL_GPL(uni_player_remove);
On Tue, Apr 14, 2015 at 03:35:27PM +0200, Arnaud Pouliquen wrote:
+const struct snd_pcm_hardware uni_player_pcm_hw = {
- .info = (
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE),
- .formats = (SNDRV_PCM_FMTBIT_S32_LE |
- SNDRV_PCM_FMTBIT_S16_LE),
Please use the normal kernel coding style, standard indentation and no brackets.
- .rates = SNDRV_PCM_RATE_CONTINUOUS,
- .rate_min = UNIPERIF_MIN_RATE,
- .rate_max = UNIPERIF_MAX_RATE,
- .channels_min = UNIPERIF_MIN_CHANNELS,
- .channels_max = UNIPERIF_MAX_CHANNELS,
- .periods_min = UNIPERIF_PERIODS_MIN,
- .periods_max = UNIPERIF_PERIODS_MAX,
- .period_bytes_min = UNIPERIF_PERIODS_BYTES_MIN,
- .period_bytes_max = UNIPERIF_PERIODS_BYTES_MAX,
- .buffer_bytes_max = UNIPERIF_BUFFER_BYTES_MAX
Just use the values directly, the indirection is just making it harder to find what's actually set.
+static inline int reset_player(struct uniperif *player) +{
- int count = 10;
- if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
while (GET_UNIPERIF_SOFT_RST_SOFT_RST(player) && count) {
udelay(5);
count--;
}
Braces for the if too. A cpu_relax() wouldn't go amiss in here either.
+static inline int get_property_hdl(struct device *dev, struct device_node *np,
const char *prop, int idx)
+{
This is very suspicous - why do you need this and if you need it why not add it to the generic DT code?
+static void uni_player_work(struct work_struct *work) +{
- struct uniperif *player =
container_of(work, struct uniperif, delayed_work.work);
- spin_lock(&player->lock);
- if (uni_player_suspend(player))
dev_err(player->dev, "%s: failed to suspend player", __func__);
- spin_unlock(&player->lock);
+}
What is this for, and why is there a spinlock which isn't IRQ safe? If it's sensible to do this under a spinlock it seems like it might not need to be done in a workqueue...
There's a lot of very coarse grained spinlock usage in this driver which I'm having a hard time understanding, at the very least the decisions about locking need to be documented much more clearly and I suspect that either things need to be finer grained, we should be using mutexes more or both.
+static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) +{
- irqreturn_t ret = IRQ_NONE;
- struct uniperif *player = dev_id;
- unsigned int status;
- unsigned int tmp;
- /* Get interrupt status & clear them immediately */
- preempt_disable();
- status = GET_UNIPERIF_ITS(player);
- SET_UNIPERIF_ITS_BCLR(player, status);
- preempt_enable();
preempt_disable()? Why is this being done, if you're doing unusual stuff like this the code needs to be very clear about what the locking rules are?
- if ((player->state == UNIPERIF_STATE_STANDBY) ||
(player->state == UNIPERIF_STATE_STOPPED)) {
/* unexpected IRQ: do nothing */
dev_warn(player->dev, "unexpected IRQ: status flag: %#x",
status);
return IRQ_HANDLED;
We didn't handle it, we ignored it (after acknowledging it...). I'd expect this check to be before we look at the hardware if it's needed.
- };
Extra semicolon here.
- snd_pcm_stream_lock(player->substream);
Again please explain the locking if we're doing something unusual.
- /* Check for fifo error (under run) */
- if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) {
dev_err(player->dev, "FIFO underflow error detected");
/* Interrupt is just for information when underflow recovery */
if (player->info->underflow_enabled) {
/* Update state to underflow */
player->state = UNIPERIF_STATE_UNDERFLOW;
Why would underflow recovery be optional?
- if (clk_set_rate(player->clk, rate_adjusted) < 0) {
dev_err(player->dev, "Failed to clk set rate %d !\n",
rate_adjusted);
return -EINVAL;
- }
Pass back the error code you got.
- rate_achieved = clk_get_rate(player->clk);
- if (!rate_achieved)
return -EINVAL;
That check doesn't seem right - if the clock was set to 1Hz instead of 1MHz it'll report success. Either trust that clk_set_rate() will give you an error if it fails or have some sort of reasonable check on the actual set value if that's important.
+static int snd_sti_clk_adjustment_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- struct uniperif *player = snd_kcontrol_chip(kcontrol);
- spin_lock(&player->lock);
- ucontrol->value.integer.value[0] = player->clk_adj;
- spin_unlock(&player->lock);
- return 0;
+}
There was some discussion of interfaces for fine tuning clock rates with some other drivers recently, we need to think about this in some standard fashion. Please split this out into a separate patch. In general it's best to introduce unusual/new things like this and the IEC setting in separate patches to simplify review and allow the easy bits to get merged without being held up by complicated things like this.
- of_property_read_u32(pnode, "version", &info->ver);
- of_property_read_u32(pnode, "uniperiph-id", &info->uni_num);
We can't read this stuff from the hardware?
On 04/24/2015 08:15 PM, Mark Brown wrote:
On Tue, Apr 14, 2015 at 03:35:27PM +0200, Arnaud Pouliquen wrote:
+const struct snd_pcm_hardware uni_player_pcm_hw = {
- .info = (
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE),
- .formats = (SNDRV_PCM_FMTBIT_S32_LE |
- SNDRV_PCM_FMTBIT_S16_LE),
Please use the normal kernel coding style, standard indentation and no brackets.
- .rates = SNDRV_PCM_RATE_CONTINUOUS,
- .rate_min = UNIPERIF_MIN_RATE,
- .rate_max = UNIPERIF_MAX_RATE,
- .channels_min = UNIPERIF_MIN_CHANNELS,
- .channels_max = UNIPERIF_MAX_CHANNELS,
- .periods_min = UNIPERIF_PERIODS_MIN,
- .periods_max = UNIPERIF_PERIODS_MAX,
- .period_bytes_min = UNIPERIF_PERIODS_BYTES_MIN,
- .period_bytes_max = UNIPERIF_PERIODS_BYTES_MAX,
- .buffer_bytes_max = UNIPERIF_BUFFER_BYTES_MAX
Just use the values directly, the indirection is just making it harder to find what's actually set.
+static inline int reset_player(struct uniperif *player) +{
- int count = 10;
- if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
while (GET_UNIPERIF_SOFT_RST_SOFT_RST(player) && count) {
udelay(5);
count--;
}
Braces for the if too. A cpu_relax() wouldn't go amiss in here either.
+static inline int get_property_hdl(struct device *dev, struct device_node *np,
const char *prop, int idx)
+{
This is very suspicous - why do you need this and if you need it why not add it to the generic DT code?
+static void uni_player_work(struct work_struct *work) +{
- struct uniperif *player =
container_of(work, struct uniperif, delayed_work.work);
- spin_lock(&player->lock);
- if (uni_player_suspend(player))
dev_err(player->dev, "%s: failed to suspend player", __func__);
- spin_unlock(&player->lock);
+}
What is this for, and why is there a spinlock which isn't IRQ safe? If it's sensible to do this under a spinlock it seems like it might not need to be done in a workqueue...
There's a lot of very coarse grained spinlock usage in this driver which I'm having a hard time understanding, at the very least the decisions about locking need to be documented much more clearly and I suspect that either things need to be finer grained, we should be using mutexes more or both.
This part is linked to the standby mode described in binding ( patch[1/7]) it manage a runtime suspend, because ASOC runtime suspend is dedicated to DAPM. As you recommend i will try to change it by a DAPM linked to CPU_DAI. Just need to find a wait to retrieve CPU_DAI context in DAPM.. Concerning spinlock I use it to protect context ( called by IRQ, on suspend, by user...). As some code is called in atomic i can not use mutex. I will review it and document it.
+static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) +{
- irqreturn_t ret = IRQ_NONE;
- struct uniperif *player = dev_id;
- unsigned int status;
- unsigned int tmp;
- /* Get interrupt status & clear them immediately */
- preempt_disable();
- status = GET_UNIPERIF_ITS(player);
- SET_UNIPERIF_ITS_BCLR(player, status);
- preempt_enable();
preempt_disable()? Why is this being done, if you're doing unusual stuff like this the code needs to be very clear about what the locking rules are?
This is used to be sure to not miss an interrupt. If preempted between both instruction i can clear an interruption flag before treat it. In this case i will receive a second interrupt with all flag to 0.
- if ((player->state == UNIPERIF_STATE_STANDBY) ||
(player->state == UNIPERIF_STATE_STOPPED)) {
/* unexpected IRQ: do nothing */
dev_warn(player->dev, "unexpected IRQ: status flag: %#x",
status);
return IRQ_HANDLED;
We didn't handle it, we ignored it (after acknowledging it...). I'd expect this check to be before we look at the hardware if it's needed.
- };
Extra semicolon here.
- snd_pcm_stream_lock(player->substream);
Again please explain the locking if we're doing something unusual.
This protect from a race condition, if application request a stop while we receive Error from IP. I call it here to avoid toprotect all snd_pcm_stop calls... but i can protect only the snd_pcm_stop calls if more clear
- /* Check for fifo error (under run) */
- if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) {
dev_err(player->dev, "FIFO underflow error detected");
/* Interrupt is just for information when underflow recovery */
if (player->info->underflow_enabled) {
/* Update state to underflow */
player->state = UNIPERIF_STATE_UNDERFLOW;
Why would underflow recovery be optional?
To propose 2 strategies: - stop on underrun ( because plop will occurs) - try to recover it: time is recovered when new data is available ( sample dropping)
- if (clk_set_rate(player->clk, rate_adjusted) < 0) {
dev_err(player->dev, "Failed to clk set rate %d !\n",
rate_adjusted);
return -EINVAL;
- }
Pass back the error code you got.
- rate_achieved = clk_get_rate(player->clk);
- if (!rate_achieved)
return -EINVAL;
That check doesn't seem right - if the clock was set to 1Hz instead of 1MHz it'll report success. Either trust that clk_set_rate() will give you an error if it fails or have some sort of reasonable check on the actual set value if that's important.
+static int snd_sti_clk_adjustment_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- struct uniperif *player = snd_kcontrol_chip(kcontrol);
- spin_lock(&player->lock);
- ucontrol->value.integer.value[0] = player->clk_adj;
- spin_unlock(&player->lock);
- return 0;
+}
There was some discussion of interfaces for fine tuning clock rates with some other drivers recently, we need to think about this in some standard fashion. Please split this out into a separate patch. In general it's best to introduce unusual/new things like this and the IEC setting in separate patches to simplify review and allow the easy bits to get merged without being held up by complicated things like this.
Ok i will add Clk and IEC controls in separate patches
- of_property_read_u32(pnode, "version", &info->ver);
- of_property_read_u32(pnode, "uniperiph-id", &info->uni_num);
We can't read this stuff from the hardware?
Unfortunately No...
On Mon, Apr 27, 2015 at 03:58:56PM +0200, Arnaud Pouliquen wrote:
On 04/24/2015 08:15 PM, Mark Brown wrote:
On Tue, Apr 14, 2015 at 03:35:27PM +0200, Arnaud Pouliquen wrote:
Please delete unneeded context from your mails and fix your mailer to format things normally. I'm not entirely sure what it's doing, it seems to be be that it's not leaving blanks between paragraphs and wrapping at odd and inconsistent places. It is very hard to read your messages as a result.
There's a lot of very coarse grained spinlock usage in this driver which I'm having a hard time understanding, at the very least the decisions about locking need to be documented much more clearly and I suspect that either things need to be finer grained, we should be using mutexes more or both.
This part is linked to the standby mode described in binding ( patch[1/7]) it manage a runtime suspend, because ASOC runtime suspend is dedicated to DAPM. As you recommend i will try to change it by a DAPM linked to CPU_DAI. Just need to find a wait to retrieve CPU_DAI context in DAPM.. Concerning spinlock I use it to protect context ( called by IRQ, on suspend, by user...). As some code is called in atomic i can not use mutex. I will review it and document it.
Please note my comments about these locks being very coarse grained - I suspect that the spinlock is covering too much.
- /* Get interrupt status & clear them immediately */
- preempt_disable();
- status = GET_UNIPERIF_ITS(player);
- SET_UNIPERIF_ITS_BCLR(player, status);
- preempt_enable();
preempt_disable()? Why is this being done, if you're doing unusual stuff like this the code needs to be very clear about what the locking rules are?
This is used to be sure to not miss an interrupt. If preempted between both instruction i can clear an interruption flag before treat it. In this case i will receive a second interrupt with all flag to 0.
That doesn't sound right, the interrupt appears to be write to clear so if we get a second interrupt between the read and write surely we'd not get another spurious interrupt? It would look like we were acknowleding the second raise. It sounds like something else is going on here.
In any case the spurious interrupt doesn't seem like it should happen a lot, nor like it should be especially serious if we can handle it.
- snd_pcm_stream_lock(player->substream);
Again please explain the locking if we're doing something unusual.
This protect from a race condition, if application request a stop while we receive Error from IP. I call it here to avoid toprotect all snd_pcm_stop calls... but i can protect only the snd_pcm_stop calls if more clear
Please do something to make it clear what you're doing.
- /* Check for fifo error (under run) */
- if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) {
dev_err(player->dev, "FIFO underflow error detected");
/* Interrupt is just for information when underflow recovery */
if (player->info->underflow_enabled) {
/* Update state to underflow */
player->state = UNIPERIF_STATE_UNDERFLOW;
Why would underflow recovery be optional?
To propose 2 strategies:
- stop on underrun ( because plop will occurs)
- try to recover it: time is recovered when new data is available ( sample
dropping)
Well, the standard behaviour is to halt on error (so that's what the rest of the stack will expect) and it doesn't sound like the recovery is really that great anyway.
Add code to manage Uniperipheral reader IP instances. These DAIs are dedicated to capture and support I2S and IEC mode.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@st.com --- sound/soc/sti/uniperif.h | 4 + sound/soc/sti/uniperif_reader.c | 493 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 497 insertions(+) create mode 100644 sound/soc/sti/uniperif_reader.c
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h index 02ac9a8..194f884 100644 --- a/sound/soc/sti/uniperif.h +++ b/sound/soc/sti/uniperif.h @@ -1229,4 +1229,8 @@ int uni_player_init(struct platform_device *pdev, struct device_node *node, struct uniperif **uni_player, int idx); int uni_player_remove(struct platform_device *pdev);
+/* uniperiph reader */ +int uni_reader_init(struct platform_device *pdev, struct device_node *node, + struct uniperif **uni_reader, int idx); +int uni_reader_remove(struct platform_device *pdev); #endif diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c new file mode 100644 index 0000000..3cc67d8 --- /dev/null +++ b/sound/soc/sti/uniperif_reader.c @@ -0,0 +1,493 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen arnaud.pouliquen@st.com + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include <sound/soc.h> + +#include "uniperif.h" + +const struct snd_pcm_hardware uni_reader_pcm_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE), + + .formats = (SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S16_LE), + + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = UNIPERIF_MIN_RATE, + .rate_max = UNIPERIF_MAX_RATE, + + .channels_min = UNIPERIF_MIN_CHANNELS, + .channels_max = UNIPERIF_MAX_CHANNELS, + + .periods_min = UNIPERIF_PERIODS_MIN, + .periods_max = UNIPERIF_PERIODS_MAX, + + .period_bytes_min = UNIPERIF_PERIODS_BYTES_MIN, + .period_bytes_max = UNIPERIF_PERIODS_BYTES_MAX, + .buffer_bytes_max = UNIPERIF_BUFFER_BYTES_MAX +}; + +static inline int get_property_hdl(struct device *dev, struct device_node *np, + const char *prop, int idx) +{ + int sz = 0; + const __be32 *phandle; + + phandle = of_get_property(np, prop, &sz); + + if (!phandle) { + dev_err(dev, "%s: ERROR: DT-property '%s' missing or invalid!", + __func__, prop); + return -EINVAL; + } + + if (idx >= sz) { + dev_err(dev, "%s: ERROR: Array-index (%u) >= array-size (%u)!", + __func__, idx, sz); + return -EINVAL; + } + + return be32_to_cpup(phandle + idx); +} + +/* + * Uniperipheral reader implementation + */ + +static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) +{ + irqreturn_t ret = IRQ_NONE; + struct uniperif *reader = dev_id; + unsigned int status; + + /* Get interrupt status & clear them immediately */ + preempt_disable(); + status = GET_UNIPERIF_ITS(reader); + SET_UNIPERIF_ITS_BCLR(reader, status); + preempt_enable(); + + if (reader->state == UNIPERIF_STATE_STOPPED) { + /* unexpected IRQ: do nothing */ + dev_warn(reader->dev, "unexpected IRQ: status flag: %#x", + status); + return IRQ_HANDLED; + }; + + /* Overflow? */ + if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { + dev_err(reader->dev, "FIFO error detected"); + + snd_pcm_stream_lock(reader->substream); + snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(reader->substream); + + return IRQ_HANDLED; + } + + return ret; +} + +static int uni_reader_prepare_pcm(struct uniperif *reader, + struct snd_pcm_runtime *runtime) +{ + int slot_width; + + if (!runtime) { + dev_err(reader->dev, "%s: invalid pointer(s)", __func__); + return -EINVAL; + } + + /* force slot width to 32 in I2S mode */ + if ((reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) + == SND_SOC_DAIFMT_I2S) { + slot_width = 32; + } else { + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + slot_width = 16; + break; + default: + slot_width = 32; + break; + } + } + + /* Number of bits per subframe (i.e one channel sample) on input. */ + switch (slot_width) { + case 32: + SET_UNIPERIF_I2S_FMT_NBIT_32(reader); + SET_UNIPERIF_I2S_FMT_DATA_SIZE_32(reader); + break; + case 16: + SET_UNIPERIF_I2S_FMT_NBIT_16(reader); + SET_UNIPERIF_I2S_FMT_DATA_SIZE_16(reader); + break; + default: + dev_err(reader->dev, "subframe format not supported"); + return -EINVAL; + } + + /* Configure data memory format */ + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + /* One data word contains two samples */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_16(reader); + break; + + case SNDRV_PCM_FORMAT_S32_LE: + /* + * Actually "16 bits/0 bits" means "32/28/24/20/18/16 bits + * on the MSB then zeros (if less than 32 bytes)"... + */ + SET_UNIPERIF_CONFIG_MEM_FMT_16_0(reader); + break; + + default: + dev_err(reader->dev, "format not supported"); + return -EINVAL; + } + + SET_UNIPERIF_CONFIG_MSTR_CLKEDGE_RISING(reader); + SET_UNIPERIF_CTRL_READER_OUT_SEL_IN_MEM(reader); + + /* + * Serial audio interface format - for detailed explanation + * see ie.: + * http: www.cirrus.com/en/pubs/appNote/AN282REV1.pdf + */ + + SET_UNIPERIF_I2S_FMT_ORDER_MSB(reader); + + /* Data clocking (changing) on the rising edge */ + SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(reader); + + /* Number of channels... */ + + if ((runtime->channels % 2) || (runtime->channels < 2) || + (runtime->channels > 10)) { + dev_err(reader->dev, "%s: invalid nb of channels", __func__); + return -EINVAL; + } + + SET_UNIPERIF_I2S_FMT_NUM_CH(reader, runtime->channels / 2); + + return 0; +} + +static int uni_reader_prepare(struct uniperif *reader, + struct snd_pcm_runtime *runtime) +{ + int transfer_size, trigger_limit; + int ret; + int count = 10; + + /* The reader should be stopped */ + if (reader->state != UNIPERIF_STATE_STOPPED) { + dev_err(reader->dev, "%s: invalid reader state %d", __func__, + reader->state); + return -EINVAL; + } + + /* Calculate transfer size (in fifo cells and bytes) for frame count */ + transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES; + + /* Calculate number of empty cells available before asserting DREQ */ + if (reader->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) + trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size; + else + /* + * Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0 + * FDMA_TRIGGER_LIMIT also controls when the state switches + * from OFF or STANDBY to AUDIO DATA. + */ + trigger_limit = transfer_size; + + /* Trigger limit must be an even number */ + if ((!trigger_limit % 2) || + (trigger_limit != 1 && transfer_size % 2) || + (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(reader))) { + dev_err(reader->dev, "invalid trigger limit %d", trigger_limit); + return -EINVAL; + } + + SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(reader, trigger_limit); + + ret = uni_reader_prepare_pcm(reader, runtime); + + switch (reader->daifmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + case SND_SOC_DAIFMT_NB_IF: + SET_UNIPERIF_I2S_FMT_LR_POL_HIG(reader); + break; + default: + SET_UNIPERIF_I2S_FMT_LR_POL_LOW(reader); + } + + switch (reader->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader); + SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(reader); + break; + case SND_SOC_DAIFMT_LEFT_J: + SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(reader); + SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(reader); + break; + case SND_SOC_DAIFMT_RIGHT_J: + SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(reader); + SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(reader); + break; + default: + dev_err(reader->dev, "format not supported"); + return -EINVAL; + } + + /* Clear any pending interrupts */ + SET_UNIPERIF_ITS_BCLR(reader, GET_UNIPERIF_ITS(reader)); + + SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(reader, 0); + + /* Set the interrupt mask */ + SET_UNIPERIF_ITM_BSET_DMA_ERROR(reader); + SET_UNIPERIF_ITM_BSET_FIFO_ERROR(reader); + SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(reader); + + /* Enable underflow recovery interrupts */ + if (reader->info->underflow_enabled) { + SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(reader); + SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(reader); + } + + /* Reset uniperipheral reader */ + SET_UNIPERIF_SOFT_RST_SOFT_RST(reader); + + while (GET_UNIPERIF_SOFT_RST_SOFT_RST(reader)) { + udelay(5); + count--; + } + if (!count) { + dev_err(reader->dev, "Failed to reset uniperif"); + return -EIO; + } + + return 0; +} + +static int uni_reader_start(struct uniperif *reader) +{ + /* The reader should be stopped ot in standby */ + if ((reader->state != UNIPERIF_STATE_STOPPED) && + (reader->state != UNIPERIF_STATE_STANDBY)) { + dev_err(reader->dev, "%s: invalid reader state", __func__); + return -EINVAL; + } + + /* Check if we are moving from standby state to started */ + if (reader->state == UNIPERIF_STATE_STANDBY) { + /* Set the reader to audio/pcm data mode */ + SET_UNIPERIF_CTRL_OPERATION_AUDIO_DATA(reader); + + /* Update state to started and return */ + reader->state = UNIPERIF_STATE_STARTED; + return 0; + } + + /* Enable reader interrupts (and clear possible stalled ones) */ + enable_irq(reader->irq); + SET_UNIPERIF_ITS_BCLR_FIFO_ERROR(reader); + SET_UNIPERIF_ITM_BSET_FIFO_ERROR(reader); + + /* Launch the reader */ + SET_UNIPERIF_CTRL_OPERATION_PCM_DATA(reader); + + /* Update state to started */ + reader->state = UNIPERIF_STATE_STARTED; + return 0; +} + +static int uni_reader_stop(struct uniperif *reader) +{ + /* The reader should not be in stopped state */ + if (reader->state == UNIPERIF_STATE_STOPPED) { + dev_err(reader->dev, "%s: invalid reader state", __func__); + return -EINVAL; + } + + /* Turn the reader off */ + SET_UNIPERIF_CTRL_OPERATION_OFF(reader); + + /* Disable interrupts */ + SET_UNIPERIF_ITM_BCLR(reader, GET_UNIPERIF_ITM(reader)); + disable_irq_nosync(reader->irq); + + /* Update state to stopped and return */ + reader->state = UNIPERIF_STATE_STOPPED; + + return 0; +} + +static int uni_reader_suspend(struct uniperif *reader) +{ + /* pinctrl: switch pinstate to sleep */ + return pinctrl_pm_select_sleep_state(reader->dev); +} + +static int uni_reader_resume(struct uniperif *reader) +{ + /* ensure that disable by default */ + SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(reader); + SET_UNIPERIF_CTRL_ROUNDING_OFF(reader); + SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(reader); + SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(reader); + + /* pinctrl: switch pinstate to default */ + return pinctrl_pm_select_default_state(reader->dev); +} + +static int uni_reader_trigger(struct uniperif *reader, int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + return uni_reader_start(reader); + case SNDRV_PCM_TRIGGER_STOP: + return uni_reader_stop(reader); + case SNDRV_PCM_TRIGGER_SUSPEND: + return uni_reader_suspend(reader); + case SNDRV_PCM_TRIGGER_RESUME: + return uni_reader_resume(reader); + default: + return -EINVAL; + } +} + +static void uni_reader_close(struct uniperif *reader) +{ + if (reader->state != UNIPERIF_STATE_STOPPED) { + /* Stop the reader */ + uni_reader_stop(reader); + } +} + +/* + * Platform driver routines + */ + +static int uni_reader_parse_dt(struct platform_device *pdev, + struct device_node *pnode, + struct uniperif *reader) +{ + struct uniperif_info *info; + + /* Allocate memory for the info structure */ + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (!pnode) { + dev_err(&pdev->dev, "%s: invalid pnode", __func__); + return -EINVAL; + } + of_property_read_u32(pnode, "version", &info->ver); + + /* Save the info structure */ + reader->info = info; + + return 0; +} + +const struct uniperif_ops uni_reader_ops = { + .close = uni_reader_close, + .prepare = uni_reader_prepare, + .trigger = uni_reader_trigger +}; + +int uni_reader_init(struct platform_device *pdev, struct device_node *node, + struct uniperif **uni_reader, int idx) +{ + struct uniperif *reader; + int ret = 0; + + reader = devm_kzalloc(&pdev->dev, sizeof(*reader), GFP_KERNEL); + if (!reader) + return -ENOMEM; + + reader->dev = &pdev->dev; + reader->state = UNIPERIF_STATE_STOPPED; + reader->hw = &uni_reader_pcm_hw; + reader->ops = &uni_reader_ops; + ret = uni_reader_parse_dt(pdev, node, reader); + + if (ret < 0) { + dev_err(reader->dev, "Failed to parse DeviceTree"); + return ret; + } + + /* Get resources */ + + reader->mem_region = platform_get_resource(pdev, IORESOURCE_MEM, idx); + + if (!reader->mem_region) { + dev_err(&pdev->dev, "Failed to get memory resource"); + return -ENODEV; + } + + reader->base = devm_ioremap_resource(&pdev->dev, + reader->mem_region); + + if (!reader->base) { + dev_err(&pdev->dev, "Failed to ioremap memory region"); + return -ENXIO; + } + + reader->fifo_phys_address = reader->mem_region->start + + UNIPERIF_FIFO_DATA_OFFSET(reader); + + reader->irq = platform_get_irq(pdev, idx); + + if (reader->irq < 0) { + dev_err(&pdev->dev, "Failed to get IRQ resource"); + return -ENXIO; + } + + ret = devm_request_irq(&pdev->dev, reader->irq, + uni_reader_irq_handler, IRQF_SHARED, + dev_name(&pdev->dev), reader); + + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request IRQ"); + return -EBUSY; + } + + /* + * request_irq() enables the interrupt immediately; as it is + * lethal in concurrent audio environment, we want to have + * it disabled for most of the time... + */ + disable_irq(reader->irq); + + *uni_reader = reader; + + /* ensure that disable by default */ + SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(reader); + SET_UNIPERIF_CTRL_ROUNDING_OFF(reader); + SET_UNIPERIF_CTRL_SPDIF_LAT_OFF(reader); + SET_UNIPERIF_CONFIG_IDLE_MOD_DISABLE(reader); + + return 0; +} +EXPORT_SYMBOL_GPL(uni_reader_init); + +int uni_reader_remove(struct platform_device *pdev) +{ + /* nothing to do */ + return 0; +} +EXPORT_SYMBOL_GPL(uni_reader_remove);
On Tue, Apr 14, 2015 at 03:35:28PM +0200, Arnaud Pouliquen wrote:
+const struct snd_pcm_hardware uni_reader_pcm_hw = {
- .info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE),
The commit message says this is a CPU DAI but a snd_pcm_hardware is a DMA controller.
+static inline int get_property_hdl(struct device *dev, struct device_node *np,
const char *prop, int idx)
This appears to be duplicated from the previous patch, as does a *lot* of the code here. Can we not share more of the code between playback and capture paths?
On 04/24/2015 08:20 PM, Mark Brown wrote:
On Tue, Apr 14, 2015 at 03:35:28PM +0200, Arnaud Pouliquen wrote:
+const struct snd_pcm_hardware uni_reader_pcm_hw = {
- .info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE),
The commit message says this is a CPU DAI but a snd_pcm_hardware is a DMA controller.
Do you means that i should just define a structure related to DAI constraints and fill snd_pcm_hardware in sti_platform.c?
+static inline int get_property_hdl(struct device *dev, struct device_node *np,
const char *prop, int idx)
This appears to be duplicated from the previous patch, as does a *lot* of the code here. Can we not share more of the code between playback and capture paths?
I spitted reader and player code,because it is 2 different IPs with some specific features and behavior ( clock, master/slave mode, IEC, standby ...). From my point of view is is more clear like this, but It is feasible to merge both code adding conditions on direction in most functions. Please tell me what you prefer. I case of merge i suppose that the best is to not define uniperif_ops struct but externalize functions...
On Mon, Apr 27, 2015 at 04:53:24PM +0200, Arnaud Pouliquen wrote:
On 04/24/2015 08:20 PM, Mark Brown wrote:
On Tue, Apr 14, 2015 at 03:35:28PM +0200, Arnaud Pouliquen wrote:
+const struct snd_pcm_hardware uni_reader_pcm_hw = {
- .info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE),
The commit message says this is a CPU DAI but a snd_pcm_hardware is a DMA controller.
Do you means that i should just define a structure related to DAI constraints and fill snd_pcm_hardware in sti_platform.c?
I mean that if I'm reviewing a DAI driver I don't expect to see definitions for a DMA controller without warning.
+static inline int get_property_hdl(struct device *dev, struct device_node *np,
const char *prop, int idx)
This appears to be duplicated from the previous patch, as does a *lot* of the code here. Can we not share more of the code between playback and capture paths?
I spitted reader and player code,because it is 2 different IPs with some specific features and behavior ( clock, master/slave mode, IEC, standby ...). From my point of view is is more clear like this, but It is feasible to merge both code adding conditions on direction in most functions. Please tell me what you prefer. I case of merge i suppose that the best is to not define uniperif_ops struct but externalize functions...
That's reasonable, we just shouldn't be seeing large chunks of obvious code duplication.
Asoc Platform driver that manages uniperipheral DAIs and associated PCM stream.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@st.com --- sound/soc/sti/sti_platform.c | 643 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 643 insertions(+) create mode 100644 sound/soc/sti/sti_platform.c
diff --git a/sound/soc/sti/sti_platform.c b/sound/soc/sti/sti_platform.c new file mode 100644 index 0000000..5c73dde --- /dev/null +++ b/sound/soc/sti/sti_platform.c @@ -0,0 +1,643 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen arnaud.pouliquen@st.com + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/module.h> + +#include <sound/dmaengine_pcm.h> + +#include "uniperif.h" + +/* + * Max buffering use case identified: + * 3 periods of 2048 frames @ 192kHz, 32 bits, 10 ch + */ +#define STI_PLATFORM_PERIODS_BYTES_MAX 196608 +#define STI_PLATFORM_PERIODS_MAX 3 +#define STI_PLATFORM_BUFFER_BYTES_MAX (STI_PLATFORM_PERIODS_BYTES_MAX * \ + STI_PLATFORM_PERIODS_MAX) + +struct sti_platform_dai { + int stream; + int (*init)(struct platform_device *pdev, struct device_node *node, + struct uniperif **uni, int idx); + int (*remove)(struct platform_device *pdev); + struct uniperif *uni; + struct snd_dmaengine_dai_dma_data dma_data; +}; + +struct sti_platform_dai uni_player = { + .stream = SNDRV_PCM_STREAM_PLAYBACK, + .init = uni_player_init, + .remove = uni_player_remove, +}; + +struct sti_platform_dai uni_reader = { + .stream = SNDRV_PCM_STREAM_CAPTURE, + .init = uni_reader_init, + .remove = uni_reader_remove, +}; + +struct sti_platform_data { + struct platform_device *pdev; + struct snd_soc_dai_driver *dai; + struct sti_platform_dai dai_data[]; /* dynamically allocated */ +}; + +struct sti_pcm_dma_params { + struct dma_chan *dma_channel; + dma_cookie_t dma_cookie; + struct dma_slave_config slave_config; +}; + +#define priv_to_dai_data(priv, i) ((priv)->dai_data + i) + +/* + * sti_platform_dai_create_ctrl + * This function is used to create Ctrl associated to DAI but also pcm device. + * Request is done by front end to associate ctrl with pcm device id + */ +int sti_platform_dai_create_ctrl(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->cpu_dai; + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + struct uniperif *uni = dai_data->uni; + struct snd_kcontrol_new *ctrl; + int i, ret = 0; + + for (i = 0; i < uni->num_ctrls; i++) { + ctrl = &uni->snd_ctrls[i]; + ctrl->index = rtd->pcm->device; + ctrl->device = rtd->pcm->device; + + ret = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(ctrl, uni)); + if (ret < 0) { + dev_err(dai->dev, "%s: Failed to add %s: %d\n", + __func__, ctrl->name, ret); + return ret; + } + } + + return 0; +} + +/* + * DAI + */ + +static int sti_platform_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + struct uniperif *uni = dai_data->uni; + + if (uni->ops->open) + return uni->ops->open(uni); + return 0; +} + +static void sti_platform_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + struct uniperif *uni = dai_data->uni; + + if (uni->ops->close) + uni->ops->close(uni); +} + +static int sti_platform_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + struct uniperif *uni = dai_data->uni; + + if (uni->ops->prepare) + return uni->ops->prepare(uni, substream->runtime); + + return 0; +} + +static int sti_platform_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + struct uniperif *uni = dai_data->uni; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (uni->ops->trigger) + return uni->ops->trigger(uni, SNDRV_PCM_TRIGGER_START); + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (uni->ops->trigger) + return uni->ops->trigger(uni, SNDRV_PCM_TRIGGER_STOP); + default: + return -EINVAL; + } + + return 0; +} + +static int sti_platform_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + struct snd_dmaengine_dai_dma_data *dma_data = &dai_data->dma_data; + int transfer_size; + + transfer_size = params_channels(params) * UNIPERIF_FIFO_FRAMES; + + dma_data = snd_soc_dai_get_dma_data(dai, substream); + dma_data->maxburst = transfer_size; + + return 0; +} + +static int sti_platform_dai_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + + dai_data->uni->daifmt = fmt; + return 0; +} + +static int sti_platform_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, + int div) +{ + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + + dai_data->uni->clk_div = div; + + return 0; +} + +static int sti_platform_dai_suspend(struct snd_soc_dai *dai) +{ + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + struct uniperif *uni = dai_data->uni; + + if (uni->ops->trigger) + return uni->ops->trigger(uni, SNDRV_PCM_TRIGGER_SUSPEND); + + return 0; +} + +static int sti_platform_dai_resume(struct snd_soc_dai *dai) +{ + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, dai->id); + struct uniperif *uni = dai_data->uni; + + if (uni->ops->trigger) + return uni->ops->trigger(uni, SNDRV_PCM_TRIGGER_RESUME); + + return 0; +} + +static int sti_platform_dai_probe(struct snd_soc_dai *dai) +{ + struct sti_platform_data *ptf_data = snd_soc_dai_get_drvdata(dai); + + if (ptf_data->dai_data[0].stream == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = &ptf_data->dai_data[dai->id].dma_data; + else + dai->capture_dma_data = &ptf_data->dai_data[dai->id].dma_data; + + return 0; +} + +static struct snd_soc_dai_ops sti_platform_dai_ops[] = { + { + .startup = sti_platform_dai_startup, + .shutdown = sti_platform_dai_shutdown, + .prepare = sti_platform_dai_prepare, + .trigger = sti_platform_dai_trigger, + .hw_params = sti_platform_dai_hw_params, + .set_fmt = sti_platform_dai_set_fmt, + .set_clkdiv = sti_platform_dai_set_clkdiv, + } +}; + +static const struct snd_soc_dai_driver sti_platform_dai_template = { + .probe = sti_platform_dai_probe, + .ops = sti_platform_dai_ops, + .suspend = sti_platform_dai_suspend, + .resume = sti_platform_dai_resume +}; + +static const struct snd_soc_component_driver sti_platform_dai_component = { + .name = "sti_cpu_dai", +}; + +/* PCM */ + +static void sti_dma_complete(void *arg) +{ + struct snd_pcm_substream *substream = arg; + + snd_pcm_period_elapsed(substream); +} + +static int sti_pcm_open(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sti_pcm_dma_params *params; + struct snd_pcm_runtime *runtime = substream->runtime; + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, + rtd->cpu_dai->id); + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + ret = snd_soc_set_runtime_hwparams(substream, dai_data->uni->hw); + if (ret < 0) { + dev_err(rtd->dev, "error on FE hw_constraint\n"); + return ret; + } + + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(rtd->dev, "Error: pcm hw constraints failed (%d)\n", + ret); + return ret; + } + + /* update private data */ + runtime->private_data = params; + + return ret; +} + +static int sti_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct sti_pcm_dma_params *dma_params = runtime->private_data; + int size, ret = 0; + + size = params_buffer_bytes(params); + + /* use PCM Lib */ + ret = snd_pcm_lib_malloc_pages(substream, size); + if (ret < 0) { + dev_err(rtd->dev, "Can't allocate pages!\n"); + return -ENOMEM; + } + + return snd_dmaengine_pcm_prepare_slave_config(substream, params, + &dma_params->slave_config); +} + +static int sti_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct sti_pcm_dma_params *params = runtime->private_data; + struct sti_platform_data *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct sti_platform_dai *dai_data = priv_to_dai_data(priv, + rtd->cpu_dai->id); + int ret; + char prop[5]; + + if (!params->dma_channel) { + if (dai_data->stream == SNDRV_PCM_STREAM_PLAYBACK) + snprintf(prop, sizeof(prop), "tx-%d", rtd->cpu_dai->id); + else + snprintf(prop, sizeof(prop), "rx-%d", rtd->cpu_dai->id); + + params->dma_channel = + dma_request_slave_channel(rtd->platform->dev, prop); + if (!params->dma_channel) { + dev_err(rtd->dev, "Failed to request DMA channel"); + return -ENODEV; + } + } + ret = dmaengine_slave_config(params->dma_channel, + ¶ms->slave_config); + if (ret) + dev_err(rtd->dev, "Failed to configure DMA channel"); + + return ret; +} + +static int sti_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct sti_pcm_dma_params *params = runtime->private_data; + enum dma_transfer_direction direction; + struct dma_async_tx_descriptor *desc; + unsigned long flags = DMA_CTRL_ACK; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + direction = snd_pcm_substream_to_dma_direction(substream); + + desc = dmaengine_prep_dma_cyclic( + params->dma_channel, runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), + direction, flags); + if (!desc) { + dev_err(rtd->dev, "Failed to prepare DMA descriptor"); + return -ENOMEM; + } + + /* Set the dma callback */ + desc->callback = sti_dma_complete; + desc->callback_param = substream; + + /* Submit dma descriptor */ + params->dma_cookie = dmaengine_submit(desc); + dma_async_issue_pending(params->dma_channel); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (params->dma_channel) + ret = dmaengine_terminate_all(params->dma_channel); + break; + default: + dev_err(rtd->dev, "%s: ERROR: Invalid command in pcm trigger!\n", + __func__); + return -EINVAL; + } + + return ret; +} + +static snd_pcm_uframes_t sti_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sti_pcm_dma_params *prtd = runtime->private_data; + struct dma_tx_state state; + enum dma_status status; + unsigned int buf_size; + unsigned int pos = 0; + + status = dmaengine_tx_status(prtd->dma_channel, prtd->dma_cookie, + &state); + if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { + buf_size = snd_pcm_lib_buffer_bytes(substream); + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + } + + return bytes_to_frames(substream->runtime, pos); +} + +static int sti_pcm_close(struct snd_pcm_substream *substream) +{ + struct sti_pcm_dma_params *params; + + params = substream->runtime->private_data; + if (params->dma_channel) { + dma_release_channel(params->dma_channel); + params->dma_channel = NULL; + } + kfree(params); + + return 0; +} + +int sti_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_card *card = rtd->card->snd_card; + int ret = 0; + + ret = sti_platform_dai_create_ctrl(rtd); + if (ret < 0) + return ret; + else + return snd_pcm_lib_preallocate_pages_for_all( + pcm, SNDRV_DMA_TYPE_DEV, card->dev, + STI_PLATFORM_BUFFER_BYTES_MAX, + STI_PLATFORM_BUFFER_BYTES_MAX); +} + +static struct snd_pcm_ops sti_pcm_ops = { + .open = sti_pcm_open, + .close = sti_pcm_close, + .hw_params = sti_pcm_hw_params, + .prepare = sti_pcm_prepare, + .hw_free = snd_pcm_lib_free_pages, + .trigger = sti_pcm_trigger, + .pointer = sti_pcm_pointer +}; + +static struct snd_soc_platform_driver sti_platform_platform = { + .ops = &sti_pcm_ops, + .pcm_new = sti_pcm_new, +}; + +static int sti_platform_cpu_dai_of(struct device_node *node, + struct sti_platform_data *priv, int idx) +{ + const char *str; + int ret, i; + struct device *dev = &priv->pdev->dev; + struct sti_platform_dai *dai_data = &priv->dai_data[idx]; + struct snd_soc_dai_driver *dai = &priv->dai[idx]; + struct snd_soc_pcm_stream *stream; + struct uniperif *uni; + struct { + char *name; + struct sti_platform_dai *val; + } of_fmt_table[] = { + { "uni-reader", &uni_reader }, + { "uni-player", &uni_player }, + }; + + *dai = sti_platform_dai_template; + ret = of_property_read_string(node, "dai-name", &str); + if (ret < 0) { + dev_err(dev, "%s: dai name missing.\n", __func__); + return -EINVAL; + } + dai->name = str; + + ret = of_property_read_string(node, "dai-type", &str); + if (ret == 0) { + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) + if (strcmp(str, of_fmt_table[i].name) == 0) { + *dai_data = *of_fmt_table[i].val; + ret = 0; + break; + } + } + if (ret < 0) { + dev_err(dev, "%s: invalid dai-type.\n", __func__); + return -EINVAL; + } + + /* Initialise uniperif */ + if (!dai_data->init) { + dev_err(dev, "%s: context data missing.\n", __func__); + return -EINVAL; + } + + ret = dai_data->init(priv->pdev, node, + &dai_data->uni, idx); + if (ret < 0) { + dev_err(dev, "%s: ERROR: Fail to init uniperif ( %d)!\n", + __func__, ret); + return ret; + } + + uni = dai_data->uni; + + if (priv->dai_data->stream == SNDRV_PCM_STREAM_PLAYBACK) + stream = &dai->playback; + else + stream = &dai->capture; + + stream->stream_name = dai->name; + stream->channels_min = uni->hw->channels_min; + stream->channels_max = uni->hw->channels_max; + stream->rates = uni->hw->rates; + stream->formats = uni->hw->formats; + + /* DMA settings*/ + dai_data->dma_data.addr = uni->fifo_phys_address; + dai_data->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + return 0; +} + +static int sti_platform_engine_probe(struct platform_device *pdev) +{ + /* Driver can not use snd_dmaengine_pcm_register. + * Reason is that DMA needs to load a firmware.This firmware is not + * loaded during the probe, because filesystem is not mounted. + */ + + struct sti_platform_data *priv; + struct device_node *node = pdev->dev.of_node; + int num_dais, ret; + + /* Get the number of DAI links */ + if (node && of_get_child_by_name(node, "cpu-dai")) + num_dais = of_get_child_count(node); + else + num_dais = 1; + + /* Allocate the private data and the CPU_DAI array */ + priv = devm_kzalloc(&pdev->dev, + sizeof(*priv) + + sizeof(struct sti_platform_dai) * num_dais, + GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dai = devm_kzalloc(&pdev->dev, sizeof(*priv->dai) * num_dais, + GFP_KERNEL); + if (!priv->dai) + return -ENOMEM; + + priv->pdev = pdev; + + if (of_get_child_by_name(node, "cpu-dai")) { + struct device_node *np = NULL; + int i = 0; + + for_each_child_of_node(node, np) { + ret = sti_platform_cpu_dai_of(np, priv, i); + if (ret < 0) { + of_node_put(np); + return ret; + } + i++; + } + } + + dev_set_drvdata(&pdev->dev, priv); + + ret = snd_soc_register_component(&pdev->dev, + &sti_platform_dai_component, + priv->dai, num_dais); + if (ret < 0) + return ret; + + /* Driver can not use snd_dmaengine_pcm_register. + * Reason is that DMA needs to load a firmware. + * This firmware is loaded on request_channel from file system. + * SO can not be done in probe while alsa card enumerated before + * file system is mounted + */ + + return snd_soc_register_platform(&pdev->dev, &sti_platform_platform); +} + +static int sti_platform_engine_remove(struct platform_device *pdev) +{ + struct sti_platform_data *priv = dev_get_drvdata(&pdev->dev); + struct sti_platform_dai *dai_data = priv->dai_data; + struct device_node *node = pdev->dev.of_node; + int num_dais, idx; + + /* Get the number of DAI links */ + if (node && of_get_child_by_name(node, "cpu-dai")) + num_dais = of_get_child_count(node); + else + num_dais = 1; + + for (idx = 0; idx < num_dais; idx++) { + dai_data->remove(pdev); + dai_data++; + } + + snd_soc_unregister_platform(&pdev->dev); + + return 0; +} + +static const struct of_device_id snd_soc_sti_match[] = { + { .compatible = "st,sti-audio-platform", }, + {}, +}; + +static struct platform_driver sti_platform_driver = { + .driver = { + .name = "sti-audio-platform", + .owner = THIS_MODULE, + .of_match_table = snd_soc_sti_match, + }, + .probe = sti_platform_engine_probe, + .remove = sti_platform_engine_remove +}; +module_platform_driver(sti_platform_driver); + +MODULE_DESCRIPTION("audio soc platform driver for STI platforms"); +MODULE_AUTHOR("Arnaud Pouliquen arnaud.pouliquen@st.com"); +MODULE_LICENSE("GPL");
Define the platform and codec drivers, and how to build them.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@st.com --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/sti/Kconfig | 11 +++++++++++ sound/soc/sti/Makefile | 5 +++++ 4 files changed, 18 insertions(+) create mode 100644 sound/soc/sti/Kconfig create mode 100644 sound/soc/sti/Makefile
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 3ba52da..791f2a6 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -53,6 +53,7 @@ source "sound/soc/samsung/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" source "sound/soc/spear/Kconfig" +source "sound/soc/sti/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/ux500/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 974ba70..30b2d6d 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ obj-$(CONFIG_SND_SOC) += spear/ +obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += ux500/ diff --git a/sound/soc/sti/Kconfig b/sound/soc/sti/Kconfig new file mode 100644 index 0000000..c29e219 --- /dev/null +++ b/sound/soc/sti/Kconfig @@ -0,0 +1,11 @@ +# +# STM SoC audio configuration +# +menuconfig SND_SOC_STI + tristate "SoC Audio support for STI System-On-Chip" + depends on SND_SOC + depends on ARCH_STI + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y if you want to enable ASoC-support for + any of the STI platforms (e.g. STIH416). diff --git a/sound/soc/sti/Makefile b/sound/soc/sti/Makefile new file mode 100644 index 0000000..de38cce --- /dev/null +++ b/sound/soc/sti/Makefile @@ -0,0 +1,5 @@ +# STI platform support +snd-soc-sti-objs := sti_platform.o uniperif_player.o uniperif_reader.o + +obj-$(CONFIG_SND_SOC_STI) += snd-soc-sti.o +
Codec part of the STi platform that support codec IPs. This first version does not support HDMI, but only DAC and SPDIF out.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@st.com --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/sti-sas.c | 663 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sti/Kconfig | 6 + 4 files changed, 675 insertions(+) create mode 100644 sound/soc/codecs/sti-sas.c
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 061c465..f3fe64e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -102,6 +102,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STA350 if I2C select SND_SOC_STA529 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS + select SND_SOC_STI_SAS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C select SND_SOC_TFA9879 if I2C @@ -603,6 +604,9 @@ config SND_SOC_STA529 config SND_SOC_STAC9766 tristate
+config SND_SOC_STI_SAS + tristate + config SND_SOC_TAS2552 tristate "Texas Instruments TAS2552 Mono Audio amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index abe2d7e..249ef0d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -105,6 +105,7 @@ snd-soc-sta32x-objs := sta32x.o snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o +snd-soc-sti-sas-objs := sti-sas.o snd-soc-tas5086-objs := tas5086.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o @@ -286,6 +287,7 @@ obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o +obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c new file mode 100644 index 0000000..e3448a0 --- /dev/null +++ b/sound/soc/codecs/sti-sas.c @@ -0,0 +1,663 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen arnaud.pouliquen@st.com + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/mfd/syscon.h> + +#include <sound/soc.h> + +/* chipID supported */ +#define CHIPID_STIH416 0 +#define CHIPID_STIH407 1 + +/* DAC definitions */ +/* stih416 DAC registers */ +/* sysconf 2517: Audio-DAC-Control */ +#define STIH416_AUDIO_DAC_CTRL 0x00000814 +/* sysconf 2519: Audio-Gue-Control */ +#define STIH416_AUDIO_GLUE_CTRL 0x0000081C + +/* stih407 DAC registers */ +/* sysconf 5041: Audio-Gue-Control */ +#define STIH407_AUDIO_GLUE_CTRL 0x000000A4 +/* sysconf 5042: Audio-DAC-Control */ +#define STIH407_AUDIO_DAC_CTRL 0x000000A8 + +#define STI_DAC_MIN_CHANNELS 2 +#define STI_DAC_MAX_CHANNELS 2 + +/* SPDIF definitions */ +#define STI_SPDIF_MIN_CHANNELS 2 +#define STI_SPDIF_MAX_CHANNELS 2 + +static const struct of_device_id sti_sas_codec_drv_match[]; + +enum { + STI_SAS_DAI_SPDIF_OUT, + STI_SAS_DAI_ANALOG_OUT, +}; + +enum { + BIPHASE_ENABLE, + BIPHASE_IDLE, + STI_SPDIF_MAX_RF +}; + +enum { + STIH416_DAC_MODE, + STIH416_DAC_NOT_STANDBY, + STIH416_DAC_SOFTMUTE, + STIH416_DAC_ANALOG_PWR_DW, + STIH416_DAC_ANALOG_NOT_PWR_DW_BG, + STIH416_DAC_MAX_RF +}; + +enum { + STIH407_DAC_SOFTMUTE, + STIH407_DAC_STANDBY_ANA, + STIH407_DAC_STANDBY, + STIH407_DAC_MAX_RF +}; + +struct codec_regfield { + struct regmap_field *field; + const struct reg_field regfield; +}; + +/* dac configuration fields */ +static struct codec_regfield sti_sas_dac_stih416[STIH416_DAC_MAX_RF] = { + /*STIH416_DAC_MODE*/ + {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 1, 2)}, + /*STIH416_DAC_NOT_STANDBY */ + {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 3, 3)}, + /*STIH416_DAC_SOFTMUTE*/ + {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 4, 4)}, + /*STIH416_DAC_ANALOG_PWR_DW*/ + {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 5, 5)}, + /*STIH416_DAC_ANALOG_NOT_PWR_DW_BG*/ + {NULL, REG_FIELD(STIH416_AUDIO_DAC_CTRL, 6, 6)}, +}; + +static struct codec_regfield sti_sas_dac_stih407[STIH407_DAC_MAX_RF] = { + /*STIH407_DAC_SOFTMUTE*/ + {NULL, REG_FIELD(STIH407_AUDIO_DAC_CTRL, 0, 0)}, + /*STIH407_DAC_STANDBY_ANA*/ + {NULL, REG_FIELD(STIH407_AUDIO_DAC_CTRL, 1, 1)}, + /*STIH407_DAC_STANDBY */ + {NULL, REG_FIELD(STIH407_AUDIO_DAC_CTRL, 2, 2)}, +}; + +/* SPDIF configuration fields */ +static struct codec_regfield sti_sas_spdif_stih416[STI_SPDIF_MAX_RF] = { + /*BIPHASE_ENABLE */ + {NULL, REG_FIELD(STIH416_AUDIO_GLUE_CTRL, 6, 6)}, + /*BIPHASE_IDLE*/ + {NULL, REG_FIELD(STIH416_AUDIO_GLUE_CTRL, 7, 7)}, +}; + +static struct codec_regfield sti_sas_spdif_stih407[STI_SPDIF_MAX_RF] = { + /*BIPHASE_ENABLE */ + {NULL, REG_FIELD(STIH407_AUDIO_GLUE_CTRL, 6, 6)}, + /*BIPHASE_IDLE*/ + {NULL, REG_FIELD(STIH407_AUDIO_GLUE_CTRL, 7, 7)}, +}; + +/* specify ops depends on codec version */ +struct sti_codec_ops { + int (*dai_dac_probe)(struct snd_soc_dai *dai); + int (*mute)(struct snd_soc_dai *dai, int mute); + int (*startup)(struct snd_soc_dai *); + void (*shutdown)(struct snd_soc_dai *); +}; + +struct sti_dac_audio { + const struct sti_codec_ops *ops; + struct regmap *regmap; + struct codec_regfield *regfield; + struct reset_control *rst; +}; + +struct sti_spdif_audio { + struct regmap *regmap; + struct codec_regfield *regfield; + struct reset_control *rst; +}; + +int sti_sas_dai_clk_div[]; + +struct sti_sas_codec_data { + const int chipid; + struct device *dev; + struct sti_dac_audio dac; + struct sti_spdif_audio spdif; +}; + +static void sti_sas_map_sas_registers(struct sti_sas_codec_data *data) +{ + struct sti_spdif_audio *spdif = &data->spdif; + struct sti_dac_audio *dac = &data->dac; + int i, max_rf; + + /* Get spdif regmap fields */ + for (i = 0; i < STI_SPDIF_MAX_RF; i++) + spdif->regfield[i].field = + regmap_field_alloc(spdif->regmap, + spdif->regfield[i].regfield); + /* Get DAC regmap fields */ + if (data->chipid == CHIPID_STIH407) + max_rf = STIH407_DAC_MAX_RF; + else + max_rf = STIH416_DAC_MAX_RF; + for (i = 0; i < max_rf; i++) + dac->regfield[i].field = + regmap_field_alloc(dac->regmap, + dac->regfield[i].regfield); +} + +static int sti_sas_init_sas_registers(struct device *dev, + struct sti_sas_codec_data *data) +{ + struct sti_spdif_audio *spdif = &data->spdif; + struct sti_dac_audio *dac = &data->dac; + int ret; + /* + * DAC and SPDIF are activated by default + * put them in IDLE to save power + */ + + /* Initialise bi-phase formatter to disabled */ + ret = regmap_field_write(spdif->regfield[BIPHASE_ENABLE].field, 0); + + /* Initialise bi-phase formatter idle value to 0 */ + ret |= regmap_field_write(spdif->regfield[BIPHASE_IDLE].field, 0); + if (ret < 0) { + dev_err(dev, "Failed to update SPDIF registers"); + return ret; + } + + /* init DAC configuration */ + if (data->chipid == CHIPID_STIH407) { + /* init configuration */ + ret = regmap_field_write( + dac->regfield[STIH407_DAC_STANDBY].field, 1); + ret |= regmap_field_write( + dac->regfield[STIH407_DAC_STANDBY_ANA].field, 1); + ret |= regmap_field_write( + dac->regfield[STIH407_DAC_SOFTMUTE].field, 1); + } else if (data->chipid == CHIPID_STIH416) { + ret = regmap_field_write( + dac->regfield[STIH416_DAC_NOT_STANDBY].field, 0); + ret |= regmap_field_write( + dac->regfield[STIH416_DAC_ANALOG_PWR_DW].field, 0); + ret |= regmap_field_write( + dac->regfield[STIH416_DAC_ANALOG_NOT_PWR_DW_BG].field, + 0); + } + + if (ret < 0) { + dev_err(dev, "Failed to update DAC registers"); + return ret; + } + + return ret; +} + +/* + * DAC + */ +static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /* Sanity check only */ + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(dai->codec->dev, + "%s: ERROR: Unsupporter master mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + return 0; +} + +static int stih416_dac_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + + /* get reset control */ + dac->rst = devm_reset_control_get(codec->dev, "dac_rst"); + if (IS_ERR(dac->rst)) { + dev_err(dai->codec->dev, + "%s: ERROR: DAC reset not declared in DT (%d)!\n", + __func__, (int)dac->rst); + return -EFAULT; + } + /* put the DAC into reset */ + reset_control_assert(dac->rst); + + return 0; +} + +int stih416_sas_dac_startup(struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + int ret; + + /* mute */ + ret = regmap_field_write( + dac->regfield[STIH416_DAC_SOFTMUTE].field, 1); + + /* Enable analog */ + ret |= regmap_field_write( + dac->regfield[STIH416_DAC_ANALOG_NOT_PWR_DW_BG].field, 1); + ret |= regmap_field_write( + dac->regfield[STIH416_DAC_ANALOG_PWR_DW].field, 1); + + /* Disable standby */ + ret |= regmap_field_write( + dac->regfield[STIH416_DAC_NOT_STANDBY].field, 1); + + /* Take the DAC out of reset */ + reset_control_deassert(dac->rst); + + return ret; +} + +int stih407_sas_dac_startup(struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + int ret; + + /* mute */ + ret = regmap_field_write( + dac->regfield[STIH407_DAC_SOFTMUTE].field, 1); + + /* Enable analog */ + ret |= regmap_field_write( + dac->regfield[STIH407_DAC_STANDBY_ANA].field, 0); + + /* Disable standby */ + ret |= regmap_field_write( + dac->regfield[STIH407_DAC_STANDBY].field, 0); + + return ret; +} + +int sti_sas_dac_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + + if (dac->ops->startup) + return dac->ops->startup(dai); + + return 0; +} + +void stih416_sas_dac_shutdown(struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + int ret; + + /* put the DAC into reset */ + reset_control_assert(dac->rst); + + /* Enable standby */ + ret = regmap_field_write( + dac->regfield[STIH416_DAC_NOT_STANDBY].field, 0); + + /* Disable analog */ + ret |= regmap_field_write( + dac->regfield[STIH416_DAC_ANALOG_PWR_DW].field, 0); + ret |= regmap_field_write( + dac->regfield[STIH416_DAC_ANALOG_NOT_PWR_DW_BG].field, 0); + if (ret) + dev_err(codec->dev, "error while updating DAC registers\n"); +} + +static void stih407_sas_dac_shutdown(struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + int ret; + + /* Enable standby */ + ret = regmap_field_write( + dac->regfield[STIH407_DAC_STANDBY].field, 1); + + /* Disable analog */ + ret |= regmap_field_write( + dac->regfield[STIH407_DAC_STANDBY_ANA].field, 1); + if (ret) + dev_err(codec->dev, "error while updating DAC registers\n"); +} + +static void sti_sas_dac_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + + if (dac->ops->shutdown) + dac->ops->shutdown(dai); +} + +static int stih416_sas_dac_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + + if (mute) + return regmap_field_write( + dac->regfield[STIH416_DAC_SOFTMUTE].field, 1); + else + return regmap_field_write( + dac->regfield[STIH416_DAC_SOFTMUTE].field, 0); +} + +static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + + if (mute) + return regmap_field_write( + dac->regfield[STIH407_DAC_SOFTMUTE].field, 1); + else + return regmap_field_write( + dac->regfield[STIH407_DAC_SOFTMUTE].field, 0); +} + +int sti_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_dac_audio *dac = &drvdata->dac; + + if (dac->ops->mute) + return dac->ops->mute(dai, mute); + + return 0; +} + +struct sti_codec_ops stih416_ops = { + .dai_dac_probe = stih416_dac_probe, + .mute = stih416_sas_dac_mute, + .startup = stih416_sas_dac_startup, + .shutdown = stih416_sas_dac_shutdown, +}; + +struct sti_codec_ops stih407_ops = { + .mute = stih407_sas_dac_mute, + .startup = stih407_sas_dac_startup, + .shutdown = stih407_sas_dac_shutdown, +}; + +/* + * SPDIF + */ +static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(dai->codec->dev, + "%s: ERROR: Unsupporter master mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + return 0; +} + +/* + * sti_sas_spdif_trigger + * Trigger function is used to ensure that BiPhase Formater is disabled + * before CPU dai is stopped. + * This is mandatory to avoid that BPF is stalled + */ +static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + struct sti_spdif_audio *spdif = &drvdata->spdif; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + return regmap_field_write(spdif->regfield[BIPHASE_ENABLE].field, + 1); + break; + + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + return regmap_field_write(spdif->regfield[BIPHASE_ENABLE].field, + 0); + break; + default: + return -EINVAL; + } +} + +/* + * CODEC DAIS + */ + +/* + * sti_sas_hw_params + * Request MCLK-FS clocks division to CPU_DAI based on requested rate + */ +static int sti_sas_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret, div; + + div = sti_sas_dai_clk_div[dai->id]; + ret = snd_soc_dai_set_clkdiv(cpu_dai, SND_SOC_CLOCK_OUT, div); + if (ret) + dev_warn(codec->dev, "WARN: CPU DAI not support sysclk div"); + + return 0; +} + +static struct snd_soc_dai_driver sti_sas_codec_dai[] = { + { + .name = "sas-dai-spdif-out", + .id = STI_SAS_DAI_SPDIF_OUT, + .playback = { + .stream_name = "spdif_p", + .channels_min = STI_SPDIF_MIN_CHANNELS, + .channels_max = STI_SPDIF_MAX_CHANNELS, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = (struct snd_soc_dai_ops[]) { + { + .set_fmt = sti_sas_spdif_set_fmt, + .trigger = sti_sas_spdif_trigger, + .hw_params = sti_sas_hw_params, + } + }, + }, + { + .name = "sas-dai-dac", + .id = STI_SAS_DAI_ANALOG_OUT, + .playback = { + .stream_name = "dac_p", + .channels_min = STI_DAC_MIN_CHANNELS, + .channels_max = STI_DAC_MAX_CHANNELS, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = (struct snd_soc_dai_ops[]) { + { + .set_fmt = sti_sas_dac_set_fmt, + .startup = sti_sas_dac_startup, + .shutdown = sti_sas_dac_shutdown, + .mute_stream = sti_sas_dac_mute, + .hw_params = sti_sas_hw_params, + } + }, + }, +}; + +int sti_sas_dai_clk_div[ARRAY_SIZE(sti_sas_codec_dai)] = { + 128, /* spdif out */ + 256, /* dac */ +}; + +#ifdef CONFIG_PM_SLEEP +static int sti_sas_codec_suspend(struct snd_soc_codec *codec) +{ + /* Nothing done but need to be declared for PM management*/ + return 0; +} + +static int sti_sas_codec_resume(struct snd_soc_codec *codec) +{ + struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev); + + return sti_sas_init_sas_registers(codec->dev, drvdata); +} +#else +#define sti_sas_codec_suspend NULL +#define sti_sas_codec_resume NULL +#endif + +static struct snd_soc_codec_driver sti_sas_codec_driver = { + .suspend = sti_sas_codec_suspend, + .resume = sti_sas_codec_resume, +}; + +static int sti_sas_codec_driver_probe(struct platform_device *pdev) +{ + struct device_node *pnode = pdev->dev.of_node; + int status = 0; + struct sti_sas_codec_data *drvdata; + + /* Allocate device structure */ + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_codec_data), + GFP_KERNEL); + + if (!drvdata) { + dev_err(&pdev->dev, "Failed to allocate device structure"); + return -ENOMEM; + } + /* Populate data structure depending on compatibility */ + if (!of_match_node(sti_sas_codec_drv_match, pnode)->data) { + dev_err(&pdev->dev, "data associated to device is missing"); + return -EINVAL; + } + + memcpy(drvdata, of_match_node(sti_sas_codec_drv_match, pnode)->data, + sizeof(struct sti_sas_codec_data)); + + /* Initialise device structure */ + drvdata->dev = &pdev->dev; + + /* Request the DAC & SPDIF registers memory region */ + drvdata->dac.regmap = + syscon_regmap_lookup_by_phandle(pnode, "st,syscfg"); + if (!drvdata->dac.regmap) { + dev_err(&pdev->dev, "audio registers not enabled\n"); + return -EFAULT; + } + drvdata->spdif.regmap = drvdata->dac.regmap; + + sti_sas_map_sas_registers(drvdata); + + status = sti_sas_init_sas_registers(&pdev->dev, drvdata); + if (status < 0) + return status; + + /*Set DAC dai probe */ + sti_sas_codec_dai[STI_SAS_DAI_ANALOG_OUT].probe = + drvdata->dac.ops->dai_dac_probe; + + /*Store context */ + dev_set_drvdata(&pdev->dev, drvdata); + + status = snd_soc_register_codec(&pdev->dev, &sti_sas_codec_driver, + sti_sas_codec_dai, + ARRAY_SIZE(sti_sas_codec_dai)); + + return 0; +} + +static int sti_sas_codec_driver_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +const struct sti_sas_codec_data stih416_data = { + .chipid = CHIPID_STIH416, + .dac.ops = &stih416_ops, + .dac.regfield = sti_sas_dac_stih416, + .spdif.regfield = sti_sas_spdif_stih416, +}; + +const struct sti_sas_codec_data stih407_data = { + .chipid = CHIPID_STIH407, + .dac.ops = &stih407_ops, + .dac.regfield = sti_sas_dac_stih407, + .spdif.regfield = sti_sas_spdif_stih407, +}; + +static const struct of_device_id sti_sas_codec_drv_match[] = { + { + .compatible = "st,stih416-sas-codec", + .data = &stih416_data, + }, + { + .compatible = "st,stih407-sas-codec", + .data = &stih407_data, + }, + {}, +}; + +static struct platform_driver sti_sas_codec_platform_driver = { + .driver = { + .name = "sti-sas-codec", + .owner = THIS_MODULE, + .of_match_table = sti_sas_codec_drv_match, + }, + .probe = sti_sas_codec_driver_probe, + .remove = sti_sas_codec_driver_remove, +}; + +module_platform_driver(sti_sas_codec_platform_driver); + +MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms"); +MODULE_AUTHOR("Arnaud.pouliquen@st.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/sti/Kconfig b/sound/soc/sti/Kconfig index c29e219..af5d6fd 100644 --- a/sound/soc/sti/Kconfig +++ b/sound/soc/sti/Kconfig @@ -9,3 +9,9 @@ menuconfig SND_SOC_STI help Say Y if you want to enable ASoC-support for any of the STI platforms (e.g. STIH416). + +config SND_SOC_STI_SAS + tristate "codec Audio support for STI SAS codec" + depends on SND_SOC_STI + help + Say Y if you want to include STI SAS audio codec support \ No newline at end of file
On Tue, Apr 14, 2015 at 03:35:31PM +0200, Arnaud Pouliquen wrote:
+struct codec_regfield {
- struct regmap_field *field;
- const struct reg_field regfield;
+};
The fact that you're defining this structure should be settinng off alarm bells. You shouldn't be storing the per device dynamically allocated regmap_feild in global static data, you should be storing it in the driver data for your device.
+/* specify ops depends on codec version */ +struct sti_codec_ops {
- int (*dai_dac_probe)(struct snd_soc_dai *dai);
- int (*mute)(struct snd_soc_dai *dai, int mute);
- int (*startup)(struct snd_soc_dai *);
- void (*shutdown)(struct snd_soc_dai *);
+};
Don't define an abstraction layer within your driver wrapping the core abstraction layer, this is just making things harder to follow. Just register different ops with the core if you need to.
- /* init DAC configuration */
- if (data->chipid == CHIPID_STIH407) {
/* init configuration */
ret = regmap_field_write(
dac->regfield[STIH407_DAC_STANDBY].field, 1);
ret |= regmap_field_write(
dac->regfield[STIH407_DAC_STANDBY_ANA].field, 1);
ret |= regmap_field_write(
dac->regfield[STIH407_DAC_SOFTMUTE].field, 1);
Don't or multiple return values together, check and return errors properly. That way the error value isn't corrupted if you get different errors.
- } else if (data->chipid == CHIPID_STIH416) {
Use switch statements to select among multiple values.
- dac->rst = devm_reset_control_get(codec->dev, "dac_rst");
- if (IS_ERR(dac->rst)) {
dev_err(dai->codec->dev,
"%s: ERROR: DAC reset not declared in DT (%d)!\n",
__func__, (int)dac->rst);
That error message might be wrong, it could be some other error.
return -EFAULT;
Don't ignore the error that was returned, use PTR_ERR() - this is broken for probe deferral.
+int stih407_sas_dac_startup(struct snd_soc_dai *dai) +{
- struct snd_soc_codec *codec = dai->codec;
- struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
- struct sti_dac_audio *dac = &drvdata->dac;
- int ret;
- /* mute */
- ret = regmap_field_write(
dac->regfield[STIH407_DAC_SOFTMUTE].field, 1);
- /* Enable analog */
- ret |= regmap_field_write(
dac->regfield[STIH407_DAC_STANDBY_ANA].field, 0);
- /* Disable standby */
- ret |= regmap_field_write(
dac->regfield[STIH407_DAC_STANDBY].field, 0);
This looks like at least standby and analogue should be moved to DAPM, I'm not clear why the mute is being controlled here...
+static int sti_sas_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
+{
- struct snd_soc_codec *codec = dai->codec;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret, div;
- div = sti_sas_dai_clk_div[dai->id];
- ret = snd_soc_dai_set_clkdiv(cpu_dai, SND_SOC_CLOCK_OUT, div);
- if (ret)
set_clkdiv() is an external API, you shouldn't be calling it within the driver - probably you should just not implement it and inline the functionality here.
+static int sti_sas_codec_suspend(struct snd_soc_codec *codec) +{
- /* Nothing done but need to be declared for PM management*/
- return 0;
+}
Remove empty functions, the comment is not accurate.
- /* Allocate device structure */
- drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_codec_data),
GFP_KERNEL);
- if (!drvdata) {
dev_err(&pdev->dev, "Failed to allocate device structure");
The memory allocators are already very noisy if they fail.
On 04/18/2015 03:09 PM, Mark Brown wrote:
On Tue, Apr 14, 2015 at 03:35:31PM +0200, Arnaud Pouliquen wrote:
+struct codec_regfield {
- struct regmap_field *field;
- const struct reg_field regfield;
+};
The fact that you're defining this structure should be settinng off alarm bells. You shouldn't be storing the per device dynamically allocated regmap_feild in global static data, you should be storing it in the driver data for your device.
Not clear for me. Register definitions are already part of the driver data structure (stih407_data or stih416_data) . Your point is that i should delete this structure? And then use separate regmap_field tables (dynamically allocated) for for register map and declare registers struct like this static struct reg_field sti_sas_dac_stih416[STIH416_DAC_MAX_RF] = { /*STIH416_DAC_MODE*/ {REG_FIELD(STIH416_AUDIO_DAC_CTRL, 1, 2)}, ... }
+/* specify ops depends on codec version */ +struct sti_codec_ops {
- int (*dai_dac_probe)(struct snd_soc_dai *dai);
- int (*mute)(struct snd_soc_dai *dai, int mute);
- int (*startup)(struct snd_soc_dai *);
- void (*shutdown)(struct snd_soc_dai *);
+};
Don't define an abstraction layer within your driver wrapping the core abstraction layer, this is just making things harder to follow. Just register different ops with the core if you need to.
- /* init DAC configuration */
- if (data->chipid == CHIPID_STIH407) {
/* init configuration */
ret = regmap_field_write(
dac->regfield[STIH407_DAC_STANDBY].field, 1);
ret |= regmap_field_write(
dac->regfield[STIH407_DAC_STANDBY_ANA].field, 1);
ret |= regmap_field_write(
dac->regfield[STIH407_DAC_SOFTMUTE].field, 1);
Don't or multiple return values together, check and return errors properly. That way the error value isn't corrupted if you get different errors.
- } else if (data->chipid == CHIPID_STIH416) {
Use switch statements to select among multiple values.
- dac->rst = devm_reset_control_get(codec->dev, "dac_rst");
- if (IS_ERR(dac->rst)) {
dev_err(dai->codec->dev,
"%s: ERROR: DAC reset not declared in DT (%d)!\n",
__func__, (int)dac->rst);
That error message might be wrong, it could be some other error.
return -EFAULT;
Don't ignore the error that was returned, use PTR_ERR() - this is broken for probe deferral.
+int stih407_sas_dac_startup(struct snd_soc_dai *dai) +{
- struct snd_soc_codec *codec = dai->codec;
- struct sti_sas_codec_data *drvdata = dev_get_drvdata(codec->dev);
- struct sti_dac_audio *dac = &drvdata->dac;
- int ret;
- /* mute */
- ret = regmap_field_write(
dac->regfield[STIH407_DAC_SOFTMUTE].field, 1);
- /* Enable analog */
- ret |= regmap_field_write(
dac->regfield[STIH407_DAC_STANDBY_ANA].field, 0);
- /* Disable standby */
- ret |= regmap_field_write(
dac->regfield[STIH407_DAC_STANDBY].field, 0);
This looks like at least standby and analogue should be moved to DAPM, I'm not clear why the mute is being controlled here...
+static int sti_sas_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
+{
- struct snd_soc_codec *codec = dai->codec;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret, div;
- div = sti_sas_dai_clk_div[dai->id];
- ret = snd_soc_dai_set_clkdiv(cpu_dai, SND_SOC_CLOCK_OUT, div);
- if (ret)
set_clkdiv() is an external API, you shouldn't be calling it within the driver - probably you should just not implement it and inline the functionality here.
how to do it properly? My point is that cpu codecs have a constraint on MCLK_FS division. So i need to provide it to the CPU_DAI that generates MCLK. I checked simple driver priv->mclk_fs exists but is only used to set codec dai not CPU dai... should i add call in simple card?
+static int sti_sas_codec_suspend(struct snd_soc_codec *codec) +{
- /* Nothing done but need to be declared for PM management*/
- return 0;
+}
Remove empty functions, the comment is not accurate.
- /* Allocate device structure */
- drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_codec_data),
GFP_KERNEL);
- if (!drvdata) {
dev_err(&pdev->dev, "Failed to allocate device structure");
The memory allocators are already very noisy if they fail.
On Mon, Apr 20, 2015 at 11:13:27AM +0200, Arnaud Pouliquen wrote:
On 04/18/2015 03:09 PM, Mark Brown wrote:
Register definitions are already part of the driver data structure (stih407_data or stih416_data) . Your point is that i should delete this structure? And then use separate regmap_field tables (dynamically allocated) for for register map and declare registers struct like this static struct reg_field sti_sas_dac_stih416[STIH416_DAC_MAX_RF] = { /*STIH416_DAC_MODE*/ {REG_FIELD(STIH416_AUDIO_DAC_CTRL, 1, 2)}, ... }
Yes, basically.
+static int sti_sas_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
+{
- struct snd_soc_codec *codec = dai->codec;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret, div;
- div = sti_sas_dai_clk_div[dai->id];
- ret = snd_soc_dai_set_clkdiv(cpu_dai, SND_SOC_CLOCK_OUT, div);
- if (ret)
set_clkdiv() is an external API, you shouldn't be calling it within the driver - probably you should just not implement it and inline the functionality here.
how to do it properly?
Like I say probably you should just inline the functionality here. It's not like anything else can usefully call set_clkdiv() if the driver is just going to do it itself anyawy.
My point is that cpu codecs have a constraint on MCLK_FS division. So i need to provide it to the CPU_DAI that generates MCLK. I checked simple driver priv->mclk_fs exists but is only used to set codec dai not CPU dai... should i add call in simple card?
Possibly. In general it is better to add missing functionality to generic code rather than bodge around the generic code. However in this case I expect that any user of the driver (not just simple card) may need the same configuration. If that is the case then the driver should just always do the configuration without needing the machine driver to do anything so this just doesn't need to be visible outside the driver.
participants (2)
-
Arnaud Pouliquen
-
Mark Brown