diff options
68 files changed, 1785 insertions, 394 deletions
diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml index d370c98a62c7..e847611a85f7 100644 --- a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml @@ -11,8 +11,11 @@ maintainers: description: | fsl_rpmsg is a virtual audio device. Mapping to real hardware devices - are SAI, DMA controlled by Cortex M core. What we see from Linux - side is a device which provides audio service by rpmsg channel. + are SAI, MICFIL, DMA controlled by Cortex M core. What we see from + Linux side is a device which provides audio service by rpmsg channel. + We can create different sound cards which access different hardwares + such as SAI, MICFIL, .etc through building rpmsg channels between + Cortex-A and Cortex-M. properties: compatible: @@ -85,6 +88,16 @@ properties: This is a boolean property. If present, the receiving function will be enabled. + fsl,rpmsg-channel-name: + $ref: /schemas/types.yaml#/definitions/string + description: | + A string property to assign rpmsg channel this sound card sits on. + This property can be omitted if there is only one sound card and it sits + on "rpmsg-audio-channel". + enum: + - rpmsg-audio-channel + - rpmsg-micfil-channel + required: - compatible - model @@ -107,3 +120,22 @@ examples: <&clk IMX8MN_AUDIO_PLL2_OUT>; clock-names = "ipg", "mclk", "dma", "pll8k", "pll11k"; }; + + - | + #include <dt-bindings/clock/imx8mm-clock.h> + + rpmsg_micfil: audio-controller { + compatible = "fsl,imx8mm-rpmsg-audio"; + model = "micfil-audio"; + fsl,rpmsg-channel-name = "rpmsg-micfil-channel"; + fsl,enable-lpa; + fsl,rpmsg-in; + clocks = <&clk IMX8MM_CLK_PDM_IPG>, + <&clk IMX8MM_CLK_PDM_ROOT>, + <&clk IMX8MM_CLK_SDMA3_ROOT>, + <&clk IMX8MM_AUDIO_PLL1_OUT>, + <&clk IMX8MM_AUDIO_PLL2_OUT>; + clock-names = "ipg", "mclk", "dma", "pll8k", "pll11k"; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/mvebu-audio.txt b/Documentation/devicetree/bindings/sound/mvebu-audio.txt index cb8c07c81ce4..4f5dec5cb3c2 100644 --- a/Documentation/devicetree/bindings/sound/mvebu-audio.txt +++ b/Documentation/devicetree/bindings/sound/mvebu-audio.txt @@ -6,9 +6,14 @@ Required properties: "marvell,kirkwood-audio" for Kirkwood platforms "marvell,dove-audio" for Dove platforms "marvell,armada370-audio" for Armada 370 platforms + "marvell,armada-380-audio" for Armada 38x platforms - reg: physical base address of the controller and length of memory mapped - region. + region (named "i2s_regs"). + With "marvell,armada-380-audio" two other regions are required: + first of those is dedicated for Audio PLL Configuration registers + (named "pll_regs") and the second one ("soc_ctrl") - for register + where one of exceptive I/O types (I2S or S/PDIF) is set. - interrupts: with "marvell,kirkwood-audio", the audio interrupt @@ -23,6 +28,13 @@ Required properties: "internal" for the internal clock "extclk" for the external clock +Optional properties: + +- spdif-mode: + Enable S/PDIF mode on Armada 38x SoC. Using this property + disables standard I2S I/O. Valid only with "marvell,armada-380-audio" + compatible string. + Example: i2s1: audio-controller@b4000 { diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8961.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8961.yaml new file mode 100644 index 000000000000..795d34e1e97a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8961.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/wlf,wm8961.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wolfson WM8961 Ultra-Low Power Stereo CODEC + +maintainers: + - patches@opensource.cirrus.com + +properties: + compatible: + const: wlf,wm8961 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - reg + - '#sound-dai-cells' + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + wm8961: codec@4a { + compatible = "wlf,wm8961"; + reg = <0x4a>; + #sound-dai-cells = <0>; + }; + }; diff --git a/include/sound/acp62_chip_offset_byte.h b/include/sound/acp62_chip_offset_byte.h index f03992f81168..ca38f8a0966e 100644 --- a/include/sound/acp62_chip_offset_byte.h +++ b/include/sound/acp62_chip_offset_byte.h @@ -131,6 +131,23 @@ #define ACP_I2S_WAKE_EN 0x000145C #define ACP_SW1_WAKE_EN 0x0001460 +#define ACP_SW_I2S_ERROR_REASON 0x00018B4 +#define ACP_SW_POS_TRACK_I2S_TX_CTRL 0x00018B8 +#define ACP_SW_I2S_TX_DMA_POS 0x00018BC +#define ACP_SW_POS_TRACK_BT_TX_CTRL 0x00018C0 +#define ACP_SW_BT_TX_DMA_POS 0x00018C4 +#define ACP_SW_POS_TRACK_HS_TX_CTRL 0x00018C8 +#define ACP_SW_HS_TX_DMA_POS 0x00018CC +#define ACP_SW_POS_TRACK_I2S_RX_CTRL 0x00018D0 +#define ACP_SW_I2S_RX_DMA_POS 0x00018D4 +#define ACP_SW_POS_TRACK_BT_RX_CTRL 0x00018D8 +#define ACP_SW_BT_RX_DMA_POS 0x00018DC +#define ACP_SW_POS_TRACK_HS_RX_CTRL 0x00018E0 +#define ACP_SW_HS_RX_DMA_POS 0x00018E4 +#define ACP_ERROR_INTR_MASK1 0X0001974 +#define ACP_ERROR_INTR_MASK2 0X0001978 +#define ACP_ERROR_INTR_MASK3 0X000197C + /* Registers from ACP_P1_MISC block */ #define ACP_EXTERNAL_INTR_ENB 0x0001A00 #define ACP_EXTERNAL_INTR_CNTL 0x0001A04 @@ -154,6 +171,8 @@ #define ACP_P1_SW_BT_RX_DMA_POS 0x0001A9C #define ACP_P1_SW_POS_TRACK_HS_RX_CTRL 0x0001AA0 #define ACP_P1_SW_HS_RX_DMA_POS 0x0001AA4 +#define ACP_ERROR_INTR_MASK4 0X0001AEC +#define ACP_ERROR_INTR_MASK5 0X0001AF0 /* Registers from ACP_AUDIO_BUFFERS block */ #define ACP_I2S_RX_RINGBUFADDR 0x0002000 @@ -210,6 +229,24 @@ #define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x00020CC #define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x00020D0 #define ACP_HS_TX_INTR_WATERMARK_SIZE 0x00020D4 +#define ACP_AUDIO_RX_RINGBUFADDR ACP_I2S_RX_RINGBUFADDR +#define ACP_AUDIO_RX_RINGBUFSIZE ACP_I2S_RX_RINGBUFSIZE +#define ACP_AUDIO_RX_LINKPOSITIONCNTR ACP_I2S_RX_LINKPOSITIONCNTR +#define ACP_AUDIO_RX_FIFOADDR ACP_I2S_RX_FIFOADDR +#define ACP_AUDIO_RX_FIFOSIZE ACP_I2S_RX_FIFOSIZE +#define ACP_AUDIO_RX_DMA_SIZE ACP_I2S_RX_DMA_SIZE +#define ACP_AUDIO_RX_LINEARPOSITIONCNTR_HIGH ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH +#define ACP_AUDIO_RX_LINEARPOSITIONCNTR_LOW ACP_I2S_RX_LINEARPOSITIONCNTR_LOW +#define ACP_AUDIO_RX_INTR_WATERMARK_SIZE ACP_I2S_RX_INTR_WATERMARK_SIZE +#define ACP_AUDIO_TX_RINGBUFADDR ACP_I2S_TX_RINGBUFADDR +#define ACP_AUDIO_TX_RINGBUFSIZE ACP_I2S_TX_RINGBUFSIZE +#define ACP_AUDIO_TX_LINKPOSITIONCNTR ACP_I2S_TX_LINKPOSITIONCNTR +#define ACP_AUDIO_TX_FIFOADDR ACP_I2S_TX_FIFOADDR +#define ACP_AUDIO_TX_FIFOSIZE ACP_I2S_TX_FIFOSIZE +#define ACP_AUDIO_TX_DMA_SIZE ACP_I2S_TX_DMA_SIZE +#define ACP_AUDIO_TX_LINEARPOSITIONCNTR_HIGH ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH +#define ACP_AUDIO_TX_LINEARPOSITIONCNTR_LOW ACP_I2S_TX_LINEARPOSITIONCNTR_LOW +#define ACP_AUDIO_TX_INTR_WATERMARK_SIZE ACP_I2S_TX_INTR_WATERMARK_SIZE /* Registers from ACP_I2S_TDM block */ #define ACP_I2STDM_IER 0x0002400 @@ -255,6 +292,102 @@ #define ACP_WOV_ERROR_STATUS_REGISTER 0x0002C68 #define ACP_PDM_CLKDIV 0x0002C6C +/* Registers from ACP_SW_SWCLK block */ +#define ACP_SW_EN 0x0003000 +#define ACP_SW_EN_STATUS 0x0003004 +#define ACP_SW_FRAMESIZE 0x0003008 +#define ACP_SW_SSP_COUNTER 0x000300C +#define ACP_SW_AUDIO_TX_EN 0x0003010 +#define ACP_SW_AUDIO_TX_EN_STATUS 0x0003014 +#define ACP_SW_AUDIO_TX_FRAME_FORMAT 0x0003018 +#define ACP_SW_AUDIO_TX_SAMPLEINTERVAL 0x000301C +#define ACP_SW_AUDIO_TX_HCTRL_DP0 0x0003020 +#define ACP_SW_AUDIO_TX_HCTRL_DP1 0x0003024 +#define ACP_SW_AUDIO_TX_HCTRL_DP2 0x0003028 +#define ACP_SW_AUDIO_TX_HCTRL_DP3 0x000302C +#define ACP_SW_AUDIO_TX_OFFSET_DP0 0x0003030 +#define ACP_SW_AUDIO_TX_OFFSET_DP1 0x0003034 +#define ACP_SW_AUDIO_TX_OFFSET_DP2 0x0003038 +#define ACP_SW_AUDIO_TX_OFFSET_DP3 0x000303C +#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0 0x0003040 +#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP1 0x0003044 +#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP2 0x0003048 +#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP3 0x000304C +#define ACP_SW_BT_TX_EN 0x0003050 +#define ACP_SW_BT_TX_EN_STATUS 0x0003054 +#define ACP_SW_BT_TX_FRAME_FORMAT 0x0003058 +#define ACP_SW_BT_TX_SAMPLEINTERVAL 0x000305C +#define ACP_SW_BT_TX_HCTRL 0x0003060 +#define ACP_SW_BT_TX_OFFSET 0x0003064 +#define ACP_SW_BT_TX_CHANNEL_ENABLE_DP0 0x0003068 +#define ACP_SW_HEADSET_TX_EN 0x000306C +#define ACP_SW_HEADSET_TX_EN_STATUS 0x0003070 +#define ACP_SW_HEADSET_TX_FRAME_FORMAT 0x0003074 +#define ACP_SW_HEADSET_TX_SAMPLEINTERVAL 0x0003078 +#define ACP_SW_HEADSET_TX_HCTRL 0x000307C +#define ACP_SW_HEADSET_TX_OFFSET 0x0003080 +#define ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0 0x0003084 +#define ACP_SW_AUDIO_RX_EN 0x0003088 +#define ACP_SW_AUDIO_RX_EN_STATUS 0x000308C +#define ACP_SW_AUDIO_RX_FRAME_FORMAT 0x0003090 +#define ACP_SW_AUDIO_RX_SAMPLEINTERVAL 0x0003094 +#define ACP_SW_AUDIO_RX_HCTRL_DP0 0x0003098 +#define ACP_SW_AUDIO_RX_HCTRL_DP1 0x000309C +#define ACP_SW_AUDIO_RX_HCTRL_DP2 0x0003100 +#define ACP_SW_AUDIO_RX_HCTRL_DP3 0x0003104 +#define ACP_SW_AUDIO_RX_OFFSET_DP0 0x0003108 +#define ACP_SW_AUDIO_RX_OFFSET_DP1 0x000310C +#define ACP_SW_AUDIO_RX_OFFSET_DP2 0x0003110 +#define ACP_SW_AUDIO_RX_OFFSET_DP3 0x0003114 +#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0 0x0003118 +#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP1 0x000311C +#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP2 0x0003120 +#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP3 0x0003124 +#define ACP_SW_BT_RX_EN 0x0003128 +#define ACP_SW_BT_RX_EN_STATUS 0x000312C +#define ACP_SW_BT_RX_FRAME_FORMAT 0x0003130 +#define ACP_SW_BT_RX_SAMPLEINTERVAL 0x0003134 +#define ACP_SW_BT_RX_HCTRL 0x0003138 +#define ACP_SW_BT_RX_OFFSET 0x000313C +#define ACP_SW_BT_RX_CHANNEL_ENABLE_DP0 0x0003140 +#define ACP_SW_HEADSET_RX_EN 0x0003144 +#define ACP_SW_HEADSET_RX_EN_STATUS 0x0003148 +#define ACP_SW_HEADSET_RX_FRAME_FORMAT 0x000314C +#define ACP_SW_HEADSET_RX_SAMPLEINTERVAL 0x0003150 +#define ACP_SW_HEADSET_RX_HCTRL 0x0003154 +#define ACP_SW_HEADSET_RX_OFFSET 0x0003158 +#define ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0 0x000315C +#define ACP_SW_BPT_PORT_EN 0x0003160 +#define ACP_SW_BPT_PORT_EN_STATUS 0x0003164 +#define ACP_SW_BPT_PORT_FRAME_FORMAT 0x0003168 +#define ACP_SW_BPT_PORT_SAMPLEINTERVAL 0x000316C +#define ACP_SW_BPT_PORT_HCTRL 0x0003170 +#define ACP_SW_BPT_PORT_OFFSET 0x0003174 +#define ACP_SW_BPT_PORT_CHANNEL_ENABLE 0x0003178 +#define ACP_SW_BPT_PORT_FIRST_BYTE_ADDR 0x000317C +#define ACP_SW_CLK_RESUME_CTRL 0x0003180 +#define ACP_SW_CLK_RESUME_DELAY_CNTR 0x0003184 +#define ACP_SW_BUS_RESET_CTRL 0x0003188 +#define ACP_SW_PRBS_ERR_STATUS 0x000318C +#define SW_IMM_CMD_UPPER_WORD 0x0003230 +#define SW_IMM_CMD_LOWER_QWORD 0x0003234 +#define SW_IMM_RESP_UPPER_WORD 0x0003238 +#define SW_IMM_RESP_LOWER_QWORD 0x000323C +#define SW_IMM_CMD_STS 0x0003240 +#define SW_BRA_BASE_ADDRESS 0x0003244 +#define SW_BRA_TRANSFER_SIZE 0x0003248 +#define SW_BRA_DMA_BUSY 0x000324C +#define SW_BRA_RESP 0x0003250 +#define SW_BRA_RESP_FRAME_ADDR 0x0003254 +#define SW_BRA_CURRENT_TRANSFER_SIZE 0x0003258 +#define SW_STATE_CHANGE_STATUS_0TO7 0x000325C +#define SW_STATE_CHANGE_STATUS_8TO11 0x0003260 +#define SW_STATE_CHANGE_STATUS_MASK_0TO7 0x0003264 +#define SW_STATE_CHANGE_STATUS_MASK_8TO11 0x0003268 +#define SW_CLK_FREQUENCY_CTRL 0x000326C +#define SW_ERROR_INTR_MASK 0x0003270 +#define SW_PHY_TEST_MODE_DATA_OFF 0x0003274 + /* Registers from ACP_P1_AUDIO_BUFFERS block */ #define ACP_P1_I2S_RX_RINGBUFADDR 0x0003A00 #define ACP_P1_I2S_RX_RINGBUFSIZE 0x0003A04 @@ -310,6 +443,87 @@ #define ACP_P1_HS_TX_LINEARPOSITIONCNTR_HIGH 0x0003ACC #define ACP_P1_HS_TX_LINEARPOSITIONCNTR_LOW 0x0003AD0 #define ACP_P1_HS_TX_INTR_WATERMARK_SIZE 0x0003AD4 +#define ACP_P1_AUDIO_RX_RINGBUFADDR ACP_P1_I2S_RX_RINGBUFADDR +#define ACP_P1_AUDIO_RX_RINGBUFSIZE ACP_P1_I2S_RX_RINGBUFSIZE +#define ACP_P1_AUDIO_RX_LINKPOSITIONCNTR ACP_P1_I2S_RX_LINKPOSITIONCNTR +#define ACP_P1_AUDIO_RX_FIFOADDR ACP_P1_I2S_RX_FIFOADDR +#define ACP_P1_AUDIO_RX_FIFOSIZE ACP_P1_I2S_RX_FIFOSIZE +#define ACP_P1_AUDIO_RX_DMA_SIZE ACP_P1_I2S_RX_DMA_SIZE +#define ACP_P1_AUDIO_RX_LINEARPOSITIONCNTR_HIGH ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH +#define ACP_P1_AUDIO_RX_LINEARPOSITIONCNTR_LOW ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW +#define ACP_P1_AUDIO_RX_INTR_WATERMARK_SIZE ACP_P1_I2S_RX_INTR_WATERMARK_SIZE +#define ACP_P1_AUDIO_TX_RINGBUFADDR ACP_P1_I2S_TX_RINGBUFADDR +#define ACP_P1_AUDIO_TX_RINGBUFSIZE ACP_P1_I2S_TX_RINGBUFSIZE +#define ACP_P1_AUDIO_TX_LINKPOSITIONCNTR ACP_P1_I2S_TX_LINKPOSITIONCNTR +#define ACP_P1_AUDIO_TX_FIFOADDR ACP_P1_I2S_TX_FIFOADDR +#define ACP_P1_AUDIO_TX_FIFOSIZE ACP_P1_I2S_TX_FIFOSIZE +#define ACP_P1_AUDIO_TX_DMA_SIZE ACP_P1_I2S_TX_DMA_SIZE +#define ACP_P1_AUDIO_TX_LINEARPOSITIONCNTR_HIGH ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH +#define ACP_P1_AUDIO_TX_LINEARPOSITIONCNTR_LOW ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW +#define ACP_P1_AUDIO_TX_INTR_WATERMARK_SIZE ACP_P1_I2S_TX_INTR_WATERMARK_SIZE + +/* Registers from ACP_P1_SW_SWCLK block */ +#define ACP_P1_SW_EN 0x0003C00 +#define ACP_P1_SW_EN_STATUS 0x0003C04 +#define ACP_P1_SW_FRAMESIZE 0x0003C08 +#define ACP_P1_SW_SSP_COUNTER 0x0003C0C +#define ACP_P1_SW_BT_TX_EN 0x0003C50 +#define ACP_P1_SW_BT_TX_EN_STATUS 0x0003C54 +#define ACP_P1_SW_BT_TX_FRAME_FORMAT 0x0003C58 +#define ACP_P1_SW_BT_TX_SAMPLEINTERVAL 0x0003C5C +#define ACP_P1_SW_BT_TX_HCTRL 0x0003C60 +#define ACP_P1_SW_BT_TX_OFFSET 0x0003C64 +#define ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0 0x0003C68 +#define ACP_P1_SW_BT_RX_EN 0x0003D28 +#define ACP_P1_SW_BT_RX_EN_STATUS 0x0003D2C +#define ACP_P1_SW_BT_RX_FRAME_FORMAT 0x0003D30 +#define ACP_P1_SW_BT_RX_SAMPLEINTERVAL 0x0003D34 +#define ACP_P1_SW_BT_RX_HCTRL 0x0003D38 +#define ACP_P1_SW_BT_RX_OFFSET 0x0003D3C +#define ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0 0x0003D40 +#define ACP_P1_SW_BPT_PORT_EN 0x0003D60 +#define ACP_P1_SW_BPT_PORT_EN_STATUS 0x0003D64 +#define ACP_P1_SW_BPT_PORT_FRAME_FORMAT 0x0003D68 +#define ACP_P1_SW_BPT_PORT_SAMPLEINTERVAL 0x0003D6C +#define ACP_P1_SW_BPT_PORT_HCTRL 0x0003D70 +#define ACP_P1_SW_BPT_PORT_OFFSET 0x0003D74 +#define ACP_P1_SW_BPT_PORT_CHANNEL_ENABLE 0x0003D78 +#define ACP_P1_SW_BPT_PORT_FIRST_BYTE_ADDR 0x0003D7C +#define ACP_P1_SW_CLK_RESUME_CTRL 0x0003D80 +#define ACP_P1_SW_CLK_RESUME_DELAY_CNTR 0x0003D84 +#define ACP_P1_SW_BUS_RESET_CTRL 0x0003D88 +#define ACP_P1_SW_PRBS_ERR_STATUS 0x0003D8C + +/* Registers from ACP_P1_SW_ACLK block */ +#define P1_SW_CORB_BASE_ADDRESS 0x0003E00 +#define P1_SW_CORB_WRITE_POINTER 0x0003E04 +#define P1_SW_CORB_READ_POINTER 0x0003E08 +#define P1_SW_CORB_CONTROL 0x0003E0C +#define P1_SW_CORB_SIZE 0x0003E14 +#define P1_SW_RIRB_BASE_ADDRESS 0x0003E18 +#define P1_SW_RIRB_WRITE_POINTER 0x0003E1C +#define P1_SW_RIRB_RESPONSE_INTERRUPT_COUNT 0x0003E20 +#define P1_SW_RIRB_CONTROL 0x0003E24 +#define P1_SW_RIRB_SIZE 0x0003E28 +#define P1_SW_RIRB_FIFO_MIN_THDL 0x0003E2C +#define P1_SW_IMM_CMD_UPPER_WORD 0x0003E30 +#define P1_SW_IMM_CMD_LOWER_QWORD 0x0003E34 +#define P1_SW_IMM_RESP_UPPER_WORD 0x0003E38 +#define P1_SW_IMM_RESP_LOWER_QWORD 0x0003E3C +#define P1_SW_IMM_CMD_STS 0x0003E40 +#define P1_SW_BRA_BASE_ADDRESS 0x0003E44 +#define P1_SW_BRA_TRANSFER_SIZE 0x0003E48 +#define P1_SW_BRA_DMA_BUSY 0x0003E4C +#define P1_SW_BRA_RESP 0x0003E50 +#define P1_SW_BRA_RESP_FRAME_ADDR 0x0003E54 +#define P1_SW_BRA_CURRENT_TRANSFER_SIZE 0x0003E58 +#define P1_SW_STATE_CHANGE_STATUS_0TO7 0x0003E5C +#define P1_SW_STATE_CHANGE_STATUS_8TO11 0x0003E60 +#define P1_SW_STATE_CHANGE_STATUS_MASK_0TO7 0x0003E64 +#define P1_SW_STATE_CHANGE_STATUS_MASK_8TO11 0x0003E68 +#define P1_SW_CLK_FREQUENCY_CTRL 0x0003E6C +#define P1_SW_ERROR_INTR_MASK 0x0003E70 +#define P1_SW_PHY_TEST_MODE_DATA_OFF 0x0003E74 /* Registers from ACP_SCRATCH block */ #define ACP_SCRATCH_REG_0 0x0010000 diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ebb8e7a7fc29..77495e5988c1 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -341,31 +341,27 @@ struct soc_enum; #define SND_SOC_DAPM_STREAM_STOP 0x2 #define SND_SOC_DAPM_STREAM_SUSPEND 0x4 #define SND_SOC_DAPM_STREAM_RESUME 0x8 -#define SND_SOC_DAPM_STREAM_PAUSE_PUSH 0x10 +#define SND_SOC_DAPM_STREAM_PAUSE_PUSH 0x10 #define SND_SOC_DAPM_STREAM_PAUSE_RELEASE 0x20 /* dapm event types */ -#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */ -#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */ -#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */ -#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */ -#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */ -#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */ -#define SND_SOC_DAPM_WILL_PMU 0x40 /* called at start of sequence */ -#define SND_SOC_DAPM_WILL_PMD 0x80 /* called at start of sequence */ -#define SND_SOC_DAPM_PRE_POST_PMD \ - (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD) -#define SND_SOC_DAPM_PRE_POST_PMU \ - (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU) +#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */ +#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */ +#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */ +#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */ +#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */ +#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */ +#define SND_SOC_DAPM_WILL_PMU 0x40 /* called at start of sequence */ +#define SND_SOC_DAPM_WILL_PMD 0x80 /* called at start of sequence */ +#define SND_SOC_DAPM_PRE_POST_PMD (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD) +#define SND_SOC_DAPM_PRE_POST_PMU (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU) /* convenience event type detection */ -#define SND_SOC_DAPM_EVENT_ON(e) \ - (e & (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU)) -#define SND_SOC_DAPM_EVENT_OFF(e) \ - (e & (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)) +#define SND_SOC_DAPM_EVENT_ON(e) (e & (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU)) +#define SND_SOC_DAPM_EVENT_OFF(e) (e & (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)) /* regulator widget flags */ -#define SND_SOC_DAPM_REGULATOR_BYPASS 0x1 /* bypass when disabled */ +#define SND_SOC_DAPM_REGULATOR_BYPASS 0x1 /* bypass when disabled */ struct snd_soc_dapm_widget; enum snd_soc_dapm_type; @@ -396,18 +392,13 @@ enum snd_soc_bias_level { SND_SOC_BIAS_ON = 3, }; -int dapm_regulator_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); -int dapm_clock_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); -int dapm_pinctrl_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); +int dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int dapm_clock_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int dapm_pinctrl_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); /* dapm controls */ -int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, @@ -419,30 +410,24 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uncontrol); int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget, - int num); -struct snd_soc_dapm_widget *snd_soc_dapm_new_control( - struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget, int num); +struct snd_soc_dapm_widget *snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget); -struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked( - struct snd_soc_dapm_context *dapm, +struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget); -int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, - struct snd_soc_dai *dai); +int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, struct snd_soc_dai *dai); void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai); + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai); /* dapm path setup */ int snd_soc_dapm_new_widgets(struct snd_soc_card *card); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, - struct snd_soc_card *card, - struct snd_soc_component *component); + struct snd_soc_card *card, struct snd_soc_component *component); int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, @@ -450,49 +435,36 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); -void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm); /* dapm events */ -void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, - int event); +void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event); void snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream); void snd_soc_dapm_shutdown(struct snd_soc_card *card); /* external DAPM widget events */ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int connect, - struct snd_soc_dapm_update *update); + struct snd_kcontrol *kcontrol, int connect, struct snd_soc_dapm_update *update); int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, struct snd_soc_dapm_update *update); /* dapm sys fs - used by the core */ extern struct attribute *soc_dapm_dev_attrs[]; -void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, - struct dentry *parent); +void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, struct dentry *parent); /* dapm audio pin control and status */ -int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, - const char *pin); -int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, - const char *pin); -int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, - const char *pin); -int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, - const char *pin); +int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, - const char *pin); -int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, - const char *pin); +int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm); -int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, - const char *pin); -int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, - const char *pin); -int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, - const char *pin); +int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol); /* Mostly internal - should not normally be used */ @@ -501,40 +473,35 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card); /* dapm path query */ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list, - bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, - enum snd_soc_dapm_direction)); + bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, enum snd_soc_dapm_direction)); void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list); -struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( - struct snd_kcontrol *kcontrol); - -struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget( - struct snd_kcontrol *kcontrol); +struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(struct snd_kcontrol *kcontrol); +struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(struct snd_kcontrol *kcontrol); -int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, - enum snd_soc_bias_level level); +int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); /* dapm widget types */ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ snd_soc_dapm_output, /* output pin */ - snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ - snd_soc_dapm_demux, /* connects the input to one of multiple outputs */ - snd_soc_dapm_mixer, /* mixes several analog signals together */ - snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */ - snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ - snd_soc_dapm_out_drv, /* output driver */ - snd_soc_dapm_adc, /* analog to digital converter */ - snd_soc_dapm_dac, /* digital to analog converter */ + snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */ + snd_soc_dapm_demux, /* connects the input to one of multiple outputs */ + snd_soc_dapm_mixer, /* mixes several analog signals together */ + snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */ + snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */ + snd_soc_dapm_out_drv, /* output driver */ + snd_soc_dapm_adc, /* analog to digital converter */ + snd_soc_dapm_dac, /* digital to analog converter */ snd_soc_dapm_micbias, /* microphone bias (power) - DEPRECATED: use snd_soc_dapm_supply */ - snd_soc_dapm_mic, /* microphone */ - snd_soc_dapm_hp, /* headphones */ - snd_soc_dapm_spk, /* speaker */ - snd_soc_dapm_line, /* line input/output */ + snd_soc_dapm_mic, /* microphone */ + snd_soc_dapm_hp, /* headphones */ + snd_soc_dapm_spk, /* speaker */ + snd_soc_dapm_line, /* line input/output */ snd_soc_dapm_switch, /* analog switch */ - snd_soc_dapm_vmid, /* codec bias/vmid - to minimise pops */ - snd_soc_dapm_pre, /* machine specific pre widget - exec first */ - snd_soc_dapm_post, /* machine specific post widget - exec last */ + snd_soc_dapm_vmid, /* codec bias/vmid - to minimise pops */ + snd_soc_dapm_pre, /* machine specific pre widget - exec first */ + snd_soc_dapm_post, /* machine specific post widget - exec last */ snd_soc_dapm_supply, /* power/clock supply */ snd_soc_dapm_pinctrl, /* pinctrl */ snd_soc_dapm_regulator_supply, /* external regulator */ @@ -600,9 +567,9 @@ struct snd_soc_dapm_path { }; /* status */ - u32 connect:1; /* source and sink widgets are connected */ - u32 walking:1; /* path is in the process of being walked */ - u32 weak:1; /* path ignored for power management */ + u32 connect:1; /* source and sink widgets are connected */ + u32 walking:1; /* path is in the process of being walked */ + u32 weak:1; /* path ignored for power management */ u32 is_supply:1; /* At least one of the connected widgets is a supply */ int (*connected)(struct snd_soc_dapm_widget *source, @@ -616,8 +583,8 @@ struct snd_soc_dapm_path { /* dapm widget */ struct snd_soc_dapm_widget { enum snd_soc_dapm_type id; - const char *name; /* widget name */ - const char *sname; /* stream name */ + const char *name; /* widget name */ + const char *sname; /* stream name */ struct list_head list; struct snd_soc_dapm_context *dapm; @@ -636,7 +603,7 @@ struct snd_soc_dapm_widget { unsigned char connected:1; /* connected codec pin */ unsigned char new:1; /* cnew complete */ unsigned char force:1; /* force state */ - unsigned char ignore_suspend:1; /* kept enabled over suspend */ + unsigned char ignore_suspend:1; /* kept enabled over suspend */ unsigned char new_power:1; /* power from this run */ unsigned char power_checked:1; /* power checked this run */ unsigned char is_supply:1; /* Widget is a supply type widget */ @@ -680,27 +647,24 @@ struct snd_soc_dapm_update { bool has_second_set; }; -struct snd_soc_dapm_wcache { - struct snd_soc_dapm_widget *widget; -}; - /* DAPM context */ struct snd_soc_dapm_context { enum snd_soc_bias_level bias_level; - unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ - /* Go to BIAS_OFF in suspend if the DAPM context is idle */ - unsigned int suspend_bias_off:1; - struct device *dev; /* from parent - for debug */ - struct snd_soc_component *component; /* parent component */ - struct snd_soc_card *card; /* parent card */ + /* bit field */ + unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ + unsigned int suspend_bias_off:1; /* Use BIAS_OFF in suspend if the DAPM is idle */ + + struct device *dev; /* from parent - for debug */ + struct snd_soc_component *component; /* parent component */ + struct snd_soc_card *card; /* parent card */ /* used during DAPM updates */ enum snd_soc_bias_level target_bias_level; struct list_head list; - struct snd_soc_dapm_wcache path_sink_cache; - struct snd_soc_dapm_wcache path_source_cache; + struct snd_soc_dapm_widget *wcache_sink; + struct snd_soc_dapm_widget *wcache_source; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dapm; @@ -767,11 +731,11 @@ enum snd_soc_dapm_direction { #define SND_SOC_DAPM_DIR_TO_EP(x) BIT(x) -#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN) -#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT) +#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN) +#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT) /** - * snd_soc_dapm_widget_for_each_sink_path - Iterates over all paths in the + * snd_soc_dapm_widget_for_each_path - Iterates over all paths in the * specified direction of a widget * @w: The widget * @dir: Whether to iterate over the paths where the specified widget is the @@ -782,7 +746,7 @@ enum snd_soc_dapm_direction { list_for_each_entry(p, &w->edges[dir], list_node[dir]) /** - * snd_soc_dapm_widget_for_each_sink_path_safe - Iterates over all paths in the + * snd_soc_dapm_widget_for_each_path_safe - Iterates over all paths in the * specified direction of a widget * @w: The widget * @dir: Whether to iterate over the paths where the specified widget is the @@ -790,7 +754,7 @@ enum snd_soc_dapm_direction { * @p: The path iterator variable * @next_p: Temporary storage for the next path * - * This function works like snd_soc_dapm_widget_for_each_sink_path, expect that + * This function works like snd_soc_dapm_widget_for_each_path, expect that * it is safe to remove the current path from the list while iterating */ #define snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) \ diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 5b689c663290..2864aed72998 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -78,8 +78,6 @@ struct snd_soc_dpcm { struct list_head list_be; struct list_head list_fe; - /* hw params for this link - may be different for each link */ - struct snd_pcm_hw_params hw_params; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_state; #endif diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index ac416572db0d..09b6511c0a26 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -51,7 +51,7 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas struct device *dev = dai->component->dev; struct acp_dev_data *adata = snd_soc_dai_get_drvdata(dai); struct acp_stream *stream; - int slot_len; + int slot_len, no_of_slots; switch (slot_width) { case SLOT_WIDTH_8: @@ -71,6 +71,20 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas return -EINVAL; } + switch (slots) { + case 1 ... 7: + no_of_slots = slots; + break; + case 8: + no_of_slots = 0; + break; + default: + dev_err(dev, "Unsupported slots %d\n", slots); + return -EINVAL; + } + + slots = no_of_slots; + spin_lock_irq(&adata->acp_lock); list_for_each_entry(stream, &adata->stream_list, list) { if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK) diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 4c69cb6e3400..a78cf29387a7 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -167,11 +167,14 @@ static int acp_card_hs_startup(struct snd_pcm_substream *substream) &constraints_channels); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); - if (!drvdata->soc_mclk) { - ret = acp_clk_enable(drvdata); - if (ret < 0) { - dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret); - return ret; + + if (strcmp(codec_dai->name, "rt5682s-aif1") && strcmp(codec_dai->name, "rt5682s-aif2")) { + if (!drvdata->soc_mclk) { + ret = acp_clk_enable(drvdata); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret); + return ret; + } } } @@ -280,7 +283,6 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) static const struct snd_soc_ops acp_card_rt5682s_ops = { .startup = acp_card_hs_startup, - .shutdown = acp_card_shutdown, }; static const unsigned int dmic_channels[] = { @@ -570,6 +572,52 @@ SND_SOC_DAILINK_DEF(sof_dmic, SND_SOC_DAILINK_DEF(pdm_dmic, DAILINK_COMP_ARRAY(COMP_CPU("acp-pdm-dmic"))); +static int acp_rtk_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + struct acp_card_drvdata *drvdata = card->drvdata; + int ret = 0; + + if (!component) + return 0; + + if (strncmp(component->name, "i2c-RTL5682", 11) && + strncmp(component->name, "i2c-10EC1019", 12)) + return 0; + + /* + * For Realtek's codec and amplifier components, + * the lrck and bclk must be enabled brfore their all dapms be powered on, + * and must be disabled after their all dapms be powered down + * to avoid any pop. + */ + switch (level) { + case SND_SOC_BIAS_STANDBY: + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { + clk_set_rate(drvdata->wclk, 48000); + clk_set_rate(drvdata->bclk, 48000 * 64); + + /* Increase bclk's enable_count */ + ret = clk_prepare_enable(drvdata->bclk); + if (ret < 0) + dev_err(component->dev, "Failed to enable bclk %d\n", ret); + } else { + /* + * Decrease bclk's enable_count. + * While the enable_count is 0, the bclk would be closed. + */ + clk_disable_unprepare(drvdata->bclk); + } + break; + default: + break; + } + + return ret; +} + int acp_sofdsp_dai_links_create(struct snd_soc_card *card) { struct snd_soc_dai_link *links; @@ -730,6 +778,7 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) card->dai_link = links; card->num_links = num_links; + card->set_bias_level = acp_rtk_set_bias_level; return 0; } @@ -907,6 +956,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) card->dai_link = links; card->num_links = num_links; + card->set_bias_level = acp_rtk_set_bias_level; return 0; } diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e3b90c425faf..7a13e750751a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1629,6 +1629,7 @@ config SND_SOC_TFA989X config SND_SOC_TLV320ADC3XXX tristate "Texas Instruments TLV320ADC3001/3101 audio ADC" depends on I2C + depends on GPIOLIB help Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 ADCs. @@ -1929,7 +1930,7 @@ config SND_SOC_WM8960 depends on I2C config SND_SOC_WM8961 - tristate + tristate "Wolfson Microelectronics WM8961 CODEC" depends on I2C config SND_SOC_WM8962 diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 1db73552c746..8c69c8cf9b67 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -13,6 +13,7 @@ #include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/slab.h> #include <sound/initval.h> #include <sound/pcm_params.h> @@ -46,6 +47,7 @@ struct ak4458_priv { struct device *dev; struct regmap *regmap; struct gpio_desc *reset_gpiod; + struct reset_control *reset; struct gpio_desc *mute_gpiod; int digfil; /* SSLOW, SD, SLOW bits */ int fs; /* sampling rate */ @@ -633,6 +635,12 @@ static void ak4458_reset(struct ak4458_priv *ak4458, bool active) if (ak4458->reset_gpiod) { gpiod_set_value_cansleep(ak4458->reset_gpiod, active); usleep_range(1000, 2000); + } else if (!IS_ERR_OR_NULL(ak4458->reset)) { + if (active) + reset_control_assert(ak4458->reset); + else + reset_control_deassert(ak4458->reset); + usleep_range(1000, 2000); } } @@ -668,7 +676,6 @@ static int __maybe_unused ak4458_runtime_resume(struct device *dev) if (ak4458->mute_gpiod) gpiod_set_value_cansleep(ak4458->mute_gpiod, 1); - ak4458_reset(ak4458, true); ak4458_reset(ak4458, false); regcache_cache_only(ak4458->regmap, false); @@ -748,6 +755,10 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) ak4458->drvdata = of_device_get_match_data(&i2c->dev); + ak4458->reset = devm_reset_control_get_optional_shared(ak4458->dev, NULL); + if (IS_ERR(ak4458->reset)) + return PTR_ERR(ak4458->reset); + ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ak4458->reset_gpiod)) diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index 31ae752e242f..a078dd422ea1 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -918,8 +918,8 @@ static int cs35l36_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, fs1 = CS35L36_FS1_DEFAULT_VAL; fs2 = CS35L36_FS2_DEFAULT_VAL; } else { - fs1 = 3 * ((CS35L36_FS_NOM_6MHZ * 4 + freq - 1) / freq) + 4; - fs2 = 5 * ((CS35L36_FS_NOM_6MHZ * 4 + freq - 1) / freq) + 4; + fs1 = 3 * DIV_ROUND_UP(CS35L36_FS_NOM_6MHZ * 4, freq) + 4; + fs2 = 5 * DIV_ROUND_UP(CS35L36_FS_NOM_6MHZ * 4, freq) + 4; } regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c index f90d43996a51..37629ebd90e0 100644 --- a/sound/soc/codecs/cs42l83-i2c.c +++ b/sound/soc/codecs/cs42l83-i2c.c @@ -145,7 +145,7 @@ static const struct reg_default cs42l83_reg_defaults[] = { * This is all the same as for CS42L42 but we * replace the on-reset register defaults. */ -const struct regmap_config cs42l83_regmap = { +static const struct regmap_config cs42l83_regmap = { .reg_bits = 8, .val_bits = 8, diff --git a/sound/soc/codecs/cx2072x.h b/sound/soc/codecs/cx2072x.h index ebdd567fa225..09e3a92b184f 100644 --- a/sound/soc/codecs/cx2072x.h +++ b/sound/soc/codecs/cx2072x.h @@ -177,7 +177,7 @@ #define CX2072X_PLBK_DRC_PARM_LEN 9 #define CX2072X_CLASSD_AMP_LEN 6 -/* DAI interfae type */ +/* DAI interface type */ #define CX2072X_DAI_HIFI 1 #define CX2072X_DAI_DSP 2 #define CX2072X_DAI_DSP_PWM 3 /* 4 ch, including mic and AEC */ diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index 0b8ccc5be480..4b8ec6f77337 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -130,10 +130,8 @@ static void hda_codec_unregister_dais(struct hda_codec *codec, if (strcmp(dai->driver->name, pcm->name)) continue; - if (dai->playback_widget) - snd_soc_dapm_free_widget(dai->playback_widget); - if (dai->capture_widget) - snd_soc_dapm_free_widget(dai->capture_widget); + snd_soc_dapm_free_widget(dai->playback_widget); + snd_soc_dapm_free_widget(dai->capture_widget); snd_soc_unregister_dai(dai); break; } @@ -213,7 +211,7 @@ static int hda_codec_probe(struct snd_soc_component *component) patch = (hda_codec_patch_t)codec->preset->driver_data; if (!patch) { - dev_err(&hdev->dev, "no patch specified?\n"); + dev_err(&hdev->dev, "no patch specified\n"); ret = -EINVAL; goto err; } diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c index 5201a8f6d7b6..685ba1d3a644 100644 --- a/sound/soc/codecs/jz4725b.c +++ b/sound/soc/codecs/jz4725b.c @@ -136,28 +136,89 @@ enum { #define REG_CGR3_GO1L_OFFSET 0 #define REG_CGR3_GO1L_MASK (0x1f << REG_CGR3_GO1L_OFFSET) +#define REG_CGR4_GO2R_OFFSET 0 +#define REG_CGR4_GO2R_MASK (0x1f << REG_CGR4_GO2R_OFFSET) + +#define REG_CGR5_GO2L_OFFSET 0 +#define REG_CGR5_GO2L_MASK (0x1f << REG_CGR5_GO2L_OFFSET) + +#define REG_CGR6_GO3R_OFFSET 0 +#define REG_CGR6_GO3R_MASK (0x1f << REG_CGR6_GO3R_OFFSET) + +#define REG_CGR7_GO3L_OFFSET 0 +#define REG_CGR7_GO3L_MASK (0x1f << REG_CGR7_GO3L_OFFSET) + +#define REG_CGR8_GOR_OFFSET 0 +#define REG_CGR8_GOR_MASK (0x1f << REG_CGR8_GOR_OFFSET) + +#define REG_CGR9_GOL_OFFSET 0 +#define REG_CGR9_GOL_MASK (0x1f << REG_CGR9_GOL_OFFSET) + +#define REG_CGR10_GIL_OFFSET 0 +#define REG_CGR10_GIR_OFFSET 4 + struct jz_icdc { struct regmap *regmap; void __iomem *base; struct clk *clk; }; -static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(jz4725b_dac_tlv, -2250, 0); -static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(jz4725b_line_tlv, -1500, 600); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_adc_tlv, 0, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_dac_tlv, -2250, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_mix_tlv, + 0, 11, TLV_DB_SCALE_ITEM(-2250, 0, 0), + 12, 31, TLV_DB_SCALE_ITEM(-2250, 150, 0), +); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_out_tlv, + 0, 11, TLV_DB_SCALE_ITEM(-3350, 200, 0), + 12, 23, TLV_DB_SCALE_ITEM(-1050, 100, 0), + 24, 31, TLV_DB_SCALE_ITEM( 100, 50, 0), +); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_mic_boost_tlv, 0, 2000, 0); + +static const char * const jz4725b_mic_mode_texts[] = { + "Single Ended", "Differential", +}; + +static const struct soc_enum jz4725b_mic_mode_enum = + SOC_ENUM_SINGLE(JZ4725B_CODEC_REG_CR3, REG_CR3_MICDIFF_OFFSET, + 2, jz4725b_mic_mode_texts); static const struct snd_kcontrol_new jz4725b_codec_controls[] = { - SOC_DOUBLE_TLV("Master Playback Volume", + SOC_DOUBLE_TLV("DAC Playback Volume", JZ4725B_CODEC_REG_CGR1, REG_CGR1_GODL_OFFSET, REG_CGR1_GODR_OFFSET, 0xf, 1, jz4725b_dac_tlv), - SOC_DOUBLE_R_TLV("Master Capture Volume", + SOC_DOUBLE_TLV("Master Capture Volume", + JZ4725B_CODEC_REG_CGR10, + REG_CGR10_GIL_OFFSET, + REG_CGR10_GIR_OFFSET, + 0xf, 0, jz4725b_adc_tlv), + SOC_DOUBLE_R_TLV("Mixer Line In Bypass Playback Volume", JZ4725B_CODEC_REG_CGR3, JZ4725B_CODEC_REG_CGR2, REG_CGR2_GO1R_OFFSET, - 0x1f, 1, jz4725b_line_tlv), - - SOC_SINGLE("Master Playback Switch", JZ4725B_CODEC_REG_CR1, + 0x1f, 1, jz4725b_mix_tlv), + SOC_DOUBLE_R_TLV("Mixer Mic 1 Bypass Playback Volume", + JZ4725B_CODEC_REG_CGR5, + JZ4725B_CODEC_REG_CGR4, + REG_CGR4_GO2R_OFFSET, + 0x1f, 1, jz4725b_mix_tlv), + SOC_DOUBLE_R_TLV("Mixer Mic 2 Bypass Playback Volume", + JZ4725B_CODEC_REG_CGR7, + JZ4725B_CODEC_REG_CGR6, + REG_CGR6_GO3R_OFFSET, + 0x1f, 1, jz4725b_mix_tlv), + + SOC_DOUBLE_R_TLV("Master Playback Volume", + JZ4725B_CODEC_REG_CGR9, + JZ4725B_CODEC_REG_CGR8, + REG_CGR8_GOR_OFFSET, + 0x1f, 1, jz4725b_out_tlv), + + SOC_SINGLE("DAC Playback Switch", JZ4725B_CODEC_REG_CR1, REG_CR1_DAC_MUTE_OFFSET, 1, 1), SOC_SINGLE("Deemphasize Filter Playback Switch", @@ -167,6 +228,13 @@ static const struct snd_kcontrol_new jz4725b_codec_controls[] = { SOC_SINGLE("High-Pass Filter Capture Switch", JZ4725B_CODEC_REG_CR2, REG_CR2_ADC_HPF_OFFSET, 1, 0), + + SOC_ENUM("Mic Mode Capture Switch", jz4725b_mic_mode_enum), + + SOC_SINGLE_TLV("Mic1 Boost Capture Volume", + JZ4725B_CODEC_REG_PMR2, + REG_PMR2_GIM_OFFSET, + 1, 0, jz4725b_mic_boost_tlv), }; static const char * const jz4725b_codec_adc_src_texts[] = { @@ -180,11 +248,15 @@ static SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum, jz4725b_codec_adc_src_texts, jz4725b_codec_adc_src_values); static const struct snd_kcontrol_new jz4725b_codec_adc_src_ctrl = - SOC_DAPM_ENUM("Route", jz4725b_codec_adc_src_enum); + SOC_DAPM_ENUM("ADC Source Capture Route", jz4725b_codec_adc_src_enum); static const struct snd_kcontrol_new jz4725b_codec_mixer_controls[] = { - SOC_DAPM_SINGLE("Line In Bypass", JZ4725B_CODEC_REG_CR1, + SOC_DAPM_SINGLE("Line In Bypass Playback Switch", JZ4725B_CODEC_REG_CR1, REG_CR1_BYPASS_OFFSET, 1, 0), + SOC_DAPM_SINGLE("Mic 1 Bypass Playback Switch", JZ4725B_CODEC_REG_CR3, + REG_CR3_SIDETONE1_OFFSET, 1, 0), + SOC_DAPM_SINGLE("Mic 2 Bypass Playback Switch", JZ4725B_CODEC_REG_CR3, + REG_CR3_SIDETONE2_OFFSET, 1, 0), }; static int jz4725b_out_stage_enable(struct snd_soc_dapm_widget *w, @@ -225,7 +297,7 @@ static const struct snd_soc_dapm_widget jz4725b_codec_dapm_widgets[] = { SND_SOC_DAPM_ADC("ADC", "Capture", JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_ADC_OFFSET, 1), - SND_SOC_DAPM_MUX("ADC Source", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_MUX("ADC Source Capture Route", SND_SOC_NOPM, 0, 0, &jz4725b_codec_adc_src_ctrl), /* Mixer */ @@ -236,7 +308,8 @@ static const struct snd_soc_dapm_widget jz4725b_codec_dapm_widgets[] = { SND_SOC_DAPM_MIXER("DAC to Mixer", JZ4725B_CODEC_REG_CR1, REG_CR1_DACSEL_OFFSET, 0, NULL, 0), - SND_SOC_DAPM_MIXER("Line In", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Line In", JZ4725B_CODEC_REG_PMR1, + REG_PMR1_SB_LIN_OFFSET, 1, NULL, 0), SND_SOC_DAPM_MIXER("HP Out", JZ4725B_CODEC_REG_CR1, REG_CR1_HP_DIS_OFFSET, 1, NULL, 0), @@ -278,16 +351,18 @@ static const struct snd_soc_dapm_route jz4725b_codec_dapm_routes[] = { {"Line In", NULL, "LLINEIN"}, {"Line In", NULL, "RLINEIN"}, - {"Mixer", "Line In Bypass", "Line In"}, + {"Mixer", "Mic 1 Bypass Playback Switch", "Mic 1"}, + {"Mixer", "Mic 2 Bypass Playback Switch", "Mic 2"}, + {"Mixer", "Line In Bypass Playback Switch", "Line In"}, {"DAC to Mixer", NULL, "DAC"}, {"Mixer", NULL, "DAC to Mixer"}, {"Mixer to ADC", NULL, "Mixer"}, - {"ADC Source", "Mixer", "Mixer to ADC"}, - {"ADC Source", "Line In", "Line In"}, - {"ADC Source", "Mic 1", "Mic 1"}, - {"ADC Source", "Mic 2", "Mic 2"}, - {"ADC", NULL, "ADC Source"}, + {"ADC Source Capture Route", "Mixer", "Mixer to ADC"}, + {"ADC Sourc Capture Routee", "Line In", "Line In"}, + {"ADC Source Capture Route", "Mic 1", "Mic 1"}, + {"ADC Source Capture Route", "Mic 2", "Mic 2"}, + {"ADC", NULL, "ADC Source Capture Route"}, {"Out Stage", NULL, "Mixer"}, {"HP Out", NULL, "Out Stage"}, diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index 554c33e8b62f..cc2df5f7ea19 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -503,14 +503,14 @@ static int mt6660_i2c_probe(struct i2c_client *client) dev_err(chip->dev, "read chip revision fail\n"); goto probe_fail; } + pm_runtime_set_active(chip->dev); + pm_runtime_enable(chip->dev); ret = devm_snd_soc_register_component(chip->dev, &mt6660_component_driver, &mt6660_codec_dai, 1); - if (!ret) { - pm_runtime_set_active(chip->dev); - pm_runtime_enable(chip->dev); - } + if (ret) + pm_runtime_disable(chip->dev); return ret; diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index b66bfecbb879..49f527c61a7a 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -391,18 +391,18 @@ static int rt1019_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { struct snd_soc_component *component = dai->component; - unsigned int val = 0, rx_slotnum; + unsigned int cn = 0, cl = 0, rx_slotnum; int ret = 0, first_bit; switch (slots) { case 4: - val |= RT1019_I2S_TX_4CH; + cn = RT1019_I2S_TX_4CH; break; case 6: - val |= RT1019_I2S_TX_6CH; + cn = RT1019_I2S_TX_6CH; break; case 8: - val |= RT1019_I2S_TX_8CH; + cn = RT1019_I2S_TX_8CH; break; case 2: break; @@ -412,16 +412,16 @@ static int rt1019_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, switch (slot_width) { case 20: - val |= RT1019_I2S_DL_20; + cl = RT1019_TDM_CL_20; break; case 24: - val |= RT1019_I2S_DL_24; + cl = RT1019_TDM_CL_24; break; case 32: - val |= RT1019_I2S_DL_32; + cl = RT1019_TDM_CL_32; break; case 8: - val |= RT1019_I2S_DL_8; + cl = RT1019_TDM_CL_8; break; case 16: break; @@ -470,8 +470,10 @@ static int rt1019_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, goto _set_tdm_err_; } + snd_soc_component_update_bits(component, RT1019_TDM_1, + RT1019_TDM_CL_MASK, cl); snd_soc_component_update_bits(component, RT1019_TDM_2, - RT1019_I2S_CH_TX_MASK | RT1019_I2S_DF_MASK, val); + RT1019_I2S_CH_TX_MASK, cn); _set_tdm_err_: return ret; diff --git a/sound/soc/codecs/rt1019.h b/sound/soc/codecs/rt1019.h index 64df831eeb72..48ba15efb48d 100644 --- a/sound/soc/codecs/rt1019.h +++ b/sound/soc/codecs/rt1019.h @@ -95,6 +95,12 @@ #define RT1019_TDM_BCLK_MASK (0x1 << 6) #define RT1019_TDM_BCLK_NORM (0x0 << 6) #define RT1019_TDM_BCLK_INV (0x1 << 6) +#define RT1019_TDM_CL_MASK (0x7) +#define RT1019_TDM_CL_8 (0x4) +#define RT1019_TDM_CL_32 (0x3) +#define RT1019_TDM_CL_24 (0x2) +#define RT1019_TDM_CL_20 (0x1) +#define RT1019_TDM_CL_16 (0x0) /* 0x0401 TDM Control-2 */ #define RT1019_I2S_CH_TX_MASK (0x3 << 6) diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index a2ce52dafea8..cea26f3a02b6 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1166,6 +1166,13 @@ static const struct dmi_system_id force_combo_jack_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Geminilake") } }, + { + .ident = "Intel Kabylake R RVP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform") + } + }, { } }; diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 466a37f3500c..80c673aa14db 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -1981,7 +1981,7 @@ static int rt5682s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { struct snd_soc_component *component = dai->component; - unsigned int cl, val = 0; + unsigned int cl, val = 0, tx_slotnum; if (tx_mask || rx_mask) snd_soc_component_update_bits(component, @@ -1990,6 +1990,16 @@ static int rt5682s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, snd_soc_component_update_bits(component, RT5682S_TDM_ADDA_CTRL_2, RT5682S_TDM_EN, 0); + /* Tx slot configuration */ + tx_slotnum = hweight_long(tx_mask); + if (tx_slotnum) { + if (tx_slotnum > slots) { + dev_err(component->dev, "Invalid or oversized Tx slots.\n"); + return -EINVAL; + } + val |= (tx_slotnum - 1) << RT5682S_TDM_ADC_DL_SFT; + } + switch (slots) { case 4: val |= RT5682S_TDM_TX_CH_4; @@ -2010,7 +2020,8 @@ static int rt5682s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, } snd_soc_component_update_bits(component, RT5682S_TDM_CTRL, - RT5682S_TDM_TX_CH_MASK | RT5682S_TDM_RX_CH_MASK, val); + RT5682S_TDM_TX_CH_MASK | RT5682S_TDM_RX_CH_MASK | + RT5682S_TDM_ADC_DL_MASK, val); switch (slot_width) { case 8: diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h index 824dc6543c18..45464a041765 100644 --- a/sound/soc/codecs/rt5682s.h +++ b/sound/soc/codecs/rt5682s.h @@ -899,6 +899,7 @@ #define RT5682S_TDM_RX_CH_8 (0x3 << 8) #define RT5682S_TDM_ADC_LCA_MASK (0x7 << 4) #define RT5682S_TDM_ADC_LCA_SFT 4 +#define RT5682S_TDM_ADC_DL_MASK (0x3 << 0) #define RT5682S_TDM_ADC_DL_SFT 0 /* TDM control 2 (0x007a) */ diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index e48768233e20..9c50ac356c89 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -700,8 +700,10 @@ static void headset_ramp(struct snd_soc_component *component, int ramp) struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); struct twl4030_board_params *board_params = twl4030->board_params; /* Base values for ramp delay calculation: 2^19 - 2^26 */ - unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304, - 8388608, 16777216, 33554432, 67108864}; + static const unsigned int ramp_base[] = { + 524288, 1048576, 2097152, 4194304, + 8388608, 16777216, 33554432, 67108864 + }; unsigned int delay; hs_gain = twl4030_read(component, TWL4030_REG_HS_GAIN_SET); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index c09c9ac51b3e..adaf886b0a9d 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -2099,6 +2099,9 @@ static int wm5102_probe(struct platform_device *pdev) regmap_update_bits(arizona->regmap, wm5102_digital_vu[i], WM5102_DIG_VU, WM5102_DIG_VU); + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, "ADSP2 Compressed IRQ", wm5102_adsp2_irq, wm5102); @@ -2131,9 +2134,6 @@ static int wm5102_probe(struct platform_device *pdev) goto err_spk_irqs; } - pm_runtime_enable(&pdev->dev); - pm_runtime_idle(&pdev->dev); - return ret; err_spk_irqs: @@ -2142,6 +2142,7 @@ err_dsp_irq: arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102); err_jack_codec_dev: + pm_runtime_disable(&pdev->dev); arizona_jack_codec_dev_remove(&wm5102->core); return ret; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index fc634c995834..e0b971620d0f 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2457,6 +2457,9 @@ static int wm5110_probe(struct platform_device *pdev) regmap_update_bits(arizona->regmap, wm5110_digital_vu[i], WM5110_DIG_VU, WM5110_DIG_VU); + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, "ADSP2 Compressed IRQ", wm5110_adsp2_irq, wm5110); @@ -2489,9 +2492,6 @@ static int wm5110_probe(struct platform_device *pdev) goto err_spk_irqs; } - pm_runtime_enable(&pdev->dev); - pm_runtime_idle(&pdev->dev); - return ret; err_spk_irqs: @@ -2500,6 +2500,7 @@ err_dsp_irq: arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110); err_jack_codec_dev: + pm_runtime_disable(&pdev->dev); arizona_jack_codec_dev_remove(&wm5110->core); return ret; diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 7dc6aaf65576..a4857024711d 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -971,9 +971,16 @@ static const struct i2c_device_id wm8961_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id); +static const struct of_device_id wm8961_of_match[] __maybe_unused = { + { .compatible = "wlf,wm8961", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8961_of_match); + static struct i2c_driver wm8961_i2c_driver = { .driver = { .name = "wm8961", + .of_match_table = of_match_ptr(wm8961_of_match), }, .probe_new = wm8961_i2c_probe, .id_table = wm8961_i2c_id, diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 81049664387e..b4b4355c6728 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1840,6 +1840,49 @@ SOC_SINGLE_TLV("SPKOUTR Mixer DACR Volume", WM8962_SPEAKER_MIXER_5, 4, 1, 0, inmix_tlv), }; +static int tp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret, reg, val, mask; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + dev_err(component->dev, "Failed to resume device: %d\n", ret); + return ret; + } + + reg = WM8962_ADDITIONAL_CONTROL_4; + + if (!strcmp(w->name, "TEMP_HP")) { + mask = WM8962_TEMP_ENA_HP_MASK; + val = WM8962_TEMP_ENA_HP; + } else if (!strcmp(w->name, "TEMP_SPK")) { + mask = WM8962_TEMP_ENA_SPK_MASK; + val = WM8962_TEMP_ENA_SPK; + } else { + pm_runtime_put(component->dev); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + val = 0; + fallthrough; + case SND_SOC_DAPM_POST_PMU: + ret = snd_soc_component_update_bits(component, reg, mask, val); + break; + default: + WARN(1, "Invalid event %d\n", event); + pm_runtime_put(component->dev); + return -EINVAL; + } + + pm_runtime_put(component->dev); + + return 0; +} + static int cp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -2140,8 +2183,10 @@ SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("DSP2", 1, WM8962_DSP2_POWER_MANAGEMENT, WM8962_DSP2_ENA_SHIFT, 0, dsp2_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -SND_SOC_DAPM_SUPPLY("TEMP_HP", WM8962_ADDITIONAL_CONTROL_4, 2, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("TEMP_SPK", WM8962_ADDITIONAL_CONTROL_4, 1, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("TEMP_HP", SND_SOC_NOPM, 0, 0, tp_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("TEMP_SPK", SND_SOC_NOPM, 0, 0, tp_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0, inpgal, ARRAY_SIZE(inpgal)), @@ -3763,6 +3808,11 @@ static int wm8962_i2c_probe(struct i2c_client *i2c) if (ret < 0) goto err_pm_runtime; + regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4, + WM8962_TEMP_ENA_HP_MASK, 0); + regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4, + WM8962_TEMP_ENA_SPK_MASK, 0); + regcache_cache_only(wm8962->regmap, true); /* The drivers should power up as needed */ diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index a682f8020eb6..aa2f55401a88 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -498,7 +498,7 @@ static int wm8978_configure_pll(struct snd_soc_component *component) if (4 * f_opclk < 3 * f_mclk) /* Have to use OPCLKDIV */ - opclk_div = (3 * f_mclk / 4 + f_opclk - 1) / f_opclk; + opclk_div = DIV_ROUND_UP(3 * f_mclk / 4, f_opclk); else opclk_div = 1; diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 77136a521605..c0207e9a7d53 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -1161,6 +1161,9 @@ static int wm8997_probe(struct platform_device *pdev) regmap_update_bits(arizona->regmap, wm8997_digital_vu[i], WM8997_DIG_VU, WM8997_DIG_VU); + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + arizona_init_common(arizona); ret = arizona_init_vol_limit(arizona); @@ -1179,14 +1182,12 @@ static int wm8997_probe(struct platform_device *pdev) goto err_spk_irqs; } - pm_runtime_enable(&pdev->dev); - pm_runtime_idle(&pdev->dev); - return ret; err_spk_irqs: arizona_free_spk_irqs(arizona); err_jack_codec_dev: + pm_runtime_disable(&pdev->dev); arizona_jack_codec_dev_remove(&wm8997->core); return ret; diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 79ef4e269bc9..eeaa75fb9196 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -47,11 +47,15 @@ struct fsl_micfil { struct clk *pll11k_clk; struct snd_dmaengine_dai_dma_data dma_params_rx; struct sdma_peripheral_config sdmacfg; + struct snd_soc_card *card; unsigned int dataline; char name[32]; int irq[MICFIL_IRQ_LINES]; enum quality quality; int dc_remover; + int vad_init_mode; + int vad_enabled; + int vad_detected; }; struct fsl_micfil_soc_data { @@ -152,6 +156,152 @@ static int micfil_quality_set(struct snd_kcontrol *kcontrol, return micfil_set_quality(micfil); } +static const char * const micfil_hwvad_enable[] = { + "Disable (Record only)", + "Enable (Record with Vad)", +}; + +static const char * const micfil_hwvad_init_mode[] = { + "Envelope mode", "Energy mode", +}; + +static const char * const micfil_hwvad_hpf_texts[] = { + "Filter bypass", + "Cut-off @1750Hz", + "Cut-off @215Hz", + "Cut-off @102Hz", +}; + +/* + * DC Remover Control + * Filter Bypassed 1 1 + * Cut-off @21Hz 0 0 + * Cut-off @83Hz 0 1 + * Cut-off @152HZ 1 0 + */ +static const char * const micfil_dc_remover_texts[] = { + "Cut-off @21Hz", "Cut-off @83Hz", + "Cut-off @152Hz", "Bypass", +}; + +static const struct soc_enum hwvad_enable_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_enable), + micfil_hwvad_enable); +static const struct soc_enum hwvad_init_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_hwvad_init_mode), + micfil_hwvad_init_mode); +static const struct soc_enum hwvad_hpf_enum = + SOC_ENUM_SINGLE(REG_MICFIL_VAD0_CTRL2, 0, + ARRAY_SIZE(micfil_hwvad_hpf_texts), + micfil_hwvad_hpf_texts); +static const struct soc_enum fsl_micfil_dc_remover_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_dc_remover_texts), + micfil_dc_remover_texts); + +static int micfil_put_dc_remover_state(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); + unsigned int *item = ucontrol->value.enumerated.item; + int val = snd_soc_enum_item_to_val(e, item[0]); + int i = 0, ret = 0; + u32 reg_val = 0; + + if (val < 0 || val > 3) + return -EINVAL; + + micfil->dc_remover = val; + + /* Calculate total value for all channels */ + for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) + reg_val |= val << MICFIL_DC_CHX_SHIFT(i); + + /* Update DC Remover mode for all channels */ + ret = snd_soc_component_update_bits(comp, REG_MICFIL_DC_CTRL, + MICFIL_DC_CTRL_CONFIG, reg_val); + if (ret < 0) + return ret; + + return 0; +} + +static int micfil_get_dc_remover_state(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = micfil->dc_remover; + + return 0; +} + +static int hwvad_put_enable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); + int val = snd_soc_enum_item_to_val(e, item[0]); + + micfil->vad_enabled = val; + + return 0; +} + +static int hwvad_get_enable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = micfil->vad_enabled; + + return 0; +} + +static int hwvad_put_init_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); + int val = snd_soc_enum_item_to_val(e, item[0]); + + /* 0 - Envelope-based Mode + * 1 - Energy-based Mode + */ + micfil->vad_init_mode = val; + + return 0; +} + +static int hwvad_get_init_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = micfil->vad_init_mode; + + return 0; +} + +static int hwvad_detected(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct fsl_micfil *micfil = snd_soc_component_get_drvdata(comp); + + ucontrol->value.enumerated.item[0] = micfil->vad_detected; + + return 0; +} + static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0x7, gain_tlv), @@ -172,6 +322,27 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { SOC_ENUM_EXT("MICFIL Quality Select", fsl_micfil_quality_enum, micfil_quality_get, micfil_quality_set), + SOC_ENUM_EXT("HWVAD Enablement Switch", hwvad_enable_enum, + hwvad_get_enable, hwvad_put_enable), + SOC_ENUM_EXT("HWVAD Initialization Mode", hwvad_init_mode_enum, + hwvad_get_init_mode, hwvad_put_init_mode), + SOC_ENUM("HWVAD High-Pass Filter", hwvad_hpf_enum), + SOC_SINGLE("HWVAD ZCD Switch", REG_MICFIL_VAD0_ZCD, 0, 1, 0), + SOC_SINGLE("HWVAD ZCD Auto Threshold Switch", + REG_MICFIL_VAD0_ZCD, 2, 1, 0), + SOC_ENUM_EXT("MICFIL DC Remover Control", fsl_micfil_dc_remover_enum, + micfil_get_dc_remover_state, micfil_put_dc_remover_state), + SOC_SINGLE("HWVAD Input Gain", REG_MICFIL_VAD0_CTRL2, 8, 15, 0), + SOC_SINGLE("HWVAD Sound Gain", REG_MICFIL_VAD0_SCONFIG, 0, 15, 0), + SOC_SINGLE("HWVAD Noise Gain", REG_MICFIL_VAD0_NCONFIG, 0, 15, 0), + SOC_SINGLE_RANGE("HWVAD Detector Frame Time", REG_MICFIL_VAD0_CTRL2, 16, 0, 63, 0), + SOC_SINGLE("HWVAD Detector Initialization Time", REG_MICFIL_VAD0_CTRL1, 8, 31, 0), + SOC_SINGLE("HWVAD Noise Filter Adjustment", REG_MICFIL_VAD0_NCONFIG, 8, 31, 0), + SOC_SINGLE("HWVAD ZCD Threshold", REG_MICFIL_VAD0_ZCD, 16, 1023, 0), + SOC_SINGLE("HWVAD ZCD Adjustment", REG_MICFIL_VAD0_ZCD, 8, 15, 0), + SOC_SINGLE("HWVAD ZCD And Behavior Switch", + REG_MICFIL_VAD0_ZCD, 4, 1, 0), + SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), }; /* The SRES is a self-negated bit which provides the CPU with the @@ -210,6 +381,167 @@ static int fsl_micfil_startup(struct snd_pcm_substream *substream, return 0; } +/* Enable/disable hwvad interrupts */ +static int fsl_micfil_configure_hwvad_interrupts(struct fsl_micfil *micfil, int enable) +{ + u32 vadie_reg = enable ? MICFIL_VAD0_CTRL1_IE : 0; + u32 vaderie_reg = enable ? MICFIL_VAD0_CTRL1_ERIE : 0; + + /* Voice Activity Detector Error Interruption */ + regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_ERIE, vaderie_reg); + + /* Voice Activity Detector Interruption */ + regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_IE, vadie_reg); + + return 0; +} + +/* Configuration done only in energy-based initialization mode */ +static int fsl_micfil_init_hwvad_energy_mode(struct fsl_micfil *micfil) +{ + /* Keep the VADFRENDIS bitfield cleared. */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_FRENDIS); + + /* Keep the VADPREFEN bitfield cleared. */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_PREFEN); + + /* Keep the VADSFILEN bitfield cleared. */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, + MICFIL_VAD0_SCONFIG_SFILEN); + + /* Keep the VADSMAXEN bitfield cleared. */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, + MICFIL_VAD0_SCONFIG_SMAXEN); + + /* Keep the VADNFILAUTO bitfield asserted. */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NFILAUT); + + /* Keep the VADNMINEN bitfield cleared. */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NMINEN); + + /* Keep the VADNDECEN bitfield cleared. */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NDECEN); + + /* Keep the VADNOREN bitfield cleared. */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NOREN); + + return 0; +} + +/* Configuration done only in envelope-based initialization mode */ +static int fsl_micfil_init_hwvad_envelope_mode(struct fsl_micfil *micfil) +{ + /* Assert the VADFRENDIS bitfield */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_FRENDIS); + + /* Assert the VADPREFEN bitfield. */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL2, + MICFIL_VAD0_CTRL2_PREFEN); + + /* Assert the VADSFILEN bitfield. */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, + MICFIL_VAD0_SCONFIG_SFILEN); + + /* Assert the VADSMAXEN bitfield. */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_SCONFIG, + MICFIL_VAD0_SCONFIG_SMAXEN); + + /* Clear the VADNFILAUTO bitfield */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NFILAUT); + + /* Assert the VADNMINEN bitfield. */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NMINEN); + + /* Assert the VADNDECEN bitfield. */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NDECEN); + + /* Assert VADNOREN bitfield. */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_NCONFIG, + MICFIL_VAD0_NCONFIG_NOREN); + + return 0; +} + +/* + * Hardware Voice Active Detection: The HWVAD takes data from the input + * of a selected PDM microphone to detect if there is any + * voice activity. When a voice activity is detected, an interrupt could + * be delivered to the system. Initialization in section 8.4: + * Can work in two modes: + * -> Eneveope-based mode (section 8.4.1) + * -> Energy-based mode (section 8.4.2) + * + * It is important to remark that the HWVAD detector could be enabled + * or reset only when the MICFIL isn't running i.e. when the BSY_FIL + * bit in STAT register is cleared + */ +static int fsl_micfil_hwvad_enable(struct fsl_micfil *micfil) +{ + int ret; + + micfil->vad_detected = 0; + + /* envelope-based specific initialization */ + if (micfil->vad_init_mode == MICFIL_HWVAD_ENVELOPE_MODE) + ret = fsl_micfil_init_hwvad_envelope_mode(micfil); + else + ret = fsl_micfil_init_hwvad_energy_mode(micfil); + if (ret) + return ret; + + /* Voice Activity Detector Internal Filters Initialization*/ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_ST10); + + /* Voice Activity Detector Internal Filter */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_ST10); + + /* Enable Interrupts */ + ret = fsl_micfil_configure_hwvad_interrupts(micfil, 1); + if (ret) + return ret; + + /* Voice Activity Detector Reset */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_RST); + + /* Voice Activity Detector Enabled */ + regmap_set_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_EN); + + return 0; +} + +static int fsl_micfil_hwvad_disable(struct fsl_micfil *micfil) +{ + struct device *dev = &micfil->pdev->dev; + int ret = 0; + + /* Disable HWVAD */ + regmap_clear_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_EN); + + /* Disable hwvad interrupts */ + ret = fsl_micfil_configure_hwvad_interrupts(micfil, 0); + if (ret) + dev_err(dev, "Failed to disable interrupts\n"); + + return ret; +} + static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -245,10 +577,16 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, if (ret) return ret; + if (micfil->vad_enabled) + fsl_micfil_hwvad_enable(micfil); + break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (micfil->vad_enabled) + fsl_micfil_hwvad_disable(micfil); + /* Disable the module */ ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, MICFIL_CTRL1_PDMIEN); @@ -328,6 +666,16 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) | FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr)); + /* Configure CIC OSR in VADCICOSR */ + regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_CICOSR, + FIELD_PREP(MICFIL_VAD0_CTRL1_CICOSR, 16 - osr)); + + /* Configure source channel in VADCHSEL */ + regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, + MICFIL_VAD0_CTRL1_CHSEL, + FIELD_PREP(MICFIL_VAD0_CTRL1_CHSEL, (channels - 1))); + micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg; micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg); micfil->sdmacfg.n_fifos_src = channels; @@ -351,6 +699,7 @@ static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) int ret, i; micfil->quality = QUALITY_VLOW0; + micfil->card = cpu_dai->component->card; /* set default gain to 2 */ regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x22222222); @@ -585,6 +934,71 @@ static irqreturn_t micfil_err_isr(int irq, void *devid) return IRQ_HANDLED; } +static irqreturn_t voice_detected_fn(int irq, void *devid) +{ + struct fsl_micfil *micfil = (struct fsl_micfil *)devid; + struct snd_kcontrol *kctl; + + if (!micfil->card) + return IRQ_HANDLED; + + kctl = snd_soc_card_get_kcontrol(micfil->card, "VAD Detected"); + if (!kctl) + return IRQ_HANDLED; + + if (micfil->vad_detected) + snd_ctl_notify(micfil->card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, + &kctl->id); + + return IRQ_HANDLED; +} + +static irqreturn_t hwvad_isr(int irq, void *devid) +{ + struct fsl_micfil *micfil = (struct fsl_micfil *)devid; + struct device *dev = &micfil->pdev->dev; + u32 vad0_reg; + int ret; + + regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); + + /* + * The only difference between MICFIL_VAD0_STAT_EF and + * MICFIL_VAD0_STAT_IF is that the former requires Write + * 1 to Clear. Since both flags are set, it is enough + * to only read one of them + */ + if (vad0_reg & MICFIL_VAD0_STAT_IF) { + /* Write 1 to clear */ + regmap_write_bits(micfil->regmap, REG_MICFIL_VAD0_STAT, + MICFIL_VAD0_STAT_IF, + MICFIL_VAD0_STAT_IF); + + micfil->vad_detected = 1; + } + + ret = fsl_micfil_hwvad_disable(micfil); + if (ret) + dev_err(dev, "Failed to disable hwvad\n"); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t hwvad_err_isr(int irq, void *devid) +{ + struct fsl_micfil *micfil = (struct fsl_micfil *)devid; + struct device *dev = &micfil->pdev->dev; + u32 vad0_reg; + + regmap_read(micfil->regmap, REG_MICFIL_VAD0_STAT, &vad0_reg); + + if (vad0_reg & MICFIL_VAD0_STAT_INSATF) + dev_dbg(dev, "voice activity input overflow/underflow detected\n"); + + return IRQ_HANDLED; +} + static int fsl_micfil_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -677,6 +1091,26 @@ static int fsl_micfil_probe(struct platform_device *pdev) return ret; } + /* Digital Microphone interface voice activity detector event */ + ret = devm_request_threaded_irq(&pdev->dev, micfil->irq[2], + hwvad_isr, voice_detected_fn, + IRQF_SHARED, micfil->name, micfil); + if (ret) { + dev_err(&pdev->dev, "failed to claim hwvad event irq %u\n", + micfil->irq[0]); + return ret; + } + + /* Digital Microphone interface voice activity detector error */ + ret = devm_request_irq(&pdev->dev, micfil->irq[3], + hwvad_err_isr, IRQF_SHARED, + micfil->name, micfil); + if (ret) { + dev_err(&pdev->dev, "failed to claim hwvad error irq %u\n", + micfil->irq[1]); + return ret; + } + micfil->dma_params_rx.chan_name = "rx"; micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0; micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX; diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h index d60285dd07bc..9237a1c4cb8f 100644 --- a/sound/soc/fsl/fsl_micfil.h +++ b/sound/soc/fsl/fsl_micfil.h @@ -136,10 +136,14 @@ #define FIFO_PTRWID 3 #define FIFO_LEN BIT(FIFO_PTRWID) -#define MICFIL_IRQ_LINES 2 +#define MICFIL_IRQ_LINES 4 #define MICFIL_MAX_RETRY 25 #define MICFIL_SLEEP_MIN 90000 /* in us */ #define MICFIL_SLEEP_MAX 100000 /* in us */ #define MICFIL_DMA_MAXBURST_RX 6 +/* HWVAD Constants */ +#define MICFIL_HWVAD_ENVELOPE_MODE 0 +#define MICFIL_HWVAD_ENERGY_MODE 1 + #endif /* _FSL_MICFIL_H */ diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c index bf94838bdbef..46c7868a2653 100644 --- a/sound/soc/fsl/fsl_rpmsg.c +++ b/sound/soc/fsl/fsl_rpmsg.c @@ -117,14 +117,14 @@ static struct snd_soc_dai_driver fsl_rpmsg_dai = { .playback = { .stream_name = "CPU-Playback", .channels_min = 2, - .channels_max = 2, + .channels_max = 32, .rates = SNDRV_PCM_RATE_KNOT, .formats = FSL_RPMSG_FORMATS, }, .capture = { .stream_name = "CPU-Capture", .channels_min = 2, - .channels_max = 2, + .channels_max = 32, .rates = SNDRV_PCM_RATE_KNOT, .formats = FSL_RPMSG_FORMATS, }, @@ -235,7 +235,7 @@ static int fsl_rpmsg_probe(struct platform_device *pdev) rpmsg->card_pdev = platform_device_register_data(&pdev->dev, "imx-audio-rpmsg", - PLATFORM_DEVID_NONE, + PLATFORM_DEVID_AUTO, NULL, 0); if (IS_ERR(rpmsg->card_pdev)) { diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c index 905c3a071300..d5234ac4b09b 100644 --- a/sound/soc/fsl/imx-audio-rpmsg.c +++ b/sound/soc/fsl/imx-audio-rpmsg.c @@ -88,7 +88,7 @@ static int imx_audio_rpmsg_probe(struct rpmsg_device *rpdev) /* Register platform driver for rpmsg routine */ data->rpmsg_pdev = platform_device_register_data(&rpdev->dev, IMX_PCM_DRV_NAME, - PLATFORM_DEVID_NONE, + PLATFORM_DEVID_AUTO, NULL, 0); if (IS_ERR(data->rpmsg_pdev)) { dev_err(&rpdev->dev, "failed to register rpmsg platform.\n"); @@ -110,6 +110,7 @@ static void imx_audio_rpmsg_remove(struct rpmsg_device *rpdev) static struct rpmsg_device_id imx_audio_rpmsg_id_table[] = { { .name = "rpmsg-audio-channel" }, + { .name = "rpmsg-micfil-channel" }, { }, }; diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 35049043e532..2f310994f7ee 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -178,7 +178,7 @@ static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component, msg->s_msg.param.channels = RPMSG_CH_STEREO; break; default: - ret = -EINVAL; + msg->s_msg.param.channels = params_channels(params); break; } @@ -684,7 +684,7 @@ static int imx_rpmsg_pcm_probe(struct platform_device *pdev) info->rpdev = container_of(pdev->dev.parent, struct rpmsg_device, dev); info->dev = &pdev->dev; /* Setup work queue */ - info->rpmsg_wq = alloc_ordered_workqueue("rpmsg_audio", + info->rpmsg_wq = alloc_ordered_workqueue(info->rpdev->id.name, WQ_HIGHPRI | WQ_UNBOUND | WQ_FREEZABLE); @@ -723,11 +723,15 @@ static int imx_rpmsg_pcm_probe(struct platform_device *pdev) if (ret) goto fail; - component = snd_soc_lookup_component(&pdev->dev, IMX_PCM_DRV_NAME); + component = snd_soc_lookup_component(&pdev->dev, NULL); if (!component) { ret = -EINVAL; goto fail; } + + /* platform component name is used by machine driver to link with */ + component->name = info->rpdev->id.name; + #ifdef CONFIG_DEBUG_FS component->debugfs_prefix = "rpmsg"; #endif diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 4d99f4858a14..89178106fe2c 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -58,6 +58,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev) struct platform_device *rpmsg_pdev = to_platform_device(dev); struct device_node *np = rpmsg_pdev->dev.of_node; struct of_phandle_args args; + const char *platform_name; struct imx_rpmsg *data; int ret = 0; @@ -109,7 +110,10 @@ static int imx_rpmsg_probe(struct platform_device *pdev) } data->dai.cpus->dai_name = dev_name(&rpmsg_pdev->dev); - data->dai.platforms->name = IMX_PCM_DRV_NAME; + if (!of_property_read_string(np, "fsl,rpmsg-channel-name", &platform_name)) + data->dai.platforms->name = platform_name; + else + data->dai.platforms->name = "rpmsg-audio-channel"; data->dai.playback_only = true; data->dai.capture_only = true; data->card.num_links = 1; diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index b8e2b23c9f64..7c8ce98eda9d 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -133,12 +133,14 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) buf = apl_log_payload_addr(addr); memcpy_fromio(&layout, addr, sizeof(layout)); if (!apl_is_entry_stackdump(buf + layout.read_ptr)) { + union avs_notify_msg lbs_msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); + /* * DSP awaits the remaining logs to be * gathered before dumping stack */ - msg->log.core = msg->ext.coredump.core_id; - avs_dsp_op(adev, log_buffer_status, msg); + lbs_msg.log.core = msg->ext.coredump.core_id; + avs_dsp_op(adev, log_buffer_status, &lbs_msg); } pos = dump + AVS_FW_REGS_SIZE; diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 92e37722d280..91f78eb11bc1 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -220,8 +220,10 @@ static inline void avs_ipc_err(struct avs_dev *adev, struct avs_ipc_msg *tx, /* * If IPC channel is blocked e.g.: due to ongoing recovery, * -EPERM error code is expected and thus it's not an actual error. + * + * Unsupported IPCs are of no harm either. */ - if (error == -EPERM) + if (error == -EPERM || error == AVS_IPC_NOT_SUPPORTED) dev_dbg(adev->dev, "%s 0x%08x 0x%08x failed: %d\n", name, tx->glb.primary, tx->glb.ext.val, error); else diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 87f9c18be238..01c1a5324b51 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -29,6 +29,12 @@ static const struct dmi_system_id kbl_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "Skylake Y LPDDR3 RVP3"), }, }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "AmberLake Y"), + }, + }, {} }; @@ -122,6 +128,14 @@ static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = { .tplg_filename = "rt298-tplg.bin", }, { + .id = "MX98927", + .drv_name = "avs_max98927", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "max98927-tplg.bin", + }, + { .id = "MX98373", .drv_name = "avs_max98373", .mach_params = { diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig index 4d68e3ef992b..9bd40fdd9028 100644 --- a/sound/soc/intel/avs/boards/Kconfig +++ b/sound/soc/intel/avs/boards/Kconfig @@ -36,6 +36,16 @@ config SND_SOC_INTEL_AVS_MACH_I2S_TEST This adds support for I2S test-board which can be used to verify transfer over I2S interface with SSP loopback scenarios. +config SND_SOC_INTEL_AVS_MACH_MAX98927 + tristate "max98927 I2S board" + depends on I2C + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_MAX98927 + help + This adds support for AVS with MAX98927 I2S codec configuration. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_AVS_MACH_MAX98357A tristate "max98357A I2S board" depends on I2C diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile index bc75376d58c2..4d70b8d09ce5 100644 --- a/sound/soc/intel/avs/boards/Makefile +++ b/sound/soc/intel/avs/boards/Makefile @@ -4,6 +4,7 @@ snd-soc-avs-da7219-objs := da7219.o snd-soc-avs-dmic-objs := dmic.o snd-soc-avs-hdaudio-objs := hdaudio.o snd-soc-avs-i2s-test-objs := i2s_test.o +snd-soc-avs-max98927-objs := max98927.o snd-soc-avs-max98357a-objs := max98357a.o snd-soc-avs-max98373-objs := max98373.o snd-soc-avs-nau8825-objs := nau8825.o @@ -17,6 +18,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DA7219) += snd-soc-avs-da7219.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DMIC) += snd-soc-avs-dmic.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_HDAUDIO) += snd-soc-avs-hdaudio.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_I2S_TEST) += snd-soc-avs-i2s-test.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 02ae542ad779..503a967a1c3a 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -6,6 +6,7 @@ // #include <linux/module.h> +#include <linux/platform_data/x86/soc.h> #include <linux/platform_device.h> #include <sound/jack.h> #include <sound/pcm.h> @@ -80,7 +81,10 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) int ret; jack = snd_soc_card_get_drvdata(card); - clk_freq = 19200000; + if (soc_intel_is_apl()) + clk_freq = 19200000; + else /* kbl */ + clk_freq = 24576000; ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, clk_freq, SND_SOC_CLOCK_IN); if (ret) { diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c index 073663ba140d..e68c4c7aa2ba 100644 --- a/sound/soc/intel/avs/boards/hdaudio.c +++ b/sound/soc/intel/avs/boards/hdaudio.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/module.h> #include <linux/platform_device.h> #include <sound/hda_codec.h> #include <sound/hda_i915.h> diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c new file mode 100644 index 000000000000..35c4f8f55035 --- /dev/null +++ b/sound/soc/intel/avs/boards/max98927.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <sound/soc-dapm.h> + +#define MAX98927_DEV0_NAME "i2c-MX98927:00" +#define MAX98927_DEV1_NAME "i2c-MX98927:01" +#define MAX98927_CODEC_NAME "max98927-aif1" + +static struct snd_soc_codec_conf card_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAX98927_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX98927_DEV1_NAME), + .name_prefix = "Left", + }, +}; + +static const struct snd_kcontrol_new card_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_soc_dapm_widget card_widgets[] = { + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), +}; + +static const struct snd_soc_dapm_route card_base_routes[] = { + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, +}; + +static int +avs_max98927_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate, *channels; + struct snd_mask *fmt; + + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP0 to 16 bit */ + snd_mask_none(fmt); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static int avs_max98927_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int ret = 0; + int i; + + for_each_rtd_codec_dais(runtime, i, codec_dai) { + if (!strcmp(codec_dai->component->name, MAX98927_DEV0_NAME)) + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16); + else if (!strcmp(codec_dai->component->name, MAX98927_DEV1_NAME)) + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16); + + if (ret < 0) { + dev_err(runtime->dev, "hw_params for %s failed: %d\n", + codec_dai->component->name, ret); + return ret; + } + } + + return 0; +} + +static const struct snd_soc_ops avs_max98927_ops = { + .hw_params = avs_max98927_hw_params, +}; + +static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port, + struct snd_soc_dai_link **dai_link) +{ + struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link *dl; + + dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL); + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); + if (!dl || !platform) + return -ENOMEM; + + platform->name = platform_name; + + dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); + dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); + dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs) * 2, GFP_KERNEL); + if (!dl->name || !dl->cpus || !dl->codecs) + return -ENOMEM; + + dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port); + dl->codecs[0].name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_DEV0_NAME); + dl->codecs[0].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_CODEC_NAME); + dl->codecs[1].name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_DEV1_NAME); + dl->codecs[1].dai_name = devm_kasprintf(dev, GFP_KERNEL, MAX98927_CODEC_NAME); + if (!dl->cpus->dai_name || !dl->codecs[0].name || !dl->codecs[0].dai_name || + !dl->codecs[1].name || !dl->codecs[1].dai_name) + return -ENOMEM; + + dl->num_cpus = 1; + dl->num_codecs = 2; + dl->platforms = platform; + dl->num_platforms = 1; + dl->id = 0; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->be_hw_params_fixup = avs_max98927_be_fixup; + dl->nonatomic = 1; + dl->no_pcm = 1; + dl->dpcm_capture = 1; + dl->dpcm_playback = 1; + dl->ignore_pmdown_time = 1; + dl->ops = &avs_max98927_ops; + + *dai_link = dl; + + return 0; +} + +static int avs_create_dapm_routes(struct device *dev, int ssp_port, + struct snd_soc_dapm_route **routes, int *num_routes) +{ + struct snd_soc_dapm_route *dr; + const int num_base = ARRAY_SIZE(card_base_routes); + const int num_dr = num_base + 2; + int idx; + + dr = devm_kcalloc(dev, num_dr, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + memcpy(dr, card_base_routes, num_base * sizeof(*dr)); + + idx = num_base; + dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Left HiFi Playback"); + dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); + if (!dr[idx].sink || !dr[idx].source) + return -ENOMEM; + + idx++; + dr[idx].sink = devm_kasprintf(dev, GFP_KERNEL, "Right HiFi Playback"); + dr[idx].source = devm_kasprintf(dev, GFP_KERNEL, "ssp%d Tx", ssp_port); + if (!dr[idx].sink || !dr[idx].source) + return -ENOMEM; + + *routes = dr; + *num_routes = num_dr; + + return 0; +} + +static int avs_max98927_probe(struct platform_device *pdev) +{ + struct snd_soc_dapm_route *routes; + struct snd_soc_dai_link *dai_link; + struct snd_soc_acpi_mach *mach; + struct snd_soc_card *card; + struct device *dev = &pdev->dev; + const char *pname; + int num_routes, ssp_port, ret; + + mach = dev_get_platdata(dev); + pname = mach->mach_params.platform; + ssp_port = __ffs(mach->mach_params.i2s_link_mask); + + ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link); + if (ret) { + dev_err(dev, "Failed to create dai link: %d", ret); + return ret; + } + + ret = avs_create_dapm_routes(dev, ssp_port, &routes, &num_routes); + if (ret) { + dev_err(dev, "Failed to create dapm routes: %d", ret); + return ret; + } + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->name = "avs_max98927"; + card->dev = dev; + card->owner = THIS_MODULE; + card->dai_link = dai_link; + card->num_links = 1; + card->codec_conf = card_codec_conf; + card->num_configs = ARRAY_SIZE(card_codec_conf); + card->controls = card_controls; + card->num_controls = ARRAY_SIZE(card_controls); + card->dapm_widgets = card_widgets; + card->num_dapm_widgets = ARRAY_SIZE(card_widgets); + card->dapm_routes = routes; + card->num_dapm_routes = num_routes; + card->fully_routed = true; + + ret = snd_soc_fixup_dai_links_platform_name(card, pname); + if (ret) + return ret; + + return devm_snd_soc_register_card(dev, card); +} + +static struct platform_driver avs_max98927_driver = { + .probe = avs_max98927_probe, + .driver = { + .name = "avs_max98927", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(avs_max98927_driver) + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:avs_max98927"); diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index b28d36872dcb..58c9d9edecf0 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/dmi.h> #include <linux/module.h> #include <sound/jack.h> #include <sound/pcm.h> @@ -14,6 +15,16 @@ #include <sound/soc-acpi.h> #include "../../../codecs/rt298.h" +static const struct dmi_system_id kblr_dmi_table[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"), + }, + }, + {} +}; + static const struct snd_kcontrol_new card_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Mic Jack"), @@ -96,9 +107,15 @@ avs_rt298_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_param { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + unsigned int clk_freq; int ret; - ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, 19200000, SND_SOC_CLOCK_IN); + if (dmi_first_match(kblr_dmi_table)) + clk_freq = 24000000; + else + clk_freq = 19200000; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, clk_freq, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(rtd->dev, "Set codec sysclk failed: %d\n", ret); @@ -139,7 +156,10 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + if (dmi_first_match(kblr_dmi_table)) + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + else + dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; dl->init = avs_rt298_codec_init; dl->be_hw_params_fixup = avs_rt298_be_fixup; dl->ops = &avs_rt298_ops; diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 8d502fee38b5..0aaded90a99a 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -440,7 +440,7 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); - if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) + if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); dma_set_max_seg_size(dev, UINT_MAX); @@ -580,7 +580,6 @@ static int __maybe_unused avs_suspend_common(struct avs_dev *adev) static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool purge) { struct hdac_bus *bus = &adev->base.core; - struct hdac_ext_link *hlink; int ret; snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); @@ -595,16 +594,6 @@ static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool purge) return ret; } - /* turn off the links that were off before suspend */ - list_for_each_entry(hlink, &bus->hlink_list, list) { - if (!hlink->ref_count) - snd_hdac_ext_bus_link_power_down(hlink); - } - - /* check dma status and clean up CORB/RIRB buffers */ - if (!bus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(bus); - return 0; } @@ -667,7 +656,11 @@ static const struct avs_spec apl_desc = { static const struct pci_device_id avs_ids[] = { { PCI_VDEVICE(INTEL, 0x9d70), (unsigned long)&skl_desc }, /* SKL */ + { PCI_VDEVICE(INTEL, 0xa170), (unsigned long)&skl_desc }, /* SKL-H */ { PCI_VDEVICE(INTEL, 0x9d71), (unsigned long)&skl_desc }, /* KBL */ + { PCI_VDEVICE(INTEL, 0xa171), (unsigned long)&skl_desc }, /* KBL-H */ + { PCI_VDEVICE(INTEL, 0xa2f0), (unsigned long)&skl_desc }, /* KBL-S */ + { PCI_VDEVICE(INTEL, 0xa3f0), (unsigned long)&skl_desc }, /* CML-V */ { PCI_VDEVICE(INTEL, 0x5a98), (unsigned long)&apl_desc }, /* APL */ { PCI_VDEVICE(INTEL, 0x3198), (unsigned long)&apl_desc }, /* GML */ { 0 } diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 020d85c7520d..152f8d0bdf8e 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -74,7 +74,7 @@ int avs_dsp_disable_d0ix(struct avs_dev *adev) struct avs_ipc *ipc = adev->ipc; /* Prevent PG only on the first disable. */ - if (atomic_add_return(1, &ipc->d0ix_disable_depth) == 1) { + if (atomic_inc_return(&ipc->d0ix_disable_depth) == 1) { cancel_delayed_work_sync(&ipc->d0ix_work); return avs_dsp_set_d0ix(adev, false); } @@ -192,7 +192,8 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header) /* update size in case of LARGE_CONFIG_GET */ if (msg.msg_target == AVS_MOD_MSG && msg.global_msg_type == AVS_MOD_LARGE_CONFIG_GET) - ipc->rx.size = msg.ext.large_config.data_off_size; + ipc->rx.size = min_t(u32, AVS_MAILBOX_SIZE, + msg.ext.large_config.data_off_size); memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev), ipc->rx.size); trace_avs_msg_payload(ipc->rx.data, ipc->rx.size); diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 34923558dfa5..eb10e45790e7 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -43,7 +43,7 @@ /* Occasionally, engineering (release candidate) firmware is provided for testing. */ static bool debug_ignore_fw_version; module_param_named(ignore_fw_version, debug_ignore_fw_version, bool, 0444); -MODULE_PARM_DESC(ignore_fw_version, "Verify FW version 0=yes (default), 1=no"); +MODULE_PARM_DESC(ignore_fw_version, "Ignore firmware version check 0=no (default), 1=yes"); #define AVS_LIB_NAME_SIZE 8 diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index d4bcee1aabcf..6b0fecbf07c3 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -687,20 +687,13 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info) int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size) { - int ret; - - ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, - AVS_BASEFW_ENABLE_LOGS, log_info, size); - if (ret) - dev_err(adev->dev, "enable logs failed: %d\n", ret); - - return ret; + return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, + AVS_BASEFW_ENABLE_LOGS, log_info, size); } int avs_ipc_set_system_time(struct avs_dev *adev) { struct avs_sys_time sys_time; - int ret; u64 us; /* firmware expects UTC time in micro seconds */ @@ -708,12 +701,8 @@ int avs_ipc_set_system_time(struct avs_dev *adev) sys_time.val_l = us & UINT_MAX; sys_time.val_u = us >> 32; - ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, - AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time)); - if (ret) - dev_err(adev->dev, "set system time failed: %d\n", ret); - - return ret; + return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, + AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time)); } int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index c0f90dba9af8..02b3b7a74783 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -150,6 +150,8 @@ union avs_module_msg { }; } __packed; +#define AVS_IPC_NOT_SUPPORTED 15 + union avs_reply_msg { u64 val; struct { diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 95cb87339400..293336c2fc63 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -1016,10 +1016,8 @@ static void avs_component_hda_unregister_dais(struct snd_soc_component *componen if (!strstr(dai->driver->name, name)) continue; - if (dai->playback_widget) - snd_soc_dapm_free_widget(dai->playback_widget); - if (dai->capture_widget) - snd_soc_dapm_free_widget(dai->capture_widget); + snd_soc_dapm_free_widget(dai->playback_widget); + snd_soc_dapm_free_widget(dai->capture_widget); snd_soc_unregister_dai(dai); } } diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index bda5ec7510fe..dc98b5cf900f 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -28,12 +28,12 @@ static int skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 info->core_mask = resource_mask; if (enable) - for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0)) { + for_each_set_bit(i, &resource_mask, num_cores) { info->logs_core[i].enable = enable; info->logs_core[i].min_priority = *priorities++; } else - for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0)) + for_each_set_bit(i, &resource_mask, num_cores) info->logs_core[i].enable = enable; ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 780207fe814f..c6f319bcd2c4 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -387,15 +387,6 @@ static int skl_resume(struct device *dev) snd_hdac_bus_init_cmd_io(bus); } else { ret = _skl_resume(bus); - - /* turn off the links which are off before suspend */ - list_for_each_entry(hlink, &bus->hlink_list, list) { - if (!hlink->ref_count) - snd_hdac_ext_bus_link_power_down(hlink); - } - - if (!bus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(bus); } return ret; diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 2a4ffe945177..afdf7d61e4c5 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -31,6 +31,122 @@ (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE) +/* These registers are relative to the second register region - + * audio pll configuration. + */ +#define A38X_PLL_CONF_REG0 0x0 +#define A38X_PLL_FB_CLK_DIV_OFFSET 10 +#define A38X_PLL_FB_CLK_DIV_MASK 0x7fc00 +#define A38X_PLL_CONF_REG1 0x4 +#define A38X_PLL_FREQ_OFFSET_MASK 0xffff +#define A38X_PLL_FREQ_OFFSET_VALID BIT(16) +#define A38X_PLL_SW_RESET BIT(31) +#define A38X_PLL_CONF_REG2 0x8 +#define A38X_PLL_AUDIO_POSTDIV_MASK 0x7f + +/* Bit below belongs to SoC control register corresponding to the third + * register region. + */ +#define A38X_SPDIF_MODE_ENABLE BIT(27) + +static int armada_38x_i2s_init_quirk(struct platform_device *pdev, + struct kirkwood_dma_data *priv, + struct snd_soc_dai_driver *dai_drv) +{ + struct device_node *np = pdev->dev.of_node; + u32 reg_val; + int i; + + priv->pll_config = devm_platform_ioremap_resource_byname(pdev, "pll_regs"); + if (IS_ERR(priv->pll_config)) + return -ENOMEM; + + priv->soc_control = devm_platform_ioremap_resource_byname(pdev, "soc_ctrl"); + if (IS_ERR(priv->soc_control)) + return -ENOMEM; + + /* Select one of exceptive modes: I2S or S/PDIF */ + reg_val = readl(priv->soc_control); + if (of_property_read_bool(np, "spdif-mode")) { + reg_val |= A38X_SPDIF_MODE_ENABLE; + dev_info(&pdev->dev, "using S/PDIF mode\n"); + } else { + reg_val &= ~A38X_SPDIF_MODE_ENABLE; + dev_info(&pdev->dev, "using I2S mode\n"); + } + writel(reg_val, priv->soc_control); + + /* Update available rates of mclk's fs */ + for (i = 0; i < 2; i++) { + dai_drv[i].playback.rates |= SNDRV_PCM_RATE_192000; + dai_drv[i].capture.rates |= SNDRV_PCM_RATE_192000; + } + + return 0; +} + +static inline void armada_38x_set_pll(void __iomem *base, unsigned long rate) +{ + u32 reg_val; + u16 freq_offset = 0x22b0; + u8 audio_postdiv, fb_clk_div = 0x1d; + + /* Set frequency offset value to not valid and enable PLL reset */ + reg_val = readl(base + A38X_PLL_CONF_REG1); + reg_val &= ~A38X_PLL_FREQ_OFFSET_VALID; + reg_val &= ~A38X_PLL_SW_RESET; + writel(reg_val, base + A38X_PLL_CONF_REG1); + + udelay(1); + + /* Update PLL parameters */ + switch (rate) { + default: + case 44100: + freq_offset = 0x735; + fb_clk_div = 0x1b; + audio_postdiv = 0xc; + break; + case 48000: + audio_postdiv = 0xc; + break; + case 96000: + audio_postdiv = 0x6; + break; + case 192000: + audio_postdiv = 0x3; + break; + } + + reg_val = readl(base + A38X_PLL_CONF_REG0); + reg_val &= ~A38X_PLL_FB_CLK_DIV_MASK; + reg_val |= (fb_clk_div << A38X_PLL_FB_CLK_DIV_OFFSET); + writel(reg_val, base + A38X_PLL_CONF_REG0); + + reg_val = readl(base + A38X_PLL_CONF_REG2); + reg_val &= ~A38X_PLL_AUDIO_POSTDIV_MASK; + reg_val |= audio_postdiv; + writel(reg_val, base + A38X_PLL_CONF_REG2); + + reg_val = readl(base + A38X_PLL_CONF_REG1); + reg_val &= ~A38X_PLL_FREQ_OFFSET_MASK; + reg_val |= freq_offset; + writel(reg_val, base + A38X_PLL_CONF_REG1); + + udelay(1); + + /* Disable reset */ + reg_val |= A38X_PLL_SW_RESET; + writel(reg_val, base + A38X_PLL_CONF_REG1); + + /* Wait 50us for PLL to lock */ + udelay(50); + + /* Restore frequency offset value validity */ + reg_val |= A38X_PLL_FREQ_OFFSET_VALID; + writel(reg_val, base + A38X_PLL_CONF_REG1); +} + static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { @@ -106,7 +222,10 @@ static void kirkwood_set_rate(struct snd_soc_dai *dai, * defined in kirkwood_i2s_dai */ dev_dbg(dai->dev, "%s: dco set rate = %lu\n", __func__, rate); - kirkwood_set_dco(priv->io, rate); + if (priv->pll_config) + armada_38x_set_pll(priv->pll_config, rate); + else + kirkwood_set_dco(priv->io, rate); clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO; } else { @@ -532,7 +651,10 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, priv); - priv->io = devm_platform_ioremap_resource(pdev, 0); + if (of_device_is_compatible(np, "marvell,armada-380-audio")) + priv->io = devm_platform_ioremap_resource_byname(pdev, "i2s_regs"); + else + priv->io = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->io)) return PTR_ERR(priv->io); @@ -540,6 +662,14 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) if (priv->irq < 0) return priv->irq; + if (of_device_is_compatible(np, "marvell,armada-380-audio")) { + err = armada_38x_i2s_init_quirk(pdev, priv, soc_dai); + if (err < 0) + return err; + /* Set initial pll frequency */ + armada_38x_set_pll(priv->pll_config, 44100); + } + if (np) { priv->burst = 128; /* might be 32 or 128 */ } else if (data) { @@ -623,6 +753,7 @@ static const struct of_device_id mvebu_audio_of_match[] = { { .compatible = "marvell,kirkwood-audio" }, { .compatible = "marvell,dove-audio" }, { .compatible = "marvell,armada370-audio" }, + { .compatible = "marvell,armada-380-audio" }, { } }; MODULE_DEVICE_TABLE(of, mvebu_audio_of_match); diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index a1733a6aace5..79bb9aa7f086 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -131,6 +131,8 @@ struct kirkwood_dma_data { void __iomem *io; + void __iomem *pll_config; + void __iomem *soc_control; struct clk *clk; struct clk *extclk; uint32_t ctl_play; diff --git a/sound/soc/meson/axg-pdm.c b/sound/soc/meson/axg-pdm.c index 88ac58272f95..ad43cb2a1e3f 100644 --- a/sound/soc/meson/axg-pdm.c +++ b/sound/soc/meson/axg-pdm.c @@ -169,7 +169,7 @@ static int axg_pdm_set_sysclk(struct axg_pdm *priv, unsigned int os, /* * Set the default system clock rate unless it is too fast for - * for the requested sample rate. In this case, the sample pointer + * the requested sample rate. In this case, the sample pointer * counter could overflow so set a lower system clock rate */ if (sys_rate < priv->cfg->sys_rate) diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 8a56f38dc7e8..99a3b4428591 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -782,10 +782,18 @@ static bool lpass_hdmi_regmap_volatile(struct device *dev, unsigned int reg) return true; if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) return true; + if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) + return true; for (i = 0; i < v->hdmi_rdma_channels; ++i) { if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) return true; + if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) + return true; } return false; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 7e380d71b0f8..2d269ac8c137 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1518,7 +1518,8 @@ static int rsnd_hw_params(struct snd_soc_component *component, int stream = substream->stream; for_each_dpcm_be(fe, stream, dpcm) { - struct snd_pcm_hw_params *be_params = &dpcm->hw_params; + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_hw_params *be_params = &be->dpcm[stream].hw_params; if (params_channels(hw_params) != params_channels(be_params)) io->converted_chan = params_channels(be_params); @@ -1581,9 +1582,9 @@ static int rsnd_hw_params(struct snd_soc_component *component, hw_params->cmask |= SNDRV_PCM_HW_PARAM_RATE; } else if (params_rate(hw_params) * k_up < io->converted_rate) { hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->min = - (io->converted_rate + k_up - 1) / k_up; + DIV_ROUND_UP(io->converted_rate, k_up); hw_param_interval(hw_params, SNDRV_PCM_HW_PARAM_RATE)->max = - (io->converted_rate + k_up - 1) / k_up; + DIV_ROUND_UP(io->converted_rate, k_up); hw_params->cmask |= SNDRV_PCM_HW_PARAM_RATE; } diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 659b9ade4158..e12f8244242b 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -1213,9 +1213,11 @@ int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd, int i; for_each_rtd_components(rtd, i, component) { - int ret = pm_runtime_resume_and_get(component->dev); - if (ret < 0 && ret != -EACCES) + int ret = pm_runtime_get_sync(component->dev); + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(component->dev); return soc_component_ret(component, ret); + } /* mark stream if succeeded */ soc_component_mark_push(component, stream, pm); } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d515e7a78ea8..954ca9af3e48 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -71,9 +71,9 @@ static int dapm_up_seq[] = { [snd_soc_dapm_pinctrl] = 2, [snd_soc_dapm_clock_supply] = 2, [snd_soc_dapm_supply] = 3, + [snd_soc_dapm_dai_link] = 3, [snd_soc_dapm_micbias] = 4, [snd_soc_dapm_vmid] = 4, - [snd_soc_dapm_dai_link] = 3, [snd_soc_dapm_dai_in] = 5, [snd_soc_dapm_dai_out] = 5, [snd_soc_dapm_aif_in] = 5, @@ -652,10 +652,8 @@ static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) } static struct snd_soc_dapm_widget * -dapm_wcache_lookup(struct snd_soc_dapm_wcache *wcache, const char *name) +dapm_wcache_lookup(struct snd_soc_dapm_widget *w, const char *name) { - struct snd_soc_dapm_widget *w = wcache->widget; - if (w) { struct list_head *wlist = &w->dapm->card->widgets; const int depth = 2; @@ -673,12 +671,6 @@ dapm_wcache_lookup(struct snd_soc_dapm_wcache *wcache, const char *name) return NULL; } -static inline void dapm_wcache_update(struct snd_soc_dapm_wcache *wcache, - struct snd_soc_dapm_widget *w) -{ - wcache->widget = w; -} - /** * snd_soc_dapm_force_bias_level() - Sets the DAPM bias level * @dapm: The DAPM context for which to set the level @@ -1881,58 +1873,52 @@ static void dapm_widget_set_peer_power(struct snd_soc_dapm_widget *peer, dapm_mark_dirty(peer, "peer state change"); } -static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, +static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, struct list_head *up_list, struct list_head *down_list) { struct snd_soc_dapm_path *path; + int power; + + switch (w->id) { + case snd_soc_dapm_pre: + power = 0; + goto end; + case snd_soc_dapm_post: + power = 1; + goto end; + default: + break; + } + + power = dapm_widget_power_check(w); if (w->power == power) return; trace_snd_soc_dapm_widget_power(w, power); - /* If we changed our power state perhaps our neigbours changed - * also. + /* + * If we changed our power state perhaps our neigbours + * changed also. */ snd_soc_dapm_widget_for_each_source_path(w, path) dapm_widget_set_peer_power(path->source, power, path->connect); - /* Supplies can't affect their outputs, only their inputs */ - if (!w->is_supply) { + /* + * Supplies can't affect their outputs, only their inputs + */ + if (!w->is_supply) snd_soc_dapm_widget_for_each_sink_path(w, path) - dapm_widget_set_peer_power(path->sink, power, - path->connect); - } + dapm_widget_set_peer_power(path->sink, power, path->connect); +end: if (power) dapm_seq_insert(w, up_list, true); else dapm_seq_insert(w, down_list, false); } -static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, - struct list_head *up_list, - struct list_head *down_list) -{ - int power; - - switch (w->id) { - case snd_soc_dapm_pre: - dapm_seq_insert(w, down_list, false); - break; - case snd_soc_dapm_post: - dapm_seq_insert(w, up_list, true); - break; - - default: - power = dapm_widget_power_check(w); - - dapm_widget_set_power(w, power, up_list, down_list); - break; - } -} - static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm) { if (dapm->idle_bias_off) @@ -2497,6 +2483,9 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) struct snd_soc_dapm_path *p, *next_p; enum snd_soc_dapm_direction dir; + if (!w) + return; + list_del(&w->list); list_del(&w->dirty); /* @@ -2516,12 +2505,6 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) } EXPORT_SYMBOL_GPL(snd_soc_dapm_free_widget); -void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm) -{ - dapm->path_sink_cache.widget = NULL; - dapm->path_source_cache.widget = NULL; -} - /* free all dapm widgets and resources */ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) { @@ -2532,7 +2515,9 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) continue; snd_soc_dapm_free_widget(w); } - snd_soc_dapm_reset_cache(dapm); + + dapm->wcache_sink = NULL; + dapm->wcache_source = NULL; } static struct snd_soc_dapm_widget *dapm_find_widget( @@ -2838,7 +2823,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink)) { - struct snd_soc_dapm_widget *widgets[2]; enum snd_soc_dapm_direction dir; struct snd_soc_dapm_path *path; int ret; @@ -2874,8 +2858,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, path->node[SND_SOC_DAPM_DIR_IN] = wsource; path->node[SND_SOC_DAPM_DIR_OUT] = wsink; - widgets[SND_SOC_DAPM_DIR_IN] = wsource; - widgets[SND_SOC_DAPM_DIR_OUT] = wsink; path->connected = connected; INIT_LIST_HEAD(&path->list); @@ -2917,12 +2899,13 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, } list_add(&path->list, &dapm->card->paths); + snd_soc_dapm_for_each_direction(dir) - list_add(&path->list_node[dir], &widgets[dir]->edges[dir]); + list_add(&path->list_node[dir], &path->node[dir]->edges[dir]); snd_soc_dapm_for_each_direction(dir) { - dapm_update_widget_flags(widgets[dir]); - dapm_mark_dirty(widgets[dir], "Route added"); + dapm_update_widget_flags(path->node[dir]); + dapm_mark_dirty(path->node[dir], "Route added"); } if (dapm->card->instantiated && path->connect) @@ -2961,8 +2944,8 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, source = route->source; } - wsource = dapm_wcache_lookup(&dapm->path_source_cache, source); - wsink = dapm_wcache_lookup(&dapm->path_sink_cache, sink); + wsource = dapm_wcache_lookup(dapm->wcache_source, source); + wsink = dapm_wcache_lookup(dapm->wcache_sink, sink); if (wsink && wsource) goto skip_search; @@ -3006,30 +2989,27 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, if (!wsource) wsource = wtsource; - if (wsource == NULL) { - dev_err(dapm->dev, "ASoC: no source widget found for %s\n", - route->source); - return -ENODEV; - } - if (wsink == NULL) { - dev_err(dapm->dev, "ASoC: no sink widget found for %s\n", - route->sink); - return -ENODEV; - } + ret = -ENODEV; + if (!wsource) + goto err; + if (!wsink) + goto err; skip_search: - dapm_wcache_update(&dapm->path_sink_cache, wsink); - dapm_wcache_update(&dapm->path_source_cache, wsource); + /* update cache */ + dapm->wcache_sink = wsink; + dapm->wcache_source = wsource; ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control, route->connected); - if (ret) - goto err; - - return 0; err: - dev_warn(dapm->dev, "ASoC: no dapm match for %s --> %s --> %s\n", - source, route->control, sink); + if (ret) + dev_err(dapm->dev, "ASoC: Failed to add route %s%s -%s%s%s> %s%s\n", + source, !wsource ? "(*)" : "", + !route->control ? "" : "> [", + !route->control ? "" : route->control, + !route->control ? "" : "] -", + sink, !wsink ? "(*)" : ""); return ret; } @@ -3115,13 +3095,8 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); for (i = 0; i < num; i++) { int r = snd_soc_dapm_add_route(dapm, route); - if (r < 0) { - dev_err(dapm->dev, "ASoC: Failed to add route %s -> %s -> %s\n", - route->source, - route->control ? route->control : "direct", - route->sink); + if (r < 0) ret = r; - } route++; } mutex_unlock(&dapm->card->dapm_mutex); @@ -4157,56 +4132,53 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dapm_widget template; struct snd_soc_dapm_widget *w; + const struct snd_kcontrol_new *kcontrol_news; + int num_kcontrols; const char **w_param_text; unsigned long private_value = 0; char *link_name; - int ret; + int ret = -ENOMEM; link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", rtd->dai_link->name, id); if (!link_name) - return ERR_PTR(-ENOMEM); - - memset(&template, 0, sizeof(template)); - template.reg = SND_SOC_NOPM; - template.id = snd_soc_dapm_dai_link; - template.name = link_name; - template.event = snd_soc_dai_link_event; - template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD; - template.kcontrol_news = NULL; + goto name_fail; /* allocate memory for control, only in case of multiple configs */ + w_param_text = NULL; + kcontrol_news = NULL; + num_kcontrols = 0; if (rtd->dai_link->num_params > 1) { w_param_text = devm_kcalloc(card->dev, rtd->dai_link->num_params, sizeof(char *), GFP_KERNEL); - if (!w_param_text) { - ret = -ENOMEM; + if (!w_param_text) goto param_fail; - } - template.num_kcontrols = 1; - template.kcontrol_news = - snd_soc_dapm_alloc_kcontrol(card, - link_name, - rtd->dai_link->params, - rtd->dai_link->num_params, - w_param_text, &private_value); - if (!template.kcontrol_news) { - ret = -ENOMEM; + num_kcontrols = 1; + kcontrol_news = snd_soc_dapm_alloc_kcontrol(card, link_name, + rtd->dai_link->params, + rtd->dai_link->num_params, + w_param_text, &private_value); + if (!kcontrol_news) goto param_fail; - } - } else { - w_param_text = NULL; } + + memset(&template, 0, sizeof(template)); + template.reg = SND_SOC_NOPM; + template.id = snd_soc_dapm_dai_link; + template.name = link_name; + template.event = snd_soc_dai_link_event; + template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD; + template.kcontrol_news = kcontrol_news; + template.num_kcontrols = num_kcontrols; + dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template); if (IS_ERR(w)) { ret = PTR_ERR(w); - dev_err(rtd->dev, "ASoC: Failed to create %s widget: %d\n", - link_name, ret); goto outfree_kcontrol_news; } @@ -4220,6 +4192,9 @@ outfree_kcontrol_news: rtd->dai_link->num_params, w_param_text); param_fail: devm_kfree(card->dev, link_name); +name_fail: + dev_err(rtd->dev, "ASoC: Failed to create %s-%s widget: %d\n", + rtd->dai_link->name, id, ret); return ERR_PTR(ret); } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index fb87d6d23408..d8e4677f3002 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -155,7 +155,7 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - params = &dpcm->hw_params; + params = &be->dpcm[stream].hw_params; offset += scnprintf(buf + offset, size - offset, "- %s\n", be->dai_link->name); @@ -1980,6 +1980,8 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) int ret; for_each_dpcm_be(fe, stream, dpcm) { + struct snd_pcm_hw_params hw_params; + be = dpcm->be; be_substream = snd_soc_dpcm_get_substream(be, stream); @@ -1988,16 +1990,16 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) continue; /* copy params for each dpcm */ - memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params, + memcpy(&hw_params, &fe->dpcm[stream].hw_params, sizeof(struct snd_pcm_hw_params)); /* perform any hw_params fixups */ - ret = snd_soc_link_be_hw_params_fixup(be, &dpcm->hw_params); + ret = snd_soc_link_be_hw_params_fixup(be, &hw_params); if (ret < 0) goto unwind; /* copy the fixed-up hw params for BE dai */ - memcpy(&be->dpcm[stream].hw_params, &dpcm->hw_params, + memcpy(&be->dpcm[stream].hw_params, &hw_params, sizeof(struct snd_pcm_hw_params)); /* only allow hw_params() if no connected FEs are running */ @@ -2012,7 +2014,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: hw_params BE %s\n", be->dai_link->name); - ret = __soc_pcm_hw_params(be, be_substream, &dpcm->hw_params); + ret = __soc_pcm_hw_params(be, be_substream, &hw_params); if (ret < 0) goto unwind; diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 19d0b1909bfd..da26f0ce9abc 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -37,10 +37,12 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) { struct sof_ipc4_msg notification_data = {{ 0 }}; struct snd_sof_dev *sdev = context; + bool ack_received = false; bool ipc_irq = false; u32 hipcida, hipctdr; hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA); + hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) { /* DSP received the message */ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, @@ -49,9 +51,9 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) cnl_ipc_dsp_done(sdev); ipc_irq = true; + ack_received = true; } - hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) { /* Message from DSP (reply or notification) */ u32 hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, @@ -70,6 +72,7 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) spin_lock_irq(&sdev->ipc_lock); snd_sof_ipc_get_reply(sdev); + cnl_ipc_host_done(sdev); snd_sof_ipc_reply(sdev, data->primary); spin_unlock_irq(&sdev->ipc_lock); @@ -86,10 +89,10 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) sdev->ipc->msg.rx_data = ¬ification_data; snd_sof_ipc_msgs_rx(sdev); sdev->ipc->msg.rx_data = NULL; - } - /* Let DSP know that we have finished processing the message */ - cnl_ipc_host_done(sdev); + /* Let DSP know that we have finished processing the message */ + cnl_ipc_host_done(sdev); + } ipc_irq = true; } @@ -98,6 +101,13 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) /* This interrupt is not shared so no need to return IRQ_NONE. */ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); + if (ack_received) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + + if (hdev->delayed_ipc_tx_msg) + cnl_ipc4_send_msg(sdev, hdev->delayed_ipc_tx_msg); + } + return IRQ_HANDLED; } @@ -251,8 +261,16 @@ static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg, int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc4_msg *msg_data = msg->msg_data; + if (hda_ipc4_tx_is_busy(sdev)) { + hdev->delayed_ipc_tx_msg = msg; + return 0; + } + + hdev->delayed_ipc_tx_msg = NULL; + /* send the message via mailbox */ if (msg_data->data_size) sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 3c76f843454b..799c50fe24da 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -677,10 +677,6 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - struct hdac_bus *bus = sof_to_bus(sdev); - struct hdac_ext_link *hlink = NULL; -#endif int ret; /* display codec must be powered before link reset */ @@ -707,16 +703,6 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) if (sdev->system_suspend_target == SOF_SUSPEND_NONE) hda_codec_jack_check(sdev); } - - /* turn off the links that were off before suspend */ - list_for_each_entry(hlink, &bus->hlink_list, list) { - if (!hlink->ref_count) - snd_hdac_ext_bus_link_power_down(hlink); - } - - /* check dma status and clean up CORB/RIRB buffers */ - if (!bus->cmd_dma_state) - snd_hdac_bus_stop_cmd_io(bus); #endif /* enable ppcap interrupt */ diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 9b3667c705e4..a7c454e03952 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -69,8 +69,16 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc4_msg *msg_data = msg->msg_data; + if (hda_ipc4_tx_is_busy(sdev)) { + hdev->delayed_ipc_tx_msg = msg; + return 0; + } + + hdev->delayed_ipc_tx_msg = NULL; + /* send the message via mailbox */ if (msg_data->data_size) sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, @@ -122,10 +130,13 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) { struct sof_ipc4_msg notification_data = {{ 0 }}; struct snd_sof_dev *sdev = context; + bool ack_received = false; bool ipc_irq = false; u32 hipcie, hipct; hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE); + hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); + if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { /* DSP received the message */ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL, @@ -133,9 +144,9 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) hda_dsp_ipc_dsp_done(sdev); ipc_irq = true; + ack_received = true; } - hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); if (hipct & HDA_DSP_REG_HIPCT_BUSY) { /* Message from DSP (reply or notification) */ u32 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, @@ -158,6 +169,7 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) spin_lock_irq(&sdev->ipc_lock); snd_sof_ipc_get_reply(sdev); + hda_dsp_ipc_host_done(sdev); snd_sof_ipc_reply(sdev, data->primary); spin_unlock_irq(&sdev->ipc_lock); @@ -174,10 +186,10 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) sdev->ipc->msg.rx_data = ¬ification_data; snd_sof_ipc_msgs_rx(sdev); sdev->ipc->msg.rx_data = NULL; - } - /* Let DSP know that we have finished processing the message */ - hda_dsp_ipc_host_done(sdev); + /* Let DSP know that we have finished processing the message */ + hda_dsp_ipc_host_done(sdev); + } ipc_irq = true; } @@ -186,6 +198,13 @@ irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) /* This interrupt is not shared so no need to return IRQ_NONE. */ dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); + if (ack_received) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + + if (hdev->delayed_ipc_tx_msg) + hda_dsp_ipc4_send_msg(sdev, hdev->delayed_ipc_tx_msg); + } + return IRQ_HANDLED; } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c6c6ea8a73f6..d63f843dc7aa 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -681,6 +681,17 @@ void hda_ipc4_dump(struct snd_sof_dev *sdev) hipci, hipcie, hipct, hipcte, hipcctl); } +bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + u32 val; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->ipc_req); + + return !!(val & chip->ipc_req_mask); +} + static int hda_init(struct snd_sof_dev *sdev) { struct hda_bus *hbus; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 2ab3c3840b92..65657d145dc2 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -521,6 +521,14 @@ struct sof_intel_hda_dev { /* Intel NHLT information */ struct nhlt_acpi_table *nhlt; + + /* + * Pointing to the IPC message if immediate sending was not possible + * because the downlink communication channel was BUSY at the time. + * The message will be re-tried when the channel becomes free (the ACK + * is received from the DSP for the previous message) + */ + struct snd_sof_ipc_msg *delayed_ipc_tx_msg; }; static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -852,6 +860,7 @@ int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask); irqreturn_t cnl_ipc4_irq_thread(int irq, void *context); int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context); +bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev); int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); void hda_ipc4_dump(struct snd_sof_dev *sdev); extern struct sdw_intel_ops sdw_callback; diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 10298532816f..054b9ab721ff 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -90,8 +90,16 @@ static bool mtl_dsp_check_sdw_irq(struct snd_sof_dev *sdev) static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc4_msg *msg_data = msg->msg_data; + if (hda_ipc4_tx_is_busy(sdev)) { + hdev->delayed_ipc_tx_msg = msg; + return 0; + } + + hdev->delayed_ipc_tx_msg = NULL; + /* send the message via mailbox */ if (msg_data->data_size) sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, @@ -492,11 +500,13 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) { struct sof_ipc4_msg notification_data = {{ 0 }}; struct snd_sof_dev *sdev = context; + bool ack_received = false; bool ipc_irq = false; u32 hipcida; u32 hipctdr; hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDA); + hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDR); /* reply message from DSP */ if (hipcida & MTL_DSP_REG_HFIPCXIDA_DONE) { @@ -507,9 +517,9 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) mtl_ipc_dsp_done(sdev); ipc_irq = true; + ack_received = true; } - hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDR); if (hipctdr & MTL_DSP_REG_HFIPCXTDR_BUSY) { /* Message from DSP (reply or notification) */ u32 extension = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXTDDY); @@ -530,6 +540,7 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) spin_lock_irq(&sdev->ipc_lock); snd_sof_ipc_get_reply(sdev); + mtl_ipc_host_done(sdev); snd_sof_ipc_reply(sdev, data->primary); spin_unlock_irq(&sdev->ipc_lock); @@ -546,9 +557,9 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) sdev->ipc->msg.rx_data = ¬ification_data; snd_sof_ipc_msgs_rx(sdev); sdev->ipc->msg.rx_data = NULL; - } - mtl_ipc_host_done(sdev); + mtl_ipc_host_done(sdev); + } ipc_irq = true; } @@ -558,6 +569,13 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); } + if (ack_received) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + + if (hdev->delayed_ipc_tx_msg) + mtl_ipc_send_msg(sdev, hdev->delayed_ipc_tx_msg); + } + return IRQ_HANDLED; } diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index b28af3a48b70..1fef4dcc0936 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -329,6 +329,8 @@ static int ipc3_tx_msg_unlocked(struct snd_sof_ipc *ipc, struct snd_sof_dev *sdev = ipc->sdev; int ret; + ipc3_log_header(sdev->dev, "ipc tx", hdr->cmd); + ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes); if (ret) { @@ -338,8 +340,6 @@ static int ipc3_tx_msg_unlocked(struct snd_sof_ipc *ipc, return ret; } - ipc3_log_header(sdev->dev, "ipc tx", hdr->cmd); - /* now wait for completion */ return ipc3_wait_tx_done(ipc, reply_data); } diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 6eaa18e27e5a..3c9b8692984a 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -342,6 +342,8 @@ static int ipc4_tx_msg_unlocked(struct snd_sof_ipc *ipc, if (msg_bytes > ipc->max_payload_size || reply_bytes > ipc->max_payload_size) return -EINVAL; + sof_ipc4_log_header(sdev->dev, "ipc tx ", msg_data, true); + ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes); if (ret) { dev_err_ratelimited(sdev->dev, @@ -350,8 +352,6 @@ static int ipc4_tx_msg_unlocked(struct snd_sof_ipc *ipc, return ret; } - sof_ipc4_log_header(sdev->dev, "ipc tx ", msg_data, true); - /* now wait for completion */ return ipc4_wait_tx_done(ipc, reply_data); } diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index ca5d1bb6ac59..f5ac2ab77f5b 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -869,7 +869,7 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) max_active_serializers = 1; else - max_active_serializers = (channels + slots - 1) / slots; + max_active_serializers = DIV_ROUND_UP(channels, slots); /* Default configuration */ if (mcasp->version < MCASP_VERSION_3) @@ -1002,8 +1002,7 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, */ if (mcasp->tdm_mask[stream]) { active_slots = hweight32(mcasp->tdm_mask[stream]); - active_serializers = (channels + active_slots - 1) / - active_slots; + active_serializers = DIV_ROUND_UP(channels, active_slots); if (active_serializers == 1) active_slots = channels; for (i = 0; i < total_slots; i++) { @@ -1014,7 +1013,7 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, } } } else { - active_serializers = (channels + total_slots - 1) / total_slots; + active_serializers = DIV_ROUND_UP(channels, total_slots); if (active_serializers == 1) active_slots = channels; else |