From f5030564938be112183ba3df0cdd6dea3f694c2e Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Thu, 23 Feb 2023 08:43:23 +0000 Subject: ALSA: cs35l41: Add shared boost feature Shared boost allows two amplifiers to share a single boost circuit by communicating on the MDSYNC bus. The passive amplifier does not control the boost and receives data from the active amplifier. Shared Boost is not supported in HDA Systems. Based on David Rhodes shared boost patches. Signed-off-by: Lucas Tanure Reviewed-by: David Rhodes Link: https://lore.kernel.org/r/20230223084324.9076-4-lucas.tanure@collabora.com Signed-off-by: Mark Brown --- include/sound/cs35l41.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 9ac5918269a5..7239d943942c 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -11,6 +11,7 @@ #define __CS35L41_H #include +#include #include #define CS35L41_FIRSTREG 0x00000000 @@ -677,6 +678,7 @@ #define CS35L36_PUP_DONE_IRQ_UNMASK 0x5F #define CS35L36_PUP_DONE_IRQ_MASK 0xBF +#define CS35L41_SYNC_EN_MASK BIT(8) #define CS35L41_AMP_SHORT_ERR 0x80000000 #define CS35L41_BST_SHORT_ERR 0x0100 @@ -686,6 +688,7 @@ #define CS35L41_BST_DCM_UVP_ERR 0x80 #define CS35L41_OTP_BOOT_DONE 0x02 #define CS35L41_PLL_UNLOCK 0x10 +#define CS35L41_PLL_LOCK BIT(1) #define CS35L41_OTP_BOOT_ERR 0x80000000 #define CS35L41_AMP_SHORT_ERR_RLS 0x02 @@ -705,6 +708,8 @@ #define CS35L41_INT1_MASK_DEFAULT 0x7FFCFE3F #define CS35L41_INT1_UNMASK_PUP 0xFEFFFFFF #define CS35L41_INT1_UNMASK_PDN 0xFF7FFFFF +#define CS35L41_INT3_PLL_LOCK_SHIFT 1 +#define CS35L41_INT3_PLL_LOCK_MASK BIT(CS35L41_INT3_PLL_LOCK_SHIFT) #define CS35L41_GPIO_DIR_MASK 0x80000000 #define CS35L41_GPIO_DIR_SHIFT 31 @@ -742,6 +747,11 @@ enum cs35l41_boost_type { CS35L41_INT_BOOST, CS35L41_EXT_BOOST, + CS35L41_SHD_BOOST_ACTV, + CS35L41_SHD_BOOST_PASS, + + // Not present in Binding Documentation, so no system should use this value. + // This value is only used in CLSA0100 Laptop CS35L41_EXT_BOOST_NO_VSPK_SWITCH, }; @@ -891,6 +901,7 @@ int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap); int cs35l41_init_boost(struct device *dev, struct regmap *regmap, struct cs35l41_hw_cfg *hw_cfg); bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type); -int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable); +int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable, + struct completion *pll_lock); #endif /* __CS35L41_H */ -- cgit v1.2.3 From f8c760e8fc414bb02373c5ede62fdac53ca8ccb2 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Fri, 17 Feb 2023 15:56:36 +0100 Subject: dt-bindings: soc: fsl: cpm_qe: Add TSA controller Add support for the time slot assigner (TSA) available in some PowerQUICC SoC such as MPC885 or MPC866. Signed-off-by: Herve Codina Reviewed-by: Christophe Leroy Link: https://lore.kernel.org/r/20230217145645.1768659-2-herve.codina@bootlin.com Signed-off-by: Mark Brown --- .../bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml | 215 +++++++++++++++++++++ include/dt-bindings/soc/cpm1-fsl,tsa.h | 13 ++ 2 files changed, 228 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml create mode 100644 include/dt-bindings/soc/cpm1-fsl,tsa.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml new file mode 100644 index 000000000000..332e902bcc21 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml @@ -0,0 +1,215 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/fsl/cpm_qe/fsl,cpm1-tsa.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: PowerQUICC CPM Time-slot assigner (TSA) controller + +maintainers: + - Herve Codina + +description: + The TSA is the time-slot assigner that can be found on some PowerQUICC SoC. + Its purpose is to route some TDM time-slots to other internal serial + controllers. + +properties: + compatible: + items: + - enum: + - fsl,mpc885-tsa + - fsl,mpc866-tsa + - const: fsl,cpm1-tsa + + reg: + items: + - description: SI (Serial Interface) register base + - description: SI RAM base + + reg-names: + items: + - const: si_regs + - const: si_ram + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + '#fsl,serial-cells': + $ref: /schemas/types.yaml#/definitions/uint32 + const: 1 + description: + TSA consumers that use a phandle to TSA need to pass the serial identifier + with this phandle (defined in dt-bindings/soc/fsl,tsa.h). + For instance "fsl,tsa-serial = <&tsa FSL_CPM_TSA_SCC4>;". + +patternProperties: + '^tdm@[0-1]$': + description: + The TDM managed by this controller + type: object + + additionalProperties: false + + properties: + reg: + minimum: 0 + maximum: 1 + description: + The TDM number for this TDM, 0 for TDMa and 1 for TDMb + + fsl,common-rxtx-pins: + $ref: /schemas/types.yaml#/definitions/flag + description: + The hardware can use four dedicated pins for Tx clock, Tx sync, Rx + clock and Rx sync or use only two pins, Tx/Rx clock and Tx/Rx sync. + Without the 'fsl,common-rxtx-pins' property, the four pins are used. + With the 'fsl,common-rxtx-pins' property, two pins are used. + + clocks: + minItems: 2 + items: + - description: External clock connected to L1RSYNC pin + - description: External clock connected to L1RCLK pin + - description: External clock connected to L1TSYNC pin + - description: External clock connected to L1TCLK pin + + clock-names: + minItems: 2 + items: + - const: l1rsync + - const: l1rclk + - const: l1tsync + - const: l1tclk + + fsl,rx-frame-sync-delay-bits: + enum: [0, 1, 2, 3] + default: 0 + description: | + Receive frame sync delay in number of bits. + Indicates the delay between the Rx sync and the first bit of the Rx + frame. 0 for no bit delay. 1, 2 or 3 for 1, 2 or 3 bits delay. + + fsl,tx-frame-sync-delay-bits: + enum: [0, 1, 2, 3] + default: 0 + description: | + Transmit frame sync delay in number of bits. + Indicates the delay between the Tx sync and the first bit of the Tx + frame. 0 for no bit delay. 1, 2 or 3 for 1, 2 or 3 bits delay. + + fsl,clock-falling-edge: + $ref: /schemas/types.yaml#/definitions/flag + description: + Data is sent on falling edge of the clock (and received on the rising + edge). If 'clock-falling-edge' is not present, data is sent on the + rising edge (and received on the falling edge). + + fsl,fsync-rising-edge: + $ref: /schemas/types.yaml#/definitions/flag + description: + Frame sync pulses are sampled with the rising edge of the channel + clock. If 'fsync-rising-edge' is not present, pulses are sampled with + the falling edge. + + fsl,double-speed-clock: + $ref: /schemas/types.yaml#/definitions/flag + description: + The channel clock is twice the data rate. + + patternProperties: + '^fsl,[rt]x-ts-routes$': + $ref: /schemas/types.yaml#/definitions/uint32-matrix + description: | + A list of tuple that indicates the Tx or Rx time-slots routes. + items: + items: + - description: + The number of time-slots + minimum: 1 + maximum: 64 + - description: | + The source (Tx) or destination (Rx) serial interface + (dt-bindings/soc/cpm1-fsl,tsa.h defines these values) + - 0: No destination + - 1: SCC2 + - 2: SCC3 + - 3: SCC4 + - 4: SMC1 + - 5: SMC2 + enum: [0, 1, 2, 3, 4, 5] + minItems: 1 + maxItems: 64 + + allOf: + # If fsl,common-rxtx-pins is present, only 2 clocks are needed. + # Else, the 4 clocks must be present. + - if: + required: + - fsl,common-rxtx-pins + then: + properties: + clocks: + maxItems: 2 + clock-names: + maxItems: 2 + else: + properties: + clocks: + minItems: 4 + clock-names: + minItems: 4 + + required: + - reg + - clocks + - clock-names + +required: + - compatible + - reg + - reg-names + - '#address-cells' + - '#size-cells' + - '#fsl,serial-cells' + +additionalProperties: false + +examples: + - | + #include + + tsa@ae0 { + compatible = "fsl,mpc885-tsa", "fsl,cpm1-tsa"; + reg = <0xae0 0x10>, + <0xc00 0x200>; + reg-names = "si_regs", "si_ram"; + + #address-cells = <1>; + #size-cells = <0>; + #fsl,serial-cells = <1>; + + tdm@0 { + /* TDMa */ + reg = <0>; + + clocks = <&clk_l1rsynca>, <&clk_l1rclka>; + clock-names = "l1rsync", "l1rclk"; + + fsl,common-rxtx-pins; + fsl,fsync-rising-edge; + + fsl,tx-ts-routes = <2 0>, /* TS 0..1 */ + <24 FSL_CPM_TSA_SCC4>, /* TS 2..25 */ + <1 0>, /* TS 26 */ + <5 FSL_CPM_TSA_SCC3>; /* TS 27..31 */ + + fsl,rx-ts-routes = <2 0>, /* TS 0..1 */ + <24 FSL_CPM_TSA_SCC4>, /* 2..25 */ + <1 0>, /* TS 26 */ + <5 FSL_CPM_TSA_SCC3>; /* TS 27..31 */ + }; + }; diff --git a/include/dt-bindings/soc/cpm1-fsl,tsa.h b/include/dt-bindings/soc/cpm1-fsl,tsa.h new file mode 100644 index 000000000000..2cc44e867dbe --- /dev/null +++ b/include/dt-bindings/soc/cpm1-fsl,tsa.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ + +#ifndef __DT_BINDINGS_SOC_FSL_TSA_H +#define __DT_BINDINGS_SOC_FSL_TSA_H + +#define FSL_CPM_TSA_NU 0 /* Pseuso Cell Id for not used item */ +#define FSL_CPM_TSA_SCC2 1 +#define FSL_CPM_TSA_SCC3 2 +#define FSL_CPM_TSA_SCC4 3 +#define FSL_CPM_TSA_SMC1 4 +#define FSL_CPM_TSA_SMC2 5 + +#endif -- cgit v1.2.3 From 3178d58e0b9772d690456c0bdf8c9f5e191d45f1 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Fri, 17 Feb 2023 15:56:41 +0100 Subject: soc: fsl: cpm1: Add support for QMC The QMC (QUICC Multichannel Controller) emulates up to 64 channels within one serial controller using the same TDM physical interface routed from the TSA. It is available in some PowerQUICC SoC such as the MPC885 or MPC866. It is also available on some Quicc Engine SoCs. This current version support CPM1 SoCs only and some enhancement are needed to support Quicc Engine SoCs. Signed-off-by: Herve Codina Acked-by: Li Yang Reviewed-by: Christophe Leroy Link: https://lore.kernel.org/r/20230217145645.1768659-7-herve.codina@bootlin.com Signed-off-by: Mark Brown --- drivers/soc/fsl/qe/Kconfig | 12 + drivers/soc/fsl/qe/Makefile | 1 + drivers/soc/fsl/qe/qmc.c | 1533 +++++++++++++++++++++++++++++++++++++++++++ include/soc/fsl/qe/qmc.h | 71 ++ 4 files changed, 1617 insertions(+) create mode 100644 drivers/soc/fsl/qe/qmc.c create mode 100644 include/soc/fsl/qe/qmc.h (limited to 'include') diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig index b0088495c323..f90cfdf0c763 100644 --- a/drivers/soc/fsl/qe/Kconfig +++ b/drivers/soc/fsl/qe/Kconfig @@ -44,6 +44,18 @@ config CPM_TSA This option enables support for this controller +config CPM_QMC + tristate "CPM QMC support" + depends on OF && HAS_IOMEM + depends on CPM1 || (SOC_FSL && COMPILE_TEST) + depends on CPM_TSA + help + Freescale CPM QUICC Multichannel Controller + (QMC) + + This option enables support for this + controller + config QE_TDM bool default y if FSL_UCC_HDLC diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile index 45c961acc81b..ec8506e13113 100644 --- a/drivers/soc/fsl/qe/Makefile +++ b/drivers/soc/fsl/qe/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_common.o qe_ic.o qe_io.o obj-$(CONFIG_CPM) += qe_common.o obj-$(CONFIG_CPM_TSA) += tsa.o +obj-$(CONFIG_CPM_QMC) += qmc.o obj-$(CONFIG_UCC) += ucc.o obj-$(CONFIG_UCC_SLOW) += ucc_slow.o obj-$(CONFIG_UCC_FAST) += ucc_fast.o diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c new file mode 100644 index 000000000000..cfa7207353e0 --- /dev/null +++ b/drivers/soc/fsl/qe/qmc.c @@ -0,0 +1,1533 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * QMC driver + * + * Copyright 2022 CS GROUP France + * + * Author: Herve Codina + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tsa.h" + +/* SCC general mode register high (32 bits) */ +#define SCC_GSMRL 0x00 +#define SCC_GSMRL_ENR (1 << 5) +#define SCC_GSMRL_ENT (1 << 4) +#define SCC_GSMRL_MODE_QMC (0x0A << 0) + +/* SCC general mode register low (32 bits) */ +#define SCC_GSMRH 0x04 +#define SCC_GSMRH_CTSS (1 << 7) +#define SCC_GSMRH_CDS (1 << 8) +#define SCC_GSMRH_CTSP (1 << 9) +#define SCC_GSMRH_CDP (1 << 10) + +/* SCC event register (16 bits) */ +#define SCC_SCCE 0x10 +#define SCC_SCCE_IQOV (1 << 3) +#define SCC_SCCE_GINT (1 << 2) +#define SCC_SCCE_GUN (1 << 1) +#define SCC_SCCE_GOV (1 << 0) + +/* SCC mask register (16 bits) */ +#define SCC_SCCM 0x14 +/* Multichannel base pointer (32 bits) */ +#define QMC_GBL_MCBASE 0x00 +/* Multichannel controller state (16 bits) */ +#define QMC_GBL_QMCSTATE 0x04 +/* Maximum receive buffer length (16 bits) */ +#define QMC_GBL_MRBLR 0x06 +/* Tx time-slot assignment table pointer (16 bits) */ +#define QMC_GBL_TX_S_PTR 0x08 +/* Rx pointer (16 bits) */ +#define QMC_GBL_RXPTR 0x0A +/* Global receive frame threshold (16 bits) */ +#define QMC_GBL_GRFTHR 0x0C +/* Global receive frame count (16 bits) */ +#define QMC_GBL_GRFCNT 0x0E +/* Multichannel interrupt base address (32 bits) */ +#define QMC_GBL_INTBASE 0x10 +/* Multichannel interrupt pointer (32 bits) */ +#define QMC_GBL_INTPTR 0x14 +/* Rx time-slot assignment table pointer (16 bits) */ +#define QMC_GBL_RX_S_PTR 0x18 +/* Tx pointer (16 bits) */ +#define QMC_GBL_TXPTR 0x1A +/* CRC constant (32 bits) */ +#define QMC_GBL_C_MASK32 0x1C +/* Time slot assignment table Rx (32 x 16 bits) */ +#define QMC_GBL_TSATRX 0x20 +/* Time slot assignment table Tx (32 x 16 bits) */ +#define QMC_GBL_TSATTX 0x60 +/* CRC constant (16 bits) */ +#define QMC_GBL_C_MASK16 0xA0 + +/* TSA entry (16bit entry in TSATRX and TSATTX) */ +#define QMC_TSA_VALID (1 << 15) +#define QMC_TSA_WRAP (1 << 14) +#define QMC_TSA_MASK (0x303F) +#define QMC_TSA_CHANNEL(x) ((x) << 6) + +/* Tx buffer descriptor base address (16 bits, offset from MCBASE) */ +#define QMC_SPE_TBASE 0x00 + +/* Channel mode register (16 bits) */ +#define QMC_SPE_CHAMR 0x02 +#define QMC_SPE_CHAMR_MODE_HDLC (1 << 15) +#define QMC_SPE_CHAMR_MODE_TRANSP ((0 << 15) | (1 << 13)) +#define QMC_SPE_CHAMR_ENT (1 << 12) +#define QMC_SPE_CHAMR_POL (1 << 8) +#define QMC_SPE_CHAMR_HDLC_IDLM (1 << 13) +#define QMC_SPE_CHAMR_HDLC_CRC (1 << 7) +#define QMC_SPE_CHAMR_HDLC_NOF (0x0f << 0) +#define QMC_SPE_CHAMR_TRANSP_RD (1 << 14) +#define QMC_SPE_CHAMR_TRANSP_SYNC (1 << 10) + +/* Tx internal state (32 bits) */ +#define QMC_SPE_TSTATE 0x04 +/* Tx buffer descriptor pointer (16 bits) */ +#define QMC_SPE_TBPTR 0x0C +/* Zero-insertion state (32 bits) */ +#define QMC_SPE_ZISTATE 0x14 +/* Channel’s interrupt mask flags (16 bits) */ +#define QMC_SPE_INTMSK 0x1C +/* Rx buffer descriptor base address (16 bits, offset from MCBASE) */ +#define QMC_SPE_RBASE 0x20 +/* HDLC: Maximum frame length register (16 bits) */ +#define QMC_SPE_MFLR 0x22 +/* TRANSPARENT: Transparent maximum receive length (16 bits) */ +#define QMC_SPE_TMRBLR 0x22 +/* Rx internal state (32 bits) */ +#define QMC_SPE_RSTATE 0x24 +/* Rx buffer descriptor pointer (16 bits) */ +#define QMC_SPE_RBPTR 0x2C +/* Packs 4 bytes to 1 long word before writing to buffer (32 bits) */ +#define QMC_SPE_RPACK 0x30 +/* Zero deletion state (32 bits) */ +#define QMC_SPE_ZDSTATE 0x34 + +/* Transparent synchronization (16 bits) */ +#define QMC_SPE_TRNSYNC 0x3C +#define QMC_SPE_TRNSYNC_RX(x) ((x) << 8) +#define QMC_SPE_TRNSYNC_TX(x) ((x) << 0) + +/* Interrupt related registers bits */ +#define QMC_INT_V (1 << 15) +#define QMC_INT_W (1 << 14) +#define QMC_INT_NID (1 << 13) +#define QMC_INT_IDL (1 << 12) +#define QMC_INT_GET_CHANNEL(x) (((x) & 0x0FC0) >> 6) +#define QMC_INT_MRF (1 << 5) +#define QMC_INT_UN (1 << 4) +#define QMC_INT_RXF (1 << 3) +#define QMC_INT_BSY (1 << 2) +#define QMC_INT_TXB (1 << 1) +#define QMC_INT_RXB (1 << 0) + +/* BD related registers bits */ +#define QMC_BD_RX_E (1 << 15) +#define QMC_BD_RX_W (1 << 13) +#define QMC_BD_RX_I (1 << 12) +#define QMC_BD_RX_L (1 << 11) +#define QMC_BD_RX_F (1 << 10) +#define QMC_BD_RX_CM (1 << 9) +#define QMC_BD_RX_UB (1 << 7) +#define QMC_BD_RX_LG (1 << 5) +#define QMC_BD_RX_NO (1 << 4) +#define QMC_BD_RX_AB (1 << 3) +#define QMC_BD_RX_CR (1 << 2) + +#define QMC_BD_TX_R (1 << 15) +#define QMC_BD_TX_W (1 << 13) +#define QMC_BD_TX_I (1 << 12) +#define QMC_BD_TX_L (1 << 11) +#define QMC_BD_TX_TC (1 << 10) +#define QMC_BD_TX_CM (1 << 9) +#define QMC_BD_TX_UB (1 << 7) +#define QMC_BD_TX_PAD (0x0f << 0) + +/* Numbers of BDs and interrupt items */ +#define QMC_NB_TXBDS 8 +#define QMC_NB_RXBDS 8 +#define QMC_NB_INTS 128 + +struct qmc_xfer_desc { + union { + void (*tx_complete)(void *context); + void (*rx_complete)(void *context, size_t length); + }; + void *context; +}; + +struct qmc_chan { + struct list_head list; + unsigned int id; + struct qmc *qmc; + void *__iomem s_param; + enum qmc_mode mode; + u64 tx_ts_mask; + u64 rx_ts_mask; + bool is_reverse_data; + + spinlock_t tx_lock; + cbd_t __iomem *txbds; + cbd_t __iomem *txbd_free; + cbd_t __iomem *txbd_done; + struct qmc_xfer_desc tx_desc[QMC_NB_TXBDS]; + u64 nb_tx_underrun; + bool is_tx_stopped; + + spinlock_t rx_lock; + cbd_t __iomem *rxbds; + cbd_t __iomem *rxbd_free; + cbd_t __iomem *rxbd_done; + struct qmc_xfer_desc rx_desc[QMC_NB_RXBDS]; + u64 nb_rx_busy; + int rx_pending; + bool is_rx_halted; + bool is_rx_stopped; +}; + +struct qmc { + struct device *dev; + struct tsa_serial *tsa_serial; + void *__iomem scc_regs; + void *__iomem scc_pram; + void *__iomem dpram; + u16 scc_pram_offset; + cbd_t __iomem *bd_table; + dma_addr_t bd_dma_addr; + size_t bd_size; + u16 __iomem *int_table; + u16 __iomem *int_curr; + dma_addr_t int_dma_addr; + size_t int_size; + struct list_head chan_head; + struct qmc_chan *chans[64]; +}; + +static inline void qmc_write16(void *__iomem addr, u16 val) +{ + iowrite16be(val, addr); +} + +static inline u16 qmc_read16(void *__iomem addr) +{ + return ioread16be(addr); +} + +static inline void qmc_setbits16(void *__iomem addr, u16 set) +{ + qmc_write16(addr, qmc_read16(addr) | set); +} + +static inline void qmc_clrbits16(void *__iomem addr, u16 clr) +{ + qmc_write16(addr, qmc_read16(addr) & ~clr); +} + +static inline void qmc_write32(void *__iomem addr, u32 val) +{ + iowrite32be(val, addr); +} + +static inline u32 qmc_read32(void *__iomem addr) +{ + return ioread32be(addr); +} + +static inline void qmc_setbits32(void *__iomem addr, u32 set) +{ + qmc_write32(addr, qmc_read32(addr) | set); +} + + +int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info) +{ + struct tsa_serial_info tsa_info; + int ret; + + /* Retrieve info from the TSA related serial */ + ret = tsa_serial_get_info(chan->qmc->tsa_serial, &tsa_info); + if (ret) + return ret; + + info->mode = chan->mode; + info->rx_fs_rate = tsa_info.rx_fs_rate; + info->rx_bit_rate = tsa_info.rx_bit_rate; + info->nb_tx_ts = hweight64(chan->tx_ts_mask); + info->tx_fs_rate = tsa_info.tx_fs_rate; + info->tx_bit_rate = tsa_info.tx_bit_rate; + info->nb_rx_ts = hweight64(chan->rx_ts_mask); + + return 0; +} +EXPORT_SYMBOL(qmc_chan_get_info); + +int qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param) +{ + if (param->mode != chan->mode) + return -EINVAL; + + switch (param->mode) { + case QMC_HDLC: + if ((param->hdlc.max_rx_buf_size % 4) || + (param->hdlc.max_rx_buf_size < 8)) + return -EINVAL; + + qmc_write16(chan->qmc->scc_pram + QMC_GBL_MRBLR, + param->hdlc.max_rx_buf_size - 8); + qmc_write16(chan->s_param + QMC_SPE_MFLR, + param->hdlc.max_rx_frame_size); + if (param->hdlc.is_crc32) { + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, + QMC_SPE_CHAMR_HDLC_CRC); + } else { + qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, + QMC_SPE_CHAMR_HDLC_CRC); + } + break; + + case QMC_TRANSPARENT: + qmc_write16(chan->s_param + QMC_SPE_TMRBLR, + param->transp.max_rx_buf_size); + break; + + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(qmc_chan_set_param); + +int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context), void *context) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + int ret; + + /* + * R bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->tx_lock, flags); + bd = chan->txbd_free; + + ctrl = qmc_read16(&bd->cbd_sc); + if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) { + /* We are full ... */ + ret = -EBUSY; + goto end; + } + + qmc_write16(&bd->cbd_datlen, length); + qmc_write32(&bd->cbd_bufaddr, addr); + + xfer_desc = &chan->tx_desc[bd - chan->txbds]; + xfer_desc->tx_complete = complete; + xfer_desc->context = context; + + /* Activate the descriptor */ + ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB); + wmb(); /* Be sure to flush the descriptor before control update */ + qmc_write16(&bd->cbd_sc, ctrl); + + if (!chan->is_tx_stopped) + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL); + + if (ctrl & QMC_BD_TX_W) + chan->txbd_free = chan->txbds; + else + chan->txbd_free++; + + ret = 0; + +end: + spin_unlock_irqrestore(&chan->tx_lock, flags); + return ret; +} +EXPORT_SYMBOL(qmc_chan_write_submit); + +static void qmc_chan_write_done(struct qmc_chan *chan) +{ + struct qmc_xfer_desc *xfer_desc; + void (*complete)(void *context); + unsigned long flags; + void *context; + cbd_t *__iomem bd; + u16 ctrl; + + /* + * R bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->tx_lock, flags); + bd = chan->txbd_done; + + ctrl = qmc_read16(&bd->cbd_sc); + while (!(ctrl & QMC_BD_TX_R)) { + if (!(ctrl & QMC_BD_TX_UB)) + goto end; + + xfer_desc = &chan->tx_desc[bd - chan->txbds]; + complete = xfer_desc->tx_complete; + context = xfer_desc->context; + xfer_desc->tx_complete = NULL; + xfer_desc->context = NULL; + + qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_TX_UB); + + if (ctrl & QMC_BD_TX_W) + chan->txbd_done = chan->txbds; + else + chan->txbd_done++; + + if (complete) { + spin_unlock_irqrestore(&chan->tx_lock, flags); + complete(context); + spin_lock_irqsave(&chan->tx_lock, flags); + } + + bd = chan->txbd_done; + ctrl = qmc_read16(&bd->cbd_sc); + } + +end: + spin_unlock_irqrestore(&chan->tx_lock, flags); +} + +int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context, size_t length), void *context) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + int ret; + + /* + * E bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->rx_lock, flags); + bd = chan->rxbd_free; + + ctrl = qmc_read16(&bd->cbd_sc); + if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) { + /* We are full ... */ + ret = -EBUSY; + goto end; + } + + qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */ + qmc_write32(&bd->cbd_bufaddr, addr); + + xfer_desc = &chan->rx_desc[bd - chan->rxbds]; + xfer_desc->rx_complete = complete; + xfer_desc->context = context; + + /* Activate the descriptor */ + ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB); + wmb(); /* Be sure to flush data before descriptor activation */ + qmc_write16(&bd->cbd_sc, ctrl); + + /* Restart receiver if needed */ + if (chan->is_rx_halted && !chan->is_rx_stopped) { + /* Restart receiver */ + if (chan->mode == QMC_TRANSPARENT) + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + else + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + chan->is_rx_halted = false; + } + chan->rx_pending++; + + if (ctrl & QMC_BD_RX_W) + chan->rxbd_free = chan->rxbds; + else + chan->rxbd_free++; + + ret = 0; +end: + spin_unlock_irqrestore(&chan->rx_lock, flags); + return ret; +} +EXPORT_SYMBOL(qmc_chan_read_submit); + +static void qmc_chan_read_done(struct qmc_chan *chan) +{ + void (*complete)(void *context, size_t size); + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + void *context; + u16 datalen; + u16 ctrl; + + /* + * E bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->rx_lock, flags); + bd = chan->rxbd_done; + + ctrl = qmc_read16(&bd->cbd_sc); + while (!(ctrl & QMC_BD_RX_E)) { + if (!(ctrl & QMC_BD_RX_UB)) + goto end; + + xfer_desc = &chan->rx_desc[bd - chan->rxbds]; + complete = xfer_desc->rx_complete; + context = xfer_desc->context; + xfer_desc->rx_complete = NULL; + xfer_desc->context = NULL; + + datalen = qmc_read16(&bd->cbd_datlen); + qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_RX_UB); + + if (ctrl & QMC_BD_RX_W) + chan->rxbd_done = chan->rxbds; + else + chan->rxbd_done++; + + chan->rx_pending--; + + if (complete) { + spin_unlock_irqrestore(&chan->rx_lock, flags); + complete(context, datalen); + spin_lock_irqsave(&chan->rx_lock, flags); + } + + bd = chan->rxbd_done; + ctrl = qmc_read16(&bd->cbd_sc); + } + +end: + spin_unlock_irqrestore(&chan->rx_lock, flags); +} + +static int qmc_chan_command(struct qmc_chan *chan, u8 qmc_opcode) +{ + return cpm_command(chan->id << 2, (qmc_opcode << 4) | 0x0E); +} + +static int qmc_chan_stop_rx(struct qmc_chan *chan) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chan->rx_lock, flags); + + /* Send STOP RECEIVE command */ + ret = qmc_chan_command(chan, 0x0); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Send STOP RECEIVE failed (%d)\n", + chan->id, ret); + goto end; + } + + chan->is_rx_stopped = true; + +end: + spin_unlock_irqrestore(&chan->rx_lock, flags); + return ret; +} + +static int qmc_chan_stop_tx(struct qmc_chan *chan) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chan->tx_lock, flags); + + /* Send STOP TRANSMIT command */ + ret = qmc_chan_command(chan, 0x1); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Send STOP TRANSMIT failed (%d)\n", + chan->id, ret); + goto end; + } + + chan->is_tx_stopped = true; + +end: + spin_unlock_irqrestore(&chan->tx_lock, flags); + return ret; +} + +int qmc_chan_stop(struct qmc_chan *chan, int direction) +{ + int ret; + + if (direction & QMC_CHAN_READ) { + ret = qmc_chan_stop_rx(chan); + if (ret) + return ret; + } + + if (direction & QMC_CHAN_WRITE) { + ret = qmc_chan_stop_tx(chan); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(qmc_chan_stop); + +static void qmc_chan_start_rx(struct qmc_chan *chan) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->rx_lock, flags); + + /* Restart the receiver */ + if (chan->mode == QMC_TRANSPARENT) + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + else + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + chan->is_rx_halted = false; + + chan->is_rx_stopped = false; + + spin_unlock_irqrestore(&chan->rx_lock, flags); +} + +static void qmc_chan_start_tx(struct qmc_chan *chan) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->tx_lock, flags); + + /* + * Enable channel transmitter as it could be disabled if + * qmc_chan_reset() was called. + */ + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT); + + /* Set the POL bit in the channel mode register */ + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL); + + chan->is_tx_stopped = false; + + spin_unlock_irqrestore(&chan->tx_lock, flags); +} + +int qmc_chan_start(struct qmc_chan *chan, int direction) +{ + if (direction & QMC_CHAN_READ) + qmc_chan_start_rx(chan); + + if (direction & QMC_CHAN_WRITE) + qmc_chan_start_tx(chan); + + return 0; +} +EXPORT_SYMBOL(qmc_chan_start); + +static void qmc_chan_reset_rx(struct qmc_chan *chan) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + + spin_lock_irqsave(&chan->rx_lock, flags); + bd = chan->rxbds; + do { + ctrl = qmc_read16(&bd->cbd_sc); + qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_RX_UB | QMC_BD_RX_E)); + + xfer_desc = &chan->rx_desc[bd - chan->rxbds]; + xfer_desc->rx_complete = NULL; + xfer_desc->context = NULL; + + bd++; + } while (!(ctrl & QMC_BD_RX_W)); + + chan->rxbd_free = chan->rxbds; + chan->rxbd_done = chan->rxbds; + qmc_write16(chan->s_param + QMC_SPE_RBPTR, + qmc_read16(chan->s_param + QMC_SPE_RBASE)); + + chan->rx_pending = 0; + chan->is_rx_stopped = false; + + spin_unlock_irqrestore(&chan->rx_lock, flags); +} + +static void qmc_chan_reset_tx(struct qmc_chan *chan) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + + spin_lock_irqsave(&chan->tx_lock, flags); + + /* Disable transmitter. It will be re-enable on qmc_chan_start() */ + qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT); + + bd = chan->txbds; + do { + ctrl = qmc_read16(&bd->cbd_sc); + qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_TX_UB | QMC_BD_TX_R)); + + xfer_desc = &chan->tx_desc[bd - chan->txbds]; + xfer_desc->tx_complete = NULL; + xfer_desc->context = NULL; + + bd++; + } while (!(ctrl & QMC_BD_TX_W)); + + chan->txbd_free = chan->txbds; + chan->txbd_done = chan->txbds; + qmc_write16(chan->s_param + QMC_SPE_TBPTR, + qmc_read16(chan->s_param + QMC_SPE_TBASE)); + + /* Reset TSTATE and ZISTATE to their initial value */ + qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000); + qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100); + + spin_unlock_irqrestore(&chan->tx_lock, flags); +} + +int qmc_chan_reset(struct qmc_chan *chan, int direction) +{ + if (direction & QMC_CHAN_READ) + qmc_chan_reset_rx(chan); + + if (direction & QMC_CHAN_WRITE) + qmc_chan_reset_tx(chan); + + return 0; +} +EXPORT_SYMBOL(qmc_chan_reset); + +static int qmc_check_chans(struct qmc *qmc) +{ + struct tsa_serial_info info; + bool is_one_table = false; + struct qmc_chan *chan; + u64 tx_ts_mask = 0; + u64 rx_ts_mask = 0; + u64 tx_ts_assigned_mask; + u64 rx_ts_assigned_mask; + int ret; + + /* Retrieve info from the TSA related serial */ + ret = tsa_serial_get_info(qmc->tsa_serial, &info); + if (ret) + return ret; + + /* + * If more than 32 TS are assigned to this serial, one common table is + * used for Tx and Rx and so masks must be equal for all channels. + */ + if ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) { + if (info.nb_tx_ts != info.nb_rx_ts) { + dev_err(qmc->dev, "Number of TSA Tx/Rx TS assigned are not equal\n"); + return -EINVAL; + } + is_one_table = true; + } + + + tx_ts_assigned_mask = (((u64)1) << info.nb_tx_ts) - 1; + rx_ts_assigned_mask = (((u64)1) << info.nb_rx_ts) - 1; + + list_for_each_entry(chan, &qmc->chan_head, list) { + if (chan->tx_ts_mask > tx_ts_assigned_mask) { + dev_err(qmc->dev, "chan %u uses TSA unassigned Tx TS\n", chan->id); + return -EINVAL; + } + if (tx_ts_mask & chan->tx_ts_mask) { + dev_err(qmc->dev, "chan %u uses an already used Tx TS\n", chan->id); + return -EINVAL; + } + + if (chan->rx_ts_mask > rx_ts_assigned_mask) { + dev_err(qmc->dev, "chan %u uses TSA unassigned Rx TS\n", chan->id); + return -EINVAL; + } + if (rx_ts_mask & chan->rx_ts_mask) { + dev_err(qmc->dev, "chan %u uses an already used Rx TS\n", chan->id); + return -EINVAL; + } + + if (is_one_table && (chan->tx_ts_mask != chan->rx_ts_mask)) { + dev_err(qmc->dev, "chan %u uses different Rx and Tx TS\n", chan->id); + return -EINVAL; + } + + tx_ts_mask |= chan->tx_ts_mask; + rx_ts_mask |= chan->rx_ts_mask; + } + + return 0; +} + +static unsigned int qmc_nb_chans(struct qmc *qmc) +{ + unsigned int count = 0; + struct qmc_chan *chan; + + list_for_each_entry(chan, &qmc->chan_head, list) + count++; + + return count; +} + +static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np) +{ + struct device_node *chan_np; + struct qmc_chan *chan; + const char *mode; + u32 chan_id; + u64 ts_mask; + int ret; + + for_each_available_child_of_node(np, chan_np) { + ret = of_property_read_u32(chan_np, "reg", &chan_id); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np); + of_node_put(chan_np); + return ret; + } + if (chan_id > 63) { + dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np); + of_node_put(chan_np); + return -EINVAL; + } + + chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL); + if (!chan) { + of_node_put(chan_np); + return -ENOMEM; + } + + chan->id = chan_id; + spin_lock_init(&chan->rx_lock); + spin_lock_init(&chan->tx_lock); + + ret = of_property_read_u64(chan_np, "fsl,tx-ts-mask", &ts_mask); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n", + chan_np); + of_node_put(chan_np); + return ret; + } + chan->tx_ts_mask = ts_mask; + + ret = of_property_read_u64(chan_np, "fsl,rx-ts-mask", &ts_mask); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n", + chan_np); + of_node_put(chan_np); + return ret; + } + chan->rx_ts_mask = ts_mask; + + mode = "transparent"; + ret = of_property_read_string(chan_np, "fsl,operational-mode", &mode); + if (ret && ret != -EINVAL) { + dev_err(qmc->dev, "%pOF: failed to read fsl,operational-mode\n", + chan_np); + of_node_put(chan_np); + return ret; + } + if (!strcmp(mode, "transparent")) { + chan->mode = QMC_TRANSPARENT; + } else if (!strcmp(mode, "hdlc")) { + chan->mode = QMC_HDLC; + } else { + dev_err(qmc->dev, "%pOF: Invalid fsl,operational-mode (%s)\n", + chan_np, mode); + of_node_put(chan_np); + return -EINVAL; + } + + chan->is_reverse_data = of_property_read_bool(chan_np, + "fsl,reverse-data"); + + list_add_tail(&chan->list, &qmc->chan_head); + qmc->chans[chan->id] = chan; + } + + return qmc_check_chans(qmc); +} + +static int qmc_setup_tsa_64rxtx(struct qmc *qmc, const struct tsa_serial_info *info) +{ + struct qmc_chan *chan; + unsigned int i; + u16 val; + + /* + * Use a common Tx/Rx 64 entries table. + * Everything was previously checked, Tx and Rx related stuffs are + * identical -> Used Rx related stuff to build the table + */ + + /* Invalidate all entries */ + for (i = 0; i < 64; i++) + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000); + + /* Set entries based on Rx stuff*/ + list_for_each_entry(chan, &qmc->chan_head, list) { + for (i = 0; i < info->nb_rx_ts; i++) { + if (!(chan->rx_ts_mask & (((u64)1) << i))) + continue; + + val = QMC_TSA_VALID | QMC_TSA_MASK | + QMC_TSA_CHANNEL(chan->id); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val); + } + } + + /* Set Wrap bit on last entry */ + qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2), + QMC_TSA_WRAP); + + /* Init pointers to the table */ + val = qmc->scc_pram_offset + QMC_GBL_TSATRX; + qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val); + + return 0; +} + +static int qmc_setup_tsa_32rx_32tx(struct qmc *qmc, const struct tsa_serial_info *info) +{ + struct qmc_chan *chan; + unsigned int i; + u16 val; + + /* + * Use a Tx 32 entries table and a Rx 32 entries table. + * Everything was previously checked. + */ + + /* Invalidate all entries */ + for (i = 0; i < 32; i++) { + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), 0x0000); + } + + /* Set entries based on Rx and Tx stuff*/ + list_for_each_entry(chan, &qmc->chan_head, list) { + /* Rx part */ + for (i = 0; i < info->nb_rx_ts; i++) { + if (!(chan->rx_ts_mask & (((u64)1) << i))) + continue; + + val = QMC_TSA_VALID | QMC_TSA_MASK | + QMC_TSA_CHANNEL(chan->id); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val); + } + /* Tx part */ + for (i = 0; i < info->nb_tx_ts; i++) { + if (!(chan->tx_ts_mask & (((u64)1) << i))) + continue; + + val = QMC_TSA_VALID | QMC_TSA_MASK | + QMC_TSA_CHANNEL(chan->id); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), val); + } + } + + /* Set Wrap bit on last entries */ + qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2), + QMC_TSA_WRAP); + qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATTX + ((info->nb_tx_ts - 1) * 2), + QMC_TSA_WRAP); + + /* Init Rx pointers ...*/ + val = qmc->scc_pram_offset + QMC_GBL_TSATRX; + qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val); + + /* ... and Tx pointers */ + val = qmc->scc_pram_offset + QMC_GBL_TSATTX; + qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val); + + return 0; +} + +static int qmc_setup_tsa(struct qmc *qmc) +{ + struct tsa_serial_info info; + int ret; + + /* Retrieve info from the TSA related serial */ + ret = tsa_serial_get_info(qmc->tsa_serial, &info); + if (ret) + return ret; + + /* + * Setup one common 64 entries table or two 32 entries (one for Tx and + * one for Tx) according to assigned TS numbers. + */ + return ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) ? + qmc_setup_tsa_64rxtx(qmc, &info) : + qmc_setup_tsa_32rx_32tx(qmc, &info); +} + +static int qmc_setup_chan_trnsync(struct qmc *qmc, struct qmc_chan *chan) +{ + struct tsa_serial_info info; + u16 first_rx, last_tx; + u16 trnsync; + int ret; + + /* Retrieve info from the TSA related serial */ + ret = tsa_serial_get_info(chan->qmc->tsa_serial, &info); + if (ret) + return ret; + + /* Find the first Rx TS allocated to the channel */ + first_rx = chan->rx_ts_mask ? __ffs64(chan->rx_ts_mask) + 1 : 0; + + /* Find the last Tx TS allocated to the channel */ + last_tx = fls64(chan->tx_ts_mask); + + trnsync = 0; + if (info.nb_rx_ts) + trnsync |= QMC_SPE_TRNSYNC_RX((first_rx % info.nb_rx_ts) * 2); + if (info.nb_tx_ts) + trnsync |= QMC_SPE_TRNSYNC_TX((last_tx % info.nb_tx_ts) * 2); + + qmc_write16(chan->s_param + QMC_SPE_TRNSYNC, trnsync); + + dev_dbg(qmc->dev, "chan %u: trnsync=0x%04x, rx %u/%u 0x%llx, tx %u/%u 0x%llx\n", + chan->id, trnsync, + first_rx, info.nb_rx_ts, chan->rx_ts_mask, + last_tx, info.nb_tx_ts, chan->tx_ts_mask); + + return 0; +} + +static int qmc_setup_chan(struct qmc *qmc, struct qmc_chan *chan) +{ + unsigned int i; + cbd_t __iomem *bd; + int ret; + u16 val; + + chan->qmc = qmc; + + /* Set channel specific parameter base address */ + chan->s_param = qmc->dpram + (chan->id * 64); + /* 16 bd per channel (8 rx and 8 tx) */ + chan->txbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)); + chan->rxbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS; + + chan->txbd_free = chan->txbds; + chan->txbd_done = chan->txbds; + chan->rxbd_free = chan->rxbds; + chan->rxbd_done = chan->rxbds; + + /* TBASE and TBPTR*/ + val = chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS) * sizeof(cbd_t); + qmc_write16(chan->s_param + QMC_SPE_TBASE, val); + qmc_write16(chan->s_param + QMC_SPE_TBPTR, val); + + /* RBASE and RBPTR*/ + val = ((chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS) * sizeof(cbd_t); + qmc_write16(chan->s_param + QMC_SPE_RBASE, val); + qmc_write16(chan->s_param + QMC_SPE_RBPTR, val); + qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100); + if (chan->mode == QMC_TRANSPARENT) { + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + qmc_write16(chan->s_param + QMC_SPE_TMRBLR, 60); + val = QMC_SPE_CHAMR_MODE_TRANSP | QMC_SPE_CHAMR_TRANSP_SYNC; + if (chan->is_reverse_data) + val |= QMC_SPE_CHAMR_TRANSP_RD; + qmc_write16(chan->s_param + QMC_SPE_CHAMR, val); + ret = qmc_setup_chan_trnsync(qmc, chan); + if (ret) + return ret; + } else { + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write16(chan->s_param + QMC_SPE_MFLR, 60); + qmc_write16(chan->s_param + QMC_SPE_CHAMR, + QMC_SPE_CHAMR_MODE_HDLC | QMC_SPE_CHAMR_HDLC_IDLM); + } + + /* Do not enable interrupts now. They will be enabled later */ + qmc_write16(chan->s_param + QMC_SPE_INTMSK, 0x0000); + + /* Init Rx BDs and set Wrap bit on last descriptor */ + BUILD_BUG_ON(QMC_NB_RXBDS == 0); + val = QMC_BD_RX_I; + for (i = 0; i < QMC_NB_RXBDS; i++) { + bd = chan->rxbds + i; + qmc_write16(&bd->cbd_sc, val); + } + bd = chan->rxbds + QMC_NB_RXBDS - 1; + qmc_write16(&bd->cbd_sc, val | QMC_BD_RX_W); + + /* Init Tx BDs and set Wrap bit on last descriptor */ + BUILD_BUG_ON(QMC_NB_TXBDS == 0); + val = QMC_BD_TX_I; + if (chan->mode == QMC_HDLC) + val |= QMC_BD_TX_L | QMC_BD_TX_TC; + for (i = 0; i < QMC_NB_TXBDS; i++) { + bd = chan->txbds + i; + qmc_write16(&bd->cbd_sc, val); + } + bd = chan->txbds + QMC_NB_TXBDS - 1; + qmc_write16(&bd->cbd_sc, val | QMC_BD_TX_W); + + return 0; +} + +static int qmc_setup_chans(struct qmc *qmc) +{ + struct qmc_chan *chan; + int ret; + + list_for_each_entry(chan, &qmc->chan_head, list) { + ret = qmc_setup_chan(qmc, chan); + if (ret) + return ret; + } + + return 0; +} + +static int qmc_finalize_chans(struct qmc *qmc) +{ + struct qmc_chan *chan; + int ret; + + list_for_each_entry(chan, &qmc->chan_head, list) { + /* Unmask channel interrupts */ + if (chan->mode == QMC_HDLC) { + qmc_write16(chan->s_param + QMC_SPE_INTMSK, + QMC_INT_NID | QMC_INT_IDL | QMC_INT_MRF | + QMC_INT_UN | QMC_INT_RXF | QMC_INT_BSY | + QMC_INT_TXB | QMC_INT_RXB); + } else { + qmc_write16(chan->s_param + QMC_SPE_INTMSK, + QMC_INT_UN | QMC_INT_BSY | + QMC_INT_TXB | QMC_INT_RXB); + } + + /* Forced stop the channel */ + ret = qmc_chan_stop(chan, QMC_CHAN_ALL); + if (ret) + return ret; + } + + return 0; +} + +static int qmc_setup_ints(struct qmc *qmc) +{ + unsigned int i; + u16 __iomem *last; + + /* Raz all entries */ + for (i = 0; i < (qmc->int_size / sizeof(u16)); i++) + qmc_write16(qmc->int_table + i, 0x0000); + + /* Set Wrap bit on last entry */ + if (qmc->int_size >= sizeof(u16)) { + last = qmc->int_table + (qmc->int_size / sizeof(u16)) - 1; + qmc_write16(last, QMC_INT_W); + } + + return 0; +} + +static void qmc_irq_gint(struct qmc *qmc) +{ + struct qmc_chan *chan; + unsigned int chan_id; + unsigned long flags; + u16 int_entry; + + int_entry = qmc_read16(qmc->int_curr); + while (int_entry & QMC_INT_V) { + /* Clear all but the Wrap bit */ + qmc_write16(qmc->int_curr, int_entry & QMC_INT_W); + + chan_id = QMC_INT_GET_CHANNEL(int_entry); + chan = qmc->chans[chan_id]; + if (!chan) { + dev_err(qmc->dev, "interrupt on invalid chan %u\n", chan_id); + goto int_next; + } + + if (int_entry & QMC_INT_TXB) + qmc_chan_write_done(chan); + + if (int_entry & QMC_INT_UN) { + dev_info(qmc->dev, "intr chan %u, 0x%04x (UN)\n", chan_id, + int_entry); + chan->nb_tx_underrun++; + } + + if (int_entry & QMC_INT_BSY) { + dev_info(qmc->dev, "intr chan %u, 0x%04x (BSY)\n", chan_id, + int_entry); + chan->nb_rx_busy++; + /* Restart the receiver if needed */ + spin_lock_irqsave(&chan->rx_lock, flags); + if (chan->rx_pending && !chan->is_rx_stopped) { + if (chan->mode == QMC_TRANSPARENT) + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + else + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + chan->is_rx_halted = false; + } else { + chan->is_rx_halted = true; + } + spin_unlock_irqrestore(&chan->rx_lock, flags); + } + + if (int_entry & QMC_INT_RXB) + qmc_chan_read_done(chan); + +int_next: + if (int_entry & QMC_INT_W) + qmc->int_curr = qmc->int_table; + else + qmc->int_curr++; + int_entry = qmc_read16(qmc->int_curr); + } +} + +static irqreturn_t qmc_irq_handler(int irq, void *priv) +{ + struct qmc *qmc = (struct qmc *)priv; + u16 scce; + + scce = qmc_read16(qmc->scc_regs + SCC_SCCE); + qmc_write16(qmc->scc_regs + SCC_SCCE, scce); + + if (unlikely(scce & SCC_SCCE_IQOV)) + dev_info(qmc->dev, "IRQ queue overflow\n"); + + if (unlikely(scce & SCC_SCCE_GUN)) + dev_err(qmc->dev, "Global transmitter underrun\n"); + + if (unlikely(scce & SCC_SCCE_GOV)) + dev_err(qmc->dev, "Global receiver overrun\n"); + + /* normal interrupt */ + if (likely(scce & SCC_SCCE_GINT)) + qmc_irq_gint(qmc); + + return IRQ_HANDLED; +} + +static int qmc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + unsigned int nb_chans; + struct resource *res; + struct qmc *qmc; + int irq; + int ret; + + qmc = devm_kzalloc(&pdev->dev, sizeof(*qmc), GFP_KERNEL); + if (!qmc) + return -ENOMEM; + + qmc->dev = &pdev->dev; + INIT_LIST_HEAD(&qmc->chan_head); + + qmc->scc_regs = devm_platform_ioremap_resource_byname(pdev, "scc_regs"); + if (IS_ERR(qmc->scc_regs)) + return PTR_ERR(qmc->scc_regs); + + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scc_pram"); + if (!res) + return -EINVAL; + qmc->scc_pram_offset = res->start - get_immrbase(); + qmc->scc_pram = devm_ioremap_resource(qmc->dev, res); + if (IS_ERR(qmc->scc_pram)) + return PTR_ERR(qmc->scc_pram); + + qmc->dpram = devm_platform_ioremap_resource_byname(pdev, "dpram"); + if (IS_ERR(qmc->dpram)) + return PTR_ERR(qmc->dpram); + + qmc->tsa_serial = devm_tsa_serial_get_byphandle(qmc->dev, np, "fsl,tsa-serial"); + if (IS_ERR(qmc->tsa_serial)) { + return dev_err_probe(qmc->dev, PTR_ERR(qmc->tsa_serial), + "Failed to get TSA serial\n"); + } + + /* Connect the serial (SCC) to TSA */ + ret = tsa_serial_connect(qmc->tsa_serial); + if (ret) { + dev_err(qmc->dev, "Failed to connect TSA serial\n"); + return ret; + } + + /* Parse channels informationss */ + ret = qmc_of_parse_chans(qmc, np); + if (ret) + goto err_tsa_serial_disconnect; + + nb_chans = qmc_nb_chans(qmc); + + /* Init GMSR_H and GMSR_L registers */ + qmc_write32(qmc->scc_regs + SCC_GSMRH, + SCC_GSMRH_CDS | SCC_GSMRH_CTSS | SCC_GSMRH_CDP | SCC_GSMRH_CTSP); + + /* enable QMC mode */ + qmc_write32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_MODE_QMC); + + /* + * Allocate the buffer descriptor table + * 8 rx and 8 tx descriptors per channel + */ + qmc->bd_size = (nb_chans * (QMC_NB_TXBDS + QMC_NB_RXBDS)) * sizeof(cbd_t); + qmc->bd_table = dmam_alloc_coherent(qmc->dev, qmc->bd_size, + &qmc->bd_dma_addr, GFP_KERNEL); + if (!qmc->bd_table) { + dev_err(qmc->dev, "Failed to allocate bd table\n"); + ret = -ENOMEM; + goto err_tsa_serial_disconnect; + } + memset(qmc->bd_table, 0, qmc->bd_size); + + qmc_write32(qmc->scc_pram + QMC_GBL_MCBASE, qmc->bd_dma_addr); + + /* Allocate the interrupt table */ + qmc->int_size = QMC_NB_INTS * sizeof(u16); + qmc->int_table = dmam_alloc_coherent(qmc->dev, qmc->int_size, + &qmc->int_dma_addr, GFP_KERNEL); + if (!qmc->int_table) { + dev_err(qmc->dev, "Failed to allocate interrupt table\n"); + ret = -ENOMEM; + goto err_tsa_serial_disconnect; + } + memset(qmc->int_table, 0, qmc->int_size); + + qmc->int_curr = qmc->int_table; + qmc_write32(qmc->scc_pram + QMC_GBL_INTBASE, qmc->int_dma_addr); + qmc_write32(qmc->scc_pram + QMC_GBL_INTPTR, qmc->int_dma_addr); + + /* Set MRBLR (valid for HDLC only) max MRU + max CRC */ + qmc_write16(qmc->scc_pram + QMC_GBL_MRBLR, HDLC_MAX_MRU + 4); + + qmc_write16(qmc->scc_pram + QMC_GBL_GRFTHR, 1); + qmc_write16(qmc->scc_pram + QMC_GBL_GRFCNT, 1); + + qmc_write32(qmc->scc_pram + QMC_GBL_C_MASK32, 0xDEBB20E3); + qmc_write16(qmc->scc_pram + QMC_GBL_C_MASK16, 0xF0B8); + + ret = qmc_setup_tsa(qmc); + if (ret) + goto err_tsa_serial_disconnect; + + qmc_write16(qmc->scc_pram + QMC_GBL_QMCSTATE, 0x8000); + + ret = qmc_setup_chans(qmc); + if (ret) + goto err_tsa_serial_disconnect; + + /* Init interrupts table */ + ret = qmc_setup_ints(qmc); + if (ret) + goto err_tsa_serial_disconnect; + + /* Disable and clear interrupts, set the irq handler */ + qmc_write16(qmc->scc_regs + SCC_SCCM, 0x0000); + qmc_write16(qmc->scc_regs + SCC_SCCE, 0x000F); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + goto err_tsa_serial_disconnect; + ret = devm_request_irq(qmc->dev, irq, qmc_irq_handler, 0, "qmc", qmc); + if (ret < 0) + goto err_tsa_serial_disconnect; + + /* Enable interrupts */ + qmc_write16(qmc->scc_regs + SCC_SCCM, + SCC_SCCE_IQOV | SCC_SCCE_GINT | SCC_SCCE_GUN | SCC_SCCE_GOV); + + ret = qmc_finalize_chans(qmc); + if (ret < 0) + goto err_disable_intr; + + /* Enable transmiter and receiver */ + qmc_setbits32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + platform_set_drvdata(pdev, qmc); + + return 0; + +err_disable_intr: + qmc_write16(qmc->scc_regs + SCC_SCCM, 0); + +err_tsa_serial_disconnect: + tsa_serial_disconnect(qmc->tsa_serial); + return ret; +} + +static int qmc_remove(struct platform_device *pdev) +{ + struct qmc *qmc = platform_get_drvdata(pdev); + + /* Disable transmiter and receiver */ + qmc_setbits32(qmc->scc_regs + SCC_GSMRL, 0); + + /* Disable interrupts */ + qmc_write16(qmc->scc_regs + SCC_SCCM, 0); + + /* Disconnect the serial from TSA */ + tsa_serial_disconnect(qmc->tsa_serial); + + return 0; +} + +static const struct of_device_id qmc_id_table[] = { + { .compatible = "fsl,cpm1-scc-qmc" }, + {} /* sentinel */ +}; +MODULE_DEVICE_TABLE(of, qmc_id_table); + +static struct platform_driver qmc_driver = { + .driver = { + .name = "fsl-qmc", + .of_match_table = of_match_ptr(qmc_id_table), + }, + .probe = qmc_probe, + .remove = qmc_remove, +}; +module_platform_driver(qmc_driver); + +struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name) +{ + struct of_phandle_args out_args; + struct platform_device *pdev; + struct qmc_chan *qmc_chan; + struct qmc *qmc; + int ret; + + ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0, + &out_args); + if (ret < 0) + return ERR_PTR(ret); + + if (!of_match_node(qmc_driver.driver.of_match_table, out_args.np)) { + of_node_put(out_args.np); + return ERR_PTR(-EINVAL); + } + + pdev = of_find_device_by_node(out_args.np); + of_node_put(out_args.np); + if (!pdev) + return ERR_PTR(-ENODEV); + + qmc = platform_get_drvdata(pdev); + if (!qmc) { + platform_device_put(pdev); + return ERR_PTR(-EPROBE_DEFER); + } + + if (out_args.args_count != 1) { + platform_device_put(pdev); + return ERR_PTR(-EINVAL); + } + + if (out_args.args[0] >= ARRAY_SIZE(qmc->chans)) { + platform_device_put(pdev); + return ERR_PTR(-EINVAL); + } + + qmc_chan = qmc->chans[out_args.args[0]]; + if (!qmc_chan) { + platform_device_put(pdev); + return ERR_PTR(-ENOENT); + } + + return qmc_chan; +} +EXPORT_SYMBOL(qmc_chan_get_byphandle); + +void qmc_chan_put(struct qmc_chan *chan) +{ + put_device(chan->qmc->dev); +} +EXPORT_SYMBOL(qmc_chan_put); + +static void devm_qmc_chan_release(struct device *dev, void *res) +{ + struct qmc_chan **qmc_chan = res; + + qmc_chan_put(*qmc_chan); +} + +struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, + struct device_node *np, + const char *phandle_name) +{ + struct qmc_chan *qmc_chan; + struct qmc_chan **dr; + + dr = devres_alloc(devm_qmc_chan_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + qmc_chan = qmc_chan_get_byphandle(np, phandle_name); + if (!IS_ERR(qmc_chan)) { + *dr = qmc_chan; + devres_add(dev, dr); + } else { + devres_free(dr); + } + + return qmc_chan; +} +EXPORT_SYMBOL(devm_qmc_chan_get_byphandle); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("CPM QMC driver"); +MODULE_LICENSE("GPL"); diff --git a/include/soc/fsl/qe/qmc.h b/include/soc/fsl/qe/qmc.h new file mode 100644 index 000000000000..3c61a50d2ae2 --- /dev/null +++ b/include/soc/fsl/qe/qmc.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QMC management + * + * Copyright 2022 CS GROUP France + * + * Author: Herve Codina + */ +#ifndef __SOC_FSL_QMC_H__ +#define __SOC_FSL_QMC_H__ + +#include + +struct device_node; +struct device; +struct qmc_chan; + +struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name); +void qmc_chan_put(struct qmc_chan *chan); +struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, struct device_node *np, + const char *phandle_name); + +enum qmc_mode { + QMC_TRANSPARENT, + QMC_HDLC, +}; + +struct qmc_chan_info { + enum qmc_mode mode; + unsigned long rx_fs_rate; + unsigned long rx_bit_rate; + u8 nb_rx_ts; + unsigned long tx_fs_rate; + unsigned long tx_bit_rate; + u8 nb_tx_ts; +}; + +int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info); + +struct qmc_chan_param { + enum qmc_mode mode; + union { + struct { + u16 max_rx_buf_size; + u16 max_rx_frame_size; + bool is_crc32; + } hdlc; + struct { + u16 max_rx_buf_size; + } transp; + }; +}; + +int qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param); + +int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context), void *context); + +int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context, size_t length), + void *context); + +#define QMC_CHAN_READ (1<<0) +#define QMC_CHAN_WRITE (1<<1) +#define QMC_CHAN_ALL (QMC_CHAN_READ | QMC_CHAN_WRITE) + +int qmc_chan_start(struct qmc_chan *chan, int direction); +int qmc_chan_stop(struct qmc_chan *chan, int direction); +int qmc_chan_reset(struct qmc_chan *chan, int direction); + +#endif /* __SOC_FSL_QMC_H__ */ -- cgit v1.2.3 From 11f45690b3f6c6a2b5c57dbb036df3f838f7c016 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 7 Mar 2023 14:35:54 +0200 Subject: ASoC: SOF: ipc4: Add macro to set the core_id in create_pipe message The create pipeline message can carry the target code_id which is set to 0 at the moment. Add macros to set the core_id in the message extension. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20230307123556.31328-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index d31349bf011d..49ff1558a171 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -176,6 +176,10 @@ enum sof_ipc4_pipeline_state { #define SOF_IPC4_GLB_PIPE_EXT_LP_MASK BIT(0) #define SOF_IPC4_GLB_PIPE_EXT_LP(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_LP_SHIFT) +#define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT 20 +#define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK GENMASK(23, 20) +#define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT) + /* pipeline set state ipc msg */ #define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16 #define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16) -- cgit v1.2.3 From 2e4ef6f4798c1d2951dd7bb3ae5f0d41ec3d31e8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 13 Mar 2023 13:03:40 +0200 Subject: ASoC: SOF: uapi: header: Convert sof_abi_hdr comments to kernel style Replace the comments for sof_abi_hdr to kernel style. Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Jaska Uimonen Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20230313110344.16644-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/header.h | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/sof/header.h b/include/uapi/sound/sof/header.h index e9bba93a5399..e53b62b9e2c5 100644 --- a/include/uapi/sound/sof/header.h +++ b/include/uapi/sound/sof/header.h @@ -11,19 +11,25 @@ #include -/* - * Header for all non IPC ABI data. +/** + * struct sof_abi_hdr - Header for all non IPC ABI data. + * @magic: Magic number for validation: 0x00464F53 ('S', 'O', 'F', '\0') + * @type: Component specific type + * @size: The size in bytes of the data, excluding this struct + * @abi: SOF ABI version + * @reserved: Reserved for future use + * @data: Component data - opaque to core * * Identifies data type, size and ABI. * Used by any bespoke component data structures or binary blobs. */ struct sof_abi_hdr { - __u32 magic; /**< 'S', 'O', 'F', '\0' */ - __u32 type; /**< component specific type */ - __u32 size; /**< size in bytes of data excl. this struct */ - __u32 abi; /**< SOF ABI version */ - __u32 reserved[4]; /**< reserved for future use */ - __u32 data[]; /**< Component data - opaque to core */ + __u32 magic; + __u32 type; + __u32 size; + __u32 abi; + __u32 reserved[4]; + __u32 data[]; } __packed; #define SOF_MANIFEST_DATA_TYPE_NHLT 1 -- cgit v1.2.3 From ea4a4e82f625ae451175a2a74779776b006d25a1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 13 Mar 2023 13:03:41 +0200 Subject: ASoC: SOF: uapi: header: Update sof_abi_hdr doc for IPC4 use With IPC4 the sof_abit_hdr is only used between user space (and in topology) and kernel. The same abi header is used with small differencies like different magic number and the type field have slightly different name, but similar function in IPC4 (param_id). Update the kernel documentation to highlight the differences. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Jaska Uimonen Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20230313110344.16644-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/abi.h | 2 ++ include/uapi/sound/sof/header.h | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h index 3566630ca965..45c657c3919e 100644 --- a/include/uapi/sound/sof/abi.h +++ b/include/uapi/sound/sof/abi.h @@ -60,5 +60,7 @@ /* SOF ABI magic number "SOF\0". */ #define SOF_ABI_MAGIC 0x00464F53 +/* SOF IPC4 ABI magic number "SOF4". */ +#define SOF_IPC4_ABI_MAGIC 0x34464F53 #endif diff --git a/include/uapi/sound/sof/header.h b/include/uapi/sound/sof/header.h index e53b62b9e2c5..cb3c1ace69e3 100644 --- a/include/uapi/sound/sof/header.h +++ b/include/uapi/sound/sof/header.h @@ -13,10 +13,15 @@ /** * struct sof_abi_hdr - Header for all non IPC ABI data. - * @magic: Magic number for validation: 0x00464F53 ('S', 'O', 'F', '\0') - * @type: Component specific type + * @magic: Magic number for validation + * for IPC3 data: 0x00464F53 ('S', 'O', 'F', '\0') + * for IPC4 data: 0x34464F53 ('S', 'O', 'F', '4') + * @type: module specific parameter + * for IPC3: Component specific type + * for IPC4: parameter ID (param_id) of the data * @size: The size in bytes of the data, excluding this struct - * @abi: SOF ABI version + * @abi: SOF ABI version. The version is valid in scope of the 'magic', IPC3 and + * IPC4 ABI version numbers have no relationship. * @reserved: Reserved for future use * @data: Component data - opaque to core * -- cgit v1.2.3 From 3f738e4a126c9ee082d814edeb7416697f9e2b37 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 13 Mar 2023 14:48:46 +0200 Subject: ASoC: SOF: rename a couple of tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename SOF_TKN_CAVS_AUDIO_FORMAT_IN_VALID and SOF_TKN_CAVS_AUDIO_FORMAT_OUT_VALID as SOF_TKN_CAVS_AUDIO_FORMAT_IN_VALID_BIT_DEPTH and SOF_TKN_CAVS_AUDIO_FORMAT_OUT_VALID_BIT_DEPTH respectively. These are currently not used. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20230313124856.8140-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index bacaf8a6317e..a1ef6b5c0d45 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -173,7 +173,7 @@ /* CAVS AUDIO FORMAT */ #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_RATE 1900 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_BIT_DEPTH 1901 -#define SOF_TKN_CAVS_AUDIO_FORMAT_IN_VALID_BIT 1902 +#define SOF_TKN_CAVS_AUDIO_FORMAT_IN_VALID_BIT_DEPTH 1902 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_CHANNELS 1903 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_MAP 1904 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_CFG 1905 @@ -183,7 +183,7 @@ /* intentional token numbering discontinuity, reserved for future use */ #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE 1930 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH 1931 -#define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_VALID_BIT 1932 +#define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_VALID_BIT_DEPTH 1932 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CHANNELS 1933 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_MAP 1934 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_CFG 1935 -- cgit v1.2.3 From bb79f2a608245cd92b3183d77aec6902e51de950 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 13 Mar 2023 14:48:47 +0200 Subject: ASoC: SOF: Use input/output pin consistently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we use input/output and sink/source pins interchangeably. Remove the references to sink/source pins and replace with input/output pins everywhere for consistency and clarity. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20230313124856.8140-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 12 +++---- sound/soc/sof/ipc4-topology.c | 48 ++++++++++++------------- sound/soc/sof/sof-audio.h | 32 ++++++++--------- sound/soc/sof/topology.c | 80 ++++++++++++++++++++--------------------- 4 files changed, 86 insertions(+), 86 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index a1ef6b5c0d45..0b5110427132 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -88,14 +88,14 @@ #define SOF_TKN_COMP_CPC 406 #define SOF_TKN_COMP_IS_PAGES 409 #define SOF_TKN_COMP_NUM_AUDIO_FORMATS 410 -#define SOF_TKN_COMP_NUM_SINK_PINS 411 -#define SOF_TKN_COMP_NUM_SOURCE_PINS 412 +#define SOF_TKN_COMP_NUM_INPUT_PINS 411 +#define SOF_TKN_COMP_NUM_OUTPUT_PINS 412 /* - * The token for sink/source pin binding, it specifies the widget - * name that the sink/source pin is connected from/to. + * The token for input/output pin binding, it specifies the widget + * name that the input/output pin is connected from/to. */ -#define SOF_TKN_COMP_SINK_PIN_BINDING_WNAME 413 -#define SOF_TKN_COMP_SRC_PIN_BINDING_WNAME 414 +#define SOF_TKN_COMP_INPUT_PIN_BINDING_WNAME 413 +#define SOF_TKN_COMP_OUTPUT_PIN_BINDING_WNAME 414 /* SSP */ diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 0bb16ed38e48..a501eb9befa8 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1698,17 +1698,17 @@ static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget, u32 num_pins; int i; - if (pin_type == SOF_PIN_TYPE_SOURCE) { + if (pin_type == SOF_PIN_TYPE_OUTPUT) { current_swidget = src_widget; - pin_binding = src_widget->src_pin_binding; - queue_ida = &src_widget->src_queue_ida; - num_pins = src_widget->num_source_pins; + pin_binding = src_widget->output_pin_binding; + queue_ida = &src_widget->output_queue_ida; + num_pins = src_widget->num_output_pins; buddy_name = sink_widget->widget->name; } else { current_swidget = sink_widget; - pin_binding = sink_widget->sink_pin_binding; - queue_ida = &sink_widget->sink_queue_ida; - num_pins = sink_widget->num_sink_pins; + pin_binding = sink_widget->input_pin_binding; + queue_ida = &sink_widget->input_queue_ida; + num_pins = sink_widget->num_input_pins; buddy_name = src_widget->widget->name; } @@ -1716,12 +1716,12 @@ static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget, if (num_pins < 1) { dev_err(scomp->dev, "invalid %s num_pins: %d for queue allocation for %s\n", - (pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"), + (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"), num_pins, current_swidget->widget->name); return -EINVAL; } - /* If there is only one sink/source pin, queue id must be 0 */ + /* If there is only one input/output pin, queue id must be 0 */ if (num_pins == 1) return 0; @@ -1736,7 +1736,7 @@ static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget, * mixed use pin binding array and ida for queue ID allocation. */ dev_err(scomp->dev, "no %s queue id found from pin binding array for %s\n", - (pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"), + (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"), current_swidget->widget->name); return -EINVAL; } @@ -1752,14 +1752,14 @@ static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id, char **pin_binding; int num_pins; - if (pin_type == SOF_PIN_TYPE_SOURCE) { - pin_binding = swidget->src_pin_binding; - queue_ida = &swidget->src_queue_ida; - num_pins = swidget->num_source_pins; + if (pin_type == SOF_PIN_TYPE_OUTPUT) { + pin_binding = swidget->output_pin_binding; + queue_ida = &swidget->output_queue_ida; + num_pins = swidget->num_output_pins; } else { - pin_binding = swidget->sink_pin_binding; - queue_ida = &swidget->sink_queue_ida; - num_pins = swidget->num_sink_pins; + pin_binding = swidget->input_pin_binding; + queue_ida = &swidget->input_queue_ida; + num_pins = swidget->num_input_pins; } /* Nothing to free if queue ID is not allocated with ida. */ @@ -1829,7 +1829,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * int ret; sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, - SOF_PIN_TYPE_SOURCE); + SOF_PIN_TYPE_OUTPUT); if (sroute->src_queue_id < 0) { dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n", src_widget->widget->name); @@ -1837,12 +1837,12 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * } sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, - SOF_PIN_TYPE_SINK); + SOF_PIN_TYPE_INPUT); if (sroute->dst_queue_id < 0) { dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n", sink_widget->widget->name); sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, - SOF_PIN_TYPE_SOURCE); + SOF_PIN_TYPE_OUTPUT); return sroute->dst_queue_id; } @@ -1886,8 +1886,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * return ret; out: - sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE); - sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK); + sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT); + sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT); return ret; } @@ -1932,8 +1932,8 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); out: - sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK); - sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE); + sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT); + sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT); return ret; } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 239b82f37976..a1c4d3f34153 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -30,9 +30,9 @@ */ #define SOF_WIDGET_MAX_NUM_PINS 8 -/* The type of a widget pin is either sink or source */ -#define SOF_PIN_TYPE_SINK 0 -#define SOF_PIN_TYPE_SOURCE 1 +/* Widget pin type */ +#define SOF_PIN_TYPE_INPUT 0 +#define SOF_PIN_TYPE_OUTPUT 1 /* max number of FE PCMs before BEs */ #define SOF_BE_PCM_BASE 16 @@ -433,31 +433,31 @@ struct snd_sof_widget { struct snd_sof_tuple *tuples; /* - * The allowed range for num_sink/source_pins is [0, SOF_WIDGET_MAX_NUM_PINS]. - * Widgets may have zero sink or source pins, for example the tone widget has - * zero sink pins. + * The allowed range for num_input/output_pins is [0, SOF_WIDGET_MAX_NUM_PINS]. + * Widgets may have zero input or output pins, for example the tone widget has + * zero input pins. */ - u32 num_sink_pins; - u32 num_source_pins; + u32 num_input_pins; + u32 num_output_pins; /* - * The sink/source pin binding array, it takes the form of + * The input/output pin binding array, it takes the form of * [widget_name_connected_to_pin0, widget_name_connected_to_pin1, ...], * with the index as the queue ID. * * The array is used for special pin binding. Note that even if there - * is only one sink/source pin requires special pin binding, pin binding - * should be defined for all sink/source pins in topology, for pin(s) that + * is only one input/output pin requires special pin binding, pin binding + * should be defined for all input/output pins in topology, for pin(s) that * are not used, give the value "NotConnected". * * If pin binding is not defined in topology, nothing to parse in the kernel, - * sink_pin_binding and src_pin_binding shall be NULL. + * input_pin_binding and output_pin_binding shall be NULL. */ - char **sink_pin_binding; - char **src_pin_binding; + char **input_pin_binding; + char **output_pin_binding; - struct ida src_queue_ida; - struct ida sink_queue_ida; + struct ida output_queue_ida; + struct ida input_queue_ida; void *private; /* core does not touch this */ }; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 9f3a038fe21a..9f84b4c362c3 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -416,19 +416,19 @@ static const struct sof_topology_token led_tokens[] = { }; static const struct sof_topology_token comp_pin_tokens[] = { - {SOF_TKN_COMP_NUM_SINK_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_widget, num_sink_pins)}, - {SOF_TKN_COMP_NUM_SOURCE_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_widget, num_source_pins)}, + {SOF_TKN_COMP_NUM_INPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct snd_sof_widget, num_input_pins)}, + {SOF_TKN_COMP_NUM_OUTPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct snd_sof_widget, num_output_pins)}, }; -static const struct sof_topology_token comp_sink_pin_binding_tokens[] = { - {SOF_TKN_COMP_SINK_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING, +static const struct sof_topology_token comp_input_pin_binding_tokens[] = { + {SOF_TKN_COMP_INPUT_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_string, 0}, }; -static const struct sof_topology_token comp_src_pin_binding_tokens[] = { - {SOF_TKN_COMP_SRC_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING, +static const struct sof_topology_token comp_output_pin_binding_tokens[] = { + {SOF_TKN_COMP_OUTPUT_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_string, 0}, }; @@ -1286,12 +1286,12 @@ static void sof_free_pin_binding(struct snd_sof_widget *swidget, u32 num_pins; int i; - if (pin_type == SOF_PIN_TYPE_SINK) { - pin_binding = swidget->sink_pin_binding; - num_pins = swidget->num_sink_pins; + if (pin_type == SOF_PIN_TYPE_INPUT) { + pin_binding = swidget->input_pin_binding; + num_pins = swidget->num_input_pins; } else { - pin_binding = swidget->src_pin_binding; - num_pins = swidget->num_source_pins; + pin_binding = swidget->output_pin_binding; + num_pins = swidget->num_output_pins; } if (pin_binding) { @@ -1313,14 +1313,14 @@ static int sof_parse_pin_binding(struct snd_sof_widget *swidget, int ret; int i; - if (pin_type == SOF_PIN_TYPE_SINK) { - num_pins = swidget->num_sink_pins; - pin_binding_token = comp_sink_pin_binding_tokens; - token_count = ARRAY_SIZE(comp_sink_pin_binding_tokens); + if (pin_type == SOF_PIN_TYPE_INPUT) { + num_pins = swidget->num_input_pins; + pin_binding_token = comp_input_pin_binding_tokens; + token_count = ARRAY_SIZE(comp_input_pin_binding_tokens); } else { - num_pins = swidget->num_source_pins; - pin_binding_token = comp_src_pin_binding_tokens; - token_count = ARRAY_SIZE(comp_src_pin_binding_tokens); + num_pins = swidget->num_output_pins; + pin_binding_token = comp_output_pin_binding_tokens; + token_count = ARRAY_SIZE(comp_output_pin_binding_tokens); } memset(pin_binding, 0, SOF_WIDGET_MAX_NUM_PINS * sizeof(char *)); @@ -1337,10 +1337,10 @@ static int sof_parse_pin_binding(struct snd_sof_widget *swidget, ret = -ENOMEM; goto err; } - if (pin_type == SOF_PIN_TYPE_SINK) - swidget->sink_pin_binding = pb; + if (pin_type == SOF_PIN_TYPE_INPUT) + swidget->input_pin_binding = pb; else - swidget->src_pin_binding = pb; + swidget->output_pin_binding = pb; } return 0; @@ -1379,8 +1379,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, swidget->private = NULL; mutex_init(&swidget->setup_mutex); - ida_init(&swidget->src_queue_ida); - ida_init(&swidget->sink_queue_ida); + ida_init(&swidget->output_queue_ida); + ida_init(&swidget->input_queue_ida); ret = sof_parse_tokens(scomp, swidget, comp_pin_tokens, ARRAY_SIZE(comp_pin_tokens), priv->array, @@ -1391,29 +1391,29 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, goto widget_free; } - if (swidget->num_sink_pins > SOF_WIDGET_MAX_NUM_PINS || - swidget->num_source_pins > SOF_WIDGET_MAX_NUM_PINS) { - dev_err(scomp->dev, "invalid pins for %s: [sink: %d, src: %d]\n", - swidget->widget->name, swidget->num_sink_pins, swidget->num_source_pins); + if (swidget->num_input_pins > SOF_WIDGET_MAX_NUM_PINS || + swidget->num_output_pins > SOF_WIDGET_MAX_NUM_PINS) { + dev_err(scomp->dev, "invalid pins for %s: [input: %d, output: %d]\n", + swidget->widget->name, swidget->num_input_pins, swidget->num_output_pins); ret = -EINVAL; goto widget_free; } - if (swidget->num_sink_pins > 1) { - ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_SINK); + if (swidget->num_input_pins > 1) { + ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_INPUT); /* on parsing error, pin binding is not allocated, nothing to free. */ if (ret < 0) { - dev_err(scomp->dev, "failed to parse sink pin binding for %s\n", + dev_err(scomp->dev, "failed to parse input pin binding for %s\n", w->name); goto widget_free; } } - if (swidget->num_source_pins > 1) { - ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_SOURCE); + if (swidget->num_output_pins > 1) { + ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_OUTPUT); /* on parsing error, pin binding is not allocated, nothing to free. */ if (ret < 0) { - dev_err(scomp->dev, "failed to parse source pin binding for %s\n", + dev_err(scomp->dev, "failed to parse output pin binding for %s\n", w->name); goto widget_free; } @@ -1422,7 +1422,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, dev_dbg(scomp->dev, "tplg: widget %d (%s) is ready [type: %d, pipe: %d, pins: %d / %d, stream: %s]\n", swidget->comp_id, w->name, swidget->id, index, - swidget->num_sink_pins, swidget->num_source_pins, + swidget->num_input_pins, swidget->num_output_pins, strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? w->sname : "none"); widget_ops = tplg_ops ? tplg_ops->widget : NULL; @@ -1643,11 +1643,11 @@ out: if (widget_ops && widget_ops[swidget->id].ipc_free) widget_ops[swidget->id].ipc_free(swidget); - ida_destroy(&swidget->src_queue_ida); - ida_destroy(&swidget->sink_queue_ida); + ida_destroy(&swidget->output_queue_ida); + ida_destroy(&swidget->input_queue_ida); - sof_free_pin_binding(swidget, SOF_PIN_TYPE_SINK); - sof_free_pin_binding(swidget, SOF_PIN_TYPE_SOURCE); + sof_free_pin_binding(swidget, SOF_PIN_TYPE_INPUT); + sof_free_pin_binding(swidget, SOF_PIN_TYPE_OUTPUT); kfree(swidget->tuples); -- cgit v1.2.3 From 594c1bb9ff7365b90cb4d325deb8c38ddda90557 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 13 Mar 2023 14:48:49 +0200 Subject: ASoC: SOF: ipc4-topology: Do not parse the DMA_BUFFER_SIZE token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not parse the SOF_TKN_CAVS_AUDIO_FORMAT_DMA_BUFFER_SIZE token as the dma_buffer_size can be derived from the input/output buffer size and the type of widget during copier prepare. For the deep buffer case, introduce a new token that will be used to get the deep buffer DMA size for the host copier from topology. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20230313124856.8140-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 1 + sound/soc/sof/ipc4-topology.c | 112 +++++++++++++++++----------------------- sound/soc/sof/ipc4-topology.h | 5 +- sound/soc/sof/sof-audio.h | 2 +- sound/soc/sof/topology.c | 1 - 5 files changed, 52 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 0b5110427132..9e91e2640dd4 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -197,6 +197,7 @@ /* COPIER */ #define SOF_TKN_INTEL_COPIER_NODE_TYPE 1980 +#define SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS 1981 /* ACP I2S */ #define SOF_TKN_AMD_ACPI2S_RATE 1700 diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index d56e0f12b5d0..50cd4fe3141c 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -78,8 +78,8 @@ static const struct sof_topology_token ipc4_out_audio_format_tokens[] = { offsetof(struct sof_ipc4_audio_format, fmt_cfg)}, }; -static const struct sof_topology_token ipc4_copier_gateway_cfg_tokens[] = { - {SOF_TKN_CAVS_AUDIO_FORMAT_DMA_BUFFER_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0}, +static const struct sof_topology_token ipc4_copier_deep_buffer_tokens[] = { + {SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0}, }; static const struct sof_topology_token ipc4_copier_tokens[] = { @@ -138,8 +138,8 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { [SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS] = {"IPC4 Audio format buffer size tokens", ipc4_audio_format_buffer_size_tokens, ARRAY_SIZE(ipc4_audio_format_buffer_size_tokens)}, - [SOF_COPIER_GATEWAY_CFG_TOKENS] = {"IPC4 Copier gateway config tokens", - ipc4_copier_gateway_cfg_tokens, ARRAY_SIZE(ipc4_copier_gateway_cfg_tokens)}, + [SOF_COPIER_DEEP_BUFFER_TOKENS] = {"IPC4 Copier deep buffer tokens", + ipc4_copier_deep_buffer_tokens, ARRAY_SIZE(ipc4_copier_deep_buffer_tokens)}, [SOF_COPIER_TOKENS] = {"IPC4 Copier tokens", ipc4_copier_tokens, ARRAY_SIZE(ipc4_copier_tokens)}, [SOF_AUDIO_FMT_NUM_TOKENS] = {"IPC4 Audio format number tokens", @@ -348,7 +348,7 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_copier *ipc4_copier; int node_type = 0; - int ret, i; + int ret; ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL); if (!ipc4_copier) @@ -363,13 +363,6 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) if (ret) goto free_copier; - available_fmt->dma_buffer_size = kcalloc(available_fmt->audio_fmt_num, sizeof(u32), - GFP_KERNEL); - if (!available_fmt->dma_buffer_size) { - ret = -ENOMEM; - goto free_available_fmt; - } - /* * This callback is used by host copier and module-to-module copier, * and only host copier needs to set gtw_cfg. @@ -377,21 +370,6 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) if (!WIDGET_IS_AIF(swidget->id)) goto skip_gtw_cfg; - ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size, - SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(u32), - available_fmt->audio_fmt_num); - if (ret) { - dev_err(scomp->dev, "Failed to parse dma buffer size in audio format for %s\n", - swidget->widget->name); - goto err; - } - - dev_dbg(scomp->dev, "dma buffer size:\n"); - for (i = 0; i < available_fmt->audio_fmt_num; i++) - dev_dbg(scomp->dev, "%d: %u\n", i, - available_fmt->dma_buffer_size[i]); - ret = sof_update_ipc_object(scomp, &node_type, SOF_COPIER_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(node_type), 1); @@ -399,7 +377,7 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) if (ret) { dev_err(scomp->dev, "parse host copier node type token failed %d\n", ret); - goto err; + goto free_available_fmt; } dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type); @@ -407,7 +385,7 @@ skip_gtw_cfg: ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL); if (!ipc4_copier->gtw_attr) { ret = -ENOMEM; - goto err; + goto free_available_fmt; } ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr; @@ -438,8 +416,6 @@ skip_gtw_cfg: free_gtw_attr: kfree(ipc4_copier->gtw_attr); -err: - kfree(available_fmt->dma_buffer_size); free_available_fmt: sof_ipc4_free_audio_fmt(available_fmt); free_copier: @@ -457,7 +433,6 @@ static void sof_ipc4_widget_free_comp_pcm(struct snd_sof_widget *swidget) return; available_fmt = &ipc4_copier->available_fmt; - kfree(available_fmt->dma_buffer_size); kfree(available_fmt->base_config); kfree(available_fmt->out_audio_fmt); kfree(ipc4_copier->gtw_attr); @@ -472,7 +447,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier; int node_type = 0; - int ret, i; + int ret; ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL); if (!ipc4_copier) @@ -486,33 +461,12 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) if (ret) goto free_copier; - available_fmt->dma_buffer_size = kcalloc(available_fmt->audio_fmt_num, sizeof(u32), - GFP_KERNEL); - if (!available_fmt->dma_buffer_size) { - ret = -ENOMEM; - goto free_available_fmt; - } - - ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size, - SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(u32), - available_fmt->audio_fmt_num); - if (ret) { - dev_err(scomp->dev, "Failed to parse dma buffer size in audio format for %s\n", - swidget->widget->name); - goto err; - } - - for (i = 0; i < available_fmt->audio_fmt_num; i++) - dev_dbg(scomp->dev, "%d: dma buffer size: %u\n", i, - available_fmt->dma_buffer_size[i]); - ret = sof_update_ipc_object(scomp, &node_type, SOF_COPIER_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(node_type), 1); if (ret) { dev_err(scomp->dev, "parse dai node type failed %d\n", ret); - goto err; + goto free_available_fmt; } ret = sof_update_ipc_object(scomp, ipc4_copier, @@ -520,7 +474,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) swidget->num_tuples, sizeof(u32), 1); if (ret) { dev_err(scomp->dev, "parse dai copier node token failed %d\n", ret); - goto err; + goto free_available_fmt; } dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name, @@ -554,7 +508,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) blob = kzalloc(sizeof(*blob), GFP_KERNEL); if (!blob) { ret = -ENOMEM; - goto err; + goto free_available_fmt; } list_for_each_entry(w, &sdev->widget_list, list) { @@ -583,7 +537,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL); if (!ipc4_copier->gtw_attr) { ret = -ENOMEM; - goto err; + goto free_available_fmt; } ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr; @@ -604,8 +558,6 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) free_copier_config: kfree(ipc4_copier->copier_config); -err: - kfree(available_fmt->dma_buffer_size); free_available_fmt: sof_ipc4_free_audio_fmt(available_fmt); free_copier: @@ -633,7 +585,6 @@ static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget) ipc4_copier = dai->private; available_fmt = &ipc4_copier->available_fmt; - kfree(available_fmt->dma_buffer_size); kfree(available_fmt->base_config); kfree(available_fmt->out_audio_fmt); if (ipc4_copier->dai_type != SOF_DAI_INTEL_SSP && @@ -1166,6 +1117,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, int *ipc_config_size; u32 **data; int ipc_size, ret; + u32 deep_buffer_dma_ms = 0; dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id); @@ -1177,6 +1129,16 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; + /* parse the deep buffer dma size */ + ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms, + SOF_COPIER_DEEP_BUFFER_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(u32), 1); + if (ret) { + dev_err(scomp->dev, "Failed to parse deep buffer dma size for %s\n", + swidget->widget->name); + return ret; + } + pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; ipc4_copier = (struct sof_ipc4_copier *)swidget->private; @@ -1367,8 +1329,29 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return -EINVAL; } - /* set the gateway dma_buffer_size using the matched ID returned above */ - copier_data->gtw_cfg.dma_buffer_size = available_fmt->dma_buffer_size[ret]; + /* + * Set the gateway dma_buffer_size to 2ms buffer size to meet the FW expectation. In the + * deep buffer case, set the dma_buffer_size depending on the deep_buffer_dma_ms set + * in topology. + */ + switch (swidget->id) { + case snd_soc_dapm_dai_in: + copier_data->gtw_cfg.dma_buffer_size = + SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.ibs; + break; + case snd_soc_dapm_aif_in: + copier_data->gtw_cfg.dma_buffer_size = + max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) * + copier_data->base_config.ibs; + break; + case snd_soc_dapm_dai_out: + case snd_soc_dapm_aif_out: + copier_data->gtw_cfg.dma_buffer_size = + SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.obs; + break; + default: + break; + } data = &ipc4_copier->copier_config; ipc_config_size = &ipc4_copier->ipc_config_size; @@ -2170,7 +2153,7 @@ static enum sof_tokens common_copier_token_list[] = { SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, SOF_OUT_AUDIO_FORMAT_TOKENS, - SOF_COPIER_GATEWAY_CFG_TOKENS, + SOF_COPIER_DEEP_BUFFER_TOKENS, SOF_COPIER_TOKENS, SOF_COMP_EXT_TOKENS, }; @@ -2186,7 +2169,6 @@ static enum sof_tokens dai_token_list[] = { SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, SOF_OUT_AUDIO_FORMAT_TOKENS, - SOF_COPIER_GATEWAY_CFG_TOKENS, SOF_COPIER_TOKENS, SOF_DAI_TOKENS, SOF_COMP_EXT_TOKENS, diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index addc5b55cc10..696d6c39a21b 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -55,6 +55,9 @@ #define SOF_IPC4_INVALID_NODE_ID 0xffffffff +/* FW requires minimum 2ms DMA buffer size */ +#define SOF_IPC4_MIN_DMA_BUFFER_SIZE 2 + /* * The base of multi-gateways. Multi-gateways addressing starts from * ALH_MULTI_GTW_BASE and there are ALH_MULTI_GTW_COUNT multi-sources @@ -148,7 +151,6 @@ struct ipc4_pipeline_set_state_data { * @out_audio_fmt: Available output audio format * @input_audio_fmts: Available input audio formats * @ref_audio_fmt: Reference audio format to match runtime audio format - * @dma_buffer_size: Available Gateway DMA buffer size (in bytes) * @audio_fmt_num: Number of available audio formats */ struct sof_ipc4_available_audio_format { @@ -156,7 +158,6 @@ struct sof_ipc4_available_audio_format { struct sof_ipc4_audio_format *out_audio_fmt; struct sof_ipc4_audio_format *input_audio_fmts; struct sof_ipc4_audio_format *ref_audio_fmt; - u32 *dma_buffer_size; int audio_fmt_num; }; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index a1c4d3f34153..4504f9efdc50 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -257,7 +257,7 @@ enum sof_tokens { SOF_IN_AUDIO_FORMAT_TOKENS, SOF_OUT_AUDIO_FORMAT_TOKENS, SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, - SOF_COPIER_GATEWAY_CFG_TOKENS, + SOF_COPIER_DEEP_BUFFER_TOKENS, SOF_COPIER_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, SOF_COPIER_FORMAT_TOKENS, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 9f84b4c362c3..2d76ab13b3d1 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1232,7 +1232,6 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s continue; case SOF_IN_AUDIO_FORMAT_TOKENS: case SOF_OUT_AUDIO_FORMAT_TOKENS: - case SOF_COPIER_GATEWAY_CFG_TOKENS: case SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS: num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_AUDIO_FORMATS, swidget->tuples, swidget->num_tuples); -- cgit v1.2.3 From 7ab6b1e8302cf7a9bc8808c43b3e751e4148a351 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 13 Mar 2023 14:48:52 +0200 Subject: ASoC: SOF: ipc4-topology: Modify the type of available input/output formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new struct sof_ipc4_pin_format which contains the pin index and the buffer size. Replace the type of available input/output audio formats in struct sof_ipc4_available_audio_format with this new struct type and rename them to input_pin_fmts and output_pin_fmts. Also, add a new token, SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX that will be used to parse the pin index for the audio format from topology. Currently we only set the audio format for Pin 0 in topology, so the default value will be 0 for all audio formats. Finally, parse the pin_index and the input/output buffer sizes along with audio formats into the pin_format arrays in struct sof_ipc4_available_audio_format. This makes the base_config array in struct sof_ipc4_available_audio_format redundant. So remove it. This change will allow the addition of audio formats for the non-zero pins in topology transparent to the topology parser in the kernel. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20230313124856.8140-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 1 + sound/soc/sof/ipc4-pcm.c | 2 +- sound/soc/sof/ipc4-topology.c | 181 ++++++++++++++++------------------------ sound/soc/sof/ipc4-topology.h | 27 ++++-- sound/soc/sof/sof-audio.h | 1 - sound/soc/sof/topology.c | 1 - 6 files changed, 96 insertions(+), 117 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 9e91e2640dd4..92360601b49c 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -180,6 +180,7 @@ #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_INTERLEAVING_STYLE 1906 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_FMT_CFG 1907 #define SOF_TKN_CAVS_AUDIO_FORMAT_IN_SAMPLE_TYPE 1908 +#define SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX 1909 /* intentional token numbering discontinuity, reserved for future use */ #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE 1930 #define SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH 1931 diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index a679c08d9bb1..701da5ee4e4e 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -389,7 +389,7 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, snd_mask_none(fmt); snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); - rate->min = ipc4_copier->available_fmt.input_audio_fmts->sampling_frequency; + rate->min = ipc4_copier->available_fmt.input_pin_fmts->audio_fmt.sampling_frequency; rate->max = rate->min; switch (ipc4_copier->dai_type) { diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 0e1e4fc9224c..c462db95f3b2 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -41,41 +41,44 @@ static const struct sof_topology_token ipc4_comp_tokens[] = { offsetof(struct sof_ipc4_base_module_cfg, is_pages)}, }; -static const struct sof_topology_token ipc4_audio_format_buffer_size_tokens[] = { - {SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_base_module_cfg, ibs)}, - {SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_base_module_cfg, obs)}, -}; - static const struct sof_topology_token ipc4_in_audio_format_tokens[] = { {SOF_TKN_CAVS_AUDIO_FORMAT_IN_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, sampling_frequency)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)}, {SOF_TKN_CAVS_AUDIO_FORMAT_IN_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, bit_depth)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)}, {SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, ch_map)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)}, {SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, ch_cfg)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)}, {SOF_TKN_CAVS_AUDIO_FORMAT_IN_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, offsetof(struct sof_ipc4_audio_format, interleaving_style)}, + get_token_u32, offsetof(struct sof_ipc4_pin_format, + audio_fmt.interleaving_style)}, {SOF_TKN_CAVS_AUDIO_FORMAT_IN_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, fmt_cfg)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)}, + {SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_pin_format, pin_index)}, + {SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_pin_format, buffer_size)}, }; static const struct sof_topology_token ipc4_out_audio_format_tokens[] = { {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, sampling_frequency)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)}, {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, bit_depth)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)}, {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, ch_map)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)}, {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, ch_cfg)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)}, {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, offsetof(struct sof_ipc4_audio_format, interleaving_style)}, + get_token_u32, offsetof(struct sof_ipc4_pin_format, + audio_fmt.interleaving_style)}, {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_audio_format, fmt_cfg)}, + offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)}, + {SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_pin_format, pin_index)}, + {SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_pin_format, buffer_size)}, }; static const struct sof_topology_token ipc4_copier_deep_buffer_tokens[] = { @@ -135,9 +138,6 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { ipc4_in_audio_format_tokens, ARRAY_SIZE(ipc4_in_audio_format_tokens)}, [SOF_OUT_AUDIO_FORMAT_TOKENS] = {"IPC4 Output Audio format tokens", ipc4_out_audio_format_tokens, ARRAY_SIZE(ipc4_out_audio_format_tokens)}, - [SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS] = {"IPC4 Audio format buffer size tokens", - ipc4_audio_format_buffer_size_tokens, - ARRAY_SIZE(ipc4_audio_format_buffer_size_tokens)}, [SOF_COPIER_DEEP_BUFFER_TOKENS] = {"IPC4 Copier deep buffer tokens", ipc4_copier_deep_buffer_tokens, ARRAY_SIZE(ipc4_copier_deep_buffer_tokens)}, [SOF_COPIER_TOKENS] = {"IPC4 Copier tokens", ipc4_copier_tokens, @@ -148,20 +148,18 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, }; -static void sof_ipc4_dbg_audio_format(struct device *dev, - struct sof_ipc4_audio_format *format, - size_t object_size, int num_format) +static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_format *pin_fmt, + int num_formats) { - struct sof_ipc4_audio_format *fmt; - void *ptr = format; int i; - for (i = 0; i < num_format; i++, ptr = (u8 *)ptr + object_size) { - fmt = ptr; + for (i = 0; i < num_formats; i++) { + struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt; dev_dbg(dev, - " #%d: %uHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x)\n", - i, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map, - fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg); + "Pin index #%d: %uHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x) buffer size %d\n", + pin_fmt[i].pin_index, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map, + fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg, + pin_fmt[i].buffer_size); } } @@ -179,10 +177,9 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, struct sof_ipc4_available_audio_format *available_fmt, struct sof_ipc4_base_module_cfg *module_base_cfg) { - struct sof_ipc4_base_module_cfg *base_config; - struct sof_ipc4_audio_format *out_format, *in_format; + struct sof_ipc4_pin_format *out_format, *in_format; int audio_fmt_num = 0; - int ret, i; + int ret; ret = sof_update_ipc_object(scomp, &audio_fmt_num, SOF_AUDIO_FMT_NUM_TOKENS, swidget->tuples, @@ -195,47 +192,25 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, dev_dbg(scomp->dev, "Number of audio formats: %d\n", available_fmt->audio_fmt_num); - base_config = kcalloc(available_fmt->audio_fmt_num, sizeof(*base_config), GFP_KERNEL); - if (!base_config) - return -ENOMEM; - /* set cpc and is_pages in the module's base_config */ ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(*module_base_cfg), 1); if (ret) { dev_err(scomp->dev, "parse comp tokens for %s failed, error: %d\n", swidget->widget->name, ret); - goto err; + return ret; } dev_dbg(scomp->dev, "widget %s cpc: %d is_pages: %d\n", swidget->widget->name, module_base_cfg->cpc, module_base_cfg->is_pages); - /* copy the ibs/obs for each base_cfg */ - ret = sof_update_ipc_object(scomp, base_config, - SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(*base_config), - available_fmt->audio_fmt_num); - if (ret) { - dev_err(scomp->dev, "parse buffer size tokens failed %d\n", ret); - goto err; - } - - for (i = 0; i < available_fmt->audio_fmt_num; i++) - dev_dbg(scomp->dev, "%d: ibs: %d obs: %d\n", i, - base_config[i].ibs, base_config[i].obs); - - in_format = kcalloc(available_fmt->audio_fmt_num, - sizeof(struct sof_ipc4_audio_format), GFP_KERNEL); - if (!in_format) { - kfree(base_config); - ret = -ENOMEM; - goto err; - } + in_format = kcalloc(available_fmt->audio_fmt_num, sizeof(*in_format), GFP_KERNEL); + if (!in_format) + return -ENOMEM; - ret = sof_update_ipc_object(scomp, available_fmt->input_audio_fmts, + ret = sof_update_ipc_object(scomp, in_format, SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(struct sof_ipc4_audio_format), + swidget->num_tuples, sizeof(*in_format), available_fmt->audio_fmt_num); if (ret) { dev_err(scomp->dev, "parse base_config audio_fmt tokens failed %d\n", ret); @@ -243,8 +218,7 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, } dev_dbg(scomp->dev, "Get input audio formats for %s\n", swidget->widget->name); - sof_ipc4_dbg_audio_format(scomp->dev, available_fmt->input_audio_fmts, - sizeof(struct sof_ipc4_audio_format), + sof_ipc4_dbg_audio_format(scomp->dev, available_fmt->input_pin_fmts, available_fmt->audio_fmt_num); out_format = kcalloc(available_fmt->audio_fmt_num, sizeof(*out_format), GFP_KERNEL); @@ -264,12 +238,10 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, } dev_dbg(scomp->dev, "Get output audio formats for %s\n", swidget->widget->name); - sof_ipc4_dbg_audio_format(scomp->dev, out_format, sizeof(*out_format), - available_fmt->audio_fmt_num); + sof_ipc4_dbg_audio_format(scomp->dev, out_format, available_fmt->audio_fmt_num); - available_fmt->base_config = base_config; - available_fmt->out_audio_fmt = out_format; - available_fmt->input_audio_fmts = in_format; + available_fmt->output_pin_fmts = out_format; + available_fmt->input_pin_fmts = in_format; return 0; @@ -277,8 +249,6 @@ err_out: kfree(out_format); err_in: kfree(in_format); -err: - kfree(base_config); return ret; } @@ -286,12 +256,10 @@ err: static void sof_ipc4_free_audio_fmt(struct sof_ipc4_available_audio_format *available_fmt) { - kfree(available_fmt->base_config); - available_fmt->base_config = NULL; - kfree(available_fmt->out_audio_fmt); - available_fmt->out_audio_fmt = NULL; - kfree(available_fmt->input_audio_fmts); - available_fmt->input_audio_fmts = NULL; + kfree(available_fmt->output_pin_fmts); + available_fmt->output_pin_fmts = NULL; + kfree(available_fmt->input_pin_fmts); + available_fmt->input_pin_fmts = NULL; } static void sof_ipc4_widget_free_comp_pipeline(struct snd_sof_widget *swidget) @@ -431,8 +399,7 @@ static void sof_ipc4_widget_free_comp_pcm(struct snd_sof_widget *swidget) return; available_fmt = &ipc4_copier->available_fmt; - kfree(available_fmt->base_config); - kfree(available_fmt->out_audio_fmt); + kfree(available_fmt->output_pin_fmts); kfree(ipc4_copier->gtw_attr); kfree(ipc4_copier); swidget->private = NULL; @@ -584,8 +551,7 @@ static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget) ipc4_copier = dai->private; available_fmt = &ipc4_copier->available_fmt; - kfree(available_fmt->base_config); - kfree(available_fmt->out_audio_fmt); + kfree(available_fmt->output_pin_fmts); if (ipc4_copier->dai_type != SOF_DAI_INTEL_SSP && ipc4_copier->dai_type != SOF_DAI_INTEL_DMIC) kfree(ipc4_copier->copier_config); @@ -866,14 +832,14 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params, struct sof_ipc4_available_audio_format *available_fmt) { - struct sof_ipc4_audio_format *fmt = available_fmt->ref_audio_fmt; + struct sof_ipc4_pin_format *pin_fmt = available_fmt->ref_audio_fmt; u32 valid_bits; u32 channels; u32 rate; int sample_valid_bits; int i; - if (!fmt) { + if (!pin_fmt) { dev_err(sdev->dev, "no reference formats for %s\n", swidget->widget->name); return -EINVAL; } @@ -902,7 +868,9 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, * Search supported audio formats to match rate, channels ,and * sample_valid_bytes from runtime params */ - for (i = 0; i < available_fmt->audio_fmt_num; i++, fmt++) { + for (i = 0; i < available_fmt->audio_fmt_num; i++) { + struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt; + rate = fmt->sampling_frequency; channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); @@ -921,15 +889,16 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, } /* copy input format */ - memcpy(&base_config->audio_fmt, &available_fmt->input_audio_fmts[i], sizeof(*fmt)); + memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt, + sizeof(*out_format)); /* set base_cfg ibs/obs */ - base_config->ibs = available_fmt->base_config[i].ibs; - base_config->obs = available_fmt->base_config[i].obs; + base_config->ibs = available_fmt->input_pin_fmts[i].buffer_size; + base_config->obs = available_fmt->output_pin_fmts[i].buffer_size; dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name); - sof_ipc4_dbg_audio_format(sdev->dev, &base_config->audio_fmt, - sizeof(*base_config), 1); + sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1); + if (out_format) { /* * Current topology defines pin 0 input and output formats only in pairs. @@ -938,10 +907,10 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, * input format. This logic will need to be updated when the format definitions * in topology change. */ - memcpy(out_format, &available_fmt->out_audio_fmt[i], sizeof(*fmt)); + memcpy(out_format, &available_fmt->output_pin_fmts[i].audio_fmt, + sizeof(*out_format)); dev_dbg(sdev->dev, "Init output audio formats for %s\n", swidget->widget->name); - sof_ipc4_dbg_audio_format(sdev->dev, out_format, - sizeof(*out_format), 1); + sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[i], 1); } /* Return the index of the matched format */ @@ -1144,13 +1113,13 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, available_fmt = &ipc4_copier->available_fmt; /* - * Use the input_audio_fmts to match pcm params for playback and the out_audio_fmt + * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts * for capture. */ if (dir == SNDRV_PCM_STREAM_PLAYBACK) - available_fmt->ref_audio_fmt = available_fmt->input_audio_fmts; + available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; else - available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt; + available_fmt->ref_audio_fmt = available_fmt->output_pin_fmts; copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; copier_data->gtw_cfg.node_id |= @@ -1170,7 +1139,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; if (dir == SNDRV_PCM_STREAM_CAPTURE) { - available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt; + available_fmt->ref_audio_fmt = available_fmt->output_pin_fmts; /* * modify the input params for the dai copier as it only supports @@ -1180,7 +1149,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, snd_mask_none(fmt); snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); } else { - available_fmt->ref_audio_fmt = available_fmt->input_audio_fmts; + available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; } ref_params = pipeline_params; @@ -1201,7 +1170,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, available_fmt = &ipc4_copier->available_fmt; /* Use the input formats as the reference to match pcm params */ - available_fmt->ref_audio_fmt = available_fmt->input_audio_fmts; + available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; ref_params = pipeline_params; break; @@ -1388,7 +1357,7 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt; int ret; - available_fmt->ref_audio_fmt = available_fmt->input_audio_fmts; + available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; /* output format is not required to be sent to the FW for gain */ ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config, @@ -1414,7 +1383,7 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, int ret; /* only 32bit is supported by mixer */ - available_fmt->ref_audio_fmt = available_fmt->input_audio_fmts; + available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; /* output format is not required to be sent to the FW for mixer */ ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config, @@ -1440,7 +1409,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, struct snd_interval *rate; int ret; - available_fmt->ref_audio_fmt = available_fmt->input_audio_fmts; + available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; /* output format is not required to be sent to the FW for SRC */ ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config, @@ -2147,7 +2116,6 @@ static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link static enum sof_tokens common_copier_token_list[] = { SOF_COMP_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, - SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, SOF_OUT_AUDIO_FORMAT_TOKENS, SOF_COPIER_DEEP_BUFFER_TOKENS, @@ -2163,7 +2131,6 @@ static enum sof_tokens pipeline_token_list[] = { static enum sof_tokens dai_token_list[] = { SOF_COMP_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, - SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, SOF_OUT_AUDIO_FORMAT_TOKENS, SOF_COPIER_TOKENS, @@ -2175,8 +2142,8 @@ static enum sof_tokens pga_token_list[] = { SOF_COMP_TOKENS, SOF_GAIN_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, - SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, + SOF_OUT_AUDIO_FORMAT_TOKENS, SOF_COMP_EXT_TOKENS, }; @@ -2184,7 +2151,7 @@ static enum sof_tokens mixer_token_list[] = { SOF_COMP_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, - SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, + SOF_OUT_AUDIO_FORMAT_TOKENS, SOF_COMP_EXT_TOKENS, }; @@ -2193,7 +2160,7 @@ static enum sof_tokens src_token_list[] = { SOF_SRC_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, - SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, + SOF_OUT_AUDIO_FORMAT_TOKENS, SOF_COMP_EXT_TOKENS, }; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 696d6c39a21b..2d9b1ba549f7 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -145,19 +145,32 @@ struct ipc4_pipeline_set_state_data { DECLARE_FLEX_ARRAY(u32, pipeline_ids); } __packed; +/** + * struct sof_ipc4_pin_format - Module pin format + * @pin_index: pin index + * @buffer_size: buffer size in bytes + * @audio_fmt: audio format for the pin + * + * This structure can be used for both output or input pins and the pin_index is relative to the + * pin type i.e output/input pin + */ +struct sof_ipc4_pin_format { + u32 pin_index; + u32 buffer_size; + struct sof_ipc4_audio_format audio_fmt; +}; + /** * struct sof_ipc4_available_audio_format - Available audio formats - * @base_config: Available base config - * @out_audio_fmt: Available output audio format - * @input_audio_fmts: Available input audio formats + * @output_pin_fmts: Available output pin formats + * @input_pin_fmts: Available input pin formats * @ref_audio_fmt: Reference audio format to match runtime audio format * @audio_fmt_num: Number of available audio formats */ struct sof_ipc4_available_audio_format { - struct sof_ipc4_base_module_cfg *base_config; - struct sof_ipc4_audio_format *out_audio_fmt; - struct sof_ipc4_audio_format *input_audio_fmts; - struct sof_ipc4_audio_format *ref_audio_fmt; + struct sof_ipc4_pin_format *output_pin_fmts; + struct sof_ipc4_pin_format *input_pin_fmts; + struct sof_ipc4_pin_format *ref_audio_fmt; int audio_fmt_num; }; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 4504f9efdc50..d220af5f08fb 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -256,7 +256,6 @@ enum sof_tokens { SOF_COMP_EXT_TOKENS, SOF_IN_AUDIO_FORMAT_TOKENS, SOF_OUT_AUDIO_FORMAT_TOKENS, - SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, SOF_COPIER_DEEP_BUFFER_TOKENS, SOF_COPIER_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 2d76ab13b3d1..3a091f18731f 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1232,7 +1232,6 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s continue; case SOF_IN_AUDIO_FORMAT_TOKENS: case SOF_OUT_AUDIO_FORMAT_TOKENS: - case SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS: num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_AUDIO_FORMATS, swidget->tuples, swidget->num_tuples); -- cgit v1.2.3 From 4fdef47a44d6ff735902dfe740918f23932225ca Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 13 Mar 2023 14:48:55 +0200 Subject: ASoC: SOF: ipc4-topology: Add new tokens for input/output pin format count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for handling processing modules with different input/output pin counts, introduce two new tokens for input/output audio format counts. Use these token values to parse all the available audio formats from topology. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20230313124856.8140-11-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 2 + sound/soc/sof/ipc4-topology.c | 143 ++++++++++++++++++++++++---------------- sound/soc/sof/ipc4-topology.h | 6 +- sound/soc/sof/topology.c | 42 +++++++----- 4 files changed, 116 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 92360601b49c..bd02842124f9 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -96,6 +96,8 @@ */ #define SOF_TKN_COMP_INPUT_PIN_BINDING_WNAME 413 #define SOF_TKN_COMP_OUTPUT_PIN_BINDING_WNAME 414 +#define SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS 415 +#define SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS 416 /* SSP */ diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 3aacc440a5e4..c91a90dd8a6e 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -90,8 +90,10 @@ static const struct sof_topology_token ipc4_copier_tokens[] = { }; static const struct sof_topology_token ipc4_audio_fmt_num_tokens[] = { - {SOF_TKN_COMP_NUM_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - 0}, + {SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_available_audio_format, num_input_formats)}, + {SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_available_audio_format, num_output_formats)}, }; static const struct sof_topology_token dai_tokens[] = { @@ -178,19 +180,24 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, struct sof_ipc4_base_module_cfg *module_base_cfg) { struct sof_ipc4_pin_format *out_format, *in_format; - int audio_fmt_num = 0; int ret; - ret = sof_update_ipc_object(scomp, &audio_fmt_num, + ret = sof_update_ipc_object(scomp, available_fmt, SOF_AUDIO_FMT_NUM_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(audio_fmt_num), 1); - if (ret || audio_fmt_num <= 0) { - dev_err(scomp->dev, "Invalid number of audio formats: %d\n", audio_fmt_num); + swidget->num_tuples, sizeof(available_fmt), 1); + if (ret) { + dev_err(scomp->dev, "Failed to parse audio format token count\n"); + return ret; + } + + if (!available_fmt->num_input_formats && !available_fmt->num_output_formats) { + dev_err(scomp->dev, "No input/output pin formats set in topology\n"); return -EINVAL; } - available_fmt->audio_fmt_num = audio_fmt_num; - dev_dbg(scomp->dev, "Number of audio formats: %d\n", available_fmt->audio_fmt_num); + dev_dbg(scomp->dev, + "Number of input audio formats: %d. Number of output audio formats: %d\n", + available_fmt->num_input_formats, available_fmt->num_output_formats); /* set cpc and is_pages in the module's base_config */ ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples, @@ -204,51 +211,57 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, dev_dbg(scomp->dev, "widget %s cpc: %d is_pages: %d\n", swidget->widget->name, module_base_cfg->cpc, module_base_cfg->is_pages); - in_format = kcalloc(available_fmt->audio_fmt_num, sizeof(*in_format), GFP_KERNEL); - if (!in_format) - return -ENOMEM; + if (available_fmt->num_input_formats) { + in_format = kcalloc(available_fmt->num_input_formats, + sizeof(*in_format), GFP_KERNEL); + if (!in_format) + return -ENOMEM; + available_fmt->input_pin_fmts = in_format; + + ret = sof_update_ipc_object(scomp, in_format, + SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*in_format), + available_fmt->num_input_formats); + if (ret) { + dev_err(scomp->dev, "parse input audio fmt tokens failed %d\n", ret); + goto err_in; + } - ret = sof_update_ipc_object(scomp, in_format, - SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(*in_format), - available_fmt->audio_fmt_num); - if (ret) { - dev_err(scomp->dev, "parse base_config audio_fmt tokens failed %d\n", ret); - goto err_in; + dev_dbg(scomp->dev, "Input audio formats for %s\n", swidget->widget->name); + sof_ipc4_dbg_audio_format(scomp->dev, in_format, + available_fmt->num_input_formats); } - dev_dbg(scomp->dev, "Get input audio formats for %s\n", swidget->widget->name); - sof_ipc4_dbg_audio_format(scomp->dev, available_fmt->input_pin_fmts, - available_fmt->audio_fmt_num); - - out_format = kcalloc(available_fmt->audio_fmt_num, sizeof(*out_format), GFP_KERNEL); - if (!out_format) { - ret = -ENOMEM; - goto err_in; - } + if (available_fmt->num_output_formats) { + out_format = kcalloc(available_fmt->num_output_formats, sizeof(*out_format), + GFP_KERNEL); + if (!out_format) { + ret = -ENOMEM; + goto err_in; + } - ret = sof_update_ipc_object(scomp, out_format, - SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(*out_format), - available_fmt->audio_fmt_num); + ret = sof_update_ipc_object(scomp, out_format, + SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*out_format), + available_fmt->num_output_formats); + if (ret) { + dev_err(scomp->dev, "parse output audio fmt tokens failed\n"); + goto err_out; + } - if (ret) { - dev_err(scomp->dev, "parse output audio_fmt tokens failed\n"); - goto err_out; + available_fmt->output_pin_fmts = out_format; + dev_dbg(scomp->dev, "Output audio formats for %s\n", swidget->widget->name); + sof_ipc4_dbg_audio_format(scomp->dev, out_format, + available_fmt->num_output_formats); } - dev_dbg(scomp->dev, "Get output audio formats for %s\n", swidget->widget->name); - sof_ipc4_dbg_audio_format(scomp->dev, out_format, available_fmt->audio_fmt_num); - - available_fmt->output_pin_fmts = out_format; - available_fmt->input_pin_fmts = in_format; - return 0; err_out: kfree(out_format); err_in: kfree(in_format); + available_fmt->input_pin_fmts = NULL; return ret; } @@ -830,7 +843,7 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, struct sof_ipc4_base_module_cfg *base_config, struct snd_pcm_hw_params *params, struct sof_ipc4_available_audio_format *available_fmt, - struct sof_ipc4_pin_format *pin_fmts) + struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size) { u32 valid_bits; u32 channels; @@ -858,7 +871,7 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, return -EINVAL; } - if (!available_fmt->audio_fmt_num) { + if (!pin_fmts_size) { dev_err(sdev->dev, "no formats available for %s\n", swidget->widget->name); return -EINVAL; } @@ -867,7 +880,7 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, * Search supported audio formats to match rate, channels ,and * sample_valid_bytes from runtime params */ - for (i = 0; i < available_fmt->audio_fmt_num; i++) { + for (i = 0; i < pin_fmts_size; i++) { struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt; rate = fmt->sampling_frequency; @@ -881,22 +894,26 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, } } - if (i == available_fmt->audio_fmt_num) { + if (i == pin_fmts_size) { dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n", __func__, params_rate(params), sample_valid_bits, params_channels(params)); return -EINVAL; } /* copy input format */ - memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt, - sizeof(struct sof_ipc4_audio_format)); + if (available_fmt->num_input_formats && i < available_fmt->num_input_formats) { + memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt, + sizeof(struct sof_ipc4_audio_format)); + + /* set base_cfg ibs/obs */ + base_config->ibs = available_fmt->input_pin_fmts[i].buffer_size; - /* set base_cfg ibs/obs */ - base_config->ibs = available_fmt->input_pin_fmts[i].buffer_size; - base_config->obs = available_fmt->output_pin_fmts[i].buffer_size; + dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name); + sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1); + } - dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name); - sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1); + if (available_fmt->num_output_formats && i < available_fmt->num_output_formats) + base_config->obs = available_fmt->output_pin_fmts[i].buffer_size; /* Return the index of the matched format */ return i; @@ -1070,6 +1087,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, u32 **data; int ipc_size, ret; u32 deep_buffer_dma_ms = 0; + u32 format_list_count; dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id); @@ -1102,10 +1120,13 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts * for capture. */ - if (dir == SNDRV_PCM_STREAM_PLAYBACK) + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { format_list_to_search = available_fmt->input_pin_fmts; - else + format_list_count = available_fmt->num_input_formats; + } else { format_list_to_search = available_fmt->output_pin_fmts; + format_list_count = available_fmt->num_output_formats; + } copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; copier_data->gtw_cfg.node_id |= @@ -1126,6 +1147,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, available_fmt = &ipc4_copier->available_fmt; if (dir == SNDRV_PCM_STREAM_CAPTURE) { format_list_to_search = available_fmt->output_pin_fmts; + format_list_count = available_fmt->num_output_formats; /* * modify the input params for the dai copier as it only supports @@ -1136,6 +1158,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); } else { format_list_to_search = available_fmt->input_pin_fmts; + format_list_count = available_fmt->num_input_formats; } ref_params = pipeline_params; @@ -1157,6 +1180,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, /* Use the input formats to match pcm params */ format_list_to_search = available_fmt->input_pin_fmts; + format_list_count = available_fmt->num_input_formats; ref_params = pipeline_params; break; @@ -1169,7 +1193,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, /* set input and output audio formats */ ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params, - available_fmt, format_list_to_search); + available_fmt, format_list_to_search, format_list_count); if (ret < 0) return ret; @@ -1356,7 +1380,8 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config, pipeline_params, available_fmt, - available_fmt->input_pin_fmts); + available_fmt->input_pin_fmts, + available_fmt->num_input_formats); if (ret < 0) return ret; @@ -1379,7 +1404,8 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config, pipeline_params, available_fmt, - available_fmt->input_pin_fmts); + available_fmt->input_pin_fmts, + available_fmt->num_input_formats); if (ret < 0) return ret; @@ -1403,7 +1429,8 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config, pipeline_params, available_fmt, - available_fmt->input_pin_fmts); + available_fmt->input_pin_fmts, + available_fmt->num_input_formats); if (ret < 0) return ret; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 6359ef8736ae..fad7a628f782 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -164,12 +164,14 @@ struct sof_ipc4_pin_format { * struct sof_ipc4_available_audio_format - Available audio formats * @output_pin_fmts: Available output pin formats * @input_pin_fmts: Available input pin formats - * @audio_fmt_num: Number of available audio formats + * @num_input_formats: Number of input pin formats + * @num_output_formats: Number of output pin formats */ struct sof_ipc4_available_audio_format { struct sof_ipc4_pin_format *output_pin_fmts; struct sof_ipc4_pin_format *input_pin_fmts; - int audio_fmt_num; + u32 num_input_formats; + u32 num_output_formats; }; /** diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 3a091f18731f..b642835e14df 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1231,35 +1231,43 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s continue; case SOF_IN_AUDIO_FORMAT_TOKENS: - case SOF_OUT_AUDIO_FORMAT_TOKENS: - num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_AUDIO_FORMATS, + num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS, swidget->tuples, swidget->num_tuples); - if (num_sets < 0) { - dev_err(sdev->dev, "Invalid audio format count for %s\n", + dev_err(sdev->dev, "Invalid input audio format count for %s\n", swidget->widget->name); ret = num_sets; goto err; } - - if (num_sets > 1) { - struct snd_sof_tuple *new_tuples; - - num_tuples += token_list[object_token_list[i]].count * num_sets; - new_tuples = krealloc(swidget->tuples, - sizeof(*new_tuples) * num_tuples, GFP_KERNEL); - if (!new_tuples) { - ret = -ENOMEM; - goto err; - } - - swidget->tuples = new_tuples; + break; + case SOF_OUT_AUDIO_FORMAT_TOKENS: + num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS, + swidget->tuples, swidget->num_tuples); + if (num_sets < 0) { + dev_err(sdev->dev, "Invalid output audio format count for %s\n", + swidget->widget->name); + ret = num_sets; + goto err; } break; default: break; } + if (num_sets > 1) { + struct snd_sof_tuple *new_tuples; + + num_tuples += token_list[object_token_list[i]].count * num_sets; + new_tuples = krealloc(swidget->tuples, + sizeof(*new_tuples) * num_tuples, GFP_KERNEL); + if (!new_tuples) { + ret = -ENOMEM; + goto err; + } + + swidget->tuples = new_tuples; + } + /* copy one set of tuples per token ID into swidget->tuples */ ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), object_token_list[i], num_sets, swidget->tuples, -- cgit v1.2.3 From 0d3a5178c2994eaf91ad135816a79138055b394a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Mar 2023 01:43:54 +0000 Subject: ASoC: soc-pcm.c: remove indirect runtime copy substream->runtime will be attached when substream was opened at snd_pcm_attach_substream(). When it uses DPCM, FE substream->runtime is attached, but BE substream->runtime is not. Thus, we are copying FE substream->runtime to BE. But, we are copyig FE substream->runtime to FE dpcm->runtime first (A), and copy it to BE dpcm->runtime (B), and copy it to BE substream->runtime (C). static int dpcm_fe_dai_open(...) { ... (A) fe->dpcm[stream].runtime = fe_substream->runtime; ... } static int dpcm_be_connect(...) { ... (B) be->dpcm[stream].runtime = fe->dpcm[stream].runtime; ... } int dpcm_be_dai_startup(...) { ... (C) be_substream->runtime = be->dpcm[stream].runtime; ... } It is too roundabout and troublesome. OTOH, it is directly copying fe_substream->runtime at dpcm_be_reparent() without using be->dpcm[stream].runtime. static void dpcm_be_reparent(...) { ... for_each_dpcm_fe(be, stream, dpcm) { ... => be_substream->runtime = fe_substream->runtime; break; } } This patch removes indirect copying. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87v8je64dh.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 1 - sound/soc/soc-compress.c | 7 ------- sound/soc/soc-pcm.c | 10 ++++------ 3 files changed, 4 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 1e7d09556fe3..4d6ac7699833 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -91,7 +91,6 @@ struct snd_soc_dpcm_runtime { struct list_head fe_clients; int users; - struct snd_pcm_runtime *runtime; struct snd_pcm_hw_params hw_params; /* state and update */ diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index e7aa6f360cab..554c329ec87f 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -134,8 +134,6 @@ err_no_lock: static int soc_compr_open_fe(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *fe = cstream->private_data; - struct snd_pcm_substream *fe_substream = - fe->pcm->streams[cstream->direction].substream; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(fe, 0); struct snd_soc_dpcm *dpcm; struct snd_soc_dapm_widget_list *list; @@ -143,7 +141,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) int ret; mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); - fe->dpcm[stream].runtime = fe_substream->runtime; ret = dpcm_path_get(fe, stream, &list); if (ret < 0) @@ -153,7 +150,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) /* calculate valid and active FE <-> BE dpcms */ dpcm_process_paths(fe, stream, &list, 1); - fe->dpcm[stream].runtime = fe_substream->runtime; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; @@ -164,7 +160,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; dpcm_be_disconnect(fe, stream); - fe->dpcm[stream].runtime = NULL; goto out; } @@ -236,8 +231,6 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) mutex_unlock(&fe->card->pcm_mutex); - fe->dpcm[stream].runtime = NULL; - snd_soc_link_compr_shutdown(cstream, 0); snd_soc_compr_components_free(cstream, 0); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 5eb056b942ce..b7f8a5bcfbc6 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1230,7 +1230,6 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, dpcm->be = be; dpcm->fe = fe; - be->dpcm[stream].runtime = fe->dpcm[stream].runtime; dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; snd_soc_dpcm_stream_lock_irq(fe, stream); list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); @@ -1465,10 +1464,11 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list *list = *list_; struct snd_soc_pcm_runtime *be; struct snd_soc_dapm_widget *widget; + struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream); int i, new = 0, err; /* don't connect if FE is not running */ - if (!fe->dpcm[stream].runtime && !fe->fe_compr) + if (!fe_substream->runtime && !fe->fe_compr) return new; /* Create any new FE <--> BE connections */ @@ -1590,6 +1590,7 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) { + struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream); struct snd_soc_pcm_runtime *be; struct snd_soc_dpcm *dpcm; int err, count = 0; @@ -1629,7 +1630,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: open %s BE %s\n", stream ? "capture" : "playback", be->dai_link->name); - be_substream->runtime = be->dpcm[stream].runtime; + be_substream->runtime = fe_substream->runtime; err = __soc_pcm_open(be, be_substream); if (err < 0) { be->dpcm[stream].users--; @@ -2693,8 +2694,6 @@ static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; dpcm_be_disconnect(fe, stream); - - fe->dpcm[stream].runtime = NULL; } static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) @@ -2719,7 +2718,6 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) int stream = fe_substream->stream; snd_soc_dpcm_mutex_lock(fe); - fe->dpcm[stream].runtime = fe_substream->runtime; ret = dpcm_path_get(fe, stream, &list); if (ret < 0) -- cgit v1.2.3 From 7062e1c727ec99a9c5b40586964304d60a43f240 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Mon, 20 Mar 2023 11:22:38 +0000 Subject: firmware: cs_dsp: Introduce no_core_startstop for self-booting DSPs There are devices containing Halo Core DSPs that self-boot, cs_dsp is used to manage the running firmware but the host does not have direct control over starting and stopping the DSP and so cs_dsp should consider the DSP to be always running. Signed-off-by: Simon Trimmer Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20230320112245.115720-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 14 +++++++++++++- include/linux/firmware/cirrus/cs_dsp.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index f558b390fbfe..97b73a254380 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -301,6 +301,7 @@ struct cs_dsp_ops { static const struct cs_dsp_ops cs_dsp_adsp1_ops; static const struct cs_dsp_ops cs_dsp_adsp2_ops[]; static const struct cs_dsp_ops cs_dsp_halo_ops; +static const struct cs_dsp_ops cs_dsp_halo_ao_ops; struct cs_dsp_buf { struct list_head list; @@ -2821,7 +2822,10 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_adsp2_init, FW_CS_DSP); */ int cs_dsp_halo_init(struct cs_dsp *dsp) { - dsp->ops = &cs_dsp_halo_ops; + if (dsp->no_core_startstop) + dsp->ops = &cs_dsp_halo_ao_ops; + else + dsp->ops = &cs_dsp_halo_ops; return cs_dsp_common_init(dsp); } @@ -3187,6 +3191,14 @@ static const struct cs_dsp_ops cs_dsp_halo_ops = { .stop_core = cs_dsp_halo_stop_core, }; +static const struct cs_dsp_ops cs_dsp_halo_ao_ops = { + .parse_sizes = cs_dsp_adsp2_parse_sizes, + .validate_version = cs_dsp_halo_validate_version, + .setup_algs = cs_dsp_halo_setup_algs, + .region_to_reg = cs_dsp_halo_region_to_reg, + .show_fw_status = cs_dsp_halo_show_fw_status, +}; + /** * cs_dsp_chunk_write() - Format data to a DSP memory chunk * @ch: Pointer to the chunk structure diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index cad828e21c72..29cd11d5a3cf 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -156,6 +156,7 @@ struct cs_dsp { unsigned int sysclk_reg; unsigned int sysclk_mask; unsigned int sysclk_shift; + bool no_core_startstop; struct list_head alg_regions; -- cgit v1.2.3 From e496112529006ce0c2cfe67d1136186e2786d2e8 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 20 Mar 2023 11:22:45 +0000 Subject: ASoC: cs35l56: Add driver for Cirrus Logic CS35L56 The CS35L56 combines a high-performance mono audio amplifier, Class-H tracking inductive boost converter, Halo Core(TM) DSP and a DC-DC boost converter supporting Class-H tracking. Supported control interfaces are I2C, SPI or SoundWire. Supported audio interfaces are I2S/TDM or SoundWire. Most chip functionality is controlled by on-board ROM firmware that is always running. The driver must apply patch/tune to the firmware before using the CS35L56. Signed-off-by: Simon Trimmer Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20230320112245.115720-9-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- MAINTAINERS | 1 + include/sound/cs35l56.h | 266 +++++++ sound/soc/codecs/Kconfig | 40 + sound/soc/codecs/Makefile | 10 + sound/soc/codecs/cs35l56-i2c.c | 83 +++ sound/soc/codecs/cs35l56-sdw.c | 528 ++++++++++++++ sound/soc/codecs/cs35l56-shared.c | 390 ++++++++++ sound/soc/codecs/cs35l56-spi.c | 81 ++ sound/soc/codecs/cs35l56.c | 1461 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l56.h | 77 ++ 10 files changed, 2937 insertions(+) create mode 100644 include/sound/cs35l56.h create mode 100644 sound/soc/codecs/cs35l56-i2c.c create mode 100644 sound/soc/codecs/cs35l56-sdw.c create mode 100644 sound/soc/codecs/cs35l56-shared.c create mode 100644 sound/soc/codecs/cs35l56-spi.c create mode 100644 sound/soc/codecs/cs35l56.c create mode 100644 sound/soc/codecs/cs35l56.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 02fe90adf0af..e52938f962b5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4905,6 +4905,7 @@ L: patches@opensource.cirrus.com S: Maintained F: Documentation/devicetree/bindings/sound/cirrus,cs* F: include/dt-bindings/sound/cs* +F: include/sound/cs* F: sound/pci/hda/cs* F: sound/pci/hda/hda_cs_dsp_ctl.* F: sound/soc/codecs/cs* diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h new file mode 100644 index 000000000000..5f8ea2dfaa21 --- /dev/null +++ b/include/sound/cs35l56.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Common definitions for Cirrus Logic CS35L56 smart amp + * + * Copyright (C) 2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef __CS35L56_H +#define __CS35L56_H + +#include +#include +#include + +#define CS35L56_DEVID 0x0000000 +#define CS35L56_REVID 0x0000004 +#define CS35L56_RELID 0x000000C +#define CS35L56_OTPID 0x0000010 +#define CS35L56_SFT_RESET 0x0000020 +#define CS35L56_GLOBAL_ENABLES 0x0002014 +#define CS35L56_BLOCK_ENABLES 0x0002018 +#define CS35L56_BLOCK_ENABLES2 0x000201C +#define CS35L56_REFCLK_INPUT 0x0002C04 +#define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C +#define CS35L56_ASP1_ENABLES1 0x0004800 +#define CS35L56_ASP1_CONTROL1 0x0004804 +#define CS35L56_ASP1_CONTROL2 0x0004808 +#define CS35L56_ASP1_CONTROL3 0x000480C +#define CS35L56_ASP1_FRAME_CONTROL1 0x0004810 +#define CS35L56_ASP1_FRAME_CONTROL5 0x0004820 +#define CS35L56_ASP1_DATA_CONTROL1 0x0004830 +#define CS35L56_ASP1_DATA_CONTROL5 0x0004840 +#define CS35L56_DACPCM1_INPUT 0x0004C00 +#define CS35L56_DACPCM2_INPUT 0x0004C08 +#define CS35L56_ASP1TX1_INPUT 0x0004C20 +#define CS35L56_ASP1TX2_INPUT 0x0004C24 +#define CS35L56_ASP1TX3_INPUT 0x0004C28 +#define CS35L56_ASP1TX4_INPUT 0x0004C2C +#define CS35L56_DSP1RX1_INPUT 0x0004C40 +#define CS35L56_DSP1RX2_INPUT 0x0004C44 +#define CS35L56_SWIRE_DP3_CH1_INPUT 0x0004C70 +#define CS35L56_SWIRE_DP3_CH2_INPUT 0x0004C74 +#define CS35L56_SWIRE_DP3_CH3_INPUT 0x0004C78 +#define CS35L56_SWIRE_DP3_CH4_INPUT 0x0004C7C +#define CS35L56_SWIRE_DP3_CH5_INPUT 0x0004C80 +#define CS35L56_SWIRE_DP3_CH6_INPUT 0x0004C84 +#define CS35L56_IRQ1_CFG 0x000E000 +#define CS35L56_IRQ1_STATUS 0x000E004 +#define CS35L56_IRQ1_EINT_1 0x000E010 +#define CS35L56_IRQ1_EINT_2 0x000E014 +#define CS35L56_IRQ1_EINT_4 0x000E01C +#define CS35L56_IRQ1_EINT_8 0x000E02C +#define CS35L56_IRQ1_EINT_18 0x000E054 +#define CS35L56_IRQ1_EINT_20 0x000E05C +#define CS35L56_IRQ1_MASK_1 0x000E090 +#define CS35L56_IRQ1_MASK_2 0x000E094 +#define CS35L56_IRQ1_MASK_4 0x000E09C +#define CS35L56_IRQ1_MASK_8 0x000E0AC +#define CS35L56_IRQ1_MASK_18 0x000E0D4 +#define CS35L56_IRQ1_MASK_20 0x000E0DC +#define CS35L56_DSP_VIRTUAL1_MBOX_1 0x0011020 +#define CS35L56_DSP_VIRTUAL1_MBOX_2 0x0011024 +#define CS35L56_DSP_VIRTUAL1_MBOX_3 0x0011028 +#define CS35L56_DSP_VIRTUAL1_MBOX_4 0x001102C +#define CS35L56_DSP_VIRTUAL1_MBOX_5 0x0011030 +#define CS35L56_DSP_VIRTUAL1_MBOX_6 0x0011034 +#define CS35L56_DSP_VIRTUAL1_MBOX_7 0x0011038 +#define CS35L56_DSP_VIRTUAL1_MBOX_8 0x001103C +#define CS35L56_DSP_RESTRICT_STS1 0x00190F0 +#define CS35L56_DSP1_XMEM_PACKED_0 0x2000000 +#define CS35L56_DSP1_XMEM_PACKED_6143 0x2005FFC +#define CS35L56_DSP1_XMEM_UNPACKED32_0 0x2400000 +#define CS35L56_DSP1_XMEM_UNPACKED32_4095 0x2403FFC +#define CS35L56_DSP1_SYS_INFO_ID 0x25E0000 +#define CS35L56_DSP1_SYS_INFO_END 0x25E004C +#define CS35L56_DSP1_AHBM_WINDOW_DEBUG_0 0x25E2040 +#define CS35L56_DSP1_AHBM_WINDOW_DEBUG_1 0x25E2044 +#define CS35L56_DSP1_XMEM_UNPACKED24_0 0x2800000 +#define CS35L56_DSP1_HALO_STATE_A1 0x2801E58 +#define CS35L56_DSP1_HALO_STATE 0x28021E0 +#define CS35L56_DSP1_PM_CUR_STATE_A1 0x2804000 +#define CS35L56_DSP1_PM_CUR_STATE 0x2804308 +#define CS35L56_DSP1_XMEM_UNPACKED24_8191 0x2807FFC +#define CS35L56_DSP1_CORE_BASE 0x2B80000 +#define CS35L56_DSP1_SCRATCH1 0x2B805C0 +#define CS35L56_DSP1_SCRATCH2 0x2B805C8 +#define CS35L56_DSP1_SCRATCH3 0x2B805D0 +#define CS35L56_DSP1_SCRATCH4 0x2B805D8 +#define CS35L56_DSP1_YMEM_PACKED_0 0x2C00000 +#define CS35L56_DSP1_YMEM_PACKED_4604 0x2C047F0 +#define CS35L56_DSP1_YMEM_UNPACKED32_0 0x3000000 +#define CS35L56_DSP1_YMEM_UNPACKED32_3070 0x3002FF8 +#define CS35L56_DSP1_YMEM_UNPACKED24_0 0x3400000 +#define CS35L56_MAIN_RENDER_USER_MUTE 0x3400024 +#define CS35L56_MAIN_RENDER_USER_VOLUME 0x340002C +#define CS35L56_MAIN_POSTURE_NUMBER 0x3400094 +#define CS35L56_TRANSDUCER_ACTUAL_PS 0x3400150 +#define CS35L56_DSP1_YMEM_UNPACKED24_6141 0x3405FF4 +#define CS35L56_DSP1_PMEM_0 0x3800000 +#define CS35L56_DSP1_PMEM_5114 0x3804FE8 + +/* DEVID */ +#define CS35L56_DEVID_MASK 0x00FFFFFF + +/* REVID */ +#define CS35L56_AREVID_MASK 0x000000F0 +#define CS35L56_MTLREVID_MASK 0x0000000F +#define CS35L56_REVID_B0 0x000000B0 + +/* ASP_ENABLES1 */ +#define CS35L56_ASP_RX2_EN_SHIFT 17 +#define CS35L56_ASP_RX1_EN_SHIFT 16 +#define CS35L56_ASP_TX4_EN_SHIFT 3 +#define CS35L56_ASP_TX3_EN_SHIFT 2 +#define CS35L56_ASP_TX2_EN_SHIFT 1 +#define CS35L56_ASP_TX1_EN_SHIFT 0 + +/* ASP_CONTROL1 */ +#define CS35L56_ASP_BCLK_FREQ_MASK 0x0000003F +#define CS35L56_ASP_BCLK_FREQ_SHIFT 0 + +/* ASP_CONTROL2 */ +#define CS35L56_ASP_RX_WIDTH_MASK 0xFF000000 +#define CS35L56_ASP_RX_WIDTH_SHIFT 24 +#define CS35L56_ASP_TX_WIDTH_MASK 0x00FF0000 +#define CS35L56_ASP_TX_WIDTH_SHIFT 16 +#define CS35L56_ASP_FMT_MASK 0x00000700 +#define CS35L56_ASP_FMT_SHIFT 8 +#define CS35L56_ASP_BCLK_INV_MASK 0x00000040 +#define CS35L56_ASP_FSYNC_INV_MASK 0x00000004 + +/* ASP_CONTROL3 */ +#define CS35L56_ASP1_DOUT_HIZ_CTRL_MASK 0x00000003 + +/* ASP_DATA_CONTROL1 */ +#define CS35L56_ASP_TX_WL_MASK 0x0000003F + +/* ASP_DATA_CONTROL5 */ +#define CS35L56_ASP_RX_WL_MASK 0x0000003F + +/* ASPTXn_INPUT */ +#define CS35L56_ASP_TXn_SRC_MASK 0x0000007F + +/* SWIRETX[1..7]_SRC SDWTXn INPUT */ +#define CS35L56_SWIRETXn_SRC_MASK 0x0000007F + +/* IRQ1_STATUS */ +#define CS35L56_IRQ1_STS_MASK 0x00000001 + +/* IRQ1_EINT_1 */ +#define CS35L56_AMP_SHORT_ERR_EINT1_MASK 0x80000000 + +/* IRQ1_EINT_2 */ +#define CS35L56_DSP_VIRTUAL2_MBOX_WR_EINT1_MASK 0x00200000 + +/* IRQ1_EINT_4 */ +#define CS35L56_OTP_BOOT_DONE_MASK 0x00000002 + +/* IRQ1_EINT_8 */ +#define CS35L56_TEMP_ERR_EINT1_MASK 0x80000000 + +/* Mixer input sources */ +#define CS35L56_INPUT_SRC_NONE 0x00 +#define CS35L56_INPUT_SRC_ASP1RX1 0x08 +#define CS35L56_INPUT_SRC_ASP1RX2 0x09 +#define CS35L56_INPUT_SRC_VMON 0x18 +#define CS35L56_INPUT_SRC_IMON 0x19 +#define CS35L56_INPUT_SRC_ERR_VOL 0x20 +#define CS35L56_INPUT_SRC_CLASSH 0x21 +#define CS35L56_INPUT_SRC_VDDBMON 0x28 +#define CS35L56_INPUT_SRC_VBSTMON 0x29 +#define CS35L56_INPUT_SRC_DSP1TX1 0x32 +#define CS35L56_INPUT_SRC_DSP1TX2 0x33 +#define CS35L56_INPUT_SRC_DSP1TX3 0x34 +#define CS35L56_INPUT_SRC_DSP1TX4 0x35 +#define CS35L56_INPUT_SRC_DSP1TX5 0x36 +#define CS35L56_INPUT_SRC_DSP1TX6 0x37 +#define CS35L56_INPUT_SRC_DSP1TX7 0x38 +#define CS35L56_INPUT_SRC_DSP1TX8 0x39 +#define CS35L56_INPUT_SRC_TEMPMON 0x3A +#define CS35L56_INPUT_SRC_INTERPOLATOR 0x40 +#define CS35L56_INPUT_SRC_SWIRE_RX1 0x44 +#define CS35L56_INPUT_SRC_SWIRE_RX2 0x45 +#define CS35L56_INPUT_SRC_SWIRE_RX3 0x46 +#define CS35L56_INPUT_MASK 0x7F + +#define CS35L56_NUM_INPUT_SRC 22 + +/* ASP formats */ +#define CS35L56_ASP_FMT_DSP_A 0 +#define CS35L56_ASP_FMT_I2S 2 + +/* ASP HiZ modes */ +#define CS35L56_ASP_UNUSED_HIZ_OFF_HIZ 3 + +/* MAIN_RENDER_ACTUAL_PS */ +#define CS35L56_PS0 0 +#define CS35L56_PS3 3 + +/* CS35L56_DSP_RESTRICT_STS1 */ +#define CS35L56_RESTRICTED_MASK 0x7 + +/* CS35L56_MAIN_RENDER_USER_MUTE */ +#define CS35L56_MAIN_RENDER_USER_MUTE_MASK 1 + +/* CS35L56_MAIN_RENDER_USER_VOLUME */ +#define CS35L56_MAIN_RENDER_USER_VOLUME_MIN -400 +#define CS35L56_MAIN_RENDER_USER_VOLUME_MAX 400 +#define CS35L56_MAIN_RENDER_USER_VOLUME_MASK 0x0000FFC0 +#define CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT 6 +#define CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT 9 + +/* CS35L56_MAIN_POSTURE_NUMBER */ +#define CS35L56_MAIN_POSTURE_MIN 0 +#define CS35L56_MAIN_POSTURE_MAX 255 +#define CS35L56_MAIN_POSTURE_MASK CS35L56_MAIN_POSTURE_MAX + +/* Software Values */ +#define CS35L56_HALO_STATE_SHUTDOWN 1 +#define CS35L56_HALO_STATE_BOOT_DONE 2 + +#define CS35L56_MBOX_CMD_AUDIO_PLAY 0x0B000001 +#define CS35L56_MBOX_CMD_AUDIO_PAUSE 0x0B000002 +#define CS35L56_MBOX_CMD_HIBERNATE_NOW 0x02000001 +#define CS35L56_MBOX_CMD_WAKEUP 0x02000002 +#define CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE 0x02000003 +#define CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE 0x02000004 +#define CS35L56_MBOX_CMD_SHUTDOWN 0x02000005 +#define CS35L56_MBOX_CMD_SYSTEM_RESET 0x02000007 + +#define CS35L56_MBOX_TIMEOUT_US 5000 +#define CS35L56_MBOX_POLL_US 250 + +#define CS35L56_PS0_POLL_US 500 +#define CS35L56_PS0_TIMEOUT_US 50000 +#define CS35L56_PS3_POLL_US 500 +#define CS35L56_PS3_TIMEOUT_US 300000 + +#define CS35L56_CONTROL_PORT_READY_US 2200 +#define CS35L56_HALO_STATE_POLL_US 1000 +#define CS35L56_HALO_STATE_TIMEOUT_US 50000 +#define CS35L56_HIBERNATE_WAKE_POLL_US 500 +#define CS35L56_HIBERNATE_WAKE_TIMEOUT_US 5000 +#define CS35L56_RESET_PULSE_MIN_US 1100 + +#define CS35L56_SDW1_PLAYBACK_PORT 1 +#define CS35L56_SDW1_CAPTURE_PORT 3 + +#define CS35L56_NUM_BULK_SUPPLIES 3 +#define CS35L56_NUM_DSP_REGIONS 5 + +extern struct regmap_config cs35l56_regmap_i2c; +extern struct regmap_config cs35l56_regmap_spi; +extern struct regmap_config cs35l56_regmap_sdw; + +extern const struct cs_dsp_region cs35l56_dsp1_regions[CS35L56_NUM_DSP_REGIONS]; +extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; +extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; + +void cs35l56_patch(struct device *dev, struct regmap *regmap, u8 revid); +void cs35l56_reread_firmware_registers(struct device *dev, struct regmap *regmap); +int cs35l56_get_bclk_freq_id(unsigned int freq); +void cs35l56_fill_supply_names(struct regulator_bulk_data *data); + +#endif /* ifndef __CS35L56_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 07747565c3b5..c9fb09cf8e6b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -68,6 +68,9 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS35L41_I2C imply SND_SOC_CS35L45_I2C imply SND_SOC_CS35L45_SPI + imply SND_SOC_CS35L56_I2C + imply SND_SOC_CS35L56_SPI + imply SND_SOC_CS35L56_SDW imply SND_SOC_CS42L42 imply SND_SOC_CS42L42_SDW imply SND_SOC_CS42L51_I2C @@ -364,6 +367,7 @@ config SND_SOC_WM_ADSP default y if SND_SOC_WM2200=y default y if SND_SOC_CS35L41_SPI=y default y if SND_SOC_CS35L41_I2C=y + default y if SND_SOC_CS35L56=y default m if SND_SOC_MADERA=m default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m @@ -371,6 +375,7 @@ config SND_SOC_WM_ADSP default m if SND_SOC_WM2200=m default m if SND_SOC_CS35L41_SPI=m default m if SND_SOC_CS35L41_I2C=m + default m if SND_SOC_CS35L56=m config SND_SOC_AB8500_CODEC tristate @@ -711,6 +716,41 @@ config SND_SOC_CS35L45_I2C Enable support for Cirrus Logic CS35L45 smart speaker amplifier with I2C control. +config SND_SOC_CS35L56 + tristate + +config SND_SOC_CS35L56_SHARED + tristate + +config SND_SOC_CS35L56_I2C + tristate "Cirrus Logic CS35L56 CODEC (I2C)" + depends on I2C + depends on SOUNDWIRE || !SOUNDWIRE + select REGMAP_I2C + select SND_SOC_CS35L56 + select SND_SOC_CS35L56_SHARED + help + Enable support for Cirrus Logic CS35L56 boosted amplifier with I2C control + +config SND_SOC_CS35L56_SPI + tristate "Cirrus Logic CS35L56 CODEC (SPI)" + depends on SPI_MASTER + depends on SOUNDWIRE || !SOUNDWIRE + select REGMAP_SPI + select SND_SOC_CS35L56 + select SND_SOC_CS35L56_SHARED + help + Enable support for Cirrus Logic CS35L56 boosted amplifier with SPI control + +config SND_SOC_CS35L56_SDW + tristate "Cirrus Logic CS35L56 CODEC (SDW)" + depends on SOUNDWIRE + select REGMAP + select SND_SOC_CS35L56 + select SND_SOC_CS35L56_SHARED + help + Enable support for Cirrus Logic CS35L56 boosted amplifier with SoundWire control + config SND_SOC_CS42L42_CORE tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f1ca18f7946c..25ebce58a0ba 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -66,6 +66,11 @@ snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o snd-soc-cs35l45-objs := cs35l45.o cs35l45-tables.o snd-soc-cs35l45-spi-objs := cs35l45-spi.o snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o +snd-soc-cs35l56-objs := cs35l56.o +snd-soc-cs35l56-shared-objs := cs35l56-shared.o +snd-soc-cs35l56-i2c-objs := cs35l56-i2c.o +snd-soc-cs35l56-spi-objs := cs35l56-spi.o +snd-soc-cs35l56-sdw-objs := cs35l56-sdw.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o snd-soc-cs42l42-sdw-objs := cs42l42-sdw.o @@ -433,6 +438,11 @@ obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o +obj-$(CONFIG_SND_SOC_CS35L56) += snd-soc-cs35l56.o +obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o +obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o +obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o +obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o obj-$(CONFIG_SND_SOC_CS42L42_SDW) += snd-soc-cs42l42-sdw.o diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c new file mode 100644 index 000000000000..4b7f034a7670 --- /dev/null +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// CS35L56 ALSA SoC audio driver I2C binding +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l56.h" + +static int cs35l56_i2c_probe(struct i2c_client *client) +{ + struct cs35l56_private *cs35l56; + struct device *dev = &client->dev; + const struct regmap_config *regmap_config = &cs35l56_regmap_i2c; + int ret; + + cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL); + if (!cs35l56) + return -ENOMEM; + + cs35l56->dev = dev; + cs35l56->irq = client->irq; + cs35l56->can_hibernate = true; + + i2c_set_clientdata(client, cs35l56); + cs35l56->regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(cs35l56->regmap)) { + ret = PTR_ERR(cs35l56->regmap); + return dev_err_probe(cs35l56->dev, ret, "Failed to allocate register map\n"); + } + + ret = cs35l56_common_probe(cs35l56); + if (ret != 0) + return ret; + + ret = cs35l56_init(cs35l56); + if (ret == 0) + ret = cs35l56_irq_request(cs35l56); + if (ret < 0) + cs35l56_remove(cs35l56); + + return ret; +} + +static void cs35l56_i2c_remove(struct i2c_client *client) +{ + struct cs35l56_private *cs35l56 = i2c_get_clientdata(client); + + cs35l56_remove(cs35l56); +} + +static const struct i2c_device_id cs35l56_id_i2c[] = { + { "cs35l56", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c); + +static struct i2c_driver cs35l56_i2c_driver = { + .driver = { + .name = "cs35l56", + .pm = &cs35l56_pm_ops_i2c_spi, + }, + .id_table = cs35l56_id_i2c, + .probe_new = cs35l56_i2c_probe, + .remove = cs35l56_i2c_remove, +}; + +module_i2c_driver(cs35l56_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L56 I2C driver"); +MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c new file mode 100644 index 000000000000..448ef3609f4c --- /dev/null +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// CS35L56 ALSA SoC audio driver SoundWire binding +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l56.h" + +/* Register addresses are offset when sent over SoundWire */ +#define CS35L56_SDW_ADDR_OFFSET 0x8000 + +static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf) +{ + int ret; + + ret = sdw_nread_no_pm(peripheral, reg, 4, (u8 *)buf); + if (ret != 0) { + dev_err(&peripheral->dev, "Read failed @%#x:%d\n", reg, ret); + return ret; + } + + swab32s((u32 *)buf); + + return 0; +} + +static int cs35l56_sdw_read(void *context, const void *reg_buf, + const size_t reg_size, void *val_buf, + size_t val_size) +{ + struct sdw_slave *peripheral = context; + u8 *buf8 = val_buf; + unsigned int reg, bytes; + int ret; + + reg = le32_to_cpu(*(const __le32 *)reg_buf); + reg += CS35L56_SDW_ADDR_OFFSET; + + if (val_size == 4) + return cs35l56_sdw_read_one(peripheral, reg, val_buf); + + while (val_size) { + bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */ + if (bytes > val_size) + bytes = val_size; + + ret = sdw_nread_no_pm(peripheral, reg, bytes, buf8); + if (ret != 0) { + dev_err(&peripheral->dev, "Read failed @%#x..%#x:%d\n", + reg, reg + bytes - 1, ret); + return ret; + } + + swab32_array((u32 *)buf8, bytes / 4); + val_size -= bytes; + reg += bytes; + buf8 += bytes; + } + + return 0; +} + +static inline void cs35l56_swab_copy(void *dest, const void *src, size_t nbytes) +{ + u32 *dest32 = dest; + const u32 *src32 = src; + + for (; nbytes > 0; nbytes -= 4) + *dest32++ = swab32(*src32++); +} + +static int cs35l56_sdw_write_one(struct sdw_slave *peripheral, unsigned int reg, const void *buf) +{ + u32 val_le = swab32(*(u32 *)buf); + int ret; + + ret = sdw_nwrite_no_pm(peripheral, reg, 4, (u8 *)&val_le); + if (ret != 0) { + dev_err(&peripheral->dev, "Write failed @%#x:%d\n", reg, ret); + return ret; + } + + return 0; +} + +static int cs35l56_sdw_gather_write(void *context, + const void *reg_buf, size_t reg_size, + const void *val_buf, size_t val_size) +{ + struct sdw_slave *peripheral = context; + const u8 *src_be = val_buf; + u32 val_le_buf[64]; /* Define u32 so it is 32-bit aligned */ + unsigned int reg, bytes; + int ret; + + reg = le32_to_cpu(*(const __le32 *)reg_buf); + reg += CS35L56_SDW_ADDR_OFFSET; + + if (val_size == 4) + return cs35l56_sdw_write_one(peripheral, reg, src_be); + + while (val_size) { + bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */ + if (bytes > val_size) + bytes = val_size; + if (bytes > sizeof(val_le_buf)) + bytes = sizeof(val_le_buf); + + cs35l56_swab_copy(val_le_buf, src_be, bytes); + + ret = sdw_nwrite_no_pm(peripheral, reg, bytes, (u8 *)val_le_buf); + if (ret != 0) { + dev_err(&peripheral->dev, "Write failed @%#x..%#x:%d\n", + reg, reg + bytes - 1, ret); + return ret; + } + + val_size -= bytes; + reg += bytes; + src_be += bytes; + } + + return 0; +} + +static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size) +{ + const u8 *src_buf = val_buf; + + /* First word of val_buf contains the destination address */ + return cs35l56_sdw_gather_write(context, &src_buf[0], 4, &src_buf[4], val_size - 4); +} + +/* + * Registers are big-endian on I2C and SPI but little-endian on SoundWire. + * Exported firmware controls are big-endian on I2C/SPI but little-endian on + * SoundWire. Firmware files are always big-endian and are opaque blobs. + * Present a big-endian regmap and hide the endianness swap, so that the ALSA + * byte controls always have the same byte order, and firmware file blobs + * can be written verbatim. + */ +static const struct regmap_bus cs35l56_regmap_bus_sdw = { + .read = cs35l56_sdw_read, + .write = cs35l56_sdw_write, + .gather_write = cs35l56_sdw_gather_write, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static void cs35l56_sdw_init(struct sdw_slave *peripheral) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + int ret; + + pm_runtime_get_noresume(cs35l56->dev); + + regcache_cache_only(cs35l56->regmap, false); + + ret = cs35l56_init(cs35l56); + if (ret < 0) { + regcache_cache_only(cs35l56->regmap, true); + goto out; + } + + /* + * cs35l56_init can return with !init_done if it triggered + * a soft reset. + */ + if (cs35l56->init_done) { + /* Enable SoundWire interrupts */ + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); + } + +out: + pm_runtime_mark_last_busy(cs35l56->dev); + pm_runtime_put_autosuspend(cs35l56->dev); +} + +static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, + struct sdw_slave_intr_status *status) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + + /* SoundWire core holds our pm_runtime when calling this function. */ + + dev_dbg(cs35l56->dev, "int control_port=%#x\n", status->control_port); + + if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0) + return 0; + + /* + * Prevent bus manager suspending and possibly issuing a + * bus-reset before the queued work has run. + */ + pm_runtime_get_noresume(cs35l56->dev); + + /* + * Mask and clear until it has been handled. The read of GEN_INT_STAT_1 + * is required as per the SoundWire spec for interrupt status bits + * to clear. GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. + * None of the interrupts are time-critical so use the + * power-efficient queue. + */ + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work); + + return 0; +} + +static void cs35l56_sdw_irq_work(struct work_struct *work) +{ + struct cs35l56_private *cs35l56 = container_of(work, + struct cs35l56_private, + sdw_irq_work); + + cs35l56_irq(-1, cs35l56); + + /* unmask interrupts */ + if (!cs35l56->sdw_irq_no_unmask) + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); + + pm_runtime_put_autosuspend(cs35l56->dev); +} + +static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + struct sdw_slave_prop *prop = &peripheral->prop; + struct sdw_dpn_prop *ports; + + ports = devm_kcalloc(cs35l56->dev, 2, sizeof(*ports), GFP_KERNEL); + if (!ports) + return -ENOMEM; + + prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); + prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); + prop->paging_support = true; + prop->clk_stop_mode1 = false; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; + + /* DP1 - playback */ + ports[0].num = CS35L56_SDW1_PLAYBACK_PORT; + ports[0].type = SDW_DPN_FULL; + ports[0].ch_prep_timeout = 10; + prop->sink_dpn_prop = &ports[0]; + + /* DP3 - capture */ + ports[1].num = CS35L56_SDW1_CAPTURE_PORT; + ports[1].type = SDW_DPN_FULL; + ports[1].ch_prep_timeout = 10; + prop->src_dpn_prop = &ports[1]; + + return 0; +} + +static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, + enum sdw_slave_status status) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + + switch (status) { + case SDW_SLAVE_ATTACHED: + dev_dbg(cs35l56->dev, "%s: ATTACHED\n", __func__); + if (cs35l56->sdw_attached) + break; + + if (!cs35l56->init_done || cs35l56->soft_resetting) + cs35l56_sdw_init(peripheral); + + cs35l56->sdw_attached = true; + break; + case SDW_SLAVE_UNATTACHED: + dev_dbg(cs35l56->dev, "%s: UNATTACHED\n", __func__); + cs35l56->sdw_attached = false; + break; + default: + break; + } + + return 0; +} + +static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56, + struct sdw_slave *peripheral) +{ + unsigned int curr_scale_reg, next_scale_reg; + int curr_scale, next_scale, ret; + + if (!cs35l56->init_done) + return 0; + + if (peripheral->bus->params.curr_bank) { + curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; + next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; + } else { + curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; + next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; + } + + /* + * Current clock scale value must be different to new value. + * Modify current to guarantee this. If next still has the dummy + * value we wrote when it was current, the core code has not set + * a new scale so restore its original good value + */ + curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg); + if (curr_scale < 0) { + dev_err(cs35l56->dev, "Failed to read current clock scale: %d\n", curr_scale); + return curr_scale; + } + + next_scale = sdw_read_no_pm(peripheral, next_scale_reg); + if (next_scale < 0) { + dev_err(cs35l56->dev, "Failed to read next clock scale: %d\n", next_scale); + return next_scale; + } + + if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) { + next_scale = cs35l56->old_sdw_clock_scale; + ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale); + if (ret < 0) { + dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret); + return ret; + } + } + + cs35l56->old_sdw_clock_scale = curr_scale; + ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE); + if (ret < 0) { + dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret); + return ret; + } + + dev_dbg(cs35l56->dev, "Next bus scale: %#x\n", next_scale); + + return 0; +} + +static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, + struct sdw_bus_params *params) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + int sclk; + + sclk = params->curr_dr_freq / 2; + dev_dbg(cs35l56->dev, "%s: sclk=%u c=%u r=%u\n", __func__, sclk, params->col, params->row); + + if (cs35l56->rev < 0xb0) + return cs35l56_a1_kick_divider(cs35l56, peripheral); + + return 0; +} + +static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral, + enum sdw_clk_stop_mode mode, + enum sdw_clk_stop_type type) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + + dev_dbg(cs35l56->dev, "%s: mode:%d type:%d\n", __func__, mode, type); + + return 0; +} + +static const struct sdw_slave_ops cs35l56_sdw_ops = { + .read_prop = cs35l56_sdw_read_prop, + .interrupt_callback = cs35l56_sdw_interrupt, + .update_status = cs35l56_sdw_update_status, + .bus_config = cs35l56_sdw_bus_config, +#ifdef DEBUG + .clk_stop = cs35l56_sdw_clk_stop, +#endif +}; + +static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56) +{ + struct sdw_slave *peripheral = cs35l56->sdw_peripheral; + + if (peripheral->unattach_request) { + /* Cannot access registers until bus is re-initialized. */ + dev_dbg(cs35l56->dev, "Wait for initialization_complete\n"); + if (!wait_for_completion_timeout(&peripheral->initialization_complete, + msecs_to_jiffies(5000))) { + dev_err(cs35l56->dev, "initialization_complete timed out\n"); + return -ETIMEDOUT; + } + + peripheral->unattach_request = 0; + + /* + * Don't call regcache_mark_dirty(), we can't be sure that the + * Manager really did issue a Bus Reset. + */ + } + + return 0; +} + +static int __maybe_unused cs35l56_sdw_runtime_suspend(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + if (!cs35l56->init_done) + return 0; + + return cs35l56_runtime_suspend(dev); +} + +static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "Runtime resume\n"); + + if (!cs35l56->init_done) + return 0; + + ret = cs35l56_sdw_handle_unattach(cs35l56); + if (ret < 0) + return ret; + + ret = cs35l56_runtime_resume_common(cs35l56); + if (ret) + return ret; + + /* Re-enable SoundWire interrupts */ + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); + + return 0; +} + +static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) +{ + struct device *dev = &peripheral->dev; + struct cs35l56_private *cs35l56; + int ret; + + cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL); + if (!cs35l56) + return -ENOMEM; + + cs35l56->dev = dev; + cs35l56->sdw_peripheral = peripheral; + INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); + + dev_set_drvdata(dev, cs35l56); + + cs35l56->regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw, + peripheral, &cs35l56_regmap_sdw); + if (IS_ERR(cs35l56->regmap)) { + ret = PTR_ERR(cs35l56->regmap); + return dev_err_probe(dev, ret, "Failed to allocate register map\n"); + } + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(cs35l56->regmap, true); + + ret = cs35l56_common_probe(cs35l56); + if (ret != 0) + return ret; + + return 0; +} + +static int cs35l56_sdw_remove(struct sdw_slave *peripheral) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + + /* Disable SoundWire interrupts */ + cs35l56->sdw_irq_no_unmask = true; + cancel_work_sync(&cs35l56->sdw_irq_work); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + + return cs35l56_remove(cs35l56); +} + +static const struct dev_pm_ops cs35l56_sdw_pm = { + SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) +}; + +static const struct sdw_device_id cs35l56_sdw_id[] = { + SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id); + +static struct sdw_driver cs35l56_sdw_driver = { + .driver = { + .name = "cs35l56", + .pm = &cs35l56_sdw_pm, + }, + .probe = cs35l56_sdw_probe, + .remove = cs35l56_sdw_remove, + .ops = &cs35l56_sdw_ops, + .id_table = cs35l56_sdw_id, +}; + +module_sdw_driver(cs35l56_sdw_driver); + +MODULE_DESCRIPTION("ASoC CS35L56 SoundWire driver"); +MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c new file mode 100644 index 000000000000..d8bc06ad4888 --- /dev/null +++ b/sound/soc/codecs/cs35l56-shared.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Components shared between ASoC and HDA CS35L56 drivers +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include + +#include "cs35l56.h" + +static const struct reg_default cs35l56_reg_defaults[] = { + { CS35L56_ASP1_ENABLES1, 0x00000000 }, + { CS35L56_ASP1_CONTROL1, 0x00000028 }, + { CS35L56_ASP1_CONTROL2, 0x18180200 }, + { CS35L56_ASP1_CONTROL3, 0x00000002 }, + { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, + { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, + { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, + { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, + { CS35L56_ASP1TX1_INPUT, 0x00000018 }, + { CS35L56_ASP1TX2_INPUT, 0x00000019 }, + { CS35L56_ASP1TX3_INPUT, 0x00000020 }, + { CS35L56_ASP1TX4_INPUT, 0x00000028 }, + { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, + { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, + { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, + { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 }, + { CS35L56_SWIRE_DP3_CH5_INPUT, 0x00000018 }, + { CS35L56_SWIRE_DP3_CH6_INPUT, 0x00000018 }, + { CS35L56_IRQ1_CFG, 0x00000000 }, + { CS35L56_IRQ1_MASK_1, 0x83ffffff }, + { CS35L56_IRQ1_MASK_2, 0xffff7fff }, + { CS35L56_IRQ1_MASK_4, 0xe0ffffff }, + { CS35L56_IRQ1_MASK_8, 0xfc000fff }, + { CS35L56_IRQ1_MASK_18, 0x1f7df0ff }, + { CS35L56_IRQ1_MASK_20, 0x15c00000 }, + /* CS35L56_MAIN_RENDER_USER_MUTE - soft register, no default */ + /* CS35L56_MAIN_RENDER_USER_VOLUME - soft register, no default */ + /* CS35L56_MAIN_POSTURE_NUMBER - soft register, no default */ +}; + +/* + * The Ax devices have different default register values to that of B0, + * establish a common set of register defaults. + */ +static const struct reg_sequence cs35l56_reva_patch[] = { + { CS35L56_SWIRE_DP3_CH5_INPUT, 0x00000018 }, + { CS35L56_SWIRE_DP3_CH6_INPUT, 0x00000018 }, +}; + +void cs35l56_patch(struct device *dev, struct regmap *regmap, u8 revid) +{ + int ret; + + if (revid >= CS35L56_REVID_B0) + return; + + ret = regmap_register_patch(regmap, cs35l56_reva_patch, + ARRAY_SIZE(cs35l56_reva_patch)); + if (ret) + dev_err(dev, "Failed to apply patch: %d\n", ret); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_patch, SND_SOC_CS35L56_SHARED); + +static bool cs35l56_is_dsp_memory(unsigned int reg) +{ + switch (reg) { + case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143: + case CS35L56_DSP1_XMEM_UNPACKED32_0 ... CS35L56_DSP1_XMEM_UNPACKED32_4095: + case CS35L56_DSP1_XMEM_UNPACKED24_0 ... CS35L56_DSP1_XMEM_UNPACKED24_8191: + case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604: + case CS35L56_DSP1_YMEM_UNPACKED32_0 ... CS35L56_DSP1_YMEM_UNPACKED32_3070: + case CS35L56_DSP1_YMEM_UNPACKED24_0 ... CS35L56_DSP1_YMEM_UNPACKED24_6141: + case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114: + return true; + default: + return false; + } +} + +static bool cs35l56_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L56_DEVID: + case CS35L56_REVID: + case CS35L56_RELID: + case CS35L56_OTPID: + case CS35L56_SFT_RESET: + case CS35L56_GLOBAL_ENABLES: + case CS35L56_BLOCK_ENABLES: + case CS35L56_BLOCK_ENABLES2: + case CS35L56_REFCLK_INPUT: + case CS35L56_GLOBAL_SAMPLE_RATE: + case CS35L56_ASP1_ENABLES1: + case CS35L56_ASP1_CONTROL1: + case CS35L56_ASP1_CONTROL2: + case CS35L56_ASP1_CONTROL3: + case CS35L56_ASP1_FRAME_CONTROL1: + case CS35L56_ASP1_FRAME_CONTROL5: + case CS35L56_ASP1_DATA_CONTROL1: + case CS35L56_ASP1_DATA_CONTROL5: + case CS35L56_DACPCM1_INPUT: + case CS35L56_DACPCM2_INPUT: + case CS35L56_ASP1TX1_INPUT: + case CS35L56_ASP1TX2_INPUT: + case CS35L56_ASP1TX3_INPUT: + case CS35L56_ASP1TX4_INPUT: + case CS35L56_DSP1RX1_INPUT: + case CS35L56_DSP1RX2_INPUT: + case CS35L56_SWIRE_DP3_CH1_INPUT: + case CS35L56_SWIRE_DP3_CH2_INPUT: + case CS35L56_SWIRE_DP3_CH3_INPUT: + case CS35L56_SWIRE_DP3_CH4_INPUT: + case CS35L56_SWIRE_DP3_CH5_INPUT: + case CS35L56_SWIRE_DP3_CH6_INPUT: + case CS35L56_IRQ1_CFG: + case CS35L56_IRQ1_STATUS: + case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8: + case CS35L56_IRQ1_EINT_18: + case CS35L56_IRQ1_EINT_20: + case CS35L56_IRQ1_MASK_1: + case CS35L56_IRQ1_MASK_2: + case CS35L56_IRQ1_MASK_4: + case CS35L56_IRQ1_MASK_8: + case CS35L56_IRQ1_MASK_18: + case CS35L56_IRQ1_MASK_20: + case CS35L56_DSP_VIRTUAL1_MBOX_1: + case CS35L56_DSP_VIRTUAL1_MBOX_2: + case CS35L56_DSP_VIRTUAL1_MBOX_3: + case CS35L56_DSP_VIRTUAL1_MBOX_4: + case CS35L56_DSP_VIRTUAL1_MBOX_5: + case CS35L56_DSP_VIRTUAL1_MBOX_6: + case CS35L56_DSP_VIRTUAL1_MBOX_7: + case CS35L56_DSP_VIRTUAL1_MBOX_8: + case CS35L56_DSP_RESTRICT_STS1: + case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END: + case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0: + case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1: + case CS35L56_DSP1_SCRATCH1: + case CS35L56_DSP1_SCRATCH2: + case CS35L56_DSP1_SCRATCH3: + case CS35L56_DSP1_SCRATCH4: + return true; + default: + return cs35l56_is_dsp_memory(reg); + } +} + +static bool cs35l56_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143: + case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604: + case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114: + return true; + default: + return false; + } +} + +static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L56_DEVID: + case CS35L56_REVID: + case CS35L56_RELID: + case CS35L56_OTPID: + case CS35L56_SFT_RESET: + case CS35L56_GLOBAL_ENABLES: /* owned by firmware */ + case CS35L56_BLOCK_ENABLES: /* owned by firmware */ + case CS35L56_BLOCK_ENABLES2: /* owned by firmware */ + case CS35L56_REFCLK_INPUT: /* owned by firmware */ + case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */ + case CS35L56_DACPCM1_INPUT: /* owned by firmware */ + case CS35L56_DACPCM2_INPUT: /* owned by firmware */ + case CS35L56_DSP1RX1_INPUT: /* owned by firmware */ + case CS35L56_DSP1RX2_INPUT: /* owned by firmware */ + case CS35L56_IRQ1_STATUS: + case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8: + case CS35L56_IRQ1_EINT_18: + case CS35L56_IRQ1_EINT_20: + case CS35L56_DSP_VIRTUAL1_MBOX_1: + case CS35L56_DSP_VIRTUAL1_MBOX_2: + case CS35L56_DSP_VIRTUAL1_MBOX_3: + case CS35L56_DSP_VIRTUAL1_MBOX_4: + case CS35L56_DSP_VIRTUAL1_MBOX_5: + case CS35L56_DSP_VIRTUAL1_MBOX_6: + case CS35L56_DSP_VIRTUAL1_MBOX_7: + case CS35L56_DSP_VIRTUAL1_MBOX_8: + case CS35L56_DSP_RESTRICT_STS1: + case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END: + case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0: + case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1: + case CS35L56_DSP1_SCRATCH1: + case CS35L56_DSP1_SCRATCH2: + case CS35L56_DSP1_SCRATCH3: + case CS35L56_DSP1_SCRATCH4: + return true; + case CS35L56_MAIN_RENDER_USER_MUTE: + case CS35L56_MAIN_RENDER_USER_VOLUME: + case CS35L56_MAIN_POSTURE_NUMBER: + return false; + default: + return cs35l56_is_dsp_memory(reg); + } +} + +static const u32 cs35l56_firmware_registers[] = { + CS35L56_MAIN_RENDER_USER_MUTE, + CS35L56_MAIN_RENDER_USER_VOLUME, + CS35L56_MAIN_POSTURE_NUMBER, +}; + +void cs35l56_reread_firmware_registers(struct device *dev, struct regmap *regmap) +{ + int i; + unsigned int val; + + for (i = 0; i < ARRAY_SIZE(cs35l56_firmware_registers); i++) { + regmap_read(regmap, cs35l56_firmware_registers[i], &val); + dev_dbg(dev, "%s: %d: %#x: %#x\n", __func__, + i, cs35l56_firmware_registers[i], val); + } +} +EXPORT_SYMBOL_NS_GPL(cs35l56_reread_firmware_registers, SND_SOC_CS35L56_SHARED); + +const struct cs_dsp_region cs35l56_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = CS35L56_DSP1_PMEM_0 }, + { .type = WMFW_HALO_XM_PACKED, .base = CS35L56_DSP1_XMEM_PACKED_0 }, + { .type = WMFW_HALO_YM_PACKED, .base = CS35L56_DSP1_YMEM_PACKED_0 }, + { .type = WMFW_ADSP2_XM, .base = CS35L56_DSP1_XMEM_UNPACKED24_0 }, + { .type = WMFW_ADSP2_YM, .base = CS35L56_DSP1_YMEM_UNPACKED24_0 }, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_dsp1_regions, SND_SOC_CS35L56_SHARED); + +static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = { + [0x0C] = 128000, + [0x0F] = 256000, + [0x11] = 384000, + [0x12] = 512000, + [0x15] = 768000, + [0x17] = 1024000, + [0x1A] = 1500000, + [0x1B] = 1536000, + [0x1C] = 2000000, + [0x1D] = 2048000, + [0x1E] = 2400000, + [0x20] = 3000000, + [0x21] = 3072000, + [0x23] = 4000000, + [0x24] = 4096000, + [0x25] = 4800000, + [0x27] = 6000000, + [0x28] = 6144000, + [0x29] = 6250000, + [0x2A] = 6400000, + [0x2E] = 8000000, + [0x2F] = 8192000, + [0x30] = 9600000, + [0x32] = 12000000, + [0x33] = 12288000, + [0x37] = 13500000, + [0x38] = 19200000, + [0x39] = 22579200, + [0x3B] = 24576000, +}; + +int cs35l56_get_bclk_freq_id(unsigned int freq) +{ + int i; + + if (freq == 0) + return -EINVAL; + + /* The BCLK frequency must be a valid PLL REFCLK */ + for (i = 0; i < ARRAY_SIZE(cs35l56_bclk_valid_for_pll_freq_table); ++i) { + if (cs35l56_bclk_valid_for_pll_freq_table[i] == freq) + return i; + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_get_bclk_freq_id, SND_SOC_CS35L56_SHARED); + +static const char * const cs35l56_supplies[/* auto-sized */] = { + "VDD_P", + "VDD_IO", + "VDD_A", +}; + +void cs35l56_fill_supply_names(struct regulator_bulk_data *data) +{ + int i; + + BUILD_BUG_ON(ARRAY_SIZE(cs35l56_supplies) != CS35L56_NUM_BULK_SUPPLIES); + for (i = 0; i < ARRAY_SIZE(cs35l56_supplies); i++) + data[i].supply = cs35l56_supplies[i]; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_fill_supply_names, SND_SOC_CS35L56_SHARED); + +const char * const cs35l56_tx_input_texts[] = { + "None", "ASP1RX1", "ASP1RX2", "VMON", "IMON", "ERRVOL", "CLASSH", + "VDDBMON", "VBSTMON", "DSP1TX1", "DSP1TX2", "DSP1TX3", "DSP1TX4", + "DSP1TX5", "DSP1TX6", "DSP1TX7", "DSP1TX8", "TEMPMON", + "INTERPOLATOR", "SDW1RX1", "SDW1RX2", "SDW2RX1", +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_texts, SND_SOC_CS35L56_SHARED); + +const unsigned int cs35l56_tx_input_values[] = { + CS35L56_INPUT_SRC_NONE, + CS35L56_INPUT_SRC_ASP1RX1, + CS35L56_INPUT_SRC_ASP1RX2, + CS35L56_INPUT_SRC_VMON, + CS35L56_INPUT_SRC_IMON, + CS35L56_INPUT_SRC_ERR_VOL, + CS35L56_INPUT_SRC_CLASSH, + CS35L56_INPUT_SRC_VDDBMON, + CS35L56_INPUT_SRC_VBSTMON, + CS35L56_INPUT_SRC_DSP1TX1, + CS35L56_INPUT_SRC_DSP1TX2, + CS35L56_INPUT_SRC_DSP1TX3, + CS35L56_INPUT_SRC_DSP1TX4, + CS35L56_INPUT_SRC_DSP1TX5, + CS35L56_INPUT_SRC_DSP1TX6, + CS35L56_INPUT_SRC_DSP1TX7, + CS35L56_INPUT_SRC_DSP1TX8, + CS35L56_INPUT_SRC_TEMPMON, + CS35L56_INPUT_SRC_INTERPOLATOR, + CS35L56_INPUT_SRC_SWIRE_RX1, + CS35L56_INPUT_SRC_SWIRE_RX2, + CS35L56_INPUT_SRC_SWIRE_RX3, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_values, SND_SOC_CS35L56_SHARED); + +struct regmap_config cs35l56_regmap_i2c = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L56_DSP1_PMEM_5114, + .reg_defaults = cs35l56_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults), + .volatile_reg = cs35l56_volatile_reg, + .readable_reg = cs35l56_readable_reg, + .precious_reg = cs35l56_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_i2c, SND_SOC_CS35L56_SHARED); + +struct regmap_config cs35l56_regmap_spi = { + .reg_bits = 32, + .val_bits = 32, + .pad_bits = 16, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L56_DSP1_PMEM_5114, + .reg_defaults = cs35l56_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults), + .volatile_reg = cs35l56_volatile_reg, + .readable_reg = cs35l56_readable_reg, + .precious_reg = cs35l56_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, SND_SOC_CS35L56_SHARED); + +struct regmap_config cs35l56_regmap_sdw = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L56_DSP1_PMEM_5114, + .reg_defaults = cs35l56_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults), + .volatile_reg = cs35l56_volatile_reg, + .readable_reg = cs35l56_readable_reg, + .precious_reg = cs35l56_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, SND_SOC_CS35L56_SHARED); + +MODULE_DESCRIPTION("ASoC CS35L56 Shared"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c new file mode 100644 index 000000000000..80dcf37daae2 --- /dev/null +++ b/sound/soc/codecs/cs35l56-spi.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// CS35L56 ALSA SoC audio driver SPI binding +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include + +#include "cs35l56.h" + +static int cs35l56_spi_probe(struct spi_device *spi) +{ + const struct regmap_config *regmap_config = &cs35l56_regmap_spi; + struct cs35l56_private *cs35l56; + int ret; + + cs35l56 = devm_kzalloc(&spi->dev, sizeof(struct cs35l56_private), GFP_KERNEL); + if (!cs35l56) + return -ENOMEM; + + spi_set_drvdata(spi, cs35l56); + cs35l56->regmap = devm_regmap_init_spi(spi, regmap_config); + if (IS_ERR(cs35l56->regmap)) { + ret = PTR_ERR(cs35l56->regmap); + return dev_err_probe(&spi->dev, ret, "Failed to allocate register map\n"); + return ret; + } + + cs35l56->dev = &spi->dev; + cs35l56->irq = spi->irq; + + ret = cs35l56_common_probe(cs35l56); + if (ret != 0) + return ret; + + ret = cs35l56_init(cs35l56); + if (ret == 0) + ret = cs35l56_irq_request(cs35l56); + if (ret < 0) + cs35l56_remove(cs35l56); + + return ret; +} + +static void cs35l56_spi_remove(struct spi_device *spi) +{ + struct cs35l56_private *cs35l56 = spi_get_drvdata(spi); + + cs35l56_remove(cs35l56); +} + +static const struct spi_device_id cs35l56_id_spi[] = { + { "cs35l56", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, cs35l56_id_spi); + +static struct spi_driver cs35l56_spi_driver = { + .driver = { + .name = "cs35l56", + .pm = &cs35l56_pm_ops_i2c_spi, + }, + .id_table = cs35l56_id_spi, + .probe = cs35l56_spi_probe, + .remove = cs35l56_spi_remove, +}; + +module_spi_driver(cs35l56_spi_driver); + +MODULE_DESCRIPTION("ASoC CS35L56 SPI driver"); +MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c new file mode 100644 index 000000000000..90fc79b5666d --- /dev/null +++ b/sound/soc/codecs/cs35l56.c @@ -0,0 +1,1461 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Driver for Cirrus Logic CS35L56 smart amp +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm_adsp.h" +#include "cs35l56.h" + +static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +static int cs35l56_wait_dsp_ready(struct cs35l56_private *cs35l56) +{ + int ret; + + if (!cs35l56->fw_patched) { + /* block until firmware download completes */ + ret = wait_for_completion_timeout(&cs35l56->dsp_ready_completion, + msecs_to_jiffies(25000)); + if (!ret) { + dev_err(cs35l56->dev, "dsp_ready_completion timeout\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int cs35l56_dspwait_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + int ret = cs35l56_wait_dsp_ready(cs35l56); + + if (ret) + return ret; + + return snd_soc_get_volsw(kcontrol, ucontrol); +} + +static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + int ret = cs35l56_wait_dsp_ready(cs35l56); + + if (ret) + return ret; + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0); + +static const struct snd_kcontrol_new cs35l56_controls[] = { + SOC_SINGLE_EXT("Speaker Switch", + CS35L56_MAIN_RENDER_USER_MUTE, 0, 1, 1, + cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), + SOC_SINGLE_S_EXT_TLV("Speaker Volume", + CS35L56_MAIN_RENDER_USER_VOLUME, + 6, -400, 400, 9, 0, + cs35l56_dspwait_get_volsw, + cs35l56_dspwait_put_volsw, + vol_tlv), + SOC_SINGLE_EXT("Posture Number", CS35L56_MAIN_POSTURE_NUMBER, + 0, 255, 0, + cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum, + CS35L56_ASP1TX1_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new asp1_tx1_mux = + SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum, + CS35L56_ASP1TX2_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new asp1_tx2_mux = + SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum, + CS35L56_ASP1TX3_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new asp1_tx3_mux = + SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum, + CS35L56_ASP1TX4_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new asp1_tx4_mux = + SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum, + CS35L56_SWIRE_DP3_CH1_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx1_mux = + SOC_DAPM_ENUM("SDW1TX1 SRC", cs35l56_sdw1tx1_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx2_enum, + CS35L56_SWIRE_DP3_CH2_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx2_mux = + SOC_DAPM_ENUM("SDW1TX2 SRC", cs35l56_sdw1tx2_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx3_enum, + CS35L56_SWIRE_DP3_CH3_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx3_mux = + SOC_DAPM_ENUM("SDW1TX3 SRC", cs35l56_sdw1tx3_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum, + CS35L56_SWIRE_DP3_CH4_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx4_mux = + SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx5_enum, + CS35L56_SWIRE_DP3_CH5_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx5_mux = + SOC_DAPM_ENUM("SDW1TX5 SRC", cs35l56_sdw1tx5_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx6_enum, + CS35L56_SWIRE_DP3_CH6_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx6_mux = + SOC_DAPM_ENUM("SDW1TX6 SRC", cs35l56_sdw1tx6_enum); + +static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = { + SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0), + + SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPK"), + + SND_SOC_DAPM_PGA_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, cs35l56_dsp_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_RX1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_RX2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_TX1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_TX2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_TX3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_TX4_EN_SHIFT, 0), + + SND_SOC_DAPM_MUX("ASP1 TX1 Source", SND_SOC_NOPM, 0, 0, &asp1_tx1_mux), + SND_SOC_DAPM_MUX("ASP1 TX2 Source", SND_SOC_NOPM, 0, 0, &asp1_tx2_mux), + SND_SOC_DAPM_MUX("ASP1 TX3 Source", SND_SOC_NOPM, 0, 0, &asp1_tx3_mux), + SND_SOC_DAPM_MUX("ASP1 TX4 Source", SND_SOC_NOPM, 0, 0, &asp1_tx4_mux), + + SND_SOC_DAPM_MUX("SDW1 TX1 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx1_mux), + SND_SOC_DAPM_MUX("SDW1 TX2 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx2_mux), + SND_SOC_DAPM_MUX("SDW1 TX3 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx3_mux), + SND_SOC_DAPM_MUX("SDW1 TX4 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx4_mux), + SND_SOC_DAPM_MUX("SDW1 TX5 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx5_mux), + SND_SOC_DAPM_MUX("SDW1 TX6 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx6_mux), + + SND_SOC_DAPM_SIGGEN("VMON ADC"), + SND_SOC_DAPM_SIGGEN("IMON ADC"), + SND_SOC_DAPM_SIGGEN("ERRVOL ADC"), + SND_SOC_DAPM_SIGGEN("CLASSH ADC"), + SND_SOC_DAPM_SIGGEN("VDDBMON ADC"), + SND_SOC_DAPM_SIGGEN("VBSTMON ADC"), + SND_SOC_DAPM_SIGGEN("TEMPMON ADC"), +}; + +#define CS35L56_SRC_ROUTE(name) \ + { name" Source", "ASP1RX1", "ASP1RX1" }, \ + { name" Source", "ASP1RX2", "ASP1RX2" }, \ + { name" Source", "VMON", "VMON ADC" }, \ + { name" Source", "IMON", "IMON ADC" }, \ + { name" Source", "ERRVOL", "ERRVOL ADC" }, \ + { name" Source", "CLASSH", "CLASSH ADC" }, \ + { name" Source", "VDDBMON", "VDDBMON ADC" }, \ + { name" Source", "VBSTMON", "VBSTMON ADC" }, \ + { name" Source", "DSP1TX1", "DSP1" }, \ + { name" Source", "DSP1TX2", "DSP1" }, \ + { name" Source", "DSP1TX3", "DSP1" }, \ + { name" Source", "DSP1TX4", "DSP1" }, \ + { name" Source", "DSP1TX5", "DSP1" }, \ + { name" Source", "DSP1TX6", "DSP1" }, \ + { name" Source", "DSP1TX7", "DSP1" }, \ + { name" Source", "DSP1TX8", "DSP1" }, \ + { name" Source", "TEMPMON", "TEMPMON ADC" }, \ + { name" Source", "INTERPOLATOR", "AMP" }, \ + { name" Source", "SDW1RX1", "SDW1 Playback" }, \ + { name" Source", "SDW1RX2", "SDW1 Playback" }, + +static const struct snd_soc_dapm_route cs35l56_audio_map[] = { + { "AMP", NULL, "VDD_B" }, + { "AMP", NULL, "VDD_AMP" }, + + { "ASP1RX1", NULL, "ASP1 Playback" }, + { "ASP1RX2", NULL, "ASP1 Playback" }, + { "DSP1", NULL, "ASP1RX1" }, + { "DSP1", NULL, "ASP1RX2" }, + { "DSP1", NULL, "SDW1 Playback" }, + { "AMP", NULL, "DSP1" }, + { "SPK", NULL, "AMP" }, + + CS35L56_SRC_ROUTE("ASP1 TX1") + CS35L56_SRC_ROUTE("ASP1 TX2") + CS35L56_SRC_ROUTE("ASP1 TX3") + CS35L56_SRC_ROUTE("ASP1 TX4") + + { "ASP1TX1", NULL, "ASP1 TX1 Source" }, + { "ASP1TX2", NULL, "ASP1 TX2 Source" }, + { "ASP1TX3", NULL, "ASP1 TX3 Source" }, + { "ASP1TX4", NULL, "ASP1 TX4 Source" }, + { "ASP1 Capture", NULL, "ASP1TX1" }, + { "ASP1 Capture", NULL, "ASP1TX2" }, + { "ASP1 Capture", NULL, "ASP1TX3" }, + { "ASP1 Capture", NULL, "ASP1TX4" }, + + CS35L56_SRC_ROUTE("SDW1 TX1") + CS35L56_SRC_ROUTE("SDW1 TX2") + CS35L56_SRC_ROUTE("SDW1 TX3") + CS35L56_SRC_ROUTE("SDW1 TX4") + CS35L56_SRC_ROUTE("SDW1 TX5") + CS35L56_SRC_ROUTE("SDW1 TX6") + { "SDW1 Capture", NULL, "SDW1 TX1 Source" }, + { "SDW1 Capture", NULL, "SDW1 TX2 Source" }, + { "SDW1 Capture", NULL, "SDW1 TX3 Source" }, + { "SDW1 Capture", NULL, "SDW1 TX4 Source" }, + { "SDW1 Capture", NULL, "SDW1 TX5 Source" }, + { "SDW1 Capture", NULL, "SDW1 TX6 Source" }, +}; + +static int cs35l56_mbox_send(struct cs35l56_private *cs35l56, unsigned int command) +{ + unsigned int val; + int ret; + + regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command); + ret = regmap_read_poll_timeout(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + val, (val == 0), + CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US); + if (ret) { + dev_warn(cs35l56->dev, "MBOX command %#x failed: %d\n", command, ret); + return ret; + } + + return 0; +} + +static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + dev_dbg(cs35l56->dev, "%s: %d\n", __func__, event); + + return wm_adsp_event(w, kcontrol, event); +} + +irqreturn_t cs35l56_irq(int irq, void *data) +{ + struct cs35l56_private *cs35l56 = data; + unsigned int status1 = 0, status8 = 0, status20 = 0; + unsigned int mask1, mask8, mask20; + unsigned int rv, val; + irqreturn_t ret = IRQ_NONE; + + if (!cs35l56->init_done) + return IRQ_NONE; + + mutex_lock(&cs35l56->irq_lock); + + rv = pm_runtime_resume_and_get(cs35l56->dev); + if (rv < 0) { + dev_err(cs35l56->dev, "irq: failed to get pm_runtime: %d\n", rv); + goto err_unlock; + } + + regmap_read(cs35l56->regmap, CS35L56_IRQ1_STATUS, &val); + if ((val & CS35L56_IRQ1_STS_MASK) == 0) { + dev_dbg(cs35l56->dev, "Spurious IRQ: no pending interrupt\n"); + goto err; + } + + /* Ack interrupts */ + regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_1, &status1); + regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_1, &mask1); + status1 &= ~mask1; + regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_1, status1); + + regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_8, &status8); + regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_8, &mask8); + status8 &= ~mask8; + regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_8, status8); + + regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_20, &status20); + regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_20, &mask20); + status20 &= ~mask20; + /* We don't want EINT20 but they default to unmasked: force mask */ + regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); + + dev_dbg(cs35l56->dev, "%s: %#x %#x\n", __func__, status1, status8); + + /* Check to see if unmasked bits are active */ + if (!status1 && !status8 && !status20) + goto err; + + if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) + dev_crit(cs35l56->dev, "Amp short error\n"); + + if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) + dev_crit(cs35l56->dev, "Overtemp error\n"); + + ret = IRQ_HANDLED; + +err: + pm_runtime_put(cs35l56->dev); +err_unlock: + mutex_unlock(&cs35l56->irq_lock); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_CORE); + +int cs35l56_irq_request(struct cs35l56_private *cs35l56) +{ + int ret; + + if (!cs35l56->irq) + return 0; + + ret = devm_request_threaded_irq(cs35l56->dev, cs35l56->irq, NULL, + cs35l56_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "cs35l56", cs35l56); + if (ret < 0) + dev_err(cs35l56->dev, "Failed to get IRQ: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_CORE); + +static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component); + unsigned int val; + + dev_dbg(cs35l56->dev, "%s: %#x\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_err(cs35l56->dev, "Unsupported clock source mode\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + val = CS35L56_ASP_FMT_DSP_A << CS35L56_ASP_FMT_SHIFT; + cs35l56->tdm_mode = true; + break; + case SND_SOC_DAIFMT_I2S: + val = CS35L56_ASP_FMT_I2S << CS35L56_ASP_FMT_SHIFT; + cs35l56->tdm_mode = false; + break; + default: + dev_err(cs35l56->dev, "Unsupported DAI format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + val |= CS35L56_ASP_FSYNC_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + val |= CS35L56_ASP_BCLK_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + val |= CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK; + break; + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(cs35l56->dev, "Invalid clock invert\n"); + return -EINVAL; + } + + regmap_update_bits(cs35l56->regmap, + CS35L56_ASP1_CONTROL2, + CS35L56_ASP_FMT_MASK | + CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK, + val); + + /* Hi-Z DOUT in unused slots and when all TX are disabled */ + regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL3, + CS35L56_ASP1_DOUT_HIZ_CTRL_MASK, + CS35L56_ASP_UNUSED_HIZ_OFF_HIZ); + + return 0; +} + +static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56, + unsigned int reg, unsigned long mask) +{ + unsigned int reg_val, channel_shift; + int bit_num; + + /* Init all slots to 63 */ + switch (reg) { + case CS35L56_ASP1_FRAME_CONTROL1: + reg_val = 0x3f3f3f3f; + break; + case CS35L56_ASP1_FRAME_CONTROL5: + reg_val = 0x3f3f3f; + break; + } + + /* Enable consecutive TX1..TXn for each of the slots set in mask */ + channel_shift = 0; + for_each_set_bit(bit_num, &mask, 32) { + reg_val &= ~(0x3f << channel_shift); + reg_val |= bit_num << channel_shift; + channel_shift += 8; + } + + regmap_write(cs35l56->regmap, reg, reg_val); +} + +static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + + if ((slots == 0) || (slot_width == 0)) { + dev_dbg(cs35l56->dev, "tdm config cleared\n"); + cs35l56->asp_slot_width = 0; + cs35l56->asp_slot_count = 0; + return 0; + } + + if (slot_width > (CS35L56_ASP_RX_WIDTH_MASK >> CS35L56_ASP_RX_WIDTH_SHIFT)) { + dev_err(cs35l56->dev, "tdm invalid slot width %d\n", slot_width); + return -EINVAL; + } + + /* More than 32 slots would give an unsupportable BCLK frequency */ + if (slots > 32) { + dev_err(cs35l56->dev, "tdm invalid slot count %d\n", slots); + return -EINVAL; + } + + cs35l56->asp_slot_width = (u8)slot_width; + cs35l56->asp_slot_count = (u8)slots; + + // Note: rx/tx is from point of view of the CPU end + if (tx_mask == 0) + tx_mask = 0x3; // ASPRX1/RX2 in slots 0 and 1 + + if (rx_mask == 0) + rx_mask = 0xf; // ASPTX1..TX4 in slots 0..3 + + cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL1, rx_mask); + cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL5, tx_mask); + + dev_dbg(cs35l56->dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n", + cs35l56->asp_slot_width, cs35l56->asp_slot_count, tx_mask, rx_mask); + + return 0; +} + +static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + unsigned int rate = params_rate(params); + u8 asp_width, asp_wl; + + asp_wl = params_width(params); + if (cs35l56->asp_slot_width) + asp_width = cs35l56->asp_slot_width; + else + asp_width = asp_wl; + + dev_dbg(cs35l56->dev, "%s: wl=%d, width=%d, rate=%d", __func__, asp_wl, asp_width, rate); + + if (!cs35l56->sysclk_set) { + unsigned int slots = cs35l56->asp_slot_count; + unsigned int bclk_freq; + int freq_id; + + if (slots == 0) { + slots = params_channels(params); + + /* I2S always has an even number of slots */ + if (!cs35l56->tdm_mode) + slots = round_up(slots, 2); + } + + bclk_freq = asp_width * slots * rate; + freq_id = cs35l56_get_bclk_freq_id(bclk_freq); + if (freq_id < 0) { + dev_err(cs35l56->dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq); + return -EINVAL; + } + + regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1, + CS35L56_ASP_BCLK_FREQ_MASK, + freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2, + CS35L56_ASP_RX_WIDTH_MASK, asp_width << + CS35L56_ASP_RX_WIDTH_SHIFT); + regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL5, + CS35L56_ASP_RX_WL_MASK, asp_wl); + } else { + regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2, + CS35L56_ASP_TX_WIDTH_MASK, asp_width << + CS35L56_ASP_TX_WIDTH_SHIFT); + regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL1, + CS35L56_ASP_TX_WL_MASK, asp_wl); + } + + return 0; +} + +static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + int freq_id; + + if (freq == 0) { + cs35l56->sysclk_set = false; + return 0; + } + + freq_id = cs35l56_get_bclk_freq_id(freq); + if (freq_id < 0) + return freq_id; + + regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1, + CS35L56_ASP_BCLK_FREQ_MASK, + freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT); + cs35l56->sysclk_set = true; + + return 0; +} + +static int cs35l56_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + unsigned int val; + int ret; + + dev_dbg(cs35l56->dev, "%s: %d %s\n", __func__, stream, mute ? "mute" : "unmute"); + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + if (mute) { + ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PAUSE); + } else { + ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PLAY); + if (ret == 0) { + /* Wait for firmware to enter PS0 power state */ + ret = regmap_read_poll_timeout(cs35l56->regmap, + CS35L56_TRANSDUCER_ACTUAL_PS, + val, (val == CS35L56_PS0), + CS35L56_PS0_POLL_US, + CS35L56_PS0_TIMEOUT_US); + if (ret) + dev_err(cs35l56->dev, "PS0 wait failed: %d\n", ret); + ret = 0; + } + } + + return ret; +} + +static const struct snd_soc_dai_ops cs35l56_ops = { + .set_fmt = cs35l56_asp_dai_set_fmt, + .set_tdm_slot = cs35l56_asp_dai_set_tdm_slot, + .hw_params = cs35l56_asp_dai_hw_params, + .set_sysclk = cs35l56_asp_dai_set_sysclk, + .mute_stream = cs35l56_mute_stream, +}; + +static void cs35l56_sdw_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int cs35l56_sdw_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + + /* rx/tx are from point of view of the CPU end so opposite to our rx/tx */ + cs35l56->rx_mask = tx_mask; + cs35l56->tx_mask = rx_mask; + + return 0; +} + +static int cs35l56_sdw_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_stream_config sconfig; + struct sdw_port_config pconfig; + int ret; + + dev_dbg(cs35l56->dev, "%s: rate %d\n", __func__, params_rate(params)); + + if (!cs35l56->init_done) + return -ENODEV; + + if (!sdw_stream) + return -EINVAL; + + memset(&sconfig, 0, sizeof(sconfig)); + memset(&pconfig, 0, sizeof(pconfig)); + + sconfig.frame_rate = params_rate(params); + sconfig.bps = snd_pcm_format_width(params_format(params)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sconfig.direction = SDW_DATA_DIR_RX; + pconfig.num = CS35L56_SDW1_PLAYBACK_PORT; + pconfig.ch_mask = cs35l56->rx_mask; + } else { + sconfig.direction = SDW_DATA_DIR_TX; + pconfig.num = CS35L56_SDW1_CAPTURE_PORT; + pconfig.ch_mask = cs35l56->tx_mask; + } + + if (pconfig.ch_mask == 0) { + sconfig.ch_count = params_channels(params); + pconfig.ch_mask = GENMASK(sconfig.ch_count - 1, 0); + } else { + sconfig.ch_count = hweight32(pconfig.ch_mask); + } + + ret = sdw_stream_add_slave(cs35l56->sdw_peripheral, &sconfig, &pconfig, + 1, sdw_stream); + if (ret) { + dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret); + return ret; + } + + return 0; +} + +static int cs35l56_sdw_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!cs35l56->sdw_peripheral) + return -EINVAL; + + sdw_stream_remove_slave(cs35l56->sdw_peripheral, sdw_stream); + + return 0; +} + +static int cs35l56_sdw_dai_set_stream(struct snd_soc_dai *dai, + void *sdw_stream, int direction) +{ + if (!sdw_stream) + return 0; + + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static const struct snd_soc_dai_ops cs35l56_sdw_dai_ops = { + .set_tdm_slot = cs35l56_sdw_dai_set_tdm_slot, + .shutdown = cs35l56_sdw_dai_shutdown, + .hw_params = cs35l56_sdw_dai_hw_params, + .hw_free = cs35l56_sdw_dai_hw_free, + .mute_stream = cs35l56_mute_stream, + .set_stream = cs35l56_sdw_dai_set_stream, +}; + +static struct snd_soc_dai_driver cs35l56_dai[] = { + { + .name = "cs35l56-asp1", + .id = 0, + .playback = { + .stream_name = "ASP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS35L56_RATES, + .formats = CS35L56_RX_FORMATS, + }, + .capture = { + .stream_name = "ASP1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = CS35L56_RATES, + .formats = CS35L56_TX_FORMATS, + }, + .ops = &cs35l56_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs35l56-sdw1", + .id = 1, + .playback = { + .stream_name = "SDW1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS35L56_RATES, + .formats = CS35L56_RX_FORMATS, + }, + .capture = { + .stream_name = "SDW1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = CS35L56_RATES, + .formats = CS35L56_TX_FORMATS, + }, + .symmetric_rate = 1, + .ops = &cs35l56_sdw_dai_ops, + } +}; + +static int cs35l56_wait_for_firmware_boot(struct cs35l56_private *cs35l56) +{ + unsigned int reg; + unsigned int val; + int ret; + + if (cs35l56->rev < CS35L56_REVID_B0) + reg = CS35L56_DSP1_HALO_STATE_A1; + else + reg = CS35L56_DSP1_HALO_STATE; + + ret = regmap_read_poll_timeout(cs35l56->regmap, reg, + val, + (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE), + CS35L56_HALO_STATE_POLL_US, + CS35L56_HALO_STATE_TIMEOUT_US); + + if ((ret < 0) && (ret != -ETIMEDOUT)) { + dev_err(cs35l56->dev, "Failed to read HALO_STATE: %d\n", ret); + return ret; + } + + if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) { + dev_err(cs35l56->dev, "Firmware boot fail: HALO_STATE=%#x\n", val); + return -EIO; + } + + return 0; +} + +static const struct reg_sequence cs35l56_system_reset_seq[] = { + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET), +}; + +static void cs35l56_system_reset(struct cs35l56_private *cs35l56) +{ + cs35l56->soft_resetting = true; + + /* + * Must enter cache-only first so there can't be any more register + * accesses other than the controlled system reset sequence below. + */ + regcache_cache_only(cs35l56->regmap, true); + regmap_multi_reg_write_bypassed(cs35l56->regmap, + cs35l56_system_reset_seq, + ARRAY_SIZE(cs35l56_system_reset_seq)); + + /* On SoundWire the registers won't be accessible until it re-enumerates. */ + if (cs35l56->sdw_peripheral) + return; + + usleep_range(CS35L56_CONTROL_PORT_READY_US, CS35L56_CONTROL_PORT_READY_US + 400); + regcache_cache_only(cs35l56->regmap, false); +} + +static void cs35l56_dsp_work(struct work_struct *work) +{ + struct cs35l56_private *cs35l56 = container_of(work, + struct cs35l56_private, + dsp_work); + unsigned int reg; + unsigned int val; + int ret = 0; + + if (!wait_for_completion_timeout(&cs35l56->init_completion, + msecs_to_jiffies(5000))) { + dev_err(cs35l56->dev, "%s: init_completion timed out\n", __func__); + goto complete; + } + + if (!cs35l56->init_done || cs35l56->removing) + goto complete; + + cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x", + cs35l56->secured ? "s" : "", cs35l56->rev); + + if (!cs35l56->dsp.part) + goto complete; + + pm_runtime_get_sync(cs35l56->dev); + + /* + * Disable SoundWire interrupts to prevent race with IRQ work. + * Setting sdw_irq_no_unmask prevents the handler re-enabling + * the SoundWire interrupt. + */ + if (cs35l56->sdw_peripheral) { + cs35l56->sdw_irq_no_unmask = true; + cancel_work_sync(&cs35l56->sdw_irq_work); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + } + + ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_SHUTDOWN); + if (ret) { + dev_dbg(cs35l56->dev, "%s: CS35L56_MBOX_CMD_SHUTDOWN ret %d\n", __func__, ret); + goto err; + } + + if (cs35l56->rev < CS35L56_REVID_B0) + reg = CS35L56_DSP1_PM_CUR_STATE_A1; + else + reg = CS35L56_DSP1_PM_CUR_STATE; + + ret = regmap_read_poll_timeout(cs35l56->regmap, reg, + val, (val == CS35L56_HALO_STATE_SHUTDOWN), + CS35L56_HALO_STATE_POLL_US, + CS35L56_HALO_STATE_TIMEOUT_US); + if (ret < 0) + dev_err(cs35l56->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n", + val, ret); + + /* Use wm_adsp to load and apply the firmware patch and coefficient files */ + ret = wm_adsp_power_up(&cs35l56->dsp); + if (ret) { + dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + goto err; + } + + if (cs35l56->removing) + goto err; + + mutex_lock(&cs35l56->irq_lock); + + init_completion(&cs35l56->init_completion); + + cs35l56_system_reset(cs35l56); + + if (cs35l56->sdw_peripheral) { + /* + * The system-reset causes the CS35L56 to detach from the bus. + * Wait for the manager to re-enumerate the CS35L56 and + * cs35l56_init() to run again. + */ + if (!wait_for_completion_timeout(&cs35l56->init_completion, + msecs_to_jiffies(5000))) { + dev_err(cs35l56->dev, "%s: init_completion timed out (SDW)\n", __func__); + goto err_unlock; + } + } else if (cs35l56_init(cs35l56)) { + goto err_unlock; + } + + cs35l56->fw_patched = true; + +err_unlock: + mutex_unlock(&cs35l56->irq_lock); +err: + pm_runtime_mark_last_busy(cs35l56->dev); + pm_runtime_put_autosuspend(cs35l56->dev); + + /* Re-enable SoundWire interrupts */ + if (cs35l56->sdw_peripheral) { + cs35l56->sdw_irq_no_unmask = false; + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); + } + +complete: + complete_all(&cs35l56->dsp_ready_completion); +} + +static int cs35l56_component_probe(struct snd_soc_component *component) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + struct dentry *debugfs_root = component->debugfs_root; + + BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values)); + + cs35l56->removing = false; + cs35l56->component = component; + wm_adsp2_component_probe(&cs35l56->dsp, component); + + debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->init_done); + debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->can_hibernate); + debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->fw_patched); + + queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); + + return 0; +} + +static void cs35l56_component_remove(struct snd_soc_component *component) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + cs35l56->removing = true; + complete(&cs35l56->init_completion); + cancel_work_sync(&cs35l56->dsp_work); +} + +static int cs35l56_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_STANDBY: + /* + * Wait for patching to complete when transitioning from + * BIAS_OFF to BIAS_STANDBY + */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + ret = cs35l56_wait_dsp_ready(cs35l56); + + break; + default: + break; + } + + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_cs35l56 = { + .probe = cs35l56_component_probe, + .remove = cs35l56_component_remove, + + .dapm_widgets = cs35l56_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l56_dapm_widgets), + .dapm_routes = cs35l56_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l56_audio_map), + .controls = cs35l56_controls, + .num_controls = ARRAY_SIZE(cs35l56_controls), + + .set_bias_level = cs35l56_set_bias_level, +}; + +static const struct reg_sequence cs35l56_hibernate_seq[] = { + /* This must be the last register access */ + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_HIBERNATE_NOW), +}; + +static const struct reg_sequence cs35l56_hibernate_wake_seq[] = { + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_WAKEUP), +}; + +int cs35l56_runtime_suspend(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + unsigned int val; + int ret; + + if (!cs35l56->init_done) + return 0; + + /* Firmware must have entered a power-save state */ + ret = regmap_read_poll_timeout(cs35l56->regmap, + CS35L56_TRANSDUCER_ACTUAL_PS, + val, (val >= CS35L56_PS3), + CS35L56_PS3_POLL_US, + CS35L56_PS3_TIMEOUT_US); + if (ret) + dev_warn(cs35l56->dev, "PS3 wait failed: %d\n", ret); + + /* Clear BOOT_DONE so it can be used to detect a reboot */ + regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK); + + if (!cs35l56->can_hibernate) { + regcache_cache_only(cs35l56->regmap, true); + dev_dbg(dev, "Suspended: no hibernate"); + + return 0; + } + + /* + * Enable auto-hibernate. If it is woken by some other wake source + * it will automatically return to hibernate. + */ + ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE); + if (ret) + dev_warn(cs35l56->dev, "ALLOW_HIBERNATE failed: %d\n", ret); + + /* + * Must enter cache-only first so there can't be any more register + * accesses other than the controlled hibernate sequence below. + */ + regcache_cache_only(cs35l56->regmap, true); + + regmap_multi_reg_write_bypassed(cs35l56->regmap, + cs35l56_hibernate_seq, + ARRAY_SIZE(cs35l56_hibernate_seq)); + + dev_dbg(dev, "Suspended: hibernate"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend, SND_SOC_CS35L56_CORE); + +static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + if (!cs35l56->init_done) + return 0; + + return cs35l56_runtime_resume_common(cs35l56); +} + +int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56) +{ + unsigned int val; + int ret; + + if (!cs35l56->can_hibernate) { + regcache_cache_only(cs35l56->regmap, false); + goto out_sync; + } + + if (!cs35l56->sdw_peripheral) { + /* + * Dummy transaction to trigger I2C/SPI auto-wake. This will NAK on I2C. + * Must be done before releasing cache-only. + */ + regmap_multi_reg_write_bypassed(cs35l56->regmap, + cs35l56_hibernate_wake_seq, + ARRAY_SIZE(cs35l56_hibernate_wake_seq)); + + usleep_range(CS35L56_CONTROL_PORT_READY_US, + CS35L56_CONTROL_PORT_READY_US + 400); + } + + regcache_cache_only(cs35l56->regmap, false); + + ret = cs35l56_wait_for_firmware_boot(cs35l56); + if (ret) { + dev_err(cs35l56->dev, "Hibernate wake failed: %d\n", ret); + goto err; + } + + ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); + if (ret) + goto err; + +out_sync: + /* BOOT_DONE will be 1 if the amp reset */ + regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_4, &val); + if (val & CS35L56_OTP_BOOT_DONE_MASK) { + dev_dbg(cs35l56->dev, "Registers reset in suspend\n"); + regcache_mark_dirty(cs35l56->regmap); + } + + regcache_sync(cs35l56->regmap); + + dev_dbg(cs35l56->dev, "Resumed"); + + return 0; + +err: + regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + CS35L56_MBOX_CMD_HIBERNATE_NOW); + + regcache_cache_only(cs35l56->regmap, true); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE); + +static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) +{ + struct wm_adsp *dsp; + int ret; + + cs35l56->dsp_wq = create_singlethread_workqueue("cs35l56-dsp"); + if (!cs35l56->dsp_wq) + return -ENOMEM; + + INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work); + init_completion(&cs35l56->dsp_ready_completion); + + dsp = &cs35l56->dsp; + dsp->part = "cs35l56"; + dsp->cs_dsp.num = 1; + dsp->cs_dsp.type = WMFW_HALO; + dsp->cs_dsp.rev = 0; + dsp->fw = 12; + dsp->cs_dsp.dev = cs35l56->dev; + dsp->cs_dsp.regmap = cs35l56->regmap; + dsp->cs_dsp.base = CS35L56_DSP1_CORE_BASE; + dsp->cs_dsp.base_sysinfo = CS35L56_DSP1_SYS_INFO_ID; + dsp->cs_dsp.mem = cs35l56_dsp1_regions; + dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l56_dsp1_regions); + dsp->cs_dsp.no_core_startstop = true; + dsp->wmfw_optional = true; + + dev_dbg(cs35l56->dev, "DSP system name: '%s'\n", dsp->system_name); + + ret = wm_halo_init(dsp); + if (ret != 0) { + dev_err(cs35l56->dev, "wm_halo_init failed\n"); + return ret; + } + + return 0; +} + +static int cs35l56_acpi_get_name(struct cs35l56_private *cs35l56) +{ + acpi_handle handle = ACPI_HANDLE(cs35l56->dev); + const char *sub; + + /* If there is no ACPI_HANDLE, there is no ACPI for this system, return 0 */ + if (!handle) + return 0; + + sub = acpi_get_subsystem_id(handle); + if (IS_ERR(sub)) { + /* If bad ACPI, return 0 and fallback to legacy firmware path, otherwise fail */ + if (PTR_ERR(sub) == -ENODATA) + return 0; + else + return PTR_ERR(sub); + } + + cs35l56->dsp.system_name = sub; + dev_dbg(cs35l56->dev, "Subsystem ID: %s\n", cs35l56->dsp.system_name); + + return 0; +} + +int cs35l56_common_probe(struct cs35l56_private *cs35l56) +{ + int ret; + + init_completion(&cs35l56->init_completion); + mutex_init(&cs35l56->irq_lock); + + dev_set_drvdata(cs35l56->dev, cs35l56); + + cs35l56_fill_supply_names(cs35l56->supplies); + ret = devm_regulator_bulk_get(cs35l56->dev, ARRAY_SIZE(cs35l56->supplies), + cs35l56->supplies); + if (ret != 0) + return dev_err_probe(cs35l56->dev, ret, "Failed to request supplies\n"); + + /* Reset could be controlled by the BIOS or shared by multiple amps */ + cs35l56->reset_gpio = devm_gpiod_get_optional(cs35l56->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs35l56->reset_gpio)) { + ret = PTR_ERR(cs35l56->reset_gpio); + /* + * If RESET is shared the first amp to probe will grab the reset + * line and reset all the amps + */ + if (ret != -EBUSY) + return dev_err_probe(cs35l56->dev, ret, "Failed to get reset GPIO\n"); + + dev_info(cs35l56->dev, "Reset GPIO busy, assume shared reset\n"); + cs35l56->reset_gpio = NULL; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + if (ret != 0) + return dev_err_probe(cs35l56->dev, ret, "Failed to enable supplies\n"); + + if (cs35l56->reset_gpio) { + /* satisfy minimum reset pulse width spec */ + usleep_range(CS35L56_RESET_PULSE_MIN_US, + CS35L56_RESET_PULSE_MIN_US + 400); + gpiod_set_value_cansleep(cs35l56->reset_gpio, 1); + } + + ret = cs35l56_acpi_get_name(cs35l56); + if (ret != 0) + goto err; + + ret = cs35l56_dsp_init(cs35l56); + if (ret < 0) { + dev_err_probe(cs35l56->dev, ret, "DSP init failed\n"); + goto err; + } + + ret = devm_snd_soc_register_component(cs35l56->dev, + &soc_component_dev_cs35l56, + cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); + if (ret < 0) { + dev_err_probe(cs35l56->dev, ret, "Register codec failed\n"); + goto err; + } + + return 0; + +err: + gpiod_set_value_cansleep(cs35l56->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, SND_SOC_CS35L56_CORE); + +int cs35l56_init(struct cs35l56_private *cs35l56) +{ + int ret; + unsigned int devid, revid, otpid, secured; + + /* + * Check whether the actions associated with soft reset or one time + * init need to be performed. + */ + if (cs35l56->soft_resetting) + goto post_soft_reset; + + if (cs35l56->init_done) + return 0; + + pm_runtime_set_autosuspend_delay(cs35l56->dev, 100); + pm_runtime_use_autosuspend(cs35l56->dev); + pm_runtime_set_active(cs35l56->dev); + pm_runtime_enable(cs35l56->dev); + + /* + * If the system is not using a reset_gpio then issue a + * dummy read to force a wakeup. + */ + if (!cs35l56->reset_gpio) + regmap_read(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, &devid); + + /* Wait for control port to be ready (datasheet tIRS). */ + usleep_range(CS35L56_CONTROL_PORT_READY_US, + CS35L56_CONTROL_PORT_READY_US + 400); + + /* + * The HALO_STATE register is in different locations on Ax and B0 + * devices so the REVID needs to be determined before waiting for the + * firmware to boot. + */ + ret = regmap_read(cs35l56->regmap, CS35L56_REVID, &revid); + if (ret < 0) { + dev_err(cs35l56->dev, "Get Revision ID failed\n"); + return ret; + } + cs35l56->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK); + + ret = cs35l56_wait_for_firmware_boot(cs35l56); + if (ret) + return ret; + + ret = regmap_read(cs35l56->regmap, CS35L56_DEVID, &devid); + if (ret < 0) { + dev_err(cs35l56->dev, "Get Device ID failed\n"); + return ret; + } + devid &= CS35L56_DEVID_MASK; + + switch (devid) { + case 0x35A56: + break; + default: + dev_err(cs35l56->dev, "Unknown device %x\n", devid); + return ret; + } + + ret = regmap_read(cs35l56->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); + if (ret) { + dev_err(cs35l56->dev, "Get Secure status failed\n"); + return ret; + } + + /* When any bus is restricted treat the device as secured */ + if (secured & CS35L56_RESTRICTED_MASK) + cs35l56->secured = true; + + ret = regmap_read(cs35l56->regmap, CS35L56_OTPID, &otpid); + if (ret < 0) { + dev_err(cs35l56->dev, "Get OTP ID failed\n"); + return ret; + } + + dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", + cs35l56->secured ? "s" : "", cs35l56->rev, otpid); + + cs35l56_patch(cs35l56->dev, cs35l56->regmap, cs35l56->rev); + + /* Wake source interrupts default to unmasked, so mask them */ + regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); + regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1, + CS35L56_AMP_SHORT_ERR_EINT1_MASK, + 0); + regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_8, + CS35L56_TEMP_ERR_EINT1_MASK, + 0); + + if (!cs35l56->reset_gpio) { + dev_dbg(cs35l56->dev, "No reset gpio: using soft reset\n"); + cs35l56_system_reset(cs35l56); + if (cs35l56->sdw_peripheral) { + /* Keep alive while we wait for re-enumeration */ + pm_runtime_get_noresume(cs35l56->dev); + return 0; + } + } + +post_soft_reset: + if (cs35l56->soft_resetting) { + cs35l56->soft_resetting = false; + + /* Done re-enumerating after one-time init so release the keep-alive */ + if (cs35l56->sdw_peripheral && !cs35l56->init_done) + pm_runtime_put_noidle(cs35l56->dev); + + regcache_mark_dirty(cs35l56->regmap); + ret = cs35l56_wait_for_firmware_boot(cs35l56); + if (ret) + return ret; + + dev_dbg(cs35l56->dev, "Firmware rebooted after soft reset\n"); + } + + /* Disable auto-hibernate so that runtime_pm has control */ + ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); + if (ret) + return ret; + + /* Populate soft registers in the regmap cache */ + cs35l56_reread_firmware_registers(cs35l56->dev, cs35l56->regmap); + + /* Registers could be dirty after soft reset or SoundWire enumeration */ + regcache_sync(cs35l56->regmap); + + cs35l56->init_done = true; + complete(&cs35l56->init_completion); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_init, SND_SOC_CS35L56_CORE); + +int cs35l56_remove(struct cs35l56_private *cs35l56) +{ + cs35l56->init_done = false; + + /* + * WAKE IRQs unmask if CS35L56 hibernates so free the handler to + * prevent it racing with remove(). + */ + if (cs35l56->irq) + devm_free_irq(cs35l56->dev, cs35l56->irq, cs35l56); + + flush_workqueue(cs35l56->dsp_wq); + destroy_workqueue(cs35l56->dsp_wq); + + pm_runtime_suspend(cs35l56->dev); + pm_runtime_disable(cs35l56->dev); + + regcache_cache_only(cs35l56->regmap, true); + + kfree(cs35l56->dsp.system_name); + + gpiod_set_value_cansleep(cs35l56->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_remove, SND_SOC_CS35L56_CORE); + +const struct dev_pm_ops cs35l56_pm_ops_i2c_spi = { + SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend, cs35l56_runtime_resume_i2c_spi, NULL) +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE); + +MODULE_DESCRIPTION("ASoC CS35L56 driver"); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h new file mode 100644 index 000000000000..efc4b99180f9 --- /dev/null +++ b/sound/soc/codecs/cs35l56.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Driver for Cirrus Logic CS35L56 smart amp + * + * Copyright (C) 2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS35L56_H +#define CS35L56_H + +#include +#include +#include +#include +#include +#include "wm_adsp.h" + +#define CS35L56_SDW_GEN_INT_STAT_1 0xc0 +#define CS35L56_SDW_GEN_INT_MASK_1 0xc1 +#define CS35L56_SDW_INT_MASK_CODEC_IRQ BIT(0) + +#define CS35L56_SDW_INVALID_BUS_SCALE 0xf + +#define CS35L56_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define CS35L56_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +#define CS35L56_RATES (SNDRV_PCM_RATE_48000) + +struct sdw_slave; + +struct cs35l56_private { + struct wm_adsp dsp; /* must be first member */ + struct work_struct dsp_work; + struct workqueue_struct *dsp_wq; + struct completion dsp_ready_completion; + struct mutex irq_lock; + struct snd_soc_component *component; + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES]; + int irq; + struct sdw_slave *sdw_peripheral; + u8 rev; + struct work_struct sdw_irq_work; + bool secured; + bool sdw_irq_no_unmask; + bool soft_resetting; + bool init_done; + bool sdw_attached; + bool removing; + bool fw_patched; + bool can_hibernate; + struct completion init_completion; + struct gpio_desc *reset_gpio; + + u32 rx_mask; + u32 tx_mask; + u8 asp_slot_width; + u8 asp_slot_count; + bool tdm_mode; + bool sysclk_set; + u8 old_sdw_clock_scale; +}; + +extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; + +int cs35l56_runtime_suspend(struct device *dev); +int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56); +irqreturn_t cs35l56_irq(int irq, void *data); +int cs35l56_irq_request(struct cs35l56_private *cs35l56); +int cs35l56_common_probe(struct cs35l56_private *cs35l56); +int cs35l56_init(struct cs35l56_private *cs35l56); +int cs35l56_remove(struct cs35l56_private *cs35l56); + +#endif /* ifndef CS35L56_H */ -- cgit v1.2.3 From cb3cdef33136baceada86ba2a21ba30cd53a9087 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 21 Mar 2023 11:26:53 +0200 Subject: ASoC: SOF: ipc4: Add macros for chain-dma message bits In the chained DMA mode, the firmware allocates buffers for the host and link DMA, and takes care of copying data between host- and link-DMA buffers in a low-latency thread. This is different to a regular pipeline, no processing is allowed, and the connection between host- and link DMA is handled with a dedicated IPC. This patch exposes the macros needed to create the required IPC messages. Signed-off-by: Jyri Sarha Reviewed-by: Rander Wang Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20230321092654.7292-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'include') diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 49ff1558a171..78568abe2673 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -196,6 +196,35 @@ enum sof_ipc4_pipeline_state { #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT) +/* chain dma ipc message */ +#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT 0 +#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK GENMASK(4, 0) +#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(x) (((x) << SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT) & \ + SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK) + +#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT 8 +#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK GENMASK(12, 8) +#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(x) (((x) << SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT) & \ + SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK) + +#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT 16 +#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK BIT(16) +#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT) + +#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT 17 +#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK BIT(17) +#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT) + +#define SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT 18 +#define SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK BIT(18) +#define SOF_IPC4_GLB_CHAIN_DMA_SCS(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT) + +#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT 0 +#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK GENMASK(24, 0) +#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(x) (((x) << \ + SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT) & \ + SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK) + enum sof_ipc4_channel_config { /* one channel only. */ SOF_IPC4_CHANNEL_CONFIG_MONO, -- cgit v1.2.3 From ca5ce0caa67fa9eeecaa29d895c2e4c3151c159e Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 21 Mar 2023 11:26:54 +0200 Subject: ASoC: SOF: ipc4/intel: Add support for chained DMA Add logic for setting up and tearing down chained DMA connections. Since pipelines are not used, all the logic to set the pipeline states can be bypassed, with only the DMA programming sequences remaining. In addition the same format needs to be used for host- and link-DMA, without the usual fixup to use the S32_LE format on the link. Note however that for convenience and compatibility with existing definitions, the topology relies on the concept of pipelines with a 'USE_CHAIN_DMA' token indicating that all the logic shall be bypassed. Unlike 'normal' ALSA sequences, the chain DMA is not programmed in hw_params/hw_free. The IPC message to set-up and tear-down chained DMA are sent in sof_ipc4_trigger_pipelines(), but the contents prepared earlier. Chained DMA is only supported by the Intel HDA DAI for now, and only S16_LE and S32_LE formats are supported for now. Signed-off-by: Jyri Sarha Reviewed-by: Rander Wang Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20230321092654.7292-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 1 + sound/soc/sof/intel/hda-dai-ops.c | 18 +++++- sound/soc/sof/ipc4-pcm.c | 122 ++++++++++++++++++++++++++++++++++++-- sound/soc/sof/ipc4-topology.c | 120 ++++++++++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.h | 2 + 5 files changed, 255 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index bd02842124f9..bbc37877aaff 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -54,6 +54,7 @@ #define SOF_TKN_SCHED_DYNAMIC_PIPELINE 206 #define SOF_TKN_SCHED_LP_MODE 207 #define SOF_TKN_SCHED_MEM_USAGE 208 +#define SOF_TKN_SCHED_USE_CHAIN_DMA 209 /* volume */ #define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250 diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index be109f33715f..de48f13259f1 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -277,6 +277,15 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .post_trigger = hda_ipc4_post_trigger }; +static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { + .get_hext_stream = hda_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .trigger = hda_trigger, +}; + static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -331,8 +340,15 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg { struct sof_ipc4_copier *ipc4_copier = sdai->private; - if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) + if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (pipeline->use_chain_dma) + return &hda_ipc4_chain_dma_ops; + return &hda_ipc4_dma_ops; + } break; } default: diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 4598057b7f28..db64200ba1e5 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -193,6 +193,88 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, * prepare ioctl before the START trigger. */ +/* + * Chained DMA is a special case where there is no processing on + * DSP. The samples are just moved over by host side DMA to a single + * buffer on DSP and directly from there to link DMA. However, the + * model on SOF driver has two notional pipelines, one at host DAI, + * and another at link DAI. They both shall have the use_chain_dma + * attribute. + */ + +static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, + struct snd_sof_pcm_stream_pipeline_list *pipeline_list, + int state, int cmd) +{ + bool allocate, enable, set_fifo_size; + struct sof_ipc4_msg msg = {{ 0 }}; + int i; + + switch (state) { + case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */ + allocate = true; + enable = true; + /* + * SOF assumes creation of a new stream from the presence of fifo_size + * in the message, so we must leave it out in pause release case. + */ + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) + set_fifo_size = false; + else + set_fifo_size = true; + break; + case SOF_IPC4_PIPE_PAUSED: /* Disable chained DMA. */ + allocate = true; + enable = false; + set_fifo_size = false; + break; + case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */ + allocate = false; + enable = false; + set_fifo_size = false; + break; + default: + dev_err(sdev->dev, "Unexpected state %d", state); + return -EINVAL; + } + + msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CHAIN_DMA); + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); + + /* + * To set-up the DMA chain, the host DMA ID and SCS setting + * are retrieved from the host pipeline configuration. Likewise + * the link DMA ID and fifo_size are retrieved from the link + * pipeline configuration. + */ + for (i = 0; i < pipeline_list->count; i++) { + struct snd_sof_pipeline *spipe = pipeline_list->pipelines[i]; + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (!pipeline->use_chain_dma) { + dev_err(sdev->dev, + "All pipelines in chained DMA stream should have use_chain_dma attribute set."); + return -EINVAL; + } + + msg.primary |= pipeline->msg.primary; + + /* Add fifo_size (actually DMA buffer size) field to the message */ + if (set_fifo_size) + msg.extension |= pipeline->msg.extension; + } + + if (allocate) + msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK; + + if (enable) + msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK; + + return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); +} + static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, struct snd_pcm_substream *substream, int state, int cmd) { @@ -201,6 +283,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct ipc4_pipeline_set_state_data *trigger_list; + struct snd_sof_widget *pipe_widget; + struct sof_ipc4_pipeline *pipeline; struct snd_sof_pipeline *spipe; struct snd_sof_pcm *spcm; int ret; @@ -218,6 +302,17 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, if (!pipeline_list->pipelines || !pipeline_list->count) return 0; + spipe = pipeline_list->pipelines[0]; + pipe_widget = spipe->pipe_widget; + pipeline = pipe_widget->private; + + /* + * If use_chain_dma attribute is set we proceed to chained DMA + * trigger function that handles the rest for the substream. + */ + if (pipeline->use_chain_dma) + return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd); + /* allocate memory for the pipeline data */ trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count), GFP_KERNEL); @@ -422,8 +517,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, rtd->dai_link->name); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct sof_ipc4_copier *ipc4_copier; - int ret; + bool use_chain_dma = false; + int dir; if (!dai) { dev_err(component->dev, "%s: No DAI found with name %s\n", __func__, @@ -438,9 +535,26 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, return -EINVAL; } - ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier); - if (ret) - return ret; + for_each_pcm_streams(dir) { + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, dir); + + if (w) { + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (pipeline->use_chain_dma) + use_chain_dma = true; + } + } + + /* Chain DMA does not use copiers, so no fixup needed */ + if (!use_chain_dma) { + int ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier); + + if (ret) + return ret; + } switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_SSP: diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 3a4a3267017b..f1e1aed94da4 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -19,6 +19,7 @@ #define SOF_IPC4_GAIN_PARAM_ID 0 #define SOF_IPC4_TPLG_ABI_SIZE 6 +#define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2 static DEFINE_IDA(alh_group_ida); static DEFINE_IDA(pipeline_ida); @@ -26,6 +27,8 @@ static DEFINE_IDA(pipeline_ida); static const struct sof_topology_token ipc4_sched_tokens[] = { {SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc4_pipeline, lp_mode)}, + {SOF_TKN_SCHED_USE_CHAIN_DMA, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct sof_ipc4_pipeline, use_chain_dma)}, {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc4_pipeline, core_id)}, }; @@ -475,6 +478,8 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier; + struct snd_sof_widget *pipe_widget; + struct sof_ipc4_pipeline *pipeline; int node_type = 0; int ret; @@ -512,6 +517,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); + pipe_widget = swidget->spipe->pipe_widget; + pipeline = pipe_widget->private; + if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) { + dev_err(scomp->dev, + "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n", + ipc4_copier->dai_type, SOF_DAI_INTEL_HDA); + ret = -ENODEV; + goto free_available_fmt; + } + switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_ALH: { @@ -643,6 +658,12 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) swidget->core = pipeline->core_id; + if (pipeline->use_chain_dma) { + dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name); + swidget->private = pipeline; + return 0; + } + /* parse one set of pipeline tokens */ ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(*swidget), 1); @@ -1103,11 +1124,21 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) pipeline->mem_usage = 0; if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { + if (pipeline->use_chain_dma) { + pipeline->msg.primary = 0; + pipeline->msg.extension = 0; + } ipc4_copier = swidget->private; } else if (WIDGET_IS_DAI(swidget->id)) { struct snd_sof_dai *dai = swidget->private; ipc4_copier = dai->private; + + if (pipeline->use_chain_dma) { + pipeline->msg.primary = 0; + pipeline->msg.extension = 0; + } + if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; struct sof_ipc4_alh_configuration_blob *blob; @@ -1344,13 +1375,44 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return ret; } - pipe_widget = swidget->spipe->pipe_widget; - pipeline = pipe_widget->private; ipc4_copier = (struct sof_ipc4_copier *)swidget->private; gtw_attr = ipc4_copier->gtw_attr; copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; + pipe_widget = swidget->spipe->pipe_widget; + pipeline = pipe_widget->private; + + if (pipeline->use_chain_dma) { + u32 host_dma_id; + u32 fifo_size; + + host_dma_id = platform_params->stream_tag - 1; + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); + + /* Set SCS bit for S16_LE format only */ + if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE) + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK; + + /* + * Despite its name the bitfield 'fifo_size' is used to define DMA buffer + * size. The expression calculates 2ms buffer size. + */ + fifo_size = DIV_ROUND_UP((SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS * + params_rate(fe_params) * + params_channels(fe_params) * + params_physical_width(fe_params)), 8000); + pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size); + + /* + * Chain DMA does not support stream timestamping, set node_id to invalid + * to skip the code in sof_ipc4_get_stream_start_offset(). + */ + copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID; + + return 0; + } + /* * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts * for capture. @@ -1375,6 +1437,12 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (pipeline->use_chain_dma) + return 0; + dai = swidget->private; ipc4_copier = (struct sof_ipc4_copier *)dai->private; @@ -1921,6 +1989,9 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget case snd_soc_dapm_scheduler: pipeline = swidget->private; + if (pipeline->use_chain_dma) + return 0; + dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, pipeline->mem_usage); @@ -1943,6 +2014,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget { struct sof_ipc4_copier *ipc4_copier = swidget->private; + pipeline = pipe_widget->private; + if (pipeline->use_chain_dma) + return 0; + ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data; @@ -1955,6 +2030,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier = dai->private; + pipeline = pipe_widget->private; + if (pipeline->use_chain_dma) + return 0; + ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data; @@ -2066,6 +2145,9 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc4_msg msg = {{ 0 }}; u32 header; + if (pipeline->use_chain_dma) + return 0; + header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id); header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE); header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); @@ -2082,7 +2164,11 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; ida_free(&pipeline_ida, swidget->instance_id); } else { - ida_free(&fw_module->m_ida, swidget->instance_id); + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (!pipeline->use_chain_dma) + ida_free(&fw_module->m_ida, swidget->instance_id); } mutex_unlock(&ipc4_data->pipeline_state_mutex); @@ -2234,12 +2320,27 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * { struct snd_sof_widget *src_widget = sroute->src_widget; struct snd_sof_widget *sink_widget = sroute->sink_widget; + struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; + struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget; struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; + struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private; + struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; struct sof_ipc4_msg msg = {{ 0 }}; u32 header, extension; int ret; + /* no route set up if chain DMA is used */ + if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) { + if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) { + dev_err(sdev->dev, + "use_chain_dma must be set for both src %s and sink %s pipelines\n", + src_widget->widget->name, sink_widget->widget->name); + return -EINVAL; + } + return 0; + } + sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_OUTPUT); if (sroute->src_queue_id < 0) { @@ -2310,9 +2411,17 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; struct sof_ipc4_msg msg = {{ 0 }}; + struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; + struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget; + struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private; + struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; u32 header, extension; int ret = 0; + /* no route is set up if chain DMA is used */ + if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) + return 0; + dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); @@ -2374,6 +2483,11 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_HDA: + if (pipeline->use_chain_dma) { + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); + break; + } gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; pipeline->skip_during_fe_trigger = true; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 015027b23588..cf007282867b 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -126,6 +126,7 @@ struct sof_ipc4_copier_config_set_sink_format { * @mem_usage: Memory usage * @core_id: Target core for the pipeline * @state: Pipeline state + * @use_chain_dma: flag to indicate if the firmware shall use chained DMA * @msg: message structure for pipeline * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger */ @@ -135,6 +136,7 @@ struct sof_ipc4_pipeline { uint32_t mem_usage; uint32_t core_id; int state; + bool use_chain_dma; struct sof_ipc4_msg msg; bool skip_during_fe_trigger; }; -- cgit v1.2.3 From fa8c052b4c614aa1d2d60e5c9f40e9d885bf9511 Mon Sep 17 00:00:00 2001 From: "Vlad.Karpovich" Date: Wed, 15 Mar 2023 10:47:18 -0500 Subject: ASoC: cs35l45: Support for GPIO pins configuration. Adds device tree configuration for cs35l45 GPIOs Signed-off-by: Vlad Karpovich Link: https://lore.kernel.org/r/20230315154722.3911463-1-vkarpovi@opensource.cirrus.com Signed-off-by: Mark Brown --- include/dt-bindings/sound/cs35l45.h | 57 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l45-tables.c | 14 +++++++++ sound/soc/codecs/cs35l45.c | 56 ++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l45.h | 27 ++++++++++++++++-- 4 files changed, 152 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/dt-bindings/sound/cs35l45.h b/include/dt-bindings/sound/cs35l45.h index 076da4b2c28d..25386af18445 100644 --- a/include/dt-bindings/sound/cs35l45.h +++ b/include/dt-bindings/sound/cs35l45.h @@ -17,4 +17,61 @@ #define CS35L45_ASP_TX_HIZ_UNUSED 0x1 #define CS35L45_ASP_TX_HIZ_DISABLED 0x2 +/* + * Optional GPIOX Sub-nodes: + * The cs35l45 node can have up to three "cirrus,gpio-ctrlX" ('X' = [1,2,3]) + * sub-nodes for configuring the GPIO pins. + * + * - gpio-dir : GPIO pin direction. Valid only when 'gpio-ctrl' + * is 1. + * 0 = Output + * 1 = Input (Default) + * + * - gpio-lvl : GPIO level. Valid only when 'gpio-ctrl' is 1 and 'gpio-dir' is 0. + * + * 0 = Low (Default) + * 1 = High + * + * - gpio-op-cfg : GPIO output configuration. Valid only when 'gpio-ctrl' is 1 + * and 'gpio-dir' is 0. + * + * 0 = CMOS (Default) + * 1 = Open Drain + * + * - gpio-pol : GPIO output polarity select. Valid only when 'gpio-ctrl' is 1 + * and 'gpio-dir' is 0. + * + * 0 = Non-inverted, Active High (Default) + * 1 = Inverted, Active Low + * + * - gpio-invert : Defines the polarity of the GPIO pin if configured + * as input. + * + * 0 = Not inverted (Default) + * 1 = Inverted + * + * - gpio-ctrl : Defines the function of the GPIO pin. + * + * GPIO1: + * 0 = High impedance input (Default) + * 1 = Pin acts as a GPIO, direction controlled by 'gpio-dir' + * 2 = Pin acts as MDSYNC, direction controlled by MDSYNC + * 3-7 = Reserved + * + * GPIO2: + * 0 = High impedance input (Default) + * 1 = Pin acts as a GPIO, direction controlled by 'gpio-dir' + * 2 = Pin acts as open drain INT + * 3 = Reserved + * 4 = Pin acts as push-pull output INT. Active low. + * 5 = Pin acts as push-pull output INT. Active high. + * 6,7 = Reserved + * + * GPIO3: + * 0 = High impedance input (Default) + * 1 = Pin acts as a GPIO, direction controlled by 'gpio-dir' + * 2-7 = Reserved + */ +#define CS35L45_NUM_GPIOS 0x3 + #endif /* DT_CS35L45_H */ diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c index 4b1320a2e6e9..997ea418a6dc 100644 --- a/sound/soc/codecs/cs35l45-tables.c +++ b/sound/soc/codecs/cs35l45-tables.c @@ -43,6 +43,9 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45); static const struct reg_default cs35l45_defaults[] = { { CS35L45_BLOCK_ENABLES, 0x00003323 }, { CS35L45_BLOCK_ENABLES2, 0x00000010 }, + { CS35L45_SYNC_GPIO1, 0x00000007 }, + { CS35L45_INTB_GPIO2_MCLK_REF, 0x00000005 }, + { CS35L45_GPIO3, 0x00000005 }, { CS35L45_REFCLK_INPUT, 0x00000510 }, { CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 }, { CS35L45_ASP_ENABLES1, 0x00000000 }, @@ -61,6 +64,9 @@ static const struct reg_default cs35l45_defaults[] = { { CS35L45_ASPTX4_INPUT, 0x00000028 }, { CS35L45_ASPTX5_INPUT, 0x00000048 }, { CS35L45_AMP_PCM_CONTROL, 0x00100000 }, + { CS35L45_GPIO1_CTRL1, 0x81000001 }, + { CS35L45_GPIO2_CTRL1, 0x81000001 }, + { CS35L45_GPIO3_CTRL1, 0x81000001 }, }; static bool cs35l45_readable_reg(struct device *dev, unsigned int reg) @@ -72,6 +78,9 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg) case CS35L45_BLOCK_ENABLES: case CS35L45_BLOCK_ENABLES2: case CS35L45_ERROR_RELEASE: + case CS35L45_SYNC_GPIO1: + case CS35L45_INTB_GPIO2_MCLK_REF: + case CS35L45_GPIO3: case CS35L45_REFCLK_INPUT: case CS35L45_GLOBAL_SAMPLE_RATE: case CS35L45_ASP_ENABLES1: @@ -92,6 +101,10 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg) case CS35L45_AMP_PCM_CONTROL: case CS35L45_AMP_PCM_HPF_TST: case CS35L45_IRQ1_EINT_4: + case CS35L45_GPIO_STATUS1: + case CS35L45_GPIO1_CTRL1: + case CS35L45_GPIO2_CTRL1: + case CS35L45_GPIO3_CTRL1: return true; default: return false; @@ -107,6 +120,7 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg) case CS35L45_ERROR_RELEASE: case CS35L45_AMP_PCM_HPF_TST: /* not cachable */ case CS35L45_IRQ1_EINT_4: + case CS35L45_GPIO_STATUS1: return true; default: return false; diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index 855d9f13e6ff..c87dccb3382e 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -536,7 +536,63 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev) static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45) { + struct device_node *node = cs35l45->dev->of_node; + unsigned int gpio_regs[] = {CS35L45_GPIO1_CTRL1, CS35L45_GPIO2_CTRL1, + CS35L45_GPIO3_CTRL1}; + unsigned int pad_regs[] = {CS35L45_SYNC_GPIO1, + CS35L45_INTB_GPIO2_MCLK_REF, CS35L45_GPIO3}; + struct device_node *child; unsigned int val; + char of_name[32]; + int ret, i; + + if (!node) + return 0; + + for (i = 0; i < CS35L45_NUM_GPIOS; i++) { + sprintf(of_name, "cirrus,gpio-ctrl%d", i + 1); + child = of_get_child_by_name(node, of_name); + if (!child) + continue; + + ret = of_property_read_u32(child, "gpio-dir", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, gpio_regs[i], + CS35L45_GPIO_DIR_MASK, + val << CS35L45_GPIO_DIR_SHIFT); + + ret = of_property_read_u32(child, "gpio-lvl", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, gpio_regs[i], + CS35L45_GPIO_LVL_MASK, + val << CS35L45_GPIO_LVL_SHIFT); + + ret = of_property_read_u32(child, "gpio-op-cfg", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, gpio_regs[i], + CS35L45_GPIO_OP_CFG_MASK, + val << CS35L45_GPIO_OP_CFG_SHIFT); + + ret = of_property_read_u32(child, "gpio-pol", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, gpio_regs[i], + CS35L45_GPIO_POL_MASK, + val << CS35L45_GPIO_POL_SHIFT); + + ret = of_property_read_u32(child, "gpio-ctrl", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, pad_regs[i], + CS35L45_GPIO_CTRL_MASK, + val << CS35L45_GPIO_CTRL_SHIFT); + + ret = of_property_read_u32(child, "gpio-invert", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, pad_regs[i], + CS35L45_GPIO_INVERT_MASK, + val << CS35L45_GPIO_INVERT_SHIFT); + + of_node_put(child); + } if (device_property_read_u32(cs35l45->dev, "cirrus,asp-sdout-hiz-ctrl", &val) == 0) { diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h index 53fe9d2b7b15..f3a54fc57d53 100644 --- a/sound/soc/codecs/cs35l45.h +++ b/sound/soc/codecs/cs35l45.h @@ -14,6 +14,7 @@ #include #include #include +#include #define CS35L45_DEVID 0x00000000 #define CS35L45_REVID 0x00000004 @@ -24,6 +25,9 @@ #define CS35L45_BLOCK_ENABLES 0x00002018 #define CS35L45_BLOCK_ENABLES2 0x0000201C #define CS35L45_ERROR_RELEASE 0x00002034 +#define CS35L45_SYNC_GPIO1 0x00002430 +#define CS35L45_INTB_GPIO2_MCLK_REF 0x00002434 +#define CS35L45_GPIO3 0x00002438 #define CS35L45_REFCLK_INPUT 0x00002C04 #define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C #define CS35L45_BOOST_CCM_CFG 0x00003808 @@ -48,8 +52,11 @@ #define CS35L45_AMP_PCM_CONTROL 0x00007000 #define CS35L45_AMP_PCM_HPF_TST 0x00007004 #define CS35L45_IRQ1_EINT_4 0x0000E01C -#define CS35L45_LASTREG 0x0000E01C - +#define CS35L45_GPIO_STATUS1 0x0000F000 +#define CS35L45_GPIO1_CTRL1 0x0000F008 +#define CS35L45_GPIO2_CTRL1 0x0000F00C +#define CS35L45_GPIO3_CTRL1 0x0000F010 +#define CS35L45_LASTREG 0x0000F010 /* SFT_RESET */ #define CS35L45_SOFT_RESET_TRIGGER 0x5A000000 @@ -165,6 +172,22 @@ #define CS35L45_OTP_BOOT_DONE_STS_MASK BIT(1) #define CS35L45_OTP_BUSY_MASK BIT(0) +/* GPIOX_CTRL1 */ +#define CS35L45_GPIO_DIR_SHIFT 31 +#define CS35L45_GPIO_DIR_MASK BIT(31) +#define CS35L45_GPIO_LVL_SHIFT 15 +#define CS35L45_GPIO_LVL_MASK BIT(15) +#define CS35L45_GPIO_OP_CFG_SHIFT 14 +#define CS35L45_GPIO_OP_CFG_MASK BIT(14) +#define CS35L45_GPIO_POL_SHIFT 12 +#define CS35L45_GPIO_POL_MASK BIT(12) + +/* SYNC_GPIO1, INTB_GPIO2_MCLK_REF, GPIO3 */ +#define CS35L45_GPIO_CTRL_SHIFT 20 +#define CS35L45_GPIO_CTRL_MASK GENMASK(22, 20) +#define CS35L45_GPIO_INVERT_SHIFT 19 +#define CS35L45_GPIO_INVERT_MASK BIT(19) + /* Mixer sources */ #define CS35L45_PCM_SRC_MASK 0x7F #define CS35L45_PCM_SRC_ZERO 0x00 -- cgit v1.2.3 From 0146878cf299174ec39cdd2340c19a528794d881 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 23 Mar 2023 07:52:35 +0100 Subject: ALSA: pcm: Improved XRUN handling for indirect PCM helpers As PCM ack callback may handle the XRUN situation gracefully now, change the indirect PCM helpers to give a proper error (-EPIPE). Also, change the pointer callback helpers to deal with the XRUN error properly, too. This requires the PCM core change by the commit 8c721c53dda5 ("ALSA: usb-audio: Fix recursive locking at XRUN during syncing"). Link: https://lore.kernel.org/r/20230323065237.5062-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- include/sound/pcm-indirect.h | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/pcm-indirect.h b/include/sound/pcm-indirect.h index 04127686e8d0..98e06ea73b2b 100644 --- a/include/sound/pcm-indirect.h +++ b/include/sound/pcm-indirect.h @@ -44,7 +44,7 @@ snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream, if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) diff += runtime->boundary; if (diff < 0) - return -EINVAL; + return -EPIPE; rec->sw_ready += (int)frames_to_bytes(runtime, diff); rec->appl_ptr = appl_ptr; } @@ -83,6 +83,8 @@ snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream, struct snd_pcm_indirect *rec, unsigned int ptr) { int bytes = ptr - rec->hw_io; + int err; + if (bytes < 0) bytes += rec->hw_buffer_size; rec->hw_io = ptr; @@ -90,8 +92,11 @@ snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream, rec->sw_io += bytes; if (rec->sw_io >= rec->sw_buffer_size) rec->sw_io -= rec->sw_buffer_size; - if (substream->ops->ack) - substream->ops->ack(substream); + if (substream->ops->ack) { + err = substream->ops->ack(substream); + if (err == -EPIPE) + return SNDRV_PCM_POS_XRUN; + } return bytes_to_frames(substream->runtime, rec->sw_io); } @@ -112,7 +117,7 @@ snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream, if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) diff += runtime->boundary; if (diff < 0) - return -EINVAL; + return -EPIPE; rec->sw_ready -= frames_to_bytes(runtime, diff); rec->appl_ptr = appl_ptr; } @@ -152,6 +157,8 @@ snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream, { int qsize; int bytes = ptr - rec->hw_io; + int err; + if (bytes < 0) bytes += rec->hw_buffer_size; rec->hw_io = ptr; @@ -162,8 +169,11 @@ snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream, rec->sw_io += bytes; if (rec->sw_io >= rec->sw_buffer_size) rec->sw_io -= rec->sw_buffer_size; - if (substream->ops->ack) - substream->ops->ack(substream); + if (substream->ops->ack) { + err = substream->ops->ack(substream); + if (err == -EPIPE) + return SNDRV_PCM_POS_XRUN; + } return bytes_to_frames(substream->runtime, rec->sw_io); } -- cgit v1.2.3 From ffaf886e249ee269f9084c21fbd8617ce9b83817 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 27 Mar 2023 00:34:26 +0000 Subject: ASoC: soc-core.c: add snd_soc_add_pcm_runtimes() Current ASoC supports snd_soc_add_pcm_runtime(), but user need to call it one-by-one if it has multi dai_links. This patch adds snd_soc_add_pcm_runtimes() which supports multi dai_links. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87h6u76nhq.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 5 +++-- sound/soc/intel/avs/boards/hdaudio.c | 10 ++++------ sound/soc/soc-core.c | 31 +++++++++++++++++++++---------- sound/soc/soc-topology.c | 2 +- 4 files changed, 29 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index e58b43b5da7c..57c5786a625b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1302,8 +1302,9 @@ int snd_soc_of_get_dai_link_cpus(struct device *dev, struct snd_soc_dai_link *dai_link); void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link); -int snd_soc_add_pcm_runtime(struct snd_soc_card *card, - struct snd_soc_dai_link *dai_link); +int snd_soc_add_pcm_runtimes(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link, + int num_dai_link); void snd_soc_remove_pcm_runtime(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd); diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c index e68c4c7aa2ba..a542a67e21d0 100644 --- a/sound/soc/intel/avs/boards/hdaudio.c +++ b/sound/soc/intel/avs/boards/hdaudio.c @@ -194,12 +194,10 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm) return ret; } - for (n = 0; n < pcm_count; n++) { - ret = snd_soc_add_pcm_runtime(card, &links[n]); - if (ret < 0) { - dev_err(card->dev, "add links failed: %d\n", ret); - return ret; - } + ret = snd_soc_add_pcm_runtimes(card, links, pcm_count); + if (ret < 0) { + dev_err(card->dev, "add links failed: %d\n", ret); + return ret; } ret = avs_create_dapm_routes(card->dev, codec, pcm_count, &routes, &n); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2faa0d8d0d8e..9bbcff492c1e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -959,8 +959,8 @@ EXPORT_SYMBOL_GPL(snd_soc_remove_pcm_runtime); * topology component. And machine drivers can still define static * DAI links in dai_link array. */ -int snd_soc_add_pcm_runtime(struct snd_soc_card *card, - struct snd_soc_dai_link *dai_link) +static int snd_soc_add_pcm_runtime(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai_link_component *codec, *platform, *cpu; @@ -1027,7 +1027,21 @@ _err_defer: snd_soc_remove_pcm_runtime(card, rtd); return -EPROBE_DEFER; } -EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtime); + +int snd_soc_add_pcm_runtimes(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link, + int num_dai_link) +{ + for (int i = 0; i < num_dai_link; i++) { + int ret = snd_soc_add_pcm_runtime(card, dai_link + i); + + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtimes); static void snd_soc_runtime_get_dai_fmt(struct snd_soc_pcm_runtime *rtd) { @@ -1921,8 +1935,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; struct snd_soc_component *component; - struct snd_soc_dai_link *dai_link; - int ret, i; + int ret; mutex_lock(&client_mutex); mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); @@ -1939,11 +1952,9 @@ static int snd_soc_bind_card(struct snd_soc_card *card) /* add predefined DAI links to the list */ card->num_rtd = 0; - for_each_card_prelinks(card, i, dai_link) { - ret = snd_soc_add_pcm_runtime(card, dai_link); - if (ret < 0) - goto probe_end; - } + ret = snd_soc_add_pcm_runtimes(card, card->dai_link, card->num_links); + if (ret < 0) + goto probe_end; /* card bind complete so register a sound card */ ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 7f6424fa59ab..be9849749713 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1741,7 +1741,7 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg, goto err; } - ret = snd_soc_add_pcm_runtime(tplg->comp->card, link); + ret = snd_soc_add_pcm_runtimes(tplg->comp->card, link, 1); if (ret < 0) { dev_err(tplg->dev, "ASoC: adding FE link failed\n"); goto err; -- cgit v1.2.3 From 665d30119af9ca557fc8811ea1c0e8609c165c8a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 Mar 2023 15:28:47 +0200 Subject: ALSA: ac97: Define dummy functions for snd_ac97_suspend() and resume() For allowing the build without CONFIG_PM, add definitions of dummy functions for snd_ac97_suspend() and snd_ac97_resume() without CONFIG_PM, too. Link: https://lore.kernel.org/r/20230330132847.12882-1-tiwai@suse.de Reviewed-by: Jaroslav Kysela Reviewed-by: Tasos Sahanidis Signed-off-by: Takashi Iwai --- include/sound/ac97_codec.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 49200ec26dc4..c495c6d5fbe0 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -335,6 +335,9 @@ static inline int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, #ifdef CONFIG_PM void snd_ac97_suspend(struct snd_ac97 *ac97); void snd_ac97_resume(struct snd_ac97 *ac97); +#else +static inline void snd_ac97_suspend(struct snd_ac97 *ac97) {} +static inline void snd_ac97_resume(struct snd_ac97 *ac97) {} #endif int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id, unsigned int id_mask); -- cgit v1.2.3 From 59611370f92923089ceb284072d01445164a0191 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 4 Apr 2023 12:21:05 +0300 Subject: ASoC: SOF: Add flag and state which will be used for DSP-less mode The DSPless mode of the ASoC/SOF driver can be used for hardware verification and debug on platforms with HDaudio codecs. The DSP mode is still needed on existing platforms for SSP, DMIC, SoundWire interfaces managed by the GP-DMA. This mode is also helpful to compare the legacy HDaudio driver with the ASoC/SOF driver wrt. codec management and handling. In theory we use the same code but differences are sometimes seen on jack detection and event handling. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Rander Wang Link: https://lore.kernel.org/r/20230404092115.27949-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof.h | 5 +++++ sound/soc/sof/core.c | 9 +++++++++ sound/soc/sof/sof-priv.h | 11 +++++++++++ 3 files changed, 25 insertions(+) (limited to 'include') diff --git a/include/sound/sof.h b/include/sound/sof.h index 266e66318f9c..d3c41f87ac31 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -21,6 +21,7 @@ struct snd_sof_dev; /** * enum sof_fw_state - DSP firmware state definitions * @SOF_FW_BOOT_NOT_STARTED: firmware boot is not yet started + * @SOF_DSPLESS_MODE: DSP is not used * @SOF_FW_BOOT_PREPARE: preparing for boot (firmware loading for exaqmple) * @SOF_FW_BOOT_IN_PROGRESS: firmware boot is in progress * @SOF_FW_BOOT_FAILED: firmware boot failed @@ -31,6 +32,7 @@ struct snd_sof_dev; */ enum sof_fw_state { SOF_FW_BOOT_NOT_STARTED = 0, + SOF_DSPLESS_MODE, SOF_FW_BOOT_PREPARE, SOF_FW_BOOT_IN_PROGRESS, SOF_FW_BOOT_FAILED, @@ -130,6 +132,9 @@ struct sof_dev_desc { unsigned int ipc_supported_mask; enum sof_ipc_type ipc_default; + /* The platform supports DSPless mode */ + bool dspless_mode_supported; + /* defaults paths for firmware, library and topology files */ const char *default_fw_path[SOF_IPC_TYPE_COUNT]; const char *default_lib_path[SOF_IPC_TYPE_COUNT]; diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 7de8673a01ce..06bcae631612 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -365,6 +365,15 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) if (sof_core_debug) dev_info(dev, "sof_debug value: %#x\n", sof_core_debug); + if (sof_debug_check_flag(SOF_DBG_DSPLESS_MODE)) { + if (plat_data->desc->dspless_mode_supported) { + dev_info(dev, "Switching to DSPless mode\n"); + sdev->dspless_mode_selected = true; + } else { + dev_info(dev, "DSPless mode is not supported by the platform\n"); + } + } + /* check IPC support */ if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) { dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n", diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 5f919162a555..1170989bea57 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -48,6 +48,7 @@ struct snd_sof_pcm_stream; #define SOF_DBG_FORCE_NOCODEC BIT(10) /* ignore all codec-related * configurations */ +#define SOF_DBG_DSPLESS_MODE BIT(15) /* Do not initialize and use the DSP */ /* Flag definitions used for controlling the DSP dump behavior */ #define SOF_DBG_DUMP_REGS BIT(0) @@ -528,6 +529,16 @@ struct snd_sof_dev { spinlock_t ipc_lock; /* lock for IPC users */ spinlock_t hw_lock; /* lock for HW IO access */ + /* + * When true the DSP is not used. + * It is set under the following condition: + * User sets the SOF_DBG_DSPLESS_MODE flag in sof_debug module parameter + * and + * the platform advertises that it can support such mode + * pdata->desc->dspless_mode_supported is true. + */ + bool dspless_mode_selected; + /* Main, Base firmware image */ struct sof_firmware basefw; -- cgit v1.2.3 From 7ddc7f91beb285246e926e3adf0b292b071aea33 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 2 Apr 2023 22:59:35 +0000 Subject: ASoC: soc.h: clarify Codec2Codec params snd_soc_dai_link has params/num_params, but it is unclear that params for what. This patch clarify it is params for Codec2Codec. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87o7o5c2lk.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 12 +++++++++--- sound/soc/soc-core.c | 11 +++++++++++ sound/soc/soc-dapm.c | 44 ++++++++++++++++++++++---------------------- sound/soc/soc-pcm.c | 10 +++++----- 4 files changed, 47 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 57c5786a625b..276afdb1f445 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -684,8 +684,14 @@ struct snd_soc_dai_link { int id; /* optional ID for machine driver link identification */ - const struct snd_soc_pcm_stream *params; - unsigned int num_params; + /* + * for Codec2Codec + */ + const struct snd_soc_pcm_stream *c2c_params; + unsigned int num_c2c_params; + + const struct snd_soc_pcm_stream *params; /* REMOVE ME */ + unsigned int num_params; /* REMOVE ME */ unsigned int dai_fmt; /* format to set on init */ @@ -1065,7 +1071,7 @@ struct snd_soc_pcm_runtime { struct snd_soc_dai_link *dai_link; struct snd_pcm_ops ops; - unsigned int params_select; /* currently selected param for dai link */ + unsigned int c2c_params_select; /* currently selected c2c_param for dai link */ /* Dynamic PCM BE runtime data */ struct snd_soc_dpcm_runtime dpcm[SNDRV_PCM_STREAM_LAST + 1]; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9bbcff492c1e..04f1bc8a3128 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2294,6 +2294,9 @@ EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); */ int snd_soc_register_card(struct snd_soc_card *card) { + struct snd_soc_dai_link *dai_link; + int i; + if (!card->name || !card->dev) return -EINVAL; @@ -2314,6 +2317,14 @@ int snd_soc_register_card(struct snd_soc_card *card) mutex_init(&card->dapm_mutex); mutex_init(&card->pcm_mutex); + /* REMOVE ME */ + for_each_card_prelinks(card, i, dai_link) { + if (!dai_link->c2c_params) { + dai_link->c2c_params = dai_link->params; + dai_link->num_c2c_params = dai_link->num_params; + } + } + return snd_soc_bind_card(card); } EXPORT_SYMBOL_GPL(snd_soc_register_card); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 34fdcb7ee079..e7a0c28e0cb1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1075,7 +1075,7 @@ static int dapm_new_dai_link(struct snd_soc_dapm_widget *w) struct snd_soc_pcm_runtime *rtd = w->priv; /* create control for links with > 1 config */ - if (rtd->dai_link->num_params <= 1) + if (rtd->dai_link->num_c2c_params <= 1) return 0; /* add kcontrol */ @@ -3864,7 +3864,7 @@ snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w, * either party on the link to alter the configuration if * necessary */ - config = rtd->dai_link->params + rtd->params_select; + config = rtd->dai_link->c2c_params + rtd->c2c_params_select; if (!config) { dev_err(w->dapm->dev, "ASoC: link config missing\n"); ret = -EINVAL; @@ -4010,7 +4010,7 @@ static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); struct snd_soc_pcm_runtime *rtd = w->priv; - ucontrol->value.enumerated.item[0] = rtd->params_select; + ucontrol->value.enumerated.item[0] = rtd->c2c_params_select; return 0; } @@ -4025,13 +4025,13 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, if (w->power) return -EBUSY; - if (ucontrol->value.enumerated.item[0] == rtd->params_select) + if (ucontrol->value.enumerated.item[0] == rtd->c2c_params_select) return 0; - if (ucontrol->value.enumerated.item[0] >= rtd->dai_link->num_params) + if (ucontrol->value.enumerated.item[0] >= rtd->dai_link->num_c2c_params) return -EINVAL; - rtd->params_select = ucontrol->value.enumerated.item[0]; + rtd->c2c_params_select = ucontrol->value.enumerated.item[0]; return 1; } @@ -4039,7 +4039,7 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, static void snd_soc_dapm_free_kcontrol(struct snd_soc_card *card, unsigned long *private_value, - int num_params, + int num_c2c_params, const char **w_param_text) { int count; @@ -4049,7 +4049,7 @@ snd_soc_dapm_free_kcontrol(struct snd_soc_card *card, if (!w_param_text) return; - for (count = 0 ; count < num_params; count++) + for (count = 0 ; count < num_c2c_params; count++) devm_kfree(card->dev, (void *)w_param_text[count]); devm_kfree(card->dev, w_param_text); } @@ -4057,8 +4057,8 @@ snd_soc_dapm_free_kcontrol(struct snd_soc_card *card, static struct snd_kcontrol_new * snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, char *link_name, - const struct snd_soc_pcm_stream *params, - int num_params, const char **w_param_text, + const struct snd_soc_pcm_stream *c2c_params, + int num_c2c_params, const char **w_param_text, unsigned long *private_value) { struct soc_enum w_param_enum[] = { @@ -4070,10 +4070,10 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, snd_soc_dapm_dai_link_put), }; struct snd_kcontrol_new *kcontrol_news; - const struct snd_soc_pcm_stream *config = params; + const struct snd_soc_pcm_stream *config = c2c_params; int count; - for (count = 0 ; count < num_params; count++) { + for (count = 0 ; count < num_c2c_params; count++) { if (!config->stream_name) { dev_warn(card->dapm.dev, "ASoC: anonymous config %d for dai link %s\n", @@ -4093,7 +4093,7 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, config++; } - w_param_enum[0].items = num_params; + w_param_enum[0].items = num_c2c_params; w_param_enum[0].texts = w_param_text; *private_value = @@ -4118,7 +4118,7 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, return kcontrol_news; outfree_w_param: - snd_soc_dapm_free_kcontrol(card, private_value, num_params, w_param_text); + snd_soc_dapm_free_kcontrol(card, private_value, num_c2c_params, w_param_text); return NULL; } @@ -4146,17 +4146,17 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, w_param_text = NULL; kcontrol_news = NULL; num_kcontrols = 0; - if (rtd->dai_link->num_params > 1) { + if (rtd->dai_link->num_c2c_params > 1) { w_param_text = devm_kcalloc(card->dev, - rtd->dai_link->num_params, + rtd->dai_link->num_c2c_params, sizeof(char *), GFP_KERNEL); if (!w_param_text) goto param_fail; num_kcontrols = 1; kcontrol_news = snd_soc_dapm_alloc_kcontrol(card, link_name, - rtd->dai_link->params, - rtd->dai_link->num_params, + rtd->dai_link->c2c_params, + rtd->dai_link->num_c2c_params, w_param_text, &private_value); if (!kcontrol_news) goto param_fail; @@ -4187,7 +4187,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, outfree_kcontrol_news: devm_kfree(card->dev, (void *)template.kcontrol_news); snd_soc_dapm_free_kcontrol(card, &private_value, - rtd->dai_link->num_params, w_param_text); + rtd->dai_link->num_c2c_params, w_param_text); param_fail: devm_kfree(card->dev, link_name); name_fail: @@ -4336,7 +4336,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, struct snd_pcm_str *streams = rtd->pcm->streams; int stream; - if (dai_link->params) { + if (dai_link->c2c_params) { playback_cpu = snd_soc_dai_get_widget_capture(cpu_dai); capture_cpu = snd_soc_dai_get_widget_playback(cpu_dai); } else { @@ -4349,7 +4349,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, codec = snd_soc_dai_get_widget(codec_dai, stream); if (playback_cpu && codec) { - if (dai_link->params && !rtd->c2c_widget[stream]) { + if (dai_link->c2c_params && !rtd->c2c_widget[stream]) { substream = streams[stream].substream; dai = snd_soc_dapm_new_dai(card, substream, "playback"); if (IS_ERR(dai)) @@ -4368,7 +4368,7 @@ capture: codec = snd_soc_dai_get_widget(codec_dai, stream); if (codec && capture_cpu) { - if (dai_link->params && !rtd->c2c_widget[stream]) { + if (dai_link->c2c_params && !rtd->c2c_widget[stream]) { substream = streams[stream].substream; dai = snd_soc_dapm_new_dai(card, substream, "capture"); if (IS_ERR(dai)) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b830a53ceacb..913a7d98e742 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2793,9 +2793,9 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *codec_dai; /* Adapt stream for codec2codec links */ - int cpu_capture = rtd->dai_link->params ? + int cpu_capture = rtd->dai_link->c2c_params ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; - int cpu_playback = rtd->dai_link->params ? + int cpu_playback = rtd->dai_link->c2c_params ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; for_each_rtd_codec_dais(rtd, i, codec_dai) { @@ -2839,7 +2839,7 @@ static int soc_create_pcm(struct snd_pcm **pcm, int ret; /* create the PCM */ - if (rtd->dai_link->params) { + if (rtd->dai_link->c2c_params) { snprintf(new_name, sizeof(new_name), "codec2codec(%s)", rtd->dai_link->stream_name); @@ -2896,7 +2896,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) * don't interface with the outside world or application layer * we don't have to do any special handling on close. */ - if (!rtd->dai_link->params) + if (!rtd->dai_link->c2c_params) rtd->close_delayed_work_func = snd_soc_close_delayed_work; rtd->pcm = pcm; @@ -2904,7 +2904,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) pcm->private_data = rtd; pcm->no_device_suspend = true; - if (rtd->dai_link->no_pcm || rtd->dai_link->params) { + if (rtd->dai_link->no_pcm || rtd->dai_link->c2c_params) { if (playback) pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; if (capture) -- cgit v1.2.3 From 1ea63f29c27712d6b9c45af67cd71299d849c5e3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 2 Apr 2023 23:00:17 +0000 Subject: ASoC: soc.h: remove unused params/num_params No drivers are using params/num_params any more. Let's remove these. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87iledc2ke.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 3 --- sound/soc/soc-core.c | 11 ----------- 2 files changed, 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 276afdb1f445..3833184c187f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -690,9 +690,6 @@ struct snd_soc_dai_link { const struct snd_soc_pcm_stream *c2c_params; unsigned int num_c2c_params; - const struct snd_soc_pcm_stream *params; /* REMOVE ME */ - unsigned int num_params; /* REMOVE ME */ - unsigned int dai_fmt; /* format to set on init */ enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 04f1bc8a3128..9bbcff492c1e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2294,9 +2294,6 @@ EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); */ int snd_soc_register_card(struct snd_soc_card *card) { - struct snd_soc_dai_link *dai_link; - int i; - if (!card->name || !card->dev) return -EINVAL; @@ -2317,14 +2314,6 @@ int snd_soc_register_card(struct snd_soc_card *card) mutex_init(&card->dapm_mutex); mutex_init(&card->pcm_mutex); - /* REMOVE ME */ - for_each_card_prelinks(card, i, dai_link) { - if (!dai_link->c2c_params) { - dai_link->c2c_params = dai_link->params; - dai_link->num_c2c_params = dai_link->num_params; - } - } - return snd_soc_bind_card(card); } EXPORT_SYMBOL_GPL(snd_soc_register_card); -- cgit v1.2.3 From 102882b5c62f6bfe403178bb36adef3ba542d148 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 6 Apr 2023 15:25:21 +0200 Subject: ALSA: document that struct __snd_pcm_mmap_control64 is messed up I'm not the first one to run into this, see e.g. https://lore.kernel.org/all/29QBMJU8DE71E.2YZSH8IHT5HMH@mforney.org/ Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230406132521.2252019-1-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index de6810e94abe..7eecc99ddef7 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -570,7 +570,8 @@ struct __snd_pcm_mmap_status64 { struct __snd_pcm_mmap_control64 { __pad_before_uframe __pad1; snd_pcm_uframes_t appl_ptr; /* RW: appl ptr (0...boundary-1) */ - __pad_before_uframe __pad2; + __pad_before_uframe __pad2; // This should be __pad_after_uframe, but binary + // backwards compatibility constraints prevent a fix. __pad_before_uframe __pad3; snd_pcm_uframes_t avail_min; /* RW: min available frames for wakeup */ -- cgit v1.2.3 From 34e582b559c78746c544442b3925554e0665351b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:11 +0300 Subject: ALSA: hda: add HDaudio Extended link definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new definitions for the HDaudio Extended link support, specifically new registers for SoundWire, Intel DMIC and INTEL SSP interfaces. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda_register.h | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h index d37cf43546eb..9c7872c0ca79 100644 --- a/include/sound/hda_register.h +++ b/include/sound/hda_register.h @@ -258,14 +258,27 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_ML_BASE 0x40 #define AZX_ML_INTERVAL 0x40 +/* HDaudio registers valid for HDaudio and HDaudio extended links */ #define AZX_REG_ML_LCAP 0x00 -#define AZX_REG_ML_LCTL 0x04 +#define AZX_ML_HDA_LCAP_ALT BIT(28) +#define AZX_ML_HDA_LCAP_ALT_HDA 0x0 +#define AZX_ML_HDA_LCAP_ALT_HDA_EXT 0x1 + +#define AZX_ML_HDA_LCAP_INTC BIT(27) /* only used if ALT == 1 */ +#define AZX_ML_HDA_LCAP_OFLS BIT(26) /* only used if ALT == 1 */ +#define AZX_ML_HDA_LCAP_LSS BIT(23) /* only used if ALT == 1 */ +#define AZX_ML_HDA_LCAP_SLCOUNT GENMASK(22, 20) /* only used if ALT == 1 */ + +#define AZX_REG_ML_LCTL 0x04 +#define AZX_ML_LCTL_INTSTS BIT(31) /* only used if ALT == 1 */ #define AZX_ML_LCTL_CPA BIT(23) #define AZX_ML_LCTL_CPA_SHIFT 23 #define AZX_ML_LCTL_SPA BIT(16) #define AZX_ML_LCTL_SPA_SHIFT 16 -#define AZX_ML_LCTL_SCF GENMASK(3, 0) +#define AZX_ML_LCTL_INTEN BIT(5) /* only used if ALT == 1 */ +#define AZX_ML_LCTL_OFLEN BIT(4) /* only used if ALT == 1 */ +#define AZX_ML_LCTL_SCF GENMASK(3, 0) /* only used if ALT == 0 */ #define AZX_REG_ML_LOSIDV 0x08 @@ -273,12 +286,35 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_ML_LOSIDV_STREAM_MASK 0xFFFE #define AZX_REG_ML_LSDIID 0x0C +#define AZX_REG_ML_LSDIID_OFFSET(x) (0x0C + (x) * 0x02) /* only used if ALT == 1 */ + +/* HDaudio registers only valid if LCAP.ALT == 0 */ #define AZX_REG_ML_LPSOO 0x10 #define AZX_REG_ML_LPSIO 0x12 #define AZX_REG_ML_LWALFC 0x18 #define AZX_REG_ML_LOUTPAY 0x20 #define AZX_REG_ML_LINPAY 0x30 +/* HDaudio Extended link registers only valid if LCAP.ALT == 1 */ +#define AZX_REG_ML_LSYNC 0x1C + +#define AZX_REG_ML_LSYNC_CMDSYNC BIT(24) +#define AZX_REG_ML_LSYNC_CMDSYNC_SHIFT 24 +#define AZX_REG_ML_LSYNC_SYNCGO BIT(23) +#define AZX_REG_ML_LSYNC_SYNCPU BIT(20) +#define AZX_REG_ML_LSYNC_SYNCPRD GENMASK(19, 0) + +#define AZX_REG_ML_LEPTR 0x20 + +#define AZX_REG_ML_LEPTR_ID GENMASK(31, 24) +#define AZX_REG_ML_LEPTR_ID_SHIFT 24 +#define AZX_REG_ML_LEPTR_ID_SDW 0x00 +#define AZX_REG_ML_LEPTR_ID_INTEL_SSP 0xC0 +#define AZX_REG_ML_LEPTR_ID_INTEL_DMIC 0xC1 +#define AZX_REG_ML_LEPTR_ID_INTEL_UAOL 0xC2 +#define AZX_REG_ML_LEPTR_VER GENMASK(23, 20) +#define AZX_REG_ML_LEPTR_PTR GENMASK(19, 0) + /* registers for DMA Resume Capability Structure */ #define AZX_DRSM_CAP_ID 0x5 #define AZX_REG_DRSM_CTL 0x4 -- cgit v1.2.3 From 18227585d8374ce16d3a2792deb125794710de43 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:14 +0300 Subject: ASoC: SOF: Intel: hda-mlink: move to a dedicated module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the functions will be used for SoundWire enumeration and power management, to avoid cycles in module dependencies and simplify integration all the HDaudio multi-link needs to move to a dedicated module. Drop no longer needed headers at the same time. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 31 +++++++++++++++++++++++++++++++ sound/soc/sof/intel/Kconfig | 7 +++++++ sound/soc/sof/intel/Makefile | 5 ++++- sound/soc/sof/intel/hda-ctrl.c | 1 + sound/soc/sof/intel/hda-dsp.c | 1 + sound/soc/sof/intel/hda-mlink.c | 24 +++++++++++------------- sound/soc/sof/intel/hda.c | 2 ++ sound/soc/sof/intel/hda.h | 20 -------------------- 8 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 include/sound/hda-mlink.h (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h new file mode 100644 index 000000000000..beef5f509e47 --- /dev/null +++ b/include/sound/hda-mlink.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2022-2023 Intel Corporation. All rights reserved. + */ + +struct hdac_bus; + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) + +int hda_bus_ml_get_capabilities(struct hdac_bus *bus); +void hda_bus_ml_free(struct hdac_bus *bus); +void hda_bus_ml_put_all(struct hdac_bus *bus); +void hda_bus_ml_reset_losidv(struct hdac_bus *bus); +int hda_bus_ml_resume(struct hdac_bus *bus); +int hda_bus_ml_suspend(struct hdac_bus *bus); + +#else + +static inline int +hda_bus_ml_get_capabilities(struct hdac_bus *bus) { return 0; } + +static inline void hda_bus_ml_free(struct hdac_bus *bus) { } +static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } +static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } +static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } +static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } + +#endif /* CONFIG_SND_SOC_SOF_HDA */ diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 715ba8a7f2f8..f4eeacf1f281 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -269,6 +269,13 @@ config SND_SOC_SOF_HDA_COMMON select SND_INTEL_DSP_CONFIG select SND_SOC_SOF_HDA_LINK_BASELINE select SND_SOC_SOF_HDA_PROBES + select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + +config SND_SOC_SOF_HDA_MLINK + tristate help This option is not user-selectable but automagically handled by 'select' statements at a higher level. diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 38ab86b6a9fe..fdb463c12e91 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -5,10 +5,12 @@ snd-sof-acpi-intel-bdw-objs := bdw.o snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ - hda-dai.o hda-dai-ops.o hda-bus.o hda-mlink.o \ + hda-dai.o hda-dai-ops.o hda-bus.o \ skl.o hda-loader-skl.o \ apl.o cnl.o tgl.o icl.o mtl.o hda-common-ops.o +snd-sof-intel-hda-mlink-objs := hda-mlink.o + snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o snd-sof-intel-hda-objs := hda-codec.o @@ -19,6 +21,7 @@ obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-intel-atom.o obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-acpi-intel-byt.o obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-acpi-intel-bdw.o obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o +obj-$(CONFIG_SND_SOC_SOF_HDA_MLINK) += snd-sof-intel-hda-mlink.o obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o snd-sof-pci-intel-tng-objs := pci-tng.o diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index e1dba6b79065..84bf01bd360a 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../ops.h" #include "hda.h" diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 23c05e6f424a..77df536cf901 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "../sof-audio.h" #include "../ops.h" diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index e426d5e41e52..fbf86f2350fb 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -12,21 +12,11 @@ #include #include +#include -#include #include -#include -#include -#include -#include -#include -#include -#include "../sof-audio.h" -#include "../sof-pci-dev.h" -#include "../ops.h" -#include "hda.h" - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) int hda_bus_ml_get_capabilities(struct hdac_bus *bus) { @@ -34,6 +24,7 @@ int hda_bus_ml_get_capabilities(struct hdac_bus *bus) return snd_hdac_ext_bus_get_ml_capabilities(bus); return 0; } +EXPORT_SYMBOL_NS(hda_bus_ml_get_capabilities, SND_SOC_SOF_HDA_MLINK); void hda_bus_ml_free(struct hdac_bus *bus) { @@ -47,6 +38,7 @@ void hda_bus_ml_free(struct hdac_bus *bus) kfree(hlink); } } +EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK); void hda_bus_ml_put_all(struct hdac_bus *bus) { @@ -55,6 +47,7 @@ void hda_bus_ml_put_all(struct hdac_bus *bus) list_for_each_entry(hlink, &bus->hlink_list, list) snd_hdac_ext_bus_link_put(bus, hlink); } +EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK); void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { @@ -64,6 +57,7 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus) list_for_each_entry(hlink, &bus->hlink_list, list) writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); } +EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK); int hda_bus_ml_resume(struct hdac_bus *bus) { @@ -80,10 +74,14 @@ int hda_bus_ml_resume(struct hdac_bus *bus) } return 0; } +EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK); int hda_bus_ml_suspend(struct hdac_bus *bus) { return snd_hdac_ext_bus_link_power_down_all(bus); } +EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK); #endif + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 483ec80f3160..8845ae255f1e 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "../sof-audio.h" #include "../sof-pci-dev.h" #include "../ops.h" @@ -1730,3 +1731,4 @@ MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI); MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT); MODULE_IMPORT_NS(SOUNDWIRE_INTEL); +MODULE_IMPORT_NS(SND_SOC_SOF_HDA_MLINK); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 259b34eea677..0e0cfa81a8f2 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -763,26 +763,6 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; } #endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - -int hda_bus_ml_get_capabilities(struct hdac_bus *bus); -void hda_bus_ml_free(struct hdac_bus *bus); -void hda_bus_ml_put_all(struct hdac_bus *bus); -void hda_bus_ml_reset_losidv(struct hdac_bus *bus); -int hda_bus_ml_resume(struct hdac_bus *bus); -int hda_bus_ml_suspend(struct hdac_bus *bus); - -#else - -static inline int hda_bus_ml_get_capabilities(struct hdac_bus *bus) { return 0; } -static inline void hda_bus_ml_free(struct hdac_bus *bus) { } -static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } -static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } -static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } -static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } - -#endif /* CONFIG_SND_SOC_SOF_HDA */ - /* * Trace Control. */ -- cgit v1.2.3 From 17c9b6ec35c0d6228db4f94590702a0cb03cd96a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:15 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add structures to parse ALT links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend hdac_ext_link to store information needed for ALT links. Follow-up patches will include more functional patches for power-up and down. Note that this patch suggests the use of an 'eml_lock' to serialize access to shared registers. SoundWire-specific sequence require the lock to be taken at a higher level, as a result the helpers added in follow-up patches will provide 'unlocked' versions when needed. Also note that the low-level sequences with the 'hdaml_' prefix are taken directly from the hardware specifications - naming conventions included. The code will be split in two, with locking and linked-list management handled separately to avoid mixing required hardware setup and Linux-based resource management. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 4 +- sound/soc/sof/intel/hda-mlink.c | 221 +++++++++++++++++++++++++++++++++++++++- sound/soc/sof/intel/hda.c | 2 +- 3 files changed, 219 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index beef5f509e47..8048bf01c133 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -10,7 +10,7 @@ struct hdac_bus; #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) -int hda_bus_ml_get_capabilities(struct hdac_bus *bus); +int hda_bus_ml_init(struct hdac_bus *bus); void hda_bus_ml_free(struct hdac_bus *bus); void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); @@ -20,7 +20,7 @@ int hda_bus_ml_suspend(struct hdac_bus *bus); #else static inline int -hda_bus_ml_get_capabilities(struct hdac_bus *bus) { return 0; } +hda_bus_ml_init(struct hdac_bus *bus) { return 0; } static inline void hda_bus_ml_free(struct hdac_bus *bus) { } static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index fbf86f2350fb..e6b20182f099 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -14,28 +14,239 @@ #include #include +#include #include #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) -int hda_bus_ml_get_capabilities(struct hdac_bus *bus) +/** + * struct hdac_ext2_link - HDAudio extended+alternate link + * + * @hext_link: hdac_ext_link + * @alt: flag set for alternate extended links + * @intc: boolean for interrupt capable + * @ofls: boolean for offload support + * @lss: boolean for link synchronization capabilities + * @slcount: sublink count + * @elid: extended link ID (AZX_REG_ML_LEPTR_ID_ defines) + * @elver: extended link version + * @leptr: extended link pointer + * @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits + * in LCTL register + * @base_ptr: pointer to shim/ip/shim_vs space + * @instance_offset: offset between each of @slcount instances managed by link + * @shim_offset: offset to SHIM register base + * @ip_offset: offset to IP register base + * @shim_vs_offset: offset to vendor-specific (VS) SHIM base + */ +struct hdac_ext2_link { + struct hdac_ext_link hext_link; + + /* read directly from LCAP register */ + bool alt; + bool intc; + bool ofls; + bool lss; + int slcount; + int elid; + int elver; + u32 leptr; + + struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */ + + /* internal values computed from LCAP contents */ + void __iomem *base_ptr; + u32 instance_offset; + u32 shim_offset; + u32 ip_offset; + u32 shim_vs_offset; +}; + +#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link) + +#define AZX_REG_SDW_INSTANCE_OFFSET 0x8000 +#define AZX_REG_SDW_SHIM_OFFSET 0x0 +#define AZX_REG_SDW_IP_OFFSET 0x100 +#define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000 + +/* only one instance supported */ +#define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0 +#define AZX_REG_INTEL_DMIC_IP_OFFSET 0x100 +#define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET 0x6000 + +#define AZX_REG_INTEL_SSP_INSTANCE_OFFSET 0x1000 +#define AZX_REG_INTEL_SSP_SHIM_OFFSET 0x0 +#define AZX_REG_INTEL_SSP_IP_OFFSET 0x100 +#define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET 0xC00 + +/* only one instance supported */ +#define AZX_REG_INTEL_UAOL_SHIM_OFFSET 0x0 +#define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100 +#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00 + +/* HDAML section - this part follows sequences in the hardware specification, + * including naming conventions and the use of the hdaml_ prefix. + * The code is intentionally minimal with limited dependencies on frameworks or + * helpers. Locking and scanning lists is handled at a higher level + */ + +static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link, + void __iomem *ml_addr, int link_idx) { - if (bus->mlcap) - return snd_hdac_ext_bus_get_ml_capabilities(bus); + struct hdac_ext_link *hlink = &h2link->hext_link; + u32 base_offset; + + hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP); + + h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps); + + /* handle alternate extensions */ + if (!h2link->alt) { + h2link->slcount = 1; + + /* + * LSDIID is initialized by hardware for HDaudio link, + * it needs to be setup by software for alternate links + */ + hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID); + + dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n", + link_idx, hlink->lsdiid); + + return 0; + } + + h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps); + h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps); + h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps); + + /* read slcount (increment due to zero-based hardware representation */ + h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1; + dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n", + link_idx, h2link->slcount); + + /* find IP ID and offsets */ + h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR); + + h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr); + + base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr); + h2link->base_ptr = hlink->ml_addr + base_offset; + + switch (h2link->elid) { + case AZX_REG_ML_LEPTR_ID_SDW: + h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET; + h2link->ip_offset = AZX_REG_SDW_IP_OFFSET; + h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET; + dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n", + link_idx, base_offset); + break; + case AZX_REG_ML_LEPTR_ID_INTEL_DMIC: + h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET; + h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET; + h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET; + dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n", + link_idx, base_offset); + break; + case AZX_REG_ML_LEPTR_ID_INTEL_SSP: + h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET; + h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET; + h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET; + dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n", + link_idx, base_offset); + break; + case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: + h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET; + h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET; + h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET; + dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n", + link_idx, base_offset); + break; + default: + dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n", + link_idx, h2link->elid); + return -EINVAL; + } return 0; } -EXPORT_SYMBOL_NS(hda_bus_ml_get_capabilities, SND_SOC_SOF_HDA_MLINK); + +/* END HDAML section */ + +static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + int ret; + + h2link = kzalloc(sizeof(*h2link), GFP_KERNEL); + if (!h2link) + return -ENOMEM; + + /* basic initialization */ + hlink = &h2link->hext_link; + + hlink->index = index; + hlink->bus = bus; + hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index); + + ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index); + if (ret < 0) { + kfree(h2link); + return ret; + } + + mutex_init(&h2link->eml_lock); + + list_add_tail(&hlink->list, &bus->hlink_list); + + /* + * HDaudio regular links are powered-on by default, the + * refcount needs to be initialized. + */ + if (!h2link->alt) + hlink->ref_count = 1; + + return 0; +} + +int hda_bus_ml_init(struct hdac_bus *bus) +{ + u32 link_count; + int ret; + int i; + + if (!bus->mlcap) + return 0; + + link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1; + + dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count); + + for (i = 0; i < link_count; i++) { + ret = hda_ml_alloc_h2link(bus, i); + if (ret < 0) { + hda_bus_ml_free(bus); + return ret; + } + } + return 0; +} +EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK); void hda_bus_ml_free(struct hdac_bus *bus) { struct hdac_ext_link *hlink, *_h; + struct hdac_ext2_link *h2link; if (!bus->mlcap) return; list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) { list_del(&hlink->list); - kfree(hlink); + h2link = hdac_ext_link_to_ext2(hlink); + + mutex_destroy(&h2link->eml_lock); + kfree(h2link); } } EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 8845ae255f1e..5980cf1e27a3 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -918,7 +918,7 @@ static int hda_init_caps(struct snd_sof_dev *sdev) return ret; } - hda_bus_ml_get_capabilities(bus); + hda_bus_ml_init(bus); /* Skip SoundWire if it is not supported */ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) -- cgit v1.2.3 From fc7dab8ec0b4a451dd5737aa4f06a0e8bd6efa32 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:17 +0300 Subject: ASoC: SOF: Intel: hda-mlink: introduce helpers for 'extended links' PM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helpers to program SPA/CPA bits, using a mutex to access the shared LCTL register if required. All links are managed with the same LCTLx.SPA bits. However there are quite a few implementation details to be aware of: Legacy HDaudio multi-links are powered-up when exiting reset, which requires the ref_count to be manually set to one when initializing the link. Alternate links for SoundWire/DMIC/SSP need to be explicitly powered-up before accessing the SHIM/IP/Vendor-Specific SHIM space for each sublink. DMIC/SSP/SoundWire are all different cases with a different device/dai/hlink relationship. SoundWire will handle power management with the auxiliary device resume/suspend routine. The ref_count is not necessary in this case. The DMIC/SSP will by contrast handle the power management from DAI .startup and .shutdown callbacks. The SSP has a 1:1 mapping between sublink and DAI, but it's bidirectional so the ref_count will help avoid turning off the sublink when one of the two directions is still in use. The DMIC has a single link but two DAIs for data generated at different sampling frequencies, again the ref_count will make sure the two DAIs can be used concurrently. And last the SoundWire Intel require power-up/down and bank switch to be handled with a lock already taken, so the 'eml_lock' is made optional with the _unlocked versions of the helpers. Note that the _check_power_active() implementation is similar to previous helpers in sound/hda/ext, with sleep duration and timeout aligned with hardware recommendations. If desired, this helper could be modified in a second step with .e.g. readl_poll_timeout() Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 32 ++++++++ sound/soc/sof/intel/hda-mlink.c | 163 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 8048bf01c133..6ecb4c29e472 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -12,6 +12,13 @@ struct hdac_bus; int hda_bus_ml_init(struct hdac_bus *bus); void hda_bus_ml_free(struct hdac_bus *bus); + +int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); +int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); + +int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink); +int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); + void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); @@ -23,6 +30,31 @@ static inline int hda_bus_ml_init(struct hdac_bus *bus) { return 0; } static inline void hda_bus_ml_free(struct hdac_bus *bus) { } + +static inline int +hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return 0; +} + +static inline int +hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return 0; +} + +static inline int +hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return 0; +} + +static inline int +hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return 0; +} + static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 8a66c0a67589..4d016f6ab773 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -170,6 +170,68 @@ static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link, return 0; } +/* + * Hardware recommendations are to wait ~10us before checking any hardware transition + * reported by bits changing status. + * This value does not need to be super-precise, a slack of 5us is perfectly acceptable. + * The worst-case is about 1ms before reporting an issue + */ +#define HDAML_POLL_DELAY_MIN_US 10 +#define HDAML_POLL_DELAY_SLACK_US 5 +#define HDAML_POLL_DELAY_RETRY 100 + +static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled) +{ + int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT; + int retry = HDAML_POLL_DELAY_RETRY; + u32 val; + + usleep_range(HDAML_POLL_DELAY_MIN_US, + HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US); + do { + val = readl(lctl); + if (enabled) { + if (val & mask) + return 0; + } else { + if (!(val & mask)) + return 0; + } + usleep_range(HDAML_POLL_DELAY_MIN_US, + HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US); + + } while (--retry); + + return -EIO; +} + +static int hdaml_link_init(u32 __iomem *lctl, int sublink) +{ + u32 val; + u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT; + + val = readl(lctl); + val |= mask; + + writel(val, lctl); + + return check_sublink_power(lctl, sublink, true); +} + +static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink) +{ + u32 val; + u32 mask; + + val = readl(lctl); + mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT; + val &= ~mask; + + writel(val, lctl); + + return check_sublink_power(lctl, sublink, false); +} + /* END HDAML section */ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -251,6 +313,107 @@ void hda_bus_ml_free(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK); +static struct hdac_ext2_link * +find_ext2_link(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext_link *hlink; + + list_for_each_entry(hlink, &bus->hlink_list, list) { + struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); + + if (h2link->alt == alt && h2link->elid == elid) + return h2link; + } + + return NULL; +} + +static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, + bool eml_lock) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + int ret = 0; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return -ENODEV; + + if (sublink >= h2link->slcount) + return -EINVAL; + + hlink = &h2link->hext_link; + + if (eml_lock) + mutex_lock(&h2link->eml_lock); + + if (++hlink->ref_count > 1) + goto skip_init; + + ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); + +skip_init: + if (eml_lock) + mutex_unlock(&h2link->eml_lock); + + return ret; +} + +int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK); + +static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink, + bool eml_lock) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + int ret = 0; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return -ENODEV; + + if (sublink >= h2link->slcount) + return -EINVAL; + + hlink = &h2link->hext_link; + + if (eml_lock) + mutex_lock(&h2link->eml_lock); + + if (--hlink->ref_count > 0) + goto skip_shutdown; + + ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); + +skip_shutdown: + if (eml_lock) + mutex_unlock(&h2link->eml_lock); + + return ret; +} + +int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); + void hda_bus_ml_put_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink; -- cgit v1.2.3 From 725218f1d8210ee6eba2c2e923ea26a312fb3f46 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:18 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add convenience helpers for SoundWire PM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The updated SoundWire Intel driver will need to rely on Extended HDaudio links for power management, but it doesn't need to be aware of all the HDaudio structures. Add convenience helpers to avoid polluting SoundWire drivers too much with HDaudio information. Since the SoundWire/Intel solution already takes the lock at a higher level, the _unlocked PM helpers are used. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-10-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 9 +++++++++ sound/soc/sof/intel/hda-mlink.c | 12 ++++++++++++ 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 6ecb4c29e472..c47008a603d0 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -19,6 +19,9 @@ int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); +int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink); +int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink); + void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); @@ -55,6 +58,12 @@ hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int s return 0; } +static inline int +hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) { return 0; } + +static inline int +hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) { return 0; } + static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 4d016f6ab773..5045c10bed76 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -414,6 +414,18 @@ int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, i } EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); +int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) +{ + return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) +{ + return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); + void hda_bus_ml_put_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink; -- cgit v1.2.3 From 6857c7ee202cf4b8224882bcee8e3b97c26b6484 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:19 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add helper to return sublink count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed for SoundWire integration. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-11-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 5 +++++ sound/soc/sof/intel/hda-mlink.c | 12 ++++++++++++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index c47008a603d0..d7f65cb6e175 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -13,6 +13,8 @@ struct hdac_bus; int hda_bus_ml_init(struct hdac_bus *bus); void hda_bus_ml_free(struct hdac_bus *bus); +int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid); + int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); @@ -34,6 +36,9 @@ hda_bus_ml_init(struct hdac_bus *bus) { return 0; } static inline void hda_bus_ml_free(struct hdac_bus *bus) { } +static inline int +hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) { return 0; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 5045c10bed76..02743c342f28 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -328,6 +328,18 @@ find_ext2_link(struct hdac_bus *bus, bool alt, int elid) return NULL; } +int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + return h2link->slcount; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) { -- cgit v1.2.3 From 2e4288319ad3e18a266461d3e63c86eb36f7a152 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:20 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add helpers to enable/check interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When INTC is set, LCTL exposes INTEN and INTSTS fields. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-12-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 8 ++++++ sound/soc/sof/intel/hda-mlink.c | 62 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index d7f65cb6e175..872652209a47 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -14,6 +14,8 @@ int hda_bus_ml_init(struct hdac_bus *bus); void hda_bus_ml_free(struct hdac_bus *bus); int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid); +void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable); +bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid); int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); @@ -39,6 +41,12 @@ static inline void hda_bus_ml_free(struct hdac_bus *bus) { } static inline int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) { return 0; } +static inline void +hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) { } + +static inline bool +hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) { return false; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 02743c342f28..026642659037 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -232,6 +232,28 @@ static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink) return check_sublink_power(lctl, sublink, false); } +static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable) +{ + u32 val; + + val = readl(lctl); + if (enable) + val |= AZX_ML_LCTL_INTEN; + else + val &= ~AZX_ML_LCTL_INTEN; + + writel(val, lctl); +} + +static bool hdaml_link_check_interrupt(u32 __iomem *lctl) +{ + u32 val; + + val = readl(lctl); + + return val & AZX_ML_LCTL_INTSTS; +} + /* END HDAML section */ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -340,6 +362,46 @@ int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid) } EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK); +void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return; + + if (!h2link->intc) + return; + + hlink = &h2link->hext_link; + + mutex_lock(&h2link->eml_lock); + + hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable); + + mutex_unlock(&h2link->eml_lock); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK); + +bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return false; + + if (!h2link->intc) + return false; + + hlink = &h2link->hext_link; + + return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) { -- cgit v1.2.3 From 02ba1b021c28670011b361be3be4bda49f348c43 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:21 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add helpers to set link SYNC frequency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These helpers configure the ratio between the base clock and the hardware signal used for link synchronization. The SYNCPRD is written before the first sublink is powered-up. The SYNCPU bit is set, but it will only be cleared after the link is powered-up, hence the implementation with a set/wait pattern. These helpers are currently only needed by SoundWire support, where the lock is taken at a higher level, so only the _unlocked versions are exposed for now. Note that the _wait_bit() implementation is similar to previous helpers in drivers/soundwire, but with sleep duration and timeout aligned with hardware recommendations. If desired, this helper could be modified in a second step with e.g. readl_poll_timeout(). Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-13-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 27 +++++++++++++ sound/soc/sof/intel/hda-mlink.c | 90 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 872652209a47..80af2a3996d7 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -17,6 +17,12 @@ int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid); void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable); bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid); +int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd); +int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd); + +int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid); +int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus); + int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); @@ -47,6 +53,27 @@ hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool ena static inline bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) { return false; } +static inline int +hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd) +{ + return 0; +} + +static inline int +hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd) +{ + return 0; +} + +static inline int +hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) +{ + return 0; +} + +static inline int +hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) { return 0; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 026642659037..a364d6b5935d 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -254,6 +254,46 @@ static bool hdaml_link_check_interrupt(u32 __iomem *lctl) return val & AZX_ML_LCTL_INTSTS; } +static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target) +{ + int timeout = HDAML_POLL_DELAY_RETRY; + u32 reg_read; + + do { + reg_read = readl(base + offset); + if ((reg_read & mask) == target) + return 0; + + timeout--; + usleep_range(HDAML_POLL_DELAY_MIN_US, + HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US); + } while (timeout != 0); + + return -EAGAIN; +} + +static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd) +{ + u32 val; + + val = readl(lsync); + val &= ~AZX_REG_ML_LSYNC_SYNCPRD; + val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD); + + /* + * set SYNCPU but do not wait. The bit is cleared by hardware when + * the link becomes active. + */ + val |= AZX_REG_ML_LSYNC_SYNCPU; + + writel(val, lsync); +} + +static int hdaml_link_wait_syncpu(u32 __iomem *lsync) +{ + return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0); +} + /* END HDAML section */ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -402,6 +442,56 @@ bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid) } EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK); +int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + if (!h2link->lss) + return 0; + + hlink = &h2link->hext_link; + + hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd); + + return 0; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd) +{ + return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + if (!h2link->lss) + return 0; + + hlink = &h2link->hext_link; + + return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) +{ + return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) { -- cgit v1.2.3 From 1f5a6e8b5147b7bc49c0e091f8b458e45d8ee56c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:22 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add helpers for sync_arm/sync_go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The multi-link synchronization uses the same concept and registers, but moved to the HDAudio extended links. Add helpers for sync_arm and sync_go which are the basic for the bus reset, bank switch and clock stop. Since SoundWire is the only user of those helpers, only expose the _unlocked versions for now. Note that SYNCGO is a write-only bit, so no error can be reported. We still return 0 for compatibility with the SoundWire stream management headers. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-14-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 18 +++++++++++ sound/soc/sof/intel/hda-mlink.c | 70 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 80af2a3996d7..234af269495e 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -23,6 +23,12 @@ int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd); int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid); int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus); +void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); +void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink); + +int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid); +int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus); + int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); @@ -74,6 +80,18 @@ hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid) static inline int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) { return 0; } +static inline void +hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) { } + +static inline void +hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink) { } + +static inline int +hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) { return 0; } + +static inline int +hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) { return 0; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index a364d6b5935d..c8371ab03c0a 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -294,6 +294,26 @@ static int hdaml_link_wait_syncpu(u32 __iomem *lsync) return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0); } +static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink) +{ + u32 val; + + val = readl(lsync); + val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink); + + writel(val, lsync); +} + +static void hdaml_link_sync_go(u32 __iomem *lsync) +{ + u32 val; + + val = readl(lsync); + val |= AZX_REG_ML_LSYNC_SYNCGO; + + writel(val, lsync); +} + /* END HDAML section */ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -492,6 +512,56 @@ int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK); +void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return; + + if (!h2link->lss) + return; + + hlink = &h2link->hext_link; + + hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK); + +void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink) +{ + hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + if (!h2link->lss) + return 0; + + hlink = &h2link->hext_link; + + hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC); + + return 0; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK); + +int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) +{ + return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) { -- cgit v1.2.3 From d56d205857a2f6e10a1047532134321072f758b7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:23 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add helper to check cmdsync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper is an optimization where sync_go is only called when the cmdsync field is actually set to a non-zero value. Since this is also only used by SoundWire for now, only expose the _unlocked version. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-15-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 9 +++++++++ sound/soc/sof/intel/hda-mlink.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 234af269495e..0c71b843f2cf 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -29,6 +29,9 @@ void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink); int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid); int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus); +bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid); +bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus); + int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink); int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink); @@ -92,6 +95,12 @@ hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid) { return static inline int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) { return 0; } +static inline bool +hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) { return false; } + +static inline bool +hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus) { return false; } + static inline int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index c8371ab03c0a..cbbc8639691c 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -314,6 +314,15 @@ static void hdaml_link_sync_go(u32 __iomem *lsync) writel(val, lsync); } +static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask) +{ + u32 val; + + val = readl(lsync); + + return !!(val & cmdsync_mask); +} + /* END HDAML section */ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -562,6 +571,35 @@ int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK); +bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + u32 cmdsync_mask; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return 0; + + if (!h2link->lss) + return 0; + + hlink = &h2link->hext_link; + + cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1, + AZX_REG_ML_LSYNC_CMDSYNC_SHIFT); + + return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC, + cmdsync_mask); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK); + +bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus) +{ + return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW); +} +EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK); + static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink, bool eml_lock) { -- cgit v1.2.3 From 87a6ddc0cf1c62dbc7c2cc4b5f764a2e992c5ba6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:24 +0300 Subject: ASoC: SOF: Intel: hda-mlink: program SoundWire LSDIID registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each SoundWire peripheral can be programmed from the manager side either with a regular command FIFO, or with the HDaudio CORB/RIRB DMA-based mechanism. The mapping between SoundWire peripheral and SDI address is handled with the LSDIID register. This mapping only works of course if each peripheral has a unique address across all links. This has already been enforced in previous Intel contributions allowing for an IDA-based solution for the device number allocation. The checks on the dev_num are handled at the SoundWire level, but the locking is handled at the hda-mlink level. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-16-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 5 +++++ sound/soc/sof/intel/hda-mlink.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 0c71b843f2cf..d626ef964ea7 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -41,6 +41,8 @@ int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, i int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink); int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink); +int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); + void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); @@ -131,6 +133,9 @@ hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink) { return 0 static inline int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) { return 0; } +static inline int +hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) { return 0; } + static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index cbbc8639691c..5810d6f8719c 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -323,6 +323,16 @@ static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask) return !!(val & cmdsync_mask); } +static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num) +{ + u32 val; + + val = readl(lsdiid); + val |= BIT(dev_num); + + writel(val, lsdiid); +} + /* END HDAML section */ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -698,6 +708,26 @@ int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); +int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); + if (!h2link) + return -ENODEV; + + hlink = &h2link->hext_link; + + mutex_lock(&h2link->eml_lock); + + hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num); + + mutex_unlock(&h2link->eml_lock); + + return 0; +} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK); + void hda_bus_ml_put_all(struct hdac_bus *bus) { struct hdac_ext_link *hlink; -- cgit v1.2.3 From 2b864e969be29958eb810fb611d1eac50eb7104b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:25 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add helpers to retrieve DMIC/SSP hlink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Small helpers to make DAI ops simpler. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-17-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 10 ++++++++++ sound/soc/sof/intel/hda-mlink.c | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index d626ef964ea7..bfd2c77c184d 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -7,6 +7,7 @@ */ struct hdac_bus; +struct hdac_ext_link; #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) @@ -48,6 +49,9 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); int hda_bus_ml_suspend(struct hdac_bus *bus); +struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); +struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); + #else static inline int @@ -141,4 +145,10 @@ static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } +static inline struct hdac_ext_link * +hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } + +static inline struct hdac_ext_link * +hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) { return NULL; } + #endif /* CONFIG_SND_SOC_SOF_HDA */ diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 5810d6f8719c..06613c081410 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -788,6 +788,30 @@ int hda_bus_ml_suspend(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK); +struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) +{ + struct hdac_ext2_link *h2link; + + h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP); + if (!h2link) + return NULL; + + return &h2link->hext_link; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK); + +struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) +{ + struct hdac_ext2_link *h2link; + + h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC); + if (!h2link) + return NULL; + + return &h2link->hext_link; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK); + #endif MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3 From 82958c406da4fec8f818826624c33cf2e62f4147 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:26 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add helper to offload link ownership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For DMIC and SSP, the DSP will be responsible for programming the blobs and link registers. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-18-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 7 +++++++ sound/soc/sof/intel/hda-mlink.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index bfd2c77c184d..fee42c432fa3 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -52,6 +52,8 @@ int hda_bus_ml_suspend(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); +int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable); + #else static inline int @@ -151,4 +153,9 @@ hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } static inline struct hdac_ext_link * hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) { return NULL; } +static inline int +hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) +{ + return 0; +} #endif /* CONFIG_SND_SOC_SOF_HDA */ diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index 06613c081410..b9ce07c38bb5 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -333,6 +333,18 @@ static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num) writel(val, lsdiid); } +static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable) +{ + u32 val = readl(lctl); + + if (enable) + val |= AZX_ML_LCTL_OFLEN; + else + val &= ~AZX_ML_LCTL_OFLEN; + + writel(val, lctl); +} + /* END HDAML section */ static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index) @@ -812,6 +824,30 @@ struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK); +int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return -ENODEV; + + if (!h2link->ofls) + return 0; + + hlink = &h2link->hext_link; + + mutex_lock(&h2link->eml_lock); + + hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable); + + mutex_unlock(&h2link->eml_lock); + + return 0; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK); + #endif MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3 From 681f27f302ff85ddf3f6e7fd0231059f99c0f26e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 4 Apr 2023 13:41:27 +0300 Subject: ASoC: SOF: Intel: hda-mlink: add helper to retrieve eml_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For SoundWire usages, we need to use the global eml_lock to serialize/protect all accesses to shared registers. Due to the split implementation across two subsystems, we need to pass a pointer around. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20230404104127.5629-19-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 5 +++++ sound/soc/sof/intel/hda-mlink.c | 12 ++++++++++++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index fee42c432fa3..dbc47af08135 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -52,6 +52,8 @@ int hda_bus_ml_suspend(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); +struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid); + int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable); #else @@ -153,6 +155,9 @@ hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } static inline struct hdac_ext_link * hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus) { return NULL; } +static inline struct mutex * +hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) { return NULL; } + static inline int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable) { diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index b9ce07c38bb5..775582ab7494 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -800,6 +800,18 @@ int hda_bus_ml_suspend(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK); +struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return NULL; + + return &h2link->eml_lock; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK); + struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { struct hdac_ext2_link *h2link; -- cgit v1.2.3 From 59322d35179987e85b593e504fd334de8683c835 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 11 Apr 2023 16:25:28 +0100 Subject: ASoC: cs35l56: Re-patch firmware after system suspend Check during cs35l56_system_resume() whether the firmware patch must be applied again. The FIRMWARE_MISSING flag in the PROTECTION_STATUS register indicates whether the firmware has been patched. In non-secure mode the FIRMWARE_MISSING flag is cleared at the end of dsp_work(). If it is set after system-resume we know that dsp_work() must be run again. In secure mode the pre-OS loader will have done the secure patching and cleared the FIRMWARE_MISSING flag. So this flag does not tell us whether firmware memory was lost. But the driver could only be downloading non-secure tunings, which is always safe to do. If the driver has control of RESET we will have asserted it during suspend so the firmware patch will have been lost. The driver would only have control of RESET in non-secure mode. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/168122674550.26.8545058503709956172@mailman-core.alsa-project.org Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 4 +++ sound/soc/codecs/cs35l56-sdw.c | 12 +++++++- sound/soc/codecs/cs35l56.c | 67 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 5f8ea2dfaa21..b3300bce74f4 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -95,6 +95,7 @@ #define CS35L56_MAIN_RENDER_USER_MUTE 0x3400024 #define CS35L56_MAIN_RENDER_USER_VOLUME 0x340002C #define CS35L56_MAIN_POSTURE_NUMBER 0x3400094 +#define CS35L56_PROTECTION_STATUS 0x34000D8 #define CS35L56_TRANSDUCER_ACTUAL_PS 0x3400150 #define CS35L56_DSP1_YMEM_UNPACKED24_6141 0x3405FF4 #define CS35L56_DSP1_PMEM_0 0x3800000 @@ -216,6 +217,9 @@ #define CS35L56_MAIN_POSTURE_MAX 255 #define CS35L56_MAIN_POSTURE_MASK CS35L56_MAIN_POSTURE_MAX +/* CS35L56_PROTECTION_STATUS */ +#define CS35L56_FIRMWARE_MISSING BIT(0) + /* Software Values */ #define CS35L56_HALO_STATE_SHUTDOWN 1 #define CS35L56_HALO_STATE_BOOT_DONE 2 diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 947d4e5f4dc9..e759347423cf 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -473,6 +473,16 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) return cs35l56_system_suspend(dev); } +static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + cs35l56->sdw_irq_no_unmask = false; + /* runtime_resume re-enables the interrupt */ + + return cs35l56_system_resume(dev); +} + static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) { struct device *dev = &peripheral->dev; @@ -522,7 +532,7 @@ static int cs35l56_sdw_remove(struct sdw_slave *peripheral) static const struct dev_pm_ops cs35l56_sdw_pm = { SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) + SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume) LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ }; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index eb85c27ab087..18e341744839 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -946,6 +946,7 @@ static void cs35l56_dsp_work(struct work_struct *work) goto err_unlock; } + regmap_clear_bits(cs35l56->regmap, CS35L56_PROTECTION_STATUS, CS35L56_FIRMWARE_MISSING); cs35l56->fw_patched = true; err_unlock: @@ -1026,6 +1027,8 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l56 = { .num_controls = ARRAY_SIZE(cs35l56_controls), .set_bias_level = cs35l56_set_bias_level, + + .suspend_bias_off = 1, /* see cs35l56_system_resume() */ }; static const struct reg_sequence cs35l56_hibernate_seq[] = { @@ -1156,6 +1159,47 @@ err: } EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE); +static int cs35l56_is_fw_reload_needed(struct cs35l56_private *cs35l56) +{ + unsigned int val; + int ret; + + /* Nothing to re-patch if we haven't done any patching yet. */ + if (!cs35l56->fw_patched) + return false; + + /* + * If we have control of RESET we will have asserted it so the firmware + * will need re-patching. + */ + if (cs35l56->reset_gpio) + return true; + + /* + * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so + * can't be used here to test for memory retention. + * Assume that tuning must be re-loaded. + */ + if (cs35l56->secured) + return true; + + ret = pm_runtime_resume_and_get(cs35l56->dev); + if (ret) { + dev_err(cs35l56->dev, "Failed to runtime_get: %d\n", ret); + return ret; + } + + ret = regmap_read(cs35l56->regmap, CS35L56_PROTECTION_STATUS, &val); + if (ret) + dev_err(cs35l56->dev, "Failed to read PROTECTION_STATUS: %d\n", ret); + else + ret = !!(val & CS35L56_FIRMWARE_MISSING); + + pm_runtime_put_autosuspend(cs35l56->dev); + + return ret; +} + int cs35l56_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); @@ -1273,7 +1317,28 @@ int cs35l56_system_resume(struct device *dev) if (cs35l56->irq) enable_irq(cs35l56->irq); - return ret; + if (ret) + return ret; + + /* Firmware won't have been loaded if the component hasn't probed */ + if (!cs35l56->component) + return 0; + + ret = cs35l56_is_fw_reload_needed(cs35l56); + dev_dbg(cs35l56->dev, "fw_reload_needed: %d\n", ret); + if (ret < 1) + return ret; + + cs35l56->fw_patched = false; + init_completion(&cs35l56->dsp_ready_completion); + queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); + + /* + * suspend_bias_off ensures we are now in BIAS_OFF so there will be + * a BIAS_OFF->BIAS_STANDBY transition to complete dsp patching. + */ + + return 0; } EXPORT_SYMBOL_GPL(cs35l56_system_resume); -- cgit v1.2.3 From 4a778bdc7afbc422bd513c4f1cd7a9faf4bebaab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 6 Apr 2023 00:15:48 +0000 Subject: ASoC: expand snd_soc_dapm_mutex_lock/unlock() soc.h has snd_soc_dapm_mutex_lock/unlock() definition and many drivers are using it, but soc-dapm.c is not. 1st reason is snd_soc_dapm_mutex_lock/unlock() requests snd_soc_dapm_context pointer as parameter (A), but sometimes soc-dapm.c needs to use snd_soc_card (B). (A) static inline void snd_soc_dapm_mutex_lock(struct snd_soc_dapm_context *dapm) { mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); } ^^^^^^^^^^ (B) mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); ^^^^ 2nd reason is it want to use SND_SOC_DAPM_CLASS_INIT for mutex_lock_nested(), but helper is using _RUNTIME (A). The conclusion is we want to use "dapm vs card" and "_RUNTIME vs _INIT" for dapm lock/unlock. To enable this selfish request, this patch uses _Generic macro. We can use snd_soc_dapm_mutex_lock/unlock() for both dapm and card case. snd_soc_dapm_mutex_lock(dapm); snd_soc_dapm_mutex_unlock(dapm); snd_soc_dapm_mutex_lock(card); snd_soc_dapm_mutex_unlock(card); Current soc-dapm.c is using both mutex_lock() and mutex_lock_nested(). This patch handles mutex_lock() as mutex_lock_nested(..., 0), in other words, handles below as same. mutex_lock(&card->dapm_mutex); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); Because people might misunderstand that _init() is mutex initialization, this patch renames _INIT to _ROOT and adds new snd_soc_dapm_mutex_lock_root() for it. This patch also moves snd_soc_dapm_subclass definition from soc-dapm.h to soc.h to keep related code together. Because very complex soc.h vs soc-dapm.h relationship, it is difficult/impossible to define these helper into soc-dapm.h. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87cz4hx3v0.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 5 -- include/sound/soc.h | 60 ++++++++++++++++++++++-- sound/soc/soc-dapm.c | 119 +++++++++++++++++++++++------------------------ 3 files changed, 113 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 64915ebd641e..87f8e1793af1 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -527,11 +527,6 @@ enum snd_soc_dapm_type { SND_SOC_DAPM_TYPE_COUNT }; -enum snd_soc_dapm_subclass { - SND_SOC_DAPM_CLASS_INIT = 0, - SND_SOC_DAPM_CLASS_RUNTIME = 1, -}; - /* * DAPM audio route definition. * diff --git a/include/sound/soc.h b/include/sound/soc.h index 3833184c187f..0e17e3230c7a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1364,17 +1364,67 @@ extern struct dentry *snd_soc_debugfs_root; extern const struct dev_pm_ops snd_soc_pm_ops; -/* Helper functions */ -static inline void snd_soc_dapm_mutex_lock(struct snd_soc_dapm_context *dapm) +/* + * DAPM helper functions + */ +enum snd_soc_dapm_subclass { + SND_SOC_DAPM_CLASS_ROOT = 0, + SND_SOC_DAPM_CLASS_RUNTIME = 1, +}; + +static inline void _snd_soc_dapm_mutex_lock_root_c(struct snd_soc_card *card) +{ + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_ROOT); +} + +static inline void _snd_soc_dapm_mutex_lock_c(struct snd_soc_card *card) +{ + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); +} + +static inline void _snd_soc_dapm_mutex_unlock_c(struct snd_soc_card *card) { - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + mutex_unlock(&card->dapm_mutex); } -static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm) +static inline void _snd_soc_dapm_mutex_assert_held_c(struct snd_soc_card *card) { - mutex_unlock(&dapm->card->dapm_mutex); + lockdep_assert_held(&card->dapm_mutex); } +static inline void _snd_soc_dapm_mutex_lock_root_d(struct snd_soc_dapm_context *dapm) +{ + _snd_soc_dapm_mutex_lock_root_c(dapm->card); +} + +static inline void _snd_soc_dapm_mutex_lock_d(struct snd_soc_dapm_context *dapm) +{ + _snd_soc_dapm_mutex_lock_c(dapm->card); +} + +static inline void _snd_soc_dapm_mutex_unlock_d(struct snd_soc_dapm_context *dapm) +{ + _snd_soc_dapm_mutex_unlock_c(dapm->card); +} + +static inline void _snd_soc_dapm_mutex_assert_held_d(struct snd_soc_dapm_context *dapm) +{ + _snd_soc_dapm_mutex_assert_held_c(dapm->card); +} + +#define snd_soc_dapm_mutex_lock_root(x) _Generic((x), \ + struct snd_soc_card * : _snd_soc_dapm_mutex_lock_root_c, \ + struct snd_soc_dapm_context * : _snd_soc_dapm_mutex_lock_root_d)(x) +#define snd_soc_dapm_mutex_lock(x) _Generic((x), \ + struct snd_soc_card * : _snd_soc_dapm_mutex_lock_c, \ + struct snd_soc_dapm_context * : _snd_soc_dapm_mutex_lock_d)(x) +#define snd_soc_dapm_mutex_unlock(x) _Generic((x), \ + struct snd_soc_card * : _snd_soc_dapm_mutex_unlock_c, \ + struct snd_soc_dapm_context * : _snd_soc_dapm_mutex_unlock_d)(x) +#define snd_soc_dapm_mutex_assert_held(x) _Generic((x), \ + struct snd_soc_card * : _snd_soc_dapm_mutex_assert_held_c, \ + struct snd_soc_dapm_context * : _snd_soc_dapm_mutex_assert_held_d)(x) + #include #include #include diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 36e6f261bcf7..f2f04ce693a1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -150,7 +150,7 @@ static int dapm_down_seq[] = { static void dapm_assert_locked(struct snd_soc_dapm_context *dapm) { if (snd_soc_card_is_instantiated(dapm->card)) - lockdep_assert_held(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_assert_held(dapm); } static void pop_wait(u32 pop_time) @@ -302,7 +302,7 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; - mutex_lock(&card->dapm_mutex); + snd_soc_dapm_mutex_lock_root(card); for_each_card_widgets(card, w) { if (w->is_ep) { @@ -314,7 +314,7 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card) } } - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); } EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty); @@ -604,7 +604,7 @@ static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; - lockdep_assert_held(&card->dapm_mutex); + snd_soc_dapm_mutex_assert_held(card); memset(&card->dapm_stats, 0, sizeof(card->dapm_stats)); @@ -1302,7 +1302,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, int paths; int ret; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(card); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT); @@ -1322,7 +1322,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, paths = ret; trace_snd_soc_dapm_connected(paths, stream); - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); return paths; } @@ -1952,7 +1952,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) enum snd_soc_bias_level bias; int ret; - lockdep_assert_held(&card->dapm_mutex); + snd_soc_dapm_mutex_assert_held(card); trace_snd_soc_dapm_start(card); @@ -2090,7 +2090,6 @@ static ssize_t dapm_widget_power_read_file(struct file *file, size_t count, loff_t *ppos) { struct snd_soc_dapm_widget *w = file->private_data; - struct snd_soc_card *card = w->dapm->card; enum snd_soc_dapm_direction dir, rdir; char *buf; int in, out; @@ -2101,7 +2100,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (!buf) return -ENOMEM; - mutex_lock(&card->dapm_mutex); + snd_soc_dapm_mutex_lock_root(w->dapm); /* Supply widgets are not handled by is_connected_{input,output}_ep() */ if (w->is_supply) { @@ -2145,7 +2144,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, } } - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(w->dapm); ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); @@ -2266,7 +2265,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, int found = 0; bool connect; - lockdep_assert_held(&card->dapm_mutex); + snd_soc_dapm_mutex_assert_held(card); /* find dapm widget path assoc with kcontrol */ dapm_kcontrol_for_each_path(path, kcontrol) { @@ -2293,11 +2292,11 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, struct snd_soc_card *card = dapm->card; int ret; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(card); card->update = update; ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); card->update = NULL; - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); if (ret > 0) snd_soc_dpcm_runtime_update(card); return ret; @@ -2312,7 +2311,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, struct snd_soc_dapm_path *path; int found = 0; - lockdep_assert_held(&card->dapm_mutex); + snd_soc_dapm_mutex_assert_held(card); /* find dapm widget path assoc with kcontrol */ dapm_kcontrol_for_each_path(path, kcontrol) { @@ -2358,11 +2357,11 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, struct snd_soc_card *card = dapm->card; int ret; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(card); card->update = update; ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1); card->update = NULL; - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); if (ret > 0) snd_soc_dpcm_runtime_update(card); return ret; @@ -2441,7 +2440,7 @@ static ssize_t dapm_widget_show(struct device *dev, struct snd_soc_dai *codec_dai; int i, count = 0; - mutex_lock(&rtd->card->dapm_mutex); + snd_soc_dapm_mutex_lock_root(rtd->card); for_each_rtd_codec_dais(rtd, i, codec_dai) { struct snd_soc_component *cmpnt = codec_dai->component; @@ -2449,7 +2448,7 @@ static ssize_t dapm_widget_show(struct device *dev, count = dapm_widget_show_component(cmpnt, buf, count); } - mutex_unlock(&rtd->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(rtd->card); return count; } @@ -2632,9 +2631,9 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) { int ret; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); ret = snd_soc_dapm_sync_unlocked(dapm); - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); @@ -2703,9 +2702,9 @@ int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); int ret; - mutex_lock_nested(&rtd->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(rtd->card); ret = dapm_update_dai_unlocked(substream, params, dai); - mutex_unlock(&rtd->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(rtd->card); return ret; } @@ -3090,14 +3089,14 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, { int i, ret = 0; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); for (i = 0; i < num; i++) { int r = snd_soc_dapm_add_route(dapm, route); if (r < 0) ret = r; route++; } - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return ret; } @@ -3116,12 +3115,12 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, { int i; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); for (i = 0; i < num; i++) { snd_soc_dapm_del_route(dapm, route); route++; } - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return 0; } @@ -3194,14 +3193,14 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, int i; int ret = 0; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + snd_soc_dapm_mutex_lock_root(dapm); for (i = 0; i < num; i++) { int err = snd_soc_dapm_weak_route(dapm, route); if (err) ret = err; route++; } - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return ret; } @@ -3220,7 +3219,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) struct snd_soc_dapm_widget *w; unsigned int val; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + snd_soc_dapm_mutex_lock_root(card); for_each_card_widgets(card, w) { @@ -3232,7 +3231,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) sizeof(struct snd_kcontrol *), GFP_KERNEL); if (!w->kcontrols) { - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); return -ENOMEM; } } @@ -3275,7 +3274,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) } dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -3293,7 +3292,6 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); - struct snd_soc_card *card = dapm->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; int reg = mc->reg; @@ -3304,7 +3302,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, unsigned int invert = mc->invert; unsigned int reg_val, val, rval = 0; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { reg_val = soc_dapm_read(dapm, reg); val = (reg_val >> shift) & mask; @@ -3321,7 +3319,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, if (snd_soc_volsw_is_stereo(mc)) rval = (reg_val >> width) & mask; } - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); if (invert) ucontrol->value.integer.value[0] = max - val; @@ -3379,7 +3377,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, rval = max - rval; } - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(card); /* This assumes field width < (bits in unsigned int / 2) */ if (width > sizeof(unsigned int) * 8 / 2) @@ -3421,7 +3419,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, card->update = NULL; } - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); if (ret > 0) snd_soc_dpcm_runtime_update(card); @@ -3443,17 +3441,16 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); - struct snd_soc_card *card = dapm->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) { reg_val = soc_dapm_read(dapm, e->reg); } else { reg_val = dapm_kcontrol_get_value(kcontrol); } - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); val = (reg_val >> e->shift_l) & e->mask; ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); @@ -3500,7 +3497,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(card); change = dapm_kcontrol_set_value(kcontrol, val); @@ -3521,7 +3518,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, card->update = NULL; } - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); if (ret > 0) snd_soc_dpcm_runtime_update(card); @@ -3562,12 +3559,12 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); const char *pin = (const char *)kcontrol->private_value; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(card); ucontrol->value.integer.value[0] = snd_soc_dapm_get_pin_status(&card->dapm, pin); - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); return 0; } @@ -3586,10 +3583,10 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, const char *pin = (const char *)kcontrol->private_value; int ret; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(card); ret = __snd_soc_dapm_set_pin(&card->dapm, pin, !!ucontrol->value.integer.value[0]); - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); snd_soc_dapm_sync(&card->dapm); return ret; @@ -3762,9 +3759,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); w = snd_soc_dapm_new_control_unlocked(dapm, widget); - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return w; } @@ -3787,7 +3784,7 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, int i; int ret = 0; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + snd_soc_dapm_mutex_lock_root(dapm); for (i = 0; i < num; i++) { struct snd_soc_dapm_widget *w = snd_soc_dapm_new_control_unlocked(dapm, widget); if (IS_ERR(w)) { @@ -3796,7 +3793,7 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, } widget++; } - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); @@ -4499,9 +4496,9 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, { struct snd_soc_card *card = rtd->card; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(card); soc_dapm_stream_event(rtd, stream, event); - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); } void snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream) @@ -4562,11 +4559,11 @@ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin) { int ret; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); ret = snd_soc_dapm_set_pin(dapm, pin, 1); - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return ret; } @@ -4630,11 +4627,11 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, { int ret; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, pin); - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return ret; } @@ -4674,11 +4671,11 @@ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, { int ret; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); ret = snd_soc_dapm_set_pin(dapm, pin, 0); - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return ret; } @@ -4725,11 +4722,11 @@ int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin) { int ret; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + snd_soc_dapm_mutex_lock(dapm); ret = snd_soc_dapm_set_pin(dapm, pin, 0); - mutex_unlock(&dapm->card->dapm_mutex); + snd_soc_dapm_mutex_unlock(dapm); return ret; } @@ -4826,7 +4823,7 @@ static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) LIST_HEAD(down_list); int powerdown = 0; - mutex_lock(&card->dapm_mutex); + snd_soc_dapm_mutex_lock_root(card); for_each_card_widgets(dapm->card, w) { if (w->dapm != dapm) @@ -4851,7 +4848,7 @@ static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) SND_SOC_BIAS_STANDBY); } - mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_mutex_unlock(card); } /* -- cgit v1.2.3 From 38e42f6d6c6702bbfc633fce9b579fb80cec2d59 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 6 Apr 2023 00:16:10 +0000 Subject: ASoC: expand snd_soc_dpcm_mutex_lock/unlock() soc-pcm.c has snd_soc_dpcm_mutex_lock/unlock(), but other files can't use it because it is static function. It requests snd_soc_pcm_runtime as parameter (A), but sometimes we want to use it by snd_soc_card (B). (A) static inline void snd_soc_dpcm_mutex_lock(struct snd_soc_pcm_runtime *rtd) { mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); } ^^^^^^^^^ (B) mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass); ^^^^ We want to use it with both "rtd" and "card" for dapm lock/unlock. To enable it, this patch uses _Generic macro. This patch makes snd_soc_dpcm_mutex_{un}lock() global function, and use it on each files. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87bkk1x3ud.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/soc-component.c | 12 ++++++------ sound/soc/soc-compress.c | 42 +++++++++++++++++++++--------------------- sound/soc/soc-core.c | 4 ++-- sound/soc/soc-pcm.c | 17 ++--------------- 5 files changed, 76 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0e17e3230c7a..05004c048dd5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1425,6 +1425,51 @@ static inline void _snd_soc_dapm_mutex_assert_held_d(struct snd_soc_dapm_context struct snd_soc_card * : _snd_soc_dapm_mutex_assert_held_c, \ struct snd_soc_dapm_context * : _snd_soc_dapm_mutex_assert_held_d)(x) +/* + * PCM helper functions + */ +static inline void _snd_soc_dpcm_mutex_lock_c(struct snd_soc_card *card) +{ + mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass); +} + +static inline void _snd_soc_dpcm_mutex_unlock_c(struct snd_soc_card *card) +{ + mutex_unlock(&card->pcm_mutex); +} + +static inline void _snd_soc_dpcm_mutex_assert_held_c(struct snd_soc_card *card) +{ + lockdep_assert_held(&card->pcm_mutex); +} + +static inline void _snd_soc_dpcm_mutex_lock_r(struct snd_soc_pcm_runtime *rtd) +{ + _snd_soc_dpcm_mutex_lock_c(rtd->card); +} + +static inline void _snd_soc_dpcm_mutex_unlock_r(struct snd_soc_pcm_runtime *rtd) +{ + _snd_soc_dpcm_mutex_unlock_c(rtd->card); +} + +static inline void _snd_soc_dpcm_mutex_assert_held_r(struct snd_soc_pcm_runtime *rtd) +{ + _snd_soc_dpcm_mutex_assert_held_c(rtd->card); +} + +#define snd_soc_dpcm_mutex_lock(x) _Generic((x), \ + struct snd_soc_card * : _snd_soc_dpcm_mutex_lock_c, \ + struct snd_soc_pcm_runtime * : _snd_soc_dpcm_mutex_lock_r)(x) + +#define snd_soc_dpcm_mutex_unlock(x) _Generic((x), \ + struct snd_soc_card * : _snd_soc_dpcm_mutex_unlock_c, \ + struct snd_soc_pcm_runtime * : _snd_soc_dpcm_mutex_unlock_r)(x) + +#define snd_soc_dpcm_mutex_assert_held(x) _Generic((x), \ + struct snd_soc_card * : _snd_soc_dpcm_mutex_assert_held_c, \ + struct snd_soc_pcm_runtime * : _snd_soc_dpcm_mutex_assert_held_r)(x) + #include #include #include diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 3cd6952212e1..ff25718ff2e8 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -550,7 +550,7 @@ int snd_soc_component_compr_get_caps(struct snd_compr_stream *cstream, struct snd_soc_component *component; int i, ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); for_each_rtd_components(rtd, i, component) { if (component->driver->compress_ops && @@ -561,7 +561,7 @@ int snd_soc_component_compr_get_caps(struct snd_compr_stream *cstream, } } - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); return soc_component_ret(component, ret); } @@ -574,7 +574,7 @@ int snd_soc_component_compr_get_codec_caps(struct snd_compr_stream *cstream, struct snd_soc_component *component; int i, ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); for_each_rtd_components(rtd, i, component) { if (component->driver->compress_ops && @@ -585,7 +585,7 @@ int snd_soc_component_compr_get_codec_caps(struct snd_compr_stream *cstream, } } - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); return soc_component_ret(component, ret); } @@ -638,7 +638,7 @@ int snd_soc_component_compr_copy(struct snd_compr_stream *cstream, struct snd_soc_component *component; int i, ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); for_each_rtd_components(rtd, i, component) { if (component->driver->compress_ops && @@ -649,7 +649,7 @@ int snd_soc_component_compr_copy(struct snd_compr_stream *cstream, } } - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); return soc_component_ret(component, ret); } diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 6e74a6c48986..661e9d70994f 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -62,7 +62,7 @@ static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); if (!rollback) snd_soc_runtime_deactivate(rtd, stream); @@ -84,7 +84,7 @@ static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback) if (!rollback) snd_soc_dapm_stream_stop(rtd, stream); - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); snd_soc_pcm_component_pm_runtime_put(rtd, cstream, rollback); @@ -107,7 +107,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream) if (ret < 0) goto err_no_lock; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); ret = snd_soc_dai_compr_startup(cpu_dai, cstream); if (ret < 0) @@ -123,7 +123,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream) snd_soc_runtime_activate(rtd, stream); err: - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); err_no_lock: if (ret < 0) soc_compr_clean(cstream, 1); @@ -146,7 +146,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) if (ret < 0) goto be_err; - mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(fe); /* calculate valid and active FE <-> BE dpcms */ dpcm_process_paths(fe, stream, &list, 1); @@ -182,7 +182,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; snd_soc_runtime_activate(fe, stream); - mutex_unlock(&fe->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(fe); mutex_unlock(&fe->card->mutex); @@ -209,7 +209,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); - mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(fe); snd_soc_runtime_deactivate(fe, stream); fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; @@ -229,7 +229,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) dpcm_be_disconnect(fe, stream); - mutex_unlock(&fe->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(fe); snd_soc_link_compr_shutdown(cstream, 0); @@ -249,7 +249,7 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ int ret; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); ret = snd_soc_component_compr_trigger(cstream, cmd); if (ret < 0) @@ -269,7 +269,7 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) } out: - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); return ret; } @@ -327,7 +327,7 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ int ret; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); /* * First we call set_params for the CPU DAI, then the component @@ -352,14 +352,14 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, /* cancel any delayed stream shutdown that is pending */ rtd->pop_wait = 0; - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); cancel_delayed_work_sync(&rtd->delayed_work); return 0; err: - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); return ret; } @@ -404,9 +404,9 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, ret = snd_soc_link_compr_set_params(cstream); if (ret < 0) goto out; - mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(fe); dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); - mutex_unlock(&fe->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(fe); fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; out: @@ -422,7 +422,7 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); ret = snd_soc_dai_compr_get_params(cpu_dai, cstream, params); if (ret < 0) @@ -430,7 +430,7 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, ret = snd_soc_component_compr_get_params(cstream, params); err: - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); return ret; } @@ -440,7 +440,7 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int ret; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); ret = snd_soc_dai_compr_ack(cpu_dai, cstream, bytes); if (ret < 0) @@ -448,7 +448,7 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) ret = snd_soc_component_compr_ack(cstream, bytes); err: - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); return ret; } @@ -459,7 +459,7 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, int ret; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); ret = snd_soc_dai_compr_pointer(cpu_dai, cstream, tstamp); if (ret < 0) @@ -467,7 +467,7 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, ret = snd_soc_component_compr_pointer(cstream, tstamp); out: - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); return ret; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9bbcff492c1e..4594505cdae2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -348,7 +348,7 @@ void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); int playback = SNDRV_PCM_STREAM_PLAYBACK; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_lock(rtd); dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", @@ -364,7 +364,7 @@ void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd) SND_SOC_DAPM_STREAM_STOP); } - mutex_unlock(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(rtd); } EXPORT_SYMBOL_GPL(snd_soc_close_delayed_work); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 913a7d98e742..adb69d7820a8 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -49,19 +49,6 @@ static inline int _soc_pcm_ret(struct snd_soc_pcm_runtime *rtd, return ret; } -static inline void snd_soc_dpcm_mutex_lock(struct snd_soc_pcm_runtime *rtd) -{ - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); -} - -static inline void snd_soc_dpcm_mutex_unlock(struct snd_soc_pcm_runtime *rtd) -{ - mutex_unlock(&rtd->card->pcm_mutex); -} - -#define snd_soc_dpcm_mutex_assert_held(rtd) \ - lockdep_assert_held(&(rtd)->card->pcm_mutex) - static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd, int stream) { @@ -2664,7 +2651,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) struct snd_soc_pcm_runtime *fe; int ret = 0; - mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass); + snd_soc_dpcm_mutex_lock(card); /* shutdown all old paths first */ for_each_card_rtds(card, fe) { ret = soc_dpcm_fe_runtime_update(fe, 0); @@ -2680,7 +2667,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) } out: - mutex_unlock(&card->pcm_mutex); + snd_soc_dpcm_mutex_unlock(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update); -- cgit v1.2.3 From 0f3b818486796ec8895fa4ccdf15edb759bff40a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 6 Apr 2023 00:16:27 +0000 Subject: ASoC: add snd_soc_card_mutex_lock/unlock() ASoC need to use card->mutex with _INIT or _RUNTIME, but there is no helper function for it. This patch adds its helper function and use it. Because people might misunderstand that _init() is mutex initialization, this patch renames _INIT to _ROOT and adds new snd_soc_card_mutex_lock_root() for it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87a5zlx3tw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-card.h | 17 ++++++++++++++++- sound/soc/soc-compress.c | 18 +++++++++--------- sound/soc/soc-core.c | 4 ++-- 3 files changed, 27 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc-card.h b/include/sound/soc-card.h index 9d31a5c0db33..fc94dfb0021f 100644 --- a/include/sound/soc-card.h +++ b/include/sound/soc-card.h @@ -9,10 +9,25 @@ #define __SOC_CARD_H enum snd_soc_card_subclass { - SND_SOC_CARD_CLASS_INIT = 0, + SND_SOC_CARD_CLASS_ROOT = 0, SND_SOC_CARD_CLASS_RUNTIME = 1, }; +static inline void snd_soc_card_mutex_lock_root(struct snd_soc_card *card) +{ + mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_ROOT); +} + +static inline void snd_soc_card_mutex_lock(struct snd_soc_card *card) +{ + mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); +} + +static inline void snd_soc_card_mutex_unlock(struct snd_soc_card *card) +{ + mutex_unlock(&card->mutex); +} + struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, const char *name); int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 661e9d70994f..d8715db5e415 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -140,7 +140,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ int ret; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_card_mutex_lock(fe->card); ret = dpcm_path_get(fe, stream, &list); if (ret < 0) @@ -184,7 +184,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) snd_soc_runtime_activate(fe, stream); snd_soc_dpcm_mutex_unlock(fe); - mutex_unlock(&fe->card->mutex); + snd_soc_card_mutex_unlock(fe->card); return 0; @@ -196,7 +196,7 @@ out: dpcm_path_put(&list); be_err: fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; - mutex_unlock(&fe->card->mutex); + snd_soc_card_mutex_unlock(fe->card); return ret; } @@ -207,7 +207,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) struct snd_soc_dpcm *dpcm; int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_card_mutex_lock(fe->card); snd_soc_dpcm_mutex_lock(fe); snd_soc_runtime_deactivate(fe, stream); @@ -237,7 +237,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) snd_soc_dai_compr_shutdown(cpu_dai, cstream, 0); - mutex_unlock(&fe->card->mutex); + snd_soc_card_mutex_unlock(fe->card); return 0; } @@ -284,7 +284,7 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) cmd == SND_COMPR_TRIGGER_DRAIN) return snd_soc_component_compr_trigger(cstream, cmd); - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_card_mutex_lock(fe->card); ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd); if (ret < 0) @@ -315,7 +315,7 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) out: fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; - mutex_unlock(&fe->card->mutex); + snd_soc_card_mutex_unlock(fe->card); return ret; } @@ -373,7 +373,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, int stream = cstream->direction; /* SND_COMPRESS_xxx is same as SNDRV_PCM_STREAM_xxx */ int ret; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_card_mutex_lock(fe->card); /* * Create an empty hw_params for the BE as the machine driver must @@ -411,7 +411,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, out: fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; - mutex_unlock(&fe->card->mutex); + snd_soc_card_mutex_unlock(fe->card); return ret; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4594505cdae2..b48efc3a08d2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1938,7 +1938,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card) int ret; mutex_lock(&client_mutex); - mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); + snd_soc_card_mutex_lock_root(card); snd_soc_dapm_init(&card->dapm, card, NULL); @@ -2093,7 +2093,7 @@ probe_end: if (ret < 0) soc_cleanup_card_resources(card); - mutex_unlock(&card->mutex); + snd_soc_card_mutex_unlock(card); mutex_unlock(&client_mutex); return ret; -- cgit v1.2.3 From 5ab28c78a125a724684958f4caf8210127d3f528 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2023 15:43:07 +0100 Subject: ASoC: cs35l56: Remove SDW1 TX5 and TX6 Reduce SDW1 to 4 channels and remove the controls for SDW1 TX5 and TX6. The TX5 and TX6 channels have been removed from B0 silicon. There is no need to support them on A1 silicon. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20230418144309.1100721-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 3 --- sound/soc/codecs/cs35l56-shared.c | 27 --------------------------- sound/soc/codecs/cs35l56.c | 28 +--------------------------- 3 files changed, 1 insertion(+), 57 deletions(-) (limited to 'include') diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index b3300bce74f4..dd7503464651 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -43,8 +43,6 @@ #define CS35L56_SWIRE_DP3_CH2_INPUT 0x0004C74 #define CS35L56_SWIRE_DP3_CH3_INPUT 0x0004C78 #define CS35L56_SWIRE_DP3_CH4_INPUT 0x0004C7C -#define CS35L56_SWIRE_DP3_CH5_INPUT 0x0004C80 -#define CS35L56_SWIRE_DP3_CH6_INPUT 0x0004C84 #define CS35L56_IRQ1_CFG 0x000E000 #define CS35L56_IRQ1_STATUS 0x000E004 #define CS35L56_IRQ1_EINT_1 0x000E010 @@ -262,7 +260,6 @@ extern const struct cs_dsp_region cs35l56_dsp1_regions[CS35L56_NUM_DSP_REGIONS]; extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; -void cs35l56_patch(struct device *dev, struct regmap *regmap, u8 revid); void cs35l56_reread_firmware_registers(struct device *dev, struct regmap *regmap); int cs35l56_get_bclk_freq_id(unsigned int freq); void cs35l56_fill_supply_names(struct regulator_bulk_data *data); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index d8bc06ad4888..f5fa6bb04d38 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -28,8 +28,6 @@ static const struct reg_default cs35l56_reg_defaults[] = { { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 }, - { CS35L56_SWIRE_DP3_CH5_INPUT, 0x00000018 }, - { CS35L56_SWIRE_DP3_CH6_INPUT, 0x00000018 }, { CS35L56_IRQ1_CFG, 0x00000000 }, { CS35L56_IRQ1_MASK_1, 0x83ffffff }, { CS35L56_IRQ1_MASK_2, 0xffff7fff }, @@ -42,29 +40,6 @@ static const struct reg_default cs35l56_reg_defaults[] = { /* CS35L56_MAIN_POSTURE_NUMBER - soft register, no default */ }; -/* - * The Ax devices have different default register values to that of B0, - * establish a common set of register defaults. - */ -static const struct reg_sequence cs35l56_reva_patch[] = { - { CS35L56_SWIRE_DP3_CH5_INPUT, 0x00000018 }, - { CS35L56_SWIRE_DP3_CH6_INPUT, 0x00000018 }, -}; - -void cs35l56_patch(struct device *dev, struct regmap *regmap, u8 revid) -{ - int ret; - - if (revid >= CS35L56_REVID_B0) - return; - - ret = regmap_register_patch(regmap, cs35l56_reva_patch, - ARRAY_SIZE(cs35l56_reva_patch)); - if (ret) - dev_err(dev, "Failed to apply patch: %d\n", ret); -} -EXPORT_SYMBOL_NS_GPL(cs35l56_patch, SND_SOC_CS35L56_SHARED); - static bool cs35l56_is_dsp_memory(unsigned int reg) { switch (reg) { @@ -114,8 +89,6 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg) case CS35L56_SWIRE_DP3_CH2_INPUT: case CS35L56_SWIRE_DP3_CH3_INPUT: case CS35L56_SWIRE_DP3_CH4_INPUT: - case CS35L56_SWIRE_DP3_CH5_INPUT: - case CS35L56_SWIRE_DP3_CH6_INPUT: case CS35L56_IRQ1_CFG: case CS35L56_IRQ1_STATUS: case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8: diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 1b80cae5026e..c0a857cfb8cb 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -166,24 +166,6 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum, static const struct snd_kcontrol_new sdw1_tx4_mux = SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum); -static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx5_enum, - CS35L56_SWIRE_DP3_CH5_INPUT, - 0, CS35L56_SWIRETXn_SRC_MASK, - cs35l56_tx_input_texts, - cs35l56_tx_input_values); - -static const struct snd_kcontrol_new sdw1_tx5_mux = - SOC_DAPM_ENUM("SDW1TX5 SRC", cs35l56_sdw1tx5_enum); - -static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx6_enum, - CS35L56_SWIRE_DP3_CH6_INPUT, - 0, CS35L56_SWIRETXn_SRC_MASK, - cs35l56_tx_input_texts, - cs35l56_tx_input_values); - -static const struct snd_kcontrol_new sdw1_tx6_mux = - SOC_DAPM_ENUM("SDW1TX6 SRC", cs35l56_sdw1tx6_enum); - static int cs35l56_play_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -251,8 +233,6 @@ static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = { SND_SOC_DAPM_MUX("SDW1 TX2 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx2_mux), SND_SOC_DAPM_MUX("SDW1 TX3 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx3_mux), SND_SOC_DAPM_MUX("SDW1 TX4 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx4_mux), - SND_SOC_DAPM_MUX("SDW1 TX5 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx5_mux), - SND_SOC_DAPM_MUX("SDW1 TX6 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx6_mux), SND_SOC_DAPM_SIGGEN("VMON ADC"), SND_SOC_DAPM_SIGGEN("IMON ADC"), @@ -318,14 +298,10 @@ static const struct snd_soc_dapm_route cs35l56_audio_map[] = { CS35L56_SRC_ROUTE("SDW1 TX2") CS35L56_SRC_ROUTE("SDW1 TX3") CS35L56_SRC_ROUTE("SDW1 TX4") - CS35L56_SRC_ROUTE("SDW1 TX5") - CS35L56_SRC_ROUTE("SDW1 TX6") { "SDW1 Capture", NULL, "SDW1 TX1 Source" }, { "SDW1 Capture", NULL, "SDW1 TX2 Source" }, { "SDW1 Capture", NULL, "SDW1 TX3 Source" }, { "SDW1 Capture", NULL, "SDW1 TX4 Source" }, - { "SDW1 Capture", NULL, "SDW1 TX5 Source" }, - { "SDW1 Capture", NULL, "SDW1 TX6 Source" }, }; static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, @@ -779,7 +755,7 @@ static struct snd_soc_dai_driver cs35l56_dai[] = { .capture = { .stream_name = "SDW1 Capture", .channels_min = 1, - .channels_max = 6, + .channels_max = 4, .rates = CS35L56_RATES, .formats = CS35L56_TX_FORMATS, }, @@ -1535,8 +1511,6 @@ int cs35l56_init(struct cs35l56_private *cs35l56) dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", cs35l56->secured ? "s" : "", cs35l56->rev, otpid); - cs35l56_patch(cs35l56->dev, cs35l56->regmap, cs35l56->rev); - /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1, -- cgit v1.2.3 From d3a4efb334e5f6cbb3f2741fa07a873a2a78b016 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2023 15:43:08 +0100 Subject: ASoC: cs35l56: Remove SDW2RX1 mixer source The mixer source index value for SDW2RX1 is different between A1 and B0 silicon. As the driver doesn't provide a DAI for SDW2 just remove it as a mixer source option. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20230418144309.1100721-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 3 +-- sound/soc/codecs/cs35l56-shared.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index dd7503464651..0b2f7cfc6a4a 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -181,10 +181,9 @@ #define CS35L56_INPUT_SRC_INTERPOLATOR 0x40 #define CS35L56_INPUT_SRC_SWIRE_RX1 0x44 #define CS35L56_INPUT_SRC_SWIRE_RX2 0x45 -#define CS35L56_INPUT_SRC_SWIRE_RX3 0x46 #define CS35L56_INPUT_MASK 0x7F -#define CS35L56_NUM_INPUT_SRC 22 +#define CS35L56_NUM_INPUT_SRC 21 /* ASP formats */ #define CS35L56_ASP_FMT_DSP_A 0 diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index f5fa6bb04d38..93a1b056660b 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -278,7 +278,7 @@ const char * const cs35l56_tx_input_texts[] = { "None", "ASP1RX1", "ASP1RX2", "VMON", "IMON", "ERRVOL", "CLASSH", "VDDBMON", "VBSTMON", "DSP1TX1", "DSP1TX2", "DSP1TX3", "DSP1TX4", "DSP1TX5", "DSP1TX6", "DSP1TX7", "DSP1TX8", "TEMPMON", - "INTERPOLATOR", "SDW1RX1", "SDW1RX2", "SDW2RX1", + "INTERPOLATOR", "SDW1RX1", "SDW1RX2", }; EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_texts, SND_SOC_CS35L56_SHARED); @@ -304,7 +304,6 @@ const unsigned int cs35l56_tx_input_values[] = { CS35L56_INPUT_SRC_INTERPOLATOR, CS35L56_INPUT_SRC_SWIRE_RX1, CS35L56_INPUT_SRC_SWIRE_RX2, - CS35L56_INPUT_SRC_SWIRE_RX3, }; EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_values, SND_SOC_CS35L56_SHARED); -- cgit v1.2.3 From d29a966b72fb370128096393961f2c456ff24e3d Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2023 15:43:09 +0100 Subject: ASoC: cs35l56: Rename mixer source defines for SoundWire DP1 Rename the mixer source defines from CS35L56_INPUT_SRC_SWIRE_RXn to CS35L56_INPUT_SRC_SWIRE_DP1_CHANNELn to match the latest datasheet. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20230418144309.1100721-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 4 ++-- sound/soc/codecs/cs35l56-shared.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 0b2f7cfc6a4a..002042b1c73c 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -179,8 +179,8 @@ #define CS35L56_INPUT_SRC_DSP1TX8 0x39 #define CS35L56_INPUT_SRC_TEMPMON 0x3A #define CS35L56_INPUT_SRC_INTERPOLATOR 0x40 -#define CS35L56_INPUT_SRC_SWIRE_RX1 0x44 -#define CS35L56_INPUT_SRC_SWIRE_RX2 0x45 +#define CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL1 0x44 +#define CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL2 0x45 #define CS35L56_INPUT_MASK 0x7F #define CS35L56_NUM_INPUT_SRC 21 diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 93a1b056660b..60da8c75b7b9 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -302,8 +302,8 @@ const unsigned int cs35l56_tx_input_values[] = { CS35L56_INPUT_SRC_DSP1TX8, CS35L56_INPUT_SRC_TEMPMON, CS35L56_INPUT_SRC_INTERPOLATOR, - CS35L56_INPUT_SRC_SWIRE_RX1, - CS35L56_INPUT_SRC_SWIRE_RX2, + CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL1, + CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL2, }; EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_values, SND_SOC_CS35L56_SHARED); -- cgit v1.2.3 From 9f656705c5faa18afb26d922cfc64f9fd103c38d Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 20 Apr 2023 13:33:23 +0200 Subject: ALSA: pcm: rewrite snd_pcm_playback_silence() The auto-silencer supports two modes: "thresholded" to fill up "just enough", and "top-up" to fill up "as much as possible". The two modes used rather distinct code paths, which this patch unifies. The only remaining distinction is how much we actually want to fill. This fixes a bug in thresholded mode, where we failed to use new_hw_ptr, resulting in under-fill. Top-up mode is now more well-behaved and much easier to understand in corner cases. This also updates comments in the proximity of silencing-related data structures. Signed-off-by: Oswald Buddenhagen Reviewed-by: Jaroslav Kysela Link: https://lore.kernel.org/r/20230420113324.877164-1-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- .../sound/kernel-api/writing-an-alsa-driver.rst | 17 +++-- include/sound/pcm.h | 14 ++-- include/uapi/sound/asound.h | 11 ++- sound/core/pcm_lib.c | 86 +++++++++------------- sound/core/pcm_local.h | 3 +- sound/core/pcm_native.c | 6 +- 6 files changed, 66 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst index a368529e8ed3..e37d9dba320d 100644 --- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst +++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -1577,14 +1577,19 @@ are the contents of this file: unsigned int period_step; unsigned int sleep_min; /* min ticks to sleep */ snd_pcm_uframes_t start_threshold; - snd_pcm_uframes_t stop_threshold; - snd_pcm_uframes_t silence_threshold; /* Silence filling happens when - noise is nearest than this */ - snd_pcm_uframes_t silence_size; /* Silence filling size */ + /* + * The following two thresholds alleviate playback buffer underruns; when + * hw_avail drops below the threshold, the respective action is triggered: + */ + snd_pcm_uframes_t stop_threshold; /* - stop playback */ + snd_pcm_uframes_t silence_threshold; /* - pre-fill buffer with silence */ + snd_pcm_uframes_t silence_size; /* max size of silence pre-fill; when >= boundary, + * fill played area with silence immediately */ snd_pcm_uframes_t boundary; /* pointers wrap point */ - snd_pcm_uframes_t silenced_start; - snd_pcm_uframes_t silenced_size; + /* internal data of auto-silencer */ + snd_pcm_uframes_t silence_start; /* starting pointer to silence area */ + snd_pcm_uframes_t silence_filled; /* size filled with silence */ snd_pcm_sync_id_t sync; /* hardware synchronization ID */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 27040b472a4f..19f564606ac4 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -378,18 +378,18 @@ struct snd_pcm_runtime { unsigned int rate_den; unsigned int no_period_wakeup: 1; - /* -- SW params -- */ - int tstamp_mode; /* mmap timestamp is updated */ + /* -- SW params; see struct snd_pcm_sw_params for comments -- */ + int tstamp_mode; unsigned int period_step; snd_pcm_uframes_t start_threshold; snd_pcm_uframes_t stop_threshold; - snd_pcm_uframes_t silence_threshold; /* Silence filling happens when - noise is nearest than this */ - snd_pcm_uframes_t silence_size; /* Silence filling size */ - snd_pcm_uframes_t boundary; /* pointers wrap point */ + snd_pcm_uframes_t silence_threshold; + snd_pcm_uframes_t silence_size; + snd_pcm_uframes_t boundary; + /* internal data of auto-silencer */ snd_pcm_uframes_t silence_start; /* starting pointer to silence area */ - snd_pcm_uframes_t silence_filled; /* size filled with silence */ + snd_pcm_uframes_t silence_filled; /* already filled part of silence area */ union snd_pcm_sync_id sync; /* hardware synchronization ID */ diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 7eecc99ddef7..0aa955aa8246 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -429,9 +429,14 @@ struct snd_pcm_sw_params { snd_pcm_uframes_t avail_min; /* min avail frames for wakeup */ snd_pcm_uframes_t xfer_align; /* obsolete: xfer size need to be a multiple */ snd_pcm_uframes_t start_threshold; /* min hw_avail frames for automatic start */ - snd_pcm_uframes_t stop_threshold; /* min avail frames for automatic stop */ - snd_pcm_uframes_t silence_threshold; /* min distance from noise for silence filling */ - snd_pcm_uframes_t silence_size; /* silence block size */ + /* + * The following two thresholds alleviate playback buffer underruns; when + * hw_avail drops below the threshold, the respective action is triggered: + */ + snd_pcm_uframes_t stop_threshold; /* - stop playback */ + snd_pcm_uframes_t silence_threshold; /* - pre-fill buffer with silence */ + snd_pcm_uframes_t silence_size; /* max size of silence pre-fill; when >= boundary, + * fill played area with silence immediately */ snd_pcm_uframes_t boundary; /* pointers wrap point */ unsigned int proto; /* protocol version */ unsigned int tstamp_type; /* timestamp type (req. proto >= 2.0.12) */ diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index af1eb136feb0..d21c73944efd 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -42,70 +42,56 @@ static int fill_silence_frames(struct snd_pcm_substream *substream, * * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately */ -void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr) +void snd_pcm_playback_silence(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t frames, ofs, transfer; + snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr); + snd_pcm_sframes_t added, hw_avail, frames; + snd_pcm_uframes_t noise_dist, ofs, transfer; int err; + added = appl_ptr - runtime->silence_start; + if (added) { + if (added < 0) + added += runtime->boundary; + if (added < runtime->silence_filled) + runtime->silence_filled -= added; + else + runtime->silence_filled = 0; + runtime->silence_start = appl_ptr; + } + + // This will "legitimately" turn negative on underrun, and will be mangled + // into a huge number by the boundary crossing handling. The initial state + // might also be not quite sane. The code below MUST account for these cases. + hw_avail = appl_ptr - runtime->status->hw_ptr; + if (hw_avail < 0) + hw_avail += runtime->boundary; + + noise_dist = hw_avail + runtime->silence_filled; if (runtime->silence_size < runtime->boundary) { - snd_pcm_sframes_t noise_dist, n; - snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr); - if (runtime->silence_start != appl_ptr) { - n = appl_ptr - runtime->silence_start; - if (n < 0) - n += runtime->boundary; - if ((snd_pcm_uframes_t)n < runtime->silence_filled) - runtime->silence_filled -= n; - else - runtime->silence_filled = 0; - runtime->silence_start = appl_ptr; - } - if (runtime->silence_filled >= runtime->buffer_size) - return; - noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; - if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) - return; frames = runtime->silence_threshold - noise_dist; + if (frames <= 0) + return; if (frames > runtime->silence_size) frames = runtime->silence_size; } else { - if (new_hw_ptr == ULONG_MAX) { /* initialization */ - snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); - if (avail > runtime->buffer_size) - avail = runtime->buffer_size; - runtime->silence_filled = avail > 0 ? avail : 0; - runtime->silence_start = (runtime->status->hw_ptr + - runtime->silence_filled) % - runtime->boundary; - } else { - ofs = runtime->status->hw_ptr; - frames = new_hw_ptr - ofs; - if ((snd_pcm_sframes_t)frames < 0) - frames += runtime->boundary; - runtime->silence_filled -= frames; - if ((snd_pcm_sframes_t)runtime->silence_filled < 0) { - runtime->silence_filled = 0; - runtime->silence_start = new_hw_ptr; - } else { - runtime->silence_start = ofs; - } - } - frames = runtime->buffer_size - runtime->silence_filled; + frames = runtime->buffer_size - noise_dist; + if (frames <= 0) + return; } + if (snd_BUG_ON(frames > runtime->buffer_size)) return; - if (frames == 0) - return; - ofs = runtime->silence_start % runtime->buffer_size; - while (frames > 0) { + ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size; + do { transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; err = fill_silence_frames(substream, ofs, transfer); snd_BUG_ON(err < 0); runtime->silence_filled += transfer; frames -= transfer; ofs = 0; - } + } while (frames > 0); snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); } @@ -439,10 +425,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, return 0; } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - runtime->silence_size > 0) - snd_pcm_playback_silence(substream, new_hw_ptr); - if (in_interrupt) { delta = new_hw_ptr - runtime->hw_ptr_interrupt; if (delta < 0) @@ -460,6 +442,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, runtime->hw_ptr_wrap += runtime->boundary; } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + runtime->silence_size > 0) + snd_pcm_playback_silence(substream); + update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return snd_pcm_update_state(substream, runtime); diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h index ecb21697ae3a..42fe3a4e9154 100644 --- a/sound/core/pcm_local.h +++ b/sound/core/pcm_local.h @@ -29,8 +29,7 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime); int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); -void snd_pcm_playback_silence(struct snd_pcm_substream *substream, - snd_pcm_uframes_t new_hw_ptr); +void snd_pcm_playback_silence(struct snd_pcm_substream *substream); static inline snd_pcm_uframes_t snd_pcm_avail(struct snd_pcm_substream *substream) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 94185267a7b9..3d0c4a5b701b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -958,7 +958,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (snd_pcm_running(substream)) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) - snd_pcm_playback_silence(substream, ULONG_MAX); + snd_pcm_playback_silence(substream); err = snd_pcm_update_state(substream, runtime); } snd_pcm_stream_unlock_irq(substream); @@ -1455,7 +1455,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, __snd_pcm_set_state(runtime, state); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) - snd_pcm_playback_silence(substream, ULONG_MAX); + snd_pcm_playback_silence(substream); snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART); } @@ -1916,7 +1916,7 @@ static void snd_pcm_post_reset(struct snd_pcm_substream *substream, runtime->control->appl_ptr = runtime->status->hw_ptr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) - snd_pcm_playback_silence(substream, ULONG_MAX); + snd_pcm_playback_silence(substream); snd_pcm_stream_unlock_irq(substream); } -- cgit v1.2.3 From e81995a81e25e8fab286db6539198dd97b140807 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Fri, 21 Apr 2023 16:10:06 +0200 Subject: ALSA: emu10k1: clarify various fx8010.*_mask fields extin_mask and extout_mask are used only by the SbLive! microcode, so they have no effect on Audigy. Eliminate fxbus_mask entirely, as it wasn't actually used for anything. As a drive-by, remove the pointless pad1 field from struct snd_emu10k1_fx8010 - it is not visible to user space, so it has no binary compatibility constraints. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230421141006.1005509-1-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 6 ++---- sound/pci/emu10k1/emu10k1_main.c | 7 ++++--- sound/pci/emu10k1/emufx.c | 9 ++++----- 3 files changed, 10 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 39787fecc8d9..3407ca4a1210 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1599,10 +1599,8 @@ struct snd_emu10k1_fx8010_pcm { }; struct snd_emu10k1_fx8010 { - unsigned short fxbus_mask; /* used FX buses (bitmask) */ - unsigned short extin_mask; /* used external inputs (bitmask) */ - unsigned short extout_mask; /* used external outputs (bitmask) */ - unsigned short pad1; + unsigned short extin_mask; /* used external inputs (bitmask); not used for Audigy */ + unsigned short extout_mask; /* used external outputs (bitmask); not used for Audigy */ unsigned int itram_size; /* internal TRAM size in samples */ struct snd_dma_buffer etram_pages; /* external TRAM pages and size */ unsigned int dbg; /* FX debugger register */ diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 3880f359e688..65fd6b62bc9c 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1901,11 +1901,12 @@ int snd_emu10k1_create(struct snd_card *card, pci_set_master(pci); - emu->fx8010.fxbus_mask = 0x303f; + // The masks are not used for Audigy. + // FIXME: these should come from the card_capabilites table. if (extin_mask == 0) - extin_mask = 0x3fcf; + extin_mask = 0x3fcf; // EXTIN_* if (extout_mask == 0) - extout_mask = 0x7fff; + extout_mask = 0x7fff; // EXTOUT_* emu->fx8010.extin_mask = extin_mask; emu->fx8010.extout_mask = extout_mask; emu->enable_ir = enable_ir; diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 6cf7c8b1de47..c74e66e03ae0 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -2523,7 +2523,7 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, struct snd_emu10k1_fx8010_info *info) { const char * const *fxbus, * const *extin, * const *extout; - unsigned short fxbus_mask, extin_mask, extout_mask; + unsigned short extin_mask, extout_mask; int res; info->internal_tram_size = emu->fx8010.itram_size; @@ -2531,11 +2531,10 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, fxbus = fxbuses; extin = emu->audigy ? audigy_ins : creative_ins; extout = emu->audigy ? audigy_outs : creative_outs; - fxbus_mask = emu->fx8010.fxbus_mask; - extin_mask = emu->fx8010.extin_mask; - extout_mask = emu->fx8010.extout_mask; + extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask; + extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask; for (res = 0; res < 16; res++, fxbus++, extin++, extout++) { - copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res); + copy_string(info->fxbus_names[res], *fxbus, "FXBUS", res); copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res); copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); } -- cgit v1.2.3 From d4af7ca20173e61b77584e4f2025387b7b76ef75 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Fri, 21 Apr 2023 16:10:02 +0200 Subject: ALSA: emu10k1: remove obsolete card type variable and defines The use of the variable was removed in commit 2b637da5a1b ("clean up card features"). That commit also broke user space (the ioctl structure), at which point the defines became meaningless, so I don't think purging them is a problem. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230421141006.1005452-3-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 1 - include/uapi/sound/emu10k1.h | 3 --- 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 3407ca4a1210..ddc1e71a8de5 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1689,7 +1689,6 @@ struct snd_emu10k1 { unsigned int revision; /* chip revision */ unsigned int serial; /* serial number */ unsigned short model; /* subsystem id */ - unsigned int card_type; /* EMU10K1_CARD_* */ unsigned int ecard_ctrl; /* ecard control bits */ unsigned int address_mode; /* address mode */ unsigned long dma_mask; /* PCI DMA mask */ diff --git a/include/uapi/sound/emu10k1.h b/include/uapi/sound/emu10k1.h index 1c1f1dd44611..c2414bd5aecd 100644 --- a/include/uapi/sound/emu10k1.h +++ b/include/uapi/sound/emu10k1.h @@ -15,9 +15,6 @@ * ---- FX8010 ---- */ -#define EMU10K1_CARD_CREATIVE 0x00000000 -#define EMU10K1_CARD_EMUAPS 0x00000001 - #define EMU10K1_FX8010_PCM_COUNT 8 /* -- cgit v1.2.3 From 14a5c5a44b61953b3f84a01152fe1d40838f5d91 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Fri, 21 Apr 2023 16:10:04 +0200 Subject: ALSA: emu10k1: remove unused snd_emu10k1_voice.emu field It was written, but never read from. Its value is available via the epcm field. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230421141006.1005452-5-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 1 - sound/pci/emu10k1/emu10k1_main.c | 4 +--- sound/pci/emu10k1/p16v.c | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index ddc1e71a8de5..a1ea022bcdc8 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1485,7 +1485,6 @@ enum { struct snd_emu10k1; struct snd_emu10k1_voice { - struct snd_emu10k1 *emu; int number; unsigned int use: 1, pcm: 1, diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 65fd6b62bc9c..4af7c95fc665 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1971,10 +1971,8 @@ int snd_emu10k1_create(struct snd_card *card, pgtbl[idx] = cpu_to_le32(silent_page | idx); /* set up voice indices */ - for (idx = 0; idx < NUM_G; idx++) { - emu->voices[idx].emu = emu; + for (idx = 0; idx < NUM_G; idx++) emu->voices[idx].number = idx; - } err = snd_emu10k1_init(emu, enable_ir, 0); if (err < 0) diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index 18a1b0740e6b..f5e0972187a7 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -181,7 +181,6 @@ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substrea runtime->hw = snd_p16v_playback_hw; - channel->emu = emu; channel->number = channel_id; channel->use=1; @@ -230,7 +229,6 @@ static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream runtime->hw = snd_p16v_capture_hw; - channel->emu = emu; channel->number = channel_id; channel->use=1; -- cgit v1.2.3 From 02a0d9c281401d4e1eb0f83ed337c8c6a70194d2 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Fri, 21 Apr 2023 16:10:06 +0200 Subject: ALSA: emu10k1: clean up P16V part somewhat Detach it better from the main PCM driver, which it really doesn't have much in common with. In particular, this moves the interrupt handler implementation into p16v.c, and makes it access the substream runtime status more directly, so it doesn't need to abuse structs snd_emu10k1_pcm and snd_emu10k1_voice any more. We don't need private pcm runtime data at all, as the only thing it was used for (except the back-link to the substream) was the `running` flag. So store that directly in runtime->private_data. This somewhat radical strip-down shows that this driver contains some complexity that was never actually utilized. I suppose the right way to fully utilize the hardware in a simple way would be introducing more substreams. This wouldn't require any of the removed code. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230421141006.1005452-7-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 3 +- sound/pci/emu10k1/irq.c | 32 +++----------- sound/pci/emu10k1/p16v.c | 111 +++++++++++++++++++---------------------------- 3 files changed, 50 insertions(+), 96 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index a1ea022bcdc8..ba2d48b38710 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1735,8 +1735,6 @@ struct snd_emu10k1 { spinlock_t i2c_lock; /* serialises access to i2c port */ struct snd_emu10k1_voice voices[NUM_G]; - struct snd_emu10k1_voice p16v_voices[4]; - struct snd_emu10k1_voice p16v_capture_voice; int p16v_device_offset; u32 p16v_capture_source; u32 p16v_capture_channel; @@ -1756,6 +1754,7 @@ struct snd_emu10k1 { void (*capture_efx_interrupt)(struct snd_emu10k1 *emu, unsigned int status); void (*spdif_interrupt)(struct snd_emu10k1 *emu, unsigned int status); void (*dsp_interrupt)(struct snd_emu10k1 *emu); + void (*p16v_interrupt)(struct snd_emu10k1 *emu); struct snd_pcm_substream *pcm_capture_substream; struct snd_pcm_substream *pcm_capture_mic_substream; diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c index ebb2275efb6c..dfb44e5e69a7 100644 --- a/sound/pci/emu10k1/irq.c +++ b/sound/pci/emu10k1/irq.c @@ -18,7 +18,7 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) { struct snd_emu10k1 *emu = dev_id; - unsigned int status, status2, orig_status, orig_status2; + unsigned int status, orig_status; int handled = 0; int timeout = 0; @@ -139,32 +139,10 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) status &= ~IPR_FXDSP; } if (status & IPR_P16V) { - while ((status2 = inl(emu->port + IPR2)) != 0) { - u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */ - struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]); - struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice); - - /* dev_dbg(emu->card->dev, "status2=0x%x\n", status2); */ - orig_status2 = status2; - if(status2 & mask) { - if(pvoice->use) { - snd_pcm_period_elapsed(pvoice->epcm->substream); - } else { - dev_err(emu->card->dev, - "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", - status2, mask, pvoice, - pvoice->use); - } - } - if(status2 & 0x110000) { - /* dev_info(emu->card->dev, "capture int found\n"); */ - if(cvoice->use) { - /* dev_info(emu->card->dev, "capture period_elapsed\n"); */ - snd_pcm_period_elapsed(cvoice->epcm->substream); - } - } - outl(orig_status2, emu->port + IPR2); /* ack all */ - } + if (emu->p16v_interrupt) + emu->p16v_interrupt(emu); + else + outl(0, emu->port + INTE2); status &= ~IPR_P16V; } diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index f5e0972187a7..ce4d3450959c 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -149,41 +149,19 @@ static const struct snd_pcm_hardware snd_p16v_capture_hw = { .fifo_size = 0, }; -static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime) -{ - struct snd_emu10k1_pcm *epcm = runtime->private_data; - - kfree(epcm); -} - /* open_playback callback */ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id) { - struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); - struct snd_emu10k1_voice *channel = &(emu->p16v_voices[channel_id]); - struct snd_emu10k1_pcm *epcm; struct snd_pcm_runtime *runtime = substream->runtime; int err; - epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); - /* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */ - - if (epcm == NULL) - return -ENOMEM; - epcm->emu = emu; - epcm->substream = substream; /* dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id); */ - runtime->private_data = epcm; - runtime->private_free = snd_p16v_pcm_free_substream; runtime->hw = snd_p16v_playback_hw; - channel->number = channel_id; - - channel->use=1; #if 0 /* debug */ dev_dbg(emu->card->dev, "p16v: open channel_id=%d, channel=%p, use=0x%x\n", @@ -192,7 +170,6 @@ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substrea channel_id, chip, channel); #endif /* debug */ /* channel->interrupt = snd_p16v_pcm_channel_interrupt; */ - channel->epcm = epcm; err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) return err; @@ -204,43 +181,20 @@ static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substrea return 0; } + /* open_capture callback */ static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream, int channel_id) { - struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); - struct snd_emu10k1_voice *channel = &(emu->p16v_capture_voice); - struct snd_emu10k1_pcm *epcm; struct snd_pcm_runtime *runtime = substream->runtime; int err; - epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); - /* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */ - - if (epcm == NULL) - return -ENOMEM; - epcm->emu = emu; - epcm->substream = substream; /* dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id); */ - runtime->private_data = epcm; - runtime->private_free = snd_p16v_pcm_free_substream; runtime->hw = snd_p16v_capture_hw; - channel->number = channel_id; - - channel->use=1; -#if 0 /* debug */ - dev_dbg(emu->card->dev, - "p16v: open channel_id=%d, channel=%p, use=0x%x\n", - channel_id, channel, channel->use); - dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n", - channel_id, chip, channel); -#endif /* debug */ - /* channel->interrupt = snd_p16v_pcm_channel_interrupt; */ - channel->epcm = epcm; err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) return err; @@ -252,22 +206,12 @@ static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream /* close callback */ static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream) { - struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); - //struct snd_pcm_runtime *runtime = substream->runtime; - //struct snd_emu10k1_pcm *epcm = runtime->private_data; - emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use = 0; - /* FIXME: maybe zero others */ return 0; } /* close callback */ static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream) { - struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); - //struct snd_pcm_runtime *runtime = substream->runtime; - //struct snd_emu10k1_pcm *epcm = runtime->private_data; - emu->p16v_capture_voice.use = 0; - /* FIXME: maybe zero others */ return 0; } @@ -409,13 +353,48 @@ static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb) spin_unlock_irqrestore(&emu->emu_lock, flags); } +static void snd_p16v_interrupt(struct snd_emu10k1 *emu) +{ + unsigned int status; + + while ((status = inl(emu->port + IPR2)) != 0) { + u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */ + + /* dev_dbg(emu->card->dev, "p16v status=0x%x\n", status); */ + if (status & mask) { + struct snd_pcm_substream *substream = + emu->pcm_p16v->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_pcm_runtime *runtime = substream->runtime; + + if (runtime && runtime->private_data) { + snd_pcm_period_elapsed(substream); + } else { + dev_err(emu->card->dev, + "p16v: status: 0x%08x, mask=0x%08x\n", + status, mask); + } + } + if (status & 0x110000) { + struct snd_pcm_substream *substream = + emu->pcm_p16v->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + struct snd_pcm_runtime *runtime = substream->runtime; + + /* dev_info(emu->card->dev, "capture int found\n"); */ + if (runtime && runtime->private_data) { + /* dev_info(emu->card->dev, "capture period_elapsed\n"); */ + snd_pcm_period_elapsed(substream); + } + } + outl(status, emu->port + IPR2); /* ack all */ + } +} + /* trigger_playback callback */ static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream, int cmd) { struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime; - struct snd_emu10k1_pcm *epcm; int channel; int result = 0; struct snd_pcm_substream *s; @@ -437,10 +416,9 @@ static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream, s->stream != SNDRV_PCM_STREAM_PLAYBACK) continue; runtime = s->runtime; - epcm = runtime->private_data; channel = substream->pcm->device-emu->p16v_device_offset; /* dev_dbg(emu->card->dev, "p16v channel=%d\n", channel); */ - epcm->running = running; + runtime->private_data = (void *)(ptrdiff_t)running; basic |= (0x1<runtime; - struct snd_emu10k1_pcm *epcm = runtime->private_data; int channel = 0; int result = 0; u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP; @@ -478,13 +455,13 @@ static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: snd_p16v_intr_enable(emu, inte); snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<running = 1; + runtime->private_data = (void *)1; break; case SNDRV_PCM_TRIGGER_STOP: snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<running = 0; + runtime->private_data = NULL; break; default: result = -EINVAL; @@ -499,10 +476,10 @@ snd_p16v_pcm_pointer_playback(struct snd_pcm_substream *substream) { struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_emu10k1_pcm *epcm = runtime->private_data; snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0; int channel = substream->pcm->device - emu->p16v_device_offset; - if (!epcm->running) + + if (!runtime->private_data) return 0; ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel); @@ -524,11 +501,10 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream) { struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_emu10k1_pcm *epcm = runtime->private_data; snd_pcm_uframes_t ptr, ptr1, ptr2 = 0; int channel = 0; - if (!epcm->running) + if (!runtime->private_data) return 0; ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel); @@ -589,6 +565,7 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device) pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; strcpy(pcm->name, "p16v"); emu->pcm_p16v = pcm; + emu->p16v_interrupt = snd_p16v_interrupt; for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; -- cgit v1.2.3 From 10f212bd7a693e379154891cc16959bc4884668a Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Fri, 21 Apr 2023 16:10:00 +0200 Subject: ALSA: emu10k1: properly assert E-MU FPGA access constaints Assert the validity of the registers and values, as them being out of range would indicate an error in the driver. Consequently, don't bother returning error codes; they were ignored everywhere anyway. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230421141006.1005539-1-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 6 +++--- sound/pci/emu10k1/io.c | 36 +++++++++++++++++------------------- 2 files changed, 20 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index ba2d48b38710..00c2d8cd5a8d 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1819,9 +1819,9 @@ unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data); int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int data); int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, u32 reg, u32 value); -int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value); -int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value); -int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src); +void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value); +void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value); +void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src); unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc); void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb); void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb); diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index e15092ce9848..35bc73d99d04 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -233,15 +233,15 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, return err; } -int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) +void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value) { unsigned long flags; - if (reg > 0x3f) - return 1; + if (snd_BUG_ON(reg > 0x3f)) + return; reg += 0x40; /* 0x40 upwards are registers. */ - if (value > 0x3f) /* 0 to 0x3f are values */ - return 1; + if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */ + return; spin_lock_irqsave(&emu->emu_lock, flags); outl(reg, emu->port + A_IOCFG); udelay(10); @@ -251,15 +251,13 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) udelay(10); outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ spin_unlock_irqrestore(&emu->emu_lock, flags); - - return 0; } -int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value) +void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value) { unsigned long flags; - if (reg > 0x3f) - return 1; + if (snd_BUG_ON(reg > 0x3f)) + return; reg += 0x40; /* 0x40 upwards are registers. */ spin_lock_irqsave(&emu->emu_lock, flags); outl(reg, emu->port + A_IOCFG); @@ -268,21 +266,21 @@ int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value) udelay(10); *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f); spin_unlock_irqrestore(&emu->emu_lock, flags); - - return 0; } /* Each Destination has one and only one Source, * but one Source can feed any number of Destinations simultaneously. */ -int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src) +void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src) { - snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) ); - snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) ); - snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) ); - snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) ); - - return 0; + if (snd_BUG_ON(dst & ~0x71f)) + return; + if (snd_BUG_ON(src & ~0x71f)) + return; + snd_emu1010_fpga_write(emu, 0x00, dst >> 8); + snd_emu1010_fpga_write(emu, 0x01, dst & 0x1f); + snd_emu1010_fpga_write(emu, 0x02, src >> 8); + snd_emu1010_fpga_write(emu, 0x03, src & 0x1f); } void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb) -- cgit v1.2.3 From a1c87c0b27059b4155c7aba6b34810c889e8b6a9 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Fri, 21 Apr 2023 16:10:01 +0200 Subject: ALSA: emu10k1: fix access to Audigy GPIO port As the register definition clearly states, this is a 16-bit register, yet we did all accesses as 32-bit. The writes in particular would have the potential to clear the TIMER register (depending on how the bus/card actually handles the too long writes). This commit also introduces a separate define A_GPIO which aliases A_IOCFG, which better reflects the distinct usage on E-MU cards. This is done in the same commit to keep the churn down, as we're touching all involved lines anyway. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230421141006.1005539-2-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 7 ++++- sound/pci/emu10k1/emu10k1_main.c | 62 ++++++++++++++++++++-------------------- sound/pci/emu10k1/emumixer.c | 14 ++++----- sound/pci/emu10k1/io.c | 14 ++++----- 4 files changed, 51 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 00c2d8cd5a8d..89dbd2e93410 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -251,11 +251,16 @@ #define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */ #define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ -#define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */ +#define A_GPIO 0x18 /* GPIO on Audigy card (16bits) */ #define A_GPINPUT_MASK 0xff00 #define A_GPOUTPUT_MASK 0x00ff +// The GPIO port is used for I/O config on Sound Blasters; +// card-specific info can be found in the emu_chip_details table. +// On E-MU cards the port is used as the interface to the FPGA. + // Audigy output/GPIO stuff taken from the kX drivers +#define A_IOCFG A_GPIO #define A_IOCFG_GPOUT0 0x0044 /* analog/digital */ #define A_IOCFG_DISABLE_ANALOG 0x0040 /* = 'enable' for Audigy2 (chiprev=4) */ #define A_IOCFG_ENABLE_DIGITAL 0x0004 diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 4af7c95fc665..29b6da68ca6c 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -223,8 +223,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) */ outl(0x7a0000, emu->port + 0x20); outl(0xFF000000, emu->port + 0x24); - tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */ - outl(tmp, emu->port + A_IOCFG); + tmp = inw(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */ + outw(tmp, emu->port + A_IOCFG); } if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */ int size, n; @@ -244,15 +244,15 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) * GPIO6: Unknown * GPIO7: Unknown */ - outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */ + outw(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */ } if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */ int size, n; snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f); - tmp = inl(emu->port + A_IOCFG); - outl(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */ - tmp = inl(emu->port + A_IOCFG); + tmp = inw(emu->port + A_IOCFG); + outw(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */ + tmp = inw(emu->port + A_IOCFG); size = ARRAY_SIZE(i2c_adc_init); for (n = 0; n < size; n++) snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]); @@ -308,12 +308,12 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ } else if (emu->audigy) { - unsigned int reg = inl(emu->port + A_IOCFG); - outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG); + u16 reg = inw(emu->port + A_IOCFG); + outw(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG); udelay(500); - outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG); + outw(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG); udelay(100); - outl(reg, emu->port + A_IOCFG); + outw(reg, emu->port + A_IOCFG); } else { unsigned int reg = inl(emu->port + HCFG); outl(reg | HCFG_GPOUT2, emu->port + HCFG); @@ -329,8 +329,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ } else if (emu->audigy) { /* enable analog output */ - unsigned int reg = inl(emu->port + A_IOCFG); - outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG); + u16 reg = inw(emu->port + A_IOCFG); + outw(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG); } if (emu->address_mode == 0) { @@ -354,19 +354,19 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu) } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ } else if (emu->audigy) { - outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); + outw(inw(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ /* Unmute Analog now. Set GPO6 to 1 for Apollo. * This has to be done after init ALice3 I2SOut beyond 48KHz. * So, sequence is important. */ - outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG); + outw(inw(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG); } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */ /* Unmute Analog now. */ - outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG); + outw(inw(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG); } else { /* Disable routing from AC97 line out to Front speakers */ - outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG); + outw(inw(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG); } } @@ -651,9 +651,9 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, const struct firmware *fw_entry) { int n, i; - int reg; - int value; - __always_unused unsigned int write_post; + u16 reg; + u8 value; + __always_unused u16 write_post; unsigned long flags; if (!fw_entry) @@ -666,11 +666,11 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, * FPGA CONFIG OFF -> FPGA PGMN */ spin_lock_irqsave(&emu->emu_lock, flags); - outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */ - write_post = inl(emu->port + A_IOCFG); + outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 1uS. */ + write_post = inw(emu->port + A_GPIO); udelay(100); - outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */ - write_post = inl(emu->port + A_IOCFG); + outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */ + write_post = inw(emu->port + A_GPIO); udelay(100); /* Allow FPGA memory to clean */ for (n = 0; n < fw_entry->size; n++) { value = fw_entry->data[n]; @@ -679,15 +679,15 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, if (value & 0x1) reg = reg | 0x20; value = value >> 1; - outl(reg, emu->port + A_IOCFG); - write_post = inl(emu->port + A_IOCFG); - outl(reg | 0x40, emu->port + A_IOCFG); - write_post = inl(emu->port + A_IOCFG); + outw(reg, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); + outw(reg | 0x40, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); } } /* After programming, set GPIO bit 4 high again. */ - outl(0x10, emu->port + A_IOCFG); - write_post = inl(emu->port + A_IOCFG); + outw(0x10, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); spin_unlock_irqrestore(&emu->emu_lock, flags); return 0; @@ -2053,7 +2053,7 @@ void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu) *val = snd_emu10k1_ptr_read(emu, *reg, i); } if (emu->audigy) - emu->saved_a_iocfg = inl(emu->port + A_IOCFG); + emu->saved_a_iocfg = inw(emu->port + A_IOCFG); emu->saved_hcfg = inl(emu->port + HCFG); } @@ -2080,7 +2080,7 @@ void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu) /* resore for spdif */ if (emu->audigy) - outl(emu->saved_a_iocfg, emu->port + A_IOCFG); + outw(emu->saved_a_iocfg, emu->port + A_IOCFG); outl(emu->saved_hcfg, emu->port + HCFG); val = emu->saved_ptr; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 3c115f8ab96c..754d91050af2 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -924,7 +924,7 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol, struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); unsigned int source_id; unsigned int ngain, ogain; - u32 gpio; + u16 gpio; int change = 0; unsigned long flags; u32 source; @@ -941,11 +941,11 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol, if (change) { snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */ spin_lock_irqsave(&emu->emu_lock, flags); - gpio = inl(emu->port + A_IOCFG); + gpio = inw(emu->port + A_IOCFG); if (source_id==0) - outl(gpio | 0x4, emu->port + A_IOCFG); + outw(gpio | 0x4, emu->port + A_IOCFG); else - outl(gpio & ~0x4, emu->port + A_IOCFG); + outw(gpio & ~0x4, emu->port + A_IOCFG); spin_unlock_irqrestore(&emu->emu_lock, flags); ngain = emu->i2c_capture_volume[source_id][0]; /* Left */ @@ -1632,7 +1632,7 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol, struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); if (emu->audigy) - ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0; + ucontrol->value.integer.value[0] = inw(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0; else ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0; if (emu->card_capabilities->invert_shared_spdif) @@ -1657,13 +1657,13 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol, if ( emu->card_capabilities->i2c_adc) { /* Do nothing for Audigy 2 ZS Notebook */ } else if (emu->audigy) { - reg = inl(emu->port + A_IOCFG); + reg = inw(emu->port + A_IOCFG); val = sw ? A_IOCFG_GPOUT0 : 0; change = (reg & A_IOCFG_GPOUT0) != val; if (change) { reg &= ~A_IOCFG_GPOUT0; reg |= val; - outl(reg | val, emu->port + A_IOCFG); + outw(reg | val, emu->port + A_IOCFG); } } reg = inl(emu->port + HCFG); diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index 35bc73d99d04..f0134689c320 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -243,13 +243,13 @@ void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value) if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */ return; spin_lock_irqsave(&emu->emu_lock, flags); - outl(reg, emu->port + A_IOCFG); + outw(reg, emu->port + A_GPIO); udelay(10); - outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ + outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */ udelay(10); - outl(value, emu->port + A_IOCFG); + outw(value, emu->port + A_GPIO); udelay(10); - outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ + outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */ spin_unlock_irqrestore(&emu->emu_lock, flags); } @@ -260,11 +260,11 @@ void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value) return; reg += 0x40; /* 0x40 upwards are registers. */ spin_lock_irqsave(&emu->emu_lock, flags); - outl(reg, emu->port + A_IOCFG); + outw(reg, emu->port + A_GPIO); udelay(10); - outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ + outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */ udelay(10); - *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f); + *value = ((inw(emu->port + A_GPIO) >> 8) & 0x7f); spin_unlock_irqrestore(&emu->emu_lock, flags); } -- cgit v1.2.3 From 8b2dd46d9a0370cf092fdd798dd66634abaf03e0 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 22 Apr 2023 15:24:30 +0200 Subject: ALSA: emu10k1: remove unused emu->pcm_playback_efx_substream field Amends historic commit 27ae958cf6 ("emu10k1 driver - add multichannel device hw:x,3 [2-8/8]"). Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230422132430.1057468-2-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 1 - sound/pci/emu10k1/emupcm.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 89dbd2e93410..f21441cda6b9 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1764,7 +1764,6 @@ struct snd_emu10k1 { struct snd_pcm_substream *pcm_capture_substream; struct snd_pcm_substream *pcm_capture_mic_substream; struct snd_pcm_substream *pcm_capture_efx_substream; - struct snd_pcm_substream *pcm_playback_efx_substream; struct snd_timer *timer; diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index b7830fd5c2b4..c2749ad59e10 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1044,8 +1044,6 @@ static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream) epcm->type = PLAYBACK_EFX; epcm->substream = substream; - emu->pcm_playback_efx_substream = substream; - runtime->private_data = epcm; runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_efx_playback; -- cgit v1.2.3 From 6fb861bb3caf2ca8e440f7a3fdcca35fcac917b4 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 22 Apr 2023 15:24:30 +0200 Subject: ALSA: emu10k1: fix snd_emu1010_fpga_read() input masking for rev2 cards Unlike the Alice2 chips used on 1st generation E-MU cards, the Tina/Tina2 chips used on the 2nd gen cards have only six GPIN pins, which means that we need to use a smaller mask. Failure to do so would falsify the read data if the FPGA tried to raise an IRQ right at that moment. This wasn't a problem so far, as we didn't actually enable FPGA IRQs, but that's going to change soon. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230422132430.1057490-1-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 3 ++- sound/pci/emu10k1/io.c | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index f21441cda6b9..790dedd42340 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -252,7 +252,8 @@ #define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ #define A_GPIO 0x18 /* GPIO on Audigy card (16bits) */ -#define A_GPINPUT_MASK 0xff00 +#define A_GPINPUT_MASK 0xff00 /* Alice/2 has 8 input pins */ +#define A3_GPINPUT_MASK 0x3f00 /* ... while Tina/2 has only 6 */ #define A_GPOUTPUT_MASK 0x00ff // The GPIO port is used for I/O config on Sound Blasters; diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index f0134689c320..42b06f2e5552 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -255,6 +255,9 @@ void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value) void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value) { + // The higest input pin is used as the designated interrupt trigger, + // so it needs to be masked out. + u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f; unsigned long flags; if (snd_BUG_ON(reg > 0x3f)) return; @@ -264,7 +267,7 @@ void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value) udelay(10); outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */ udelay(10); - *value = ((inw(emu->port + A_GPIO) >> 8) & 0x7f); + *value = ((inw(emu->port + A_GPIO) >> 8) & mask); spin_unlock_irqrestore(&emu->emu_lock, flags); } -- cgit v1.2.3 From a869057cd639ed41a1b16bc072fb20cb1ed1dc51 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 22 Apr 2023 18:10:15 +0200 Subject: ALSA: emu10k1: comment updates Move comments to better locations, de-duplicate, fix/remove incorrect/ outdated ones, add new ones, and unify spacing somewhat. While at it, also add testing credits for Jonathan Dowland (SB Live! Platinum) and myself (E-MU 0404b). Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230422161021.1143903-2-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 504 +++++++++++++++++++-------------------- include/uapi/sound/emu10k1.h | 3 + sound/pci/emu10k1/emu10k1.c | 11 - sound/pci/emu10k1/emu10k1_main.c | 77 +++--- sound/pci/emu10k1/emufx.c | 12 +- sound/pci/emu10k1/emumixer.c | 5 +- sound/pci/emu10k1/emupcm.c | 26 +- sound/pci/emu10k1/io.c | 8 - sound/pci/emu10k1/p16v.h | 2 +- sound/pci/emu10k1/p17v.h | 4 +- 10 files changed, 312 insertions(+), 340 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 790dedd42340..7786d5807679 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -97,9 +97,9 @@ #define IPR_CHANNELLOOP 0x00000040 /* Channel (half) loop interrupt(s) pending */ #define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */ /* highest set channel in CLIPL, CLIPH, HLIPL, */ - /* or HLIPH. When IP is written with CL set, */ + /* or HLIPH. When IPR is written with CL set, */ /* the bit in H/CLIPL or H/CLIPH corresponding */ - /* to the CIN value written will be cleared. */ + /* to the CN value written will be cleared. */ #define INTE 0x0c /* Interrupt enable register */ #define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */ @@ -180,6 +180,7 @@ #define HCFG_CODECFORMAT_MASK 0x00030000 /* CODEC format */ /* Specific to Alice2, CA0102 */ + #define HCFG_CODECFORMAT_AC97_1 0x00000000 /* AC97 CODEC format -- Ver 1.03 */ #define HCFG_CODECFORMAT_AC97_2 0x00010000 /* AC97 CODEC format -- Ver 2.1 */ #define HCFG_AUTOMUTE_ASYNC 0x00008000 /* When set, the async sample rate convertors */ @@ -200,9 +201,8 @@ /* I2S format input */ /* Rest of HCFG 0x0000000f same as below. LOCKSOUNDCACHE etc. */ - - /* Older chips */ + #define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */ #define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */ #define HCFG_GPINPUT0 0x00004000 /* External pin112 */ @@ -238,7 +238,7 @@ /* Should be set to 1 when the EMU10K1 is */ /* completely initialized. */ -//For Audigy, MPU port move to 0x70-0x74 ptr register +// On Audigy, the MPU port moved to the 0x70-0x74 ptr registers #define MUDATA 0x18 /* MPU401 data register (8 bits) */ @@ -277,12 +277,6 @@ #define A_IOCFG_REAR_JACK 0x8000 #define A_IOCFG_PHONES_JACK 0x0100 /* LiveDrive */ -/* outputs: - * for audigy2 platinum: 0xa00 - * for a2 platinum ex: 0x1c00 - * for a1 platinum: 0x0 - */ - #define TIMER 0x1a /* Timer terminal count register */ /* NOTE: After the rate is changed, a maximum */ /* of 1024 sample periods should be allowed */ @@ -323,7 +317,7 @@ /* 0x00000000 2-channel output. */ /* 0x00000200 8-channel output. */ /* 0x00000004 pauses stream/irq fail. */ - /* Rest of bits no nothing to sound output */ + /* Rest of bits do nothing to sound output */ /* bit 0: Enable P16V audio. * bit 1: Lock P16V record memory cache. * bit 2: Lock P16V playback memory cache. @@ -337,6 +331,7 @@ */ #define IPR3 0x38 /* Cdif interrupt pending register */ #define INTE3 0x3c /* Cdif interrupt enable register. */ + /************************************************************************************************/ /* PCI function 1 registers, address = + PCIBASE1 */ /************************************************************************************************/ @@ -355,11 +350,38 @@ #define JOYSTICK_BUTTONS 0x0f /* Joystick button data */ #define JOYSTICK_COMPARATOR 0xf0 /* Joystick comparator data */ - /********************************************************************************************************/ /* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */ /********************************************************************************************************/ +// No official documentation was released for EMU10K1, but some info +// about playback can be extrapolated from the EMU8K documents: +// "AWE32/EMU8000 Programmer’s Guide" (emu8kpgm.pdf) - registers +// "AWE32 Developer's Information Pack" (adip301.pdf) - high-level view + +// The short version: +// - The engine has 64 playback channels, also called voices. The channels +// operate independently, except when paired for stereo (see below). +// - PCM samples are fetched into the cache; see description of CD0 below. +// - Samples are consumed at the rate CPF_CURRENTPITCH. +// - 8-bit samples are transformed upon use: cooked = (raw ^ 0x80) << 8 +// - 8 samples are read at CCR_READADDRESS:CPF_FRACADDRESS and interpolated +// according to CCCA_INTERPROM_*. With CCCA_INTERPROM_0 selected and a zero +// CPF_FRACADDRESS, this results in CCR_READADDRESS[3] being used verbatim. +// - The value is multiplied by CVCF_CURRENTVOL. +// - The value goes through a filter with cutoff CVCF_CURRENTFILTER; +// delay stages Z1 and Z2. +// - The value is added by so-called `sends` to 4 (EMU10K1) / 8 (EMU10K2) +// of the 16 (EMU10K1) / 64 (EMU10K2) FX bus accumulators via FXRT*, +// multiplied by a per-send amount (*_FXSENDAMOUNT_*). +// The scaling of the send amounts is exponential-ish. +// - The DSP has a go at FXBUS* and outputs the values to EXTOUT* or EMU32OUT*. +// - The pitch, volume, and filter cutoff can be modulated by two envelope +// engines and two low frequency oscillators. +// - To avoid abrupt changes to the parameters (which may cause audible +// distortion), the modulation engine sets the target registers, towards +// which the current registers "swerve" gradually. + #define CPF 0x00 /* Current pitch and fraction register */ #define CPF_CURRENTPITCH_MASK 0xffff0000 /* Current pitch (linear, 0x4000 == unity pitch shift) */ #define CPF_CURRENTPITCH 0x10100000 @@ -399,7 +421,7 @@ #define PSST_LOOPSTARTADDR_MASK 0x00ffffff /* Loop start address of the specified channel */ #define PSST_LOOPSTARTADDR 0x18000006 -#define DSL 0x07 /* Send D amount and loop start address register */ +#define DSL 0x07 /* Send D amount and loop end address register */ #define DSL_FXSENDAMOUNT_D_MASK 0xff000000 /* Linear level of channel output sent to FX send bus D */ #define DSL_FXSENDAMOUNT_D 0x08180007 @@ -424,25 +446,28 @@ #define CCCA_INTERPROM_6 0x0c000000 /* Select interpolation ROM 6 */ #define CCCA_INTERPROM_7 0x0e000000 /* Select interpolation ROM 7 */ #define CCCA_8BITSELECT 0x01000000 /* 1 = Sound memory for this channel uses 8-bit samples */ + /* 8-bit samples are unsigned, 16-bit ones signed */ #define CCCA_CURRADDR_MASK 0x00ffffff /* Current address of the selected channel */ #define CCCA_CURRADDR 0x18000008 #define CCR 0x09 /* Cache control register */ #define CCR_CACHEINVALIDSIZE 0x07190009 -#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples cache for this channel */ +#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples before the read address */ #define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */ #define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */ + /* Auto-set from CPF_STEREO_MASK */ #define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */ + /* Auto-set from CCCA_8BITSELECT */ #define CCR_READADDRESS 0x06100009 -#define CCR_READADDRESS_MASK 0x003f0000 /* Location of cache just beyond current cache service */ +#define CCR_READADDRESS_MASK 0x003f0000 /* Next cached sample to play */ #define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */ /* NOTE: This is valid only if CACHELOOPFLAG is set */ #define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */ -#define CCR_CACHELOOPADDRHI 0x000000ff /* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */ +#define CCR_CACHELOOPADDRHI 0x000000ff /* CLP_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */ #define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */ /* NOTE: This register is normally not used */ -#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address (DSL_LOOPSTARTADDR [0..15]) */ +#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address low word */ #define FXRT 0x0b /* Effects send routing register */ /* NOTE: It is illegal to assign the same routing to */ @@ -454,7 +479,6 @@ #define A_HR 0x0b /* High Resolution. 24bit playback from host to DSP. */ #define MAPA 0x0c /* Cache map A */ - #define MAPB 0x0d /* Cache map B */ #define MAP_PTE_MASK0 0xfffff000 /* The 20 MSBs of the PTE indexed by the PTI */ @@ -463,7 +487,7 @@ #define MAP_PTE_MASK1 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */ #define MAP_PTI_MASK1 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ -/* 0x0e, 0x0f: Not used */ +/* 0x0e, 0x0f: Internal state, at least on Audigy */ #define ENVVOL 0x10 /* Volume envelope register */ #define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ @@ -476,9 +500,9 @@ /* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */ #define DCYSUSV 0x12 /* Volume envelope sustain and decay register */ -#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin decay phase, 1 = begin release phase */ #define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ -#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 1 = Inhibit envelope engine from writing values in */ +#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 0 = Inhibit envelope engine from writing values in */ /* this channel and from writing to pitch, filter and */ /* volume targets. */ #define DCYSUSV_DECAYTIME_MASK 0x0000007f /* Volume envelope decay time, log encoded */ @@ -499,7 +523,7 @@ /* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */ #define DCYSUSM 0x16 /* Modulation envelope decay and sustain register */ -#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin decay phase, 1 = begin release phase */ #define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ #define DCYSUSM_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */ /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ @@ -521,7 +545,6 @@ #define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */ #define IFATN_ATTENUATION 0x08000019 - #define PEFE 0x1a /* Pitch envelope and filter envelope amount register */ #define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */ /* Signed 2's complement, +/- one octave peak extremes */ @@ -529,19 +552,19 @@ #define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */ /* Signed 2's complement, +/- six octaves peak extremes */ #define PEFE_FILTERAMOUNT 0x0800001a + #define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */ #define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */ /* Signed 2's complement, +/- one octave extremes */ #define FMMOD_MOFILTER 0x000000ff /* Filter LFO modulation depth */ /* Signed 2's complement, +/- three octave extremes */ - #define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */ #define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ /* Signed 2's complement, with +/- 12dB extremes */ - #define TREMFRQ_FREQUENCY 0x000000ff /* Tremolo LFO frequency */ /* ??Hz steps, maximum of ?? Hz. */ + #define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */ #define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */ /* Signed 2's complement, +/- one octave extremes */ @@ -645,7 +668,7 @@ #define FXBA 0x47 /* FX Buffer Address */ #define FXBA_MASK 0xfffff000 /* 20 bit base address */ -#define A_HWM 0x48 /* High PCI Water Mark - word access, defaults to 3f */ +#define A_HWM 0x48 /* High PCI Water Mark - word access, defaults to 3f */ #define MICBS 0x49 /* Microphone buffer size register */ @@ -653,9 +676,7 @@ #define FXBS 0x4b /* FX buffer size register */ -/* register: 0x4c..4f: ffff-ffff current amounts, per-channel */ - -/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */ +/* The following mask values define the size of the ADC, MIC and FX buffers in bytes */ #define ADCBS_BUFSIZE_NONE 0x00000000 #define ADCBS_BUFSIZE_384 0x00000001 #define ADCBS_BUFSIZE_448 0x00000002 @@ -689,29 +710,21 @@ #define ADCBS_BUFSIZE_57344 0x0000001e #define ADCBS_BUFSIZE_65536 0x0000001f -/* Current Send B, A Amounts */ -#define A_CSBA 0x4c - -/* Current Send D, C Amounts */ -#define A_CSDC 0x4d - -/* Current Send F, E Amounts */ -#define A_CSFE 0x4e - -/* Current Send H, G Amounts */ -#define A_CSHG 0x4f - +#define A_CSBA 0x4c /* FX send B & A current amounts */ +#define A_CSDC 0x4d /* FX send D & C current amounts */ +#define A_CSFE 0x4e /* FX send F & E current amounts */ +#define A_CSHG 0x4f /* FX send H & G current amounts */ -#define CDCS 0x50 /* CD-ROM digital channel status register */ +// NOTE: 0x50,51,52: 64-bit (split over voices 0 & 1) +#define CDCS 0x50 /* CD-ROM digital channel status register */ -#define GPSCS 0x51 /* General Purpose SPDIF channel status register*/ +#define GPSCS 0x51 /* General Purpose SPDIF channel status register */ -#define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ +#define DBG 0x52 -/* S/PDIF Input C Channel Status */ -#define A_SPSC 0x52 +#define A_SPSC 0x52 /* S/PDIF Input C Channel Status */ -#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ +#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ #define A_DBG 0x53 #define A_DBG_SINGLE_STEP 0x00020000 /* Set to zero to start dsp */ @@ -720,7 +733,7 @@ #define A_DBG_SATURATION_OCCURED 0x20000000 #define A_DBG_SATURATION_ADDR 0x0ffc0000 -// NOTE: 0x54,55,56: 64-bit +// NOTE: 0x54,55,56: 64-bit (split over voices 0 & 1) #define SPCS0 0x54 /* SPDIF output Channel Status 0 register */ #define SPCS1 0x55 /* SPDIF output Channel Status 1 register */ @@ -753,17 +766,14 @@ /* 0x57: Not used */ -/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status */ +/* The 32-bit CLIx and SOLEx registers all have one bit per channel control/status */ #define CLIEL 0x58 /* Channel loop interrupt enable low register */ - #define CLIEH 0x59 /* Channel loop interrupt enable high register */ #define CLIPL 0x5a /* Channel loop interrupt pending low register */ - #define CLIPH 0x5b /* Channel loop interrupt pending high register */ #define SOLEL 0x5c /* Stop on loop enable low register */ - #define SOLEH 0x5d /* Stop on loop enable high register */ #define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ @@ -773,13 +783,12 @@ #define SPBYPASS_FORMAT 0x00000f00 /* If 1, SPDIF XX uses 24 bit, if 0 - 20 bit */ #define AC97SLOT 0x5f /* additional AC97 slots enable bits */ -#define AC97SLOT_REAR_RIGHT 0x01 /* Rear left */ -#define AC97SLOT_REAR_LEFT 0x02 /* Rear right */ -#define AC97SLOT_CNTR 0x10 /* Center enable */ -#define AC97SLOT_LFE 0x20 /* LFE enable */ +#define AC97SLOT_REAR_RIGHT 0x01 /* Rear left */ +#define AC97SLOT_REAR_LEFT 0x02 /* Rear right */ +#define AC97SLOT_CNTR 0x10 /* Center enable */ +#define AC97SLOT_LFE 0x20 /* LFE enable */ -/* PCB Revision */ -#define A_PCB 0x5f +#define A_PCB 0x5f /* PCB Revision */ // NOTE: 0x60,61,62: 64-bit #define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ @@ -819,27 +828,21 @@ #define FXIDX_MASK 0x0000ffff /* 16-bit value */ #define FXIDX_IDX 0x10000065 -/* The 32-bit HLIx and HLIPx registers all have one bit per channel control/status */ +/* The 32-bit HLIEx and HLIPx registers all have one bit per channel control/status */ #define HLIEL 0x66 /* Channel half loop interrupt enable low register */ - #define HLIEH 0x67 /* Channel half loop interrupt enable high register */ #define HLIPL 0x68 /* Channel half loop interrupt pending low register */ - #define HLIPH 0x69 /* Channel half loop interrupt pending high register */ -/* S/PDIF Host Record Index (bypasses SRC) */ -#define A_SPRI 0x6a -/* S/PDIF Host Record Address */ -#define A_SPRA 0x6b -/* S/PDIF Host Record Control */ -#define A_SPRC 0x6c -/* Delayed Interrupt Counter & Enable */ -#define A_DICE 0x6d -/* Tank Table Base */ -#define A_TTB 0x6e -/* Tank Delay Offset */ -#define A_TDOF 0x6f +#define A_SPRI 0x6a /* S/PDIF Host Record Index (bypasses SRC) */ +#define A_SPRA 0x6b /* S/PDIF Host Record Address */ +#define A_SPRC 0x6c /* S/PDIF Host Record Control */ + +#define A_DICE 0x6d /* Delayed Interrupt Counter & Enable */ + +#define A_TTB 0x6e /* Tank Table Base */ +#define A_TDOF 0x6f /* Tank Delay Offset */ /* This is the MPU port on the card (via the game port) */ #define A_MUDATA1 0x70 @@ -852,7 +855,7 @@ #define A_MUSTAT2 A_MUCMD2 /* The next two are the Audigy equivalent of FXWC */ -/* the Audigy can record any output (16bit, 48kHz, up to 64 channel simultaneously) */ +/* the Audigy can record any output (16bit, 48kHz, up to 64 channels simultaneously) */ /* Each bit selects a channel for recording */ #define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */ #define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */ @@ -880,20 +883,13 @@ #define A_PCM_96000 0x00004000 #define A_PCM_44100 0x00008000 -/* I2S0 Sample Rate Tracker Status */ -#define A_SRT3 0x77 - -/* I2S1 Sample Rate Tracker Status */ -#define A_SRT4 0x78 - -/* I2S2 Sample Rate Tracker Status */ -#define A_SRT5 0x79 +#define A_SRT3 0x77 /* I2S0 Sample Rate Tracker Status */ +#define A_SRT4 0x78 /* I2S1 Sample Rate Tracker Status */ +#define A_SRT5 0x79 /* I2S2 Sample Rate Tracker Status */ /* - default to 0x01080000 on my audigy 2 ZS --rlrevell */ -/* Tank Table DMA Address */ -#define A_TTDA 0x7a -/* Tank Table DMA Data */ -#define A_TTDD 0x7b +#define A_TTDA 0x7a /* Tank Table DMA Address */ +#define A_TTDD 0x7b /* Tank Table DMA Data */ #define A_FXRT2 0x7c #define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send E */ @@ -906,6 +902,7 @@ #define A_FXSENDAMOUNT_F_MASK 0x00FF0000 #define A_FXSENDAMOUNT_G_MASK 0x0000FF00 #define A_FXSENDAMOUNT_H_MASK 0x000000FF + /* 0x7c, 0x7e "high bit is used for filtering" */ /* The send amounts for this one are the same as used with the emu10k1 */ @@ -956,15 +953,47 @@ #define A_HIWORD_RESULT_MASK 0x007ff000 #define A_HIWORD_OPA_MASK 0x000007ff + +/************************************************************************************************/ +/* E-MU Digital Audio System overview */ +/************************************************************************************************/ + +// - These cards use a regular PCI-attached Audigy chip (Alice2/Tina/Tina2); +// the PCIe variants simply put the Audigy chip behind a PCI bridge. +// - All physical PCM I/O is routed through an additional FPGA; the regular +// EXTIN/EXTOUT ports are unconnected. +// - The FPGA has a signal routing matrix, to connect each destination (output +// socket or capture channel) to a source (input socket or playback channel). +// - The FPGA is controlled via Audigy's GPIO port, while sample data is +// transmitted via proprietary EMU32 serial links. On first-generation +// E-MU 1010 cards, Audigy's I2S inputs are also used for sample data. +// - The Audio/Micro Dock is attached to Hana via EDI, a "network" link. +// - The Audigy chip operates in slave mode; the clock is supplied by the FPGA. +// Gen1 E-MU 1010 cards have two crystals (for 44.1 kHz and 48 kHz multiples), +// while the later cards use a single crystal and a PLL chip. +// - The whole card is switched to 2x/4x mode to achieve 88.2/96/176.4/192 kHz +// sample rates. Alice2/Tina keeps running at 44.1/48 kHz, but multiple channels +// are bundled. +// - The number of available EMU32/EDI channels is hit in 2x/4x mode, so the total +// number of usable inputs/outputs is limited, esp. with ADAT in use. +// - S/PDIF is unavailable in 4x mode (only over TOSLINK on newer 1010 cards) due +// to being unspecified at 176.4/192 kHz. Therefore, the Dock's S/PDIF channels +// can overlap with the Dock's ADC/DAC's high channels. +// - The code names are mentioned below and in the emu_chip_details table. + /************************************************************************************************/ -/* EMU1010m HANA FPGA registers */ +/* EMU1010 FPGA registers */ /************************************************************************************************/ + #define EMU_HANA_DESTHI 0x00 /* 0000xxx 3 bits Link Destination */ #define EMU_HANA_DESTLO 0x01 /* 00xxxxx 5 bits */ + #define EMU_HANA_SRCHI 0x02 /* 0000xxx 3 bits Link Source */ #define EMU_HANA_SRCLO 0x03 /* 00xxxxx 5 bits */ + #define EMU_HANA_DOCK_PWR 0x04 /* 000000x 1 bits Audio Dock power */ #define EMU_HANA_DOCK_PWR_ON 0x01 /* Audio Dock power on */ + #define EMU_HANA_WCLOCK 0x05 /* 0000xxx 3 bits Word Clock source select */ /* Must be written after power on to reset DLL */ /* One is unable to detect the Audio dock without this */ @@ -1073,21 +1102,19 @@ #define EMU_HANA_0202_DAC_PAD1 0x10 /* 14dB Attenuation on 0202 DAC 1. Left and Right */ /* 0x14 - 0x1f Unused R/W registers */ -#define EMU_HANA_IRQ_STATUS 0x20 /* 000xxxx 4 bits IRQ Status */ -#if 0 /* Already defined for reg 0x09 IRQ_ENABLE */ -#define EMU_HANA_IRQ_WCLK_CHANGED 0x01 -#define EMU_HANA_IRQ_ADAT 0x02 -#define EMU_HANA_IRQ_DOCK 0x04 -#define EMU_HANA_IRQ_DOCK_LOST 0x08 -#endif + +#define EMU_HANA_IRQ_STATUS 0x20 /* 00xxxxx 5 bits IRQ Status */ + /* Same bits as for EMU_HANA_IRQ_ENABLE */ + /* Reading the register resets it. */ #define EMU_HANA_OPTION_CARDS 0x21 /* 000xxxx 4 bits Presence of option cards */ -#define EMU_HANA_OPTION_HAMOA 0x01 /* HAMOA card present */ +#define EMU_HANA_OPTION_HAMOA 0x01 /* Hamoa (analog I/O) card present */ #define EMU_HANA_OPTION_SYNC 0x02 /* Sync card present */ -#define EMU_HANA_OPTION_DOCK_ONLINE 0x04 /* Audio Dock online and FPGA configured */ -#define EMU_HANA_OPTION_DOCK_OFFLINE 0x08 /* Audio Dock online and FPGA not configured */ +#define EMU_HANA_OPTION_DOCK_ONLINE 0x04 /* Audio/Micro dock present and FPGA configured */ +#define EMU_HANA_OPTION_DOCK_OFFLINE 0x08 /* Audio/Micro dock present and FPGA not configured */ -#define EMU_HANA_ID 0x22 /* 1010101 7 bits ID byte & 0x7f = 0x55 */ +#define EMU_HANA_ID 0x22 /* 1010101 7 bits ID byte & 0x7f = 0x55 with Alice2 */ + /* 0010101 5 bits ID byte & 0x1f = 0x15 with Tina/2 */ #define EMU_HANA_MAJOR_REV 0x23 /* 0000xxx 3 bit Hana FPGA Major rev */ #define EMU_HANA_MINOR_REV 0x24 /* 0000xxx 3 bit Hana FPGA Minor rev */ @@ -1110,31 +1137,31 @@ #define EMU_HANA2_WC_SPDIF_HI 0x2e /* 0xxxxxx 6 bit HANA2 SPDIF IN Word clock, upper 6 bits */ #define EMU_HANA2_WC_SPDIF_LO 0x2f /* 0xxxxxx 6 bit HANA2 SPDIF IN Word clock, lower 6 bits */ + /* 0x30 - 0x3f Unused Read only registers */ /************************************************************************************************/ -/* EMU1010m HANA Destinations */ +/* EMU1010 Audio Destinations */ /************************************************************************************************/ -/* Hana, original 1010,1212,1820 using Alice2 - * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz +/* Hana, original 1010,1212m,1820[m] using Alice2 * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2 - * 0x01, 0x10-0x1f: 32 Elink channels to Audio Dock - * 0x01, 0x00: Dock DAC 1 Left - * 0x01, 0x04: Dock DAC 1 Right - * 0x01, 0x08: Dock DAC 2 Left - * 0x01, 0x0c: Dock DAC 2 Right - * 0x01, 0x10: Dock DAC 3 Left - * 0x01, 0x12: PHONES Left - * 0x01, 0x14: Dock DAC 3 Right - * 0x01, 0x16: PHONES Right - * 0x01, 0x18: Dock DAC 4 Left - * 0x01, 0x1a: S/PDIF Left - * 0x01, 0x1c: Dock DAC 4 Right - * 0x01, 0x1e: S/PDIF Right + * 0x01, 0x00-0x1f: 32 EDI channels to Audio Dock + * 0x00: Dock DAC 1 Left + * 0x04: Dock DAC 1 Right + * 0x08: Dock DAC 2 Left + * 0x0c: Dock DAC 2 Right + * 0x10: Dock DAC 3 Left + * 0x12: PHONES Left (n/a in 2x/4x mode; output mirrors DAC4 Left) + * 0x14: Dock DAC 3 Right + * 0x16: PHONES Right (n/a in 2x/4x mode; output mirrors DAC4 Right) + * 0x18: Dock DAC 4 Left + * 0x1a: S/PDIF Left + * 0x1c: Dock DAC 4 Right + * 0x1e: S/PDIF Right * 0x02, 0x00: Hana S/PDIF Left * 0x02, 0x01: Hana S/PDIF Right - * 0x03, 0x00: Hanoa DAC Left - * 0x03, 0x01: Hanoa DAC Right + * 0x03, 0x00: Hamoa DAC Left + * 0x03, 0x01: Hamoa DAC Right * 0x04, 0x00-0x07: Hana ADAT * 0x05, 0x00: I2S0 Left to Alice2 * 0x05, 0x01: I2S0 Right to Alice2 @@ -1146,40 +1173,29 @@ * Hana2 never released, but used Tina * Not needed. * - * Hana3, rev2 1010,1212,1616 using Tina - * Destinations for SRATEX = 1X rates: 44.1 kHz or 48 kHz + * Hana3, rev2 1010,1212m,1616[m] using Tina * 0x00, 0x00-0x0f: 16 EMU32A channels to Tina - * 0x01, 0x10-0x1f: 32 EDI channels to Micro Dock - * 0x01, 0x00: Dock DAC 1 Left - * 0x01, 0x04: Dock DAC 1 Right - * 0x01, 0x08: Dock DAC 2 Left - * 0x01, 0x0c: Dock DAC 2 Right - * 0x01, 0x10: Dock DAC 3 Left - * 0x01, 0x12: Dock S/PDIF Left - * 0x01, 0x14: Dock DAC 3 Right - * 0x01, 0x16: Dock S/PDIF Right - * 0x01, 0x18-0x1f: Dock ADAT 0-7 + * 0x01, 0x00-0x1f: 32 EDI channels to Micro Dock + * 0x00: Dock DAC 1 Left + * 0x04: Dock DAC 1 Right + * 0x08: Dock DAC 2 Left + * 0x0c: Dock DAC 2 Right + * 0x10: Dock DAC 3 Left + * 0x12: Dock S/PDIF Left + * 0x14: Dock DAC 3 Right + * 0x16: Dock S/PDIF Right + * 0x18-0x1f: Dock ADAT 0-7 * 0x02, 0x00: Hana3 S/PDIF Left * 0x02, 0x01: Hana3 S/PDIF Right - * 0x03, 0x00: Hanoa DAC Left - * 0x03, 0x01: Hanoa DAC Right + * 0x03, 0x00: Hamoa DAC Left + * 0x03, 0x01: Hamoa DAC Right * 0x04, 0x00-0x07: Hana3 ADAT 0-7 * 0x05, 0x00-0x0f: 16 EMU32B channels to Tina * 0x06-0x07: Not used * * HanaLite, rev1 0404 using Alice2 - * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz - * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2 - * 0x01: Not used - * 0x02, 0x00: S/PDIF Left - * 0x02, 0x01: S/PDIF Right - * 0x03, 0x00: DAC Left - * 0x03, 0x01: DAC Right - * 0x04-0x07: Not used - * - * HanaLiteLite, rev2 0404 using Alice2 - * Destiniations for SRATEX = 1X rates: 44.1 kHz or 48 kHz - * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2 + * HanaLiteLite, rev2 0404 using Tina + * 0x00, 0x00-0x0f: 16 EMU32 channels to Alice2/Tina * 0x01: Not used * 0x02, 0x00: S/PDIF Left * 0x02, 0x01: S/PDIF Right @@ -1188,35 +1204,21 @@ * 0x04-0x07: Not used * * Mana, Cardbus 1616 using Tina2 - * Destinations for SRATEX = 1X rates: 44.1 kHz or 48 kHz * 0x00, 0x00-0x0f: 16 EMU32A channels to Tina2 - * 0x01, 0x10-0x1f: 32 EDI channels to Micro Dock - * 0x01, 0x00: Dock DAC 1 Left - * 0x01, 0x04: Dock DAC 1 Right - * 0x01, 0x08: Dock DAC 2 Left - * 0x01, 0x0c: Dock DAC 2 Right - * 0x01, 0x10: Dock DAC 3 Left - * 0x01, 0x12: Dock S/PDIF Left - * 0x01, 0x14: Dock DAC 3 Right - * 0x01, 0x16: Dock S/PDIF Right - * 0x01, 0x18-0x1f: Dock ADAT 0-7 + * 0x01, 0x00-0x1f: 32 EDI channels to Micro Dock + * (same as rev2 1010) * 0x02: Not used * 0x03, 0x00: Mana DAC Left * 0x03, 0x01: Mana DAC Right * 0x04, 0x00-0x0f: 16 EMU32B channels to Tina2 * 0x05-0x07: Not used - * - * */ + /* 32-bit destinations of signal in the Hana FPGA. Destinations are either - * physical outputs of Hana, or outputs going to Alice2 (audigy) for capture - * - 16 x EMU_DST_ALICE2_EMU32_X. - */ -/* EMU32 = 32-bit serial channel between Alice2 (audigy) and Hana (FPGA) */ -/* EMU_DST_ALICE2_EMU32_X - data channels from Hana to Alice2 used for capture. - * Which data is fed into a EMU_DST_ALICE2_EMU32_X channel in Hana depends on - * setup of mixer control for each destination - see emumixer.c - - * snd_emu1010_output_enum_ctls[], snd_emu1010_input_enum_ctls[] + * physical outputs of Hana, or outputs going to Alice2/Tina for capture - + * 16 x EMU_DST_ALICE2_EMU32_X (2x on rev2 boards). Which data is fed into + * a channel depends on the mixer control setting for each destination - see + * emumixer.c - snd_emu1010_output_enum_ctls[], snd_emu1010_input_enum_ctls[] */ #define EMU_DST_ALICE2_EMU32_0 0x000f /* 16 EMU32 channels to Alice2 +0 to +0xf */ #define EMU_DST_ALICE2_EMU32_1 0x0000 /* 16 EMU32 channels to Alice2 +0 to +0xf */ @@ -1286,6 +1288,7 @@ #define EMU_DST_HAMOA_DAC_RIGHT2 0x0303 /* Hamoa DAC Right, 2nd or 96kHz */ #define EMU_DST_HAMOA_DAC_RIGHT3 0x0305 /* Hamoa DAC Right, 3rd or 192kHz */ #define EMU_DST_HAMOA_DAC_RIGHT4 0x0307 /* Hamoa DAC Right, 4th or 192kHz */ +// In S/MUX mode, the samples of one channel are adjacent. #define EMU_DST_HANA_ADAT 0x0400 /* Hana ADAT 8 channel out +0 to +7 */ #define EMU_DST_ALICE_I2S0_LEFT 0x0500 /* Alice2 I2S0 Left */ #define EMU_DST_ALICE_I2S0_RIGHT 0x0501 /* Alice2 I2S0 Right */ @@ -1295,39 +1298,32 @@ #define EMU_DST_ALICE_I2S2_RIGHT 0x0701 /* Alice2 I2S2 Right */ /* Additional destinations for 1616(M)/Microdock */ -/* Microdock S/PDIF OUT Left, 1st or 48kHz only */ -#define EMU_DST_MDOCK_SPDIF_LEFT1 0x0112 -/* Microdock S/PDIF OUT Left, 2nd or 96kHz */ -#define EMU_DST_MDOCK_SPDIF_LEFT2 0x0113 -/* Microdock S/PDIF OUT Right, 1st or 48kHz only */ -#define EMU_DST_MDOCK_SPDIF_RIGHT1 0x0116 -/* Microdock S/PDIF OUT Right, 2nd or 96kHz */ -#define EMU_DST_MDOCK_SPDIF_RIGHT2 0x0117 -/* Microdock S/PDIF ADAT 8 channel out +8 to +f */ -#define EMU_DST_MDOCK_ADAT 0x0118 - -/* Headphone jack on 1010 cardbus? 44.1/48kHz only? */ -#define EMU_DST_MANA_DAC_LEFT 0x0300 -/* Headphone jack on 1010 cardbus? 44.1/48kHz only? */ -#define EMU_DST_MANA_DAC_RIGHT 0x0301 + +#define EMU_DST_MDOCK_SPDIF_LEFT1 0x0112 /* Microdock S/PDIF OUT Left, 1st or 48kHz only */ +#define EMU_DST_MDOCK_SPDIF_LEFT2 0x0113 /* Microdock S/PDIF OUT Left, 2nd or 96kHz */ +#define EMU_DST_MDOCK_SPDIF_RIGHT1 0x0116 /* Microdock S/PDIF OUT Right, 1st or 48kHz only */ +#define EMU_DST_MDOCK_SPDIF_RIGHT2 0x0117 /* Microdock S/PDIF OUT Right, 2nd or 96kHz */ +#define EMU_DST_MDOCK_ADAT 0x0118 /* Microdock S/PDIF ADAT 8 channel out +8 to +f */ + +#define EMU_DST_MANA_DAC_LEFT 0x0300 /* Headphone jack on 1010 cardbus? 44.1/48kHz only? */ +#define EMU_DST_MANA_DAC_RIGHT 0x0301 /* Headphone jack on 1010 cardbus? 44.1/48kHz only? */ /************************************************************************************************/ -/* EMU1010m HANA Sources */ +/* EMU1010 Audio Sources */ /************************************************************************************************/ -/* Hana, original 1010,1212,1820 using Alice2 - * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz - * 0x00,0x00-0x1f: Silence - * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock - * 0x01, 0x00: Dock Mic A - * 0x01, 0x04: Dock Mic B - * 0x01, 0x08: Dock ADC 1 Left - * 0x01, 0x0c: Dock ADC 1 Right - * 0x01, 0x10: Dock ADC 2 Left - * 0x01, 0x14: Dock ADC 2 Right - * 0x01, 0x18: Dock ADC 3 Left - * 0x01, 0x1c: Dock ADC 3 Right - * 0x02, 0x00: Hana ADC Left - * 0x02, 0x01: Hana ADC Right +/* Hana, original 1010,1212m,1820[m] using Alice2 + * 0x00, 0x00-0x1f: Silence + * 0x01, 0x00-0x1f: 32 EDI channels from Audio Dock + * 0x00: Dock Mic A + * 0x04: Dock Mic B + * 0x08: Dock ADC 1 Left + * 0x0c: Dock ADC 1 Right + * 0x10: Dock ADC 2 Left + * 0x14: Dock ADC 2 Right + * 0x18: Dock ADC 3 Left + * 0x1c: Dock ADC 3 Right + * 0x02, 0x00: Hamoa ADC Left + * 0x02, 0x01: Hamoa ADC Right * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output * 0x04, 0x00-0x07: Hana ADAT @@ -1338,23 +1334,20 @@ * Hana2 never released, but used Tina * Not needed. * - * Hana3, rev2 1010,1212,1616 using Tina - * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz - * 0x00,0x00-0x1f: Silence - * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock - * 0x01, 0x00: Dock Mic A - * 0x01, 0x04: Dock Mic B - * 0x01, 0x08: Dock ADC 1 Left - * 0x01, 0x0c: Dock ADC 1 Right - * 0x01, 0x10: Dock ADC 2 Left - * 0x01, 0x12: Dock S/PDIF Left - * 0x01, 0x14: Dock ADC 2 Right - * 0x01, 0x16: Dock S/PDIF Right - * 0x01, 0x18-0x1f: Dock ADAT 0-7 - * 0x01, 0x18: Dock ADC 3 Left - * 0x01, 0x1c: Dock ADC 3 Right - * 0x02, 0x00: Hanoa ADC Left - * 0x02, 0x01: Hanoa ADC Right + * Hana3, rev2 1010,1212m,1616[m] using Tina + * 0x00, 0x00-0x1f: Silence + * 0x01, 0x00-0x1f: 32 EDI channels from Micro Dock + * 0x00: Dock Mic A + * 0x04: Dock Mic B + * 0x08: Dock ADC 1 Left + * 0x0c: Dock ADC 1 Right + * 0x10: Dock ADC 2 Left + * 0x12: Dock S/PDIF Left + * 0x14: Dock ADC 2 Right + * 0x16: Dock S/PDIF Right + * 0x18-0x1f: Dock ADAT 0-7 + * 0x02, 0x00: Hamoa ADC Left + * 0x02, 0x01: Hamoa ADC Right * 0x03, 0x00-0x0f: 16 inputs from Tina Emu32A output * 0x03, 0x10-0x1f: 16 inputs from Tina Emu32B output * 0x04, 0x00-0x07: Hana3 ADAT @@ -1363,58 +1356,32 @@ * 0x06-0x07: Not used * * HanaLite, rev1 0404 using Alice2 - * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz - * 0x00,0x00-0x1f: Silence + * HanaLiteLite, rev2 0404 using Tina + * 0x00, 0x00-0x1f: Silence * 0x01: Not used * 0x02, 0x00: ADC Left * 0x02, 0x01: ADC Right - * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output - * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output - * 0x04: Not used - * 0x05, 0x00: S/PDIF Left - * 0x05, 0x01: S/PDIF Right - * 0x06-0x07: Not used - * - * HanaLiteLite, rev2 0404 using Alice2 - * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz - * 0x00,0x00-0x1f: Silence - * 0x01: Not used - * 0x02, 0x00: ADC Left - * 0x02, 0x01: ADC Right - * 0x03, 0x00-0x0f: 16 inputs from Alice2 Emu32A output - * 0x03, 0x10-0x1f: 16 inputs from Alice2 Emu32B output + * 0x03, 0x00-0x0f: 16 inputs from Alice2/Tina Emu32A output + * 0x03, 0x10-0x1f: 16 inputs from Alice2/Tina Emu32B output * 0x04: Not used * 0x05, 0x00: S/PDIF Left * 0x05, 0x01: S/PDIF Right * 0x06-0x07: Not used * * Mana, Cardbus 1616 using Tina2 - * Sources SRATEX = 1X rates: 44.1 kHz or 48 kHz - * 0x00,0x00-0x1f: Silence - * 0x01, 0x10-0x1f: 32 Elink channels from Audio Dock - * 0x01, 0x00: Dock Mic A - * 0x01, 0x04: Dock Mic B - * 0x01, 0x08: Dock ADC 1 Left - * 0x01, 0x0c: Dock ADC 1 Right - * 0x01, 0x10: Dock ADC 2 Left - * 0x01, 0x12: Dock S/PDIF Left - * 0x01, 0x14: Dock ADC 2 Right - * 0x01, 0x16: Dock S/PDIF Right - * 0x01, 0x18-0x1f: Dock ADAT 0-7 - * 0x01, 0x18: Dock ADC 3 Left - * 0x01, 0x1c: Dock ADC 3 Right + * 0x00, 0x00-0x1f: Silence + * 0x01, 0x00-0x1f: 32 EDI channels from Micro Dock + * (same as rev2 1010) * 0x02: Not used - * 0x03, 0x00-0x0f: 16 inputs from Tina Emu32A output - * 0x03, 0x10-0x1f: 16 inputs from Tina Emu32B output + * 0x03, 0x00-0x0f: 16 inputs from Tina2 Emu32A output + * 0x03, 0x10-0x1f: 16 inputs from Tina2 Emu32B output * 0x04-0x07: Not used - * */ /* 32-bit sources of signal in the Hana FPGA. The sources are routed to - * destinations using mixer control for each destination - see emumixer.c - * Sources are either physical inputs of FPGA, - * or outputs from Alice (audigy) - 16 x EMU_SRC_ALICE_EMU32A + - * 16 x EMU_SRC_ALICE_EMU32B + * destinations using a mixer control for each destination - see emumixer.c. + * Sources are either physical inputs of Hana, or inputs from Alice2/Tina - + * 16 x EMU_SRC_ALICE_EMU32A + 16 x EMU_SRC_ALICE_EMU32B. */ #define EMU_SRC_SILENCE 0x0000 /* Silence */ #define EMU_SRC_DOCK_MIC_A1 0x0100 /* Audio Dock Mic A, 1st or 48kHz only */ @@ -1459,6 +1426,7 @@ #define EMU_SRC_HAMOA_ADC_RIGHT4 0x0207 /* Hamoa ADC Right, 4th or 192kHz */ #define EMU_SRC_ALICE_EMU32A 0x0300 /* Alice2 EMU32a 16 outputs. +0 to +0xf */ #define EMU_SRC_ALICE_EMU32B 0x0310 /* Alice2 EMU32b 16 outputs. +0 to +0xf */ +// In S/MUX mode, the samples of one channel are adjacent. #define EMU_SRC_HANA_ADAT 0x0400 /* Hana ADAT 8 channel in +0 to +7 */ #define EMU_SRC_HANA_SPDIF_LEFT1 0x0500 /* Hana SPDIF Left, 1st or 48kHz only */ #define EMU_SRC_HANA_SPDIF_LEFT2 0x0502 /* Hana SPDIF Left, 2nd or 96kHz */ @@ -1466,16 +1434,12 @@ #define EMU_SRC_HANA_SPDIF_RIGHT2 0x0503 /* Hana SPDIF Right, 2nd or 96kHz */ /* Additional inputs for 1616(M)/Microdock */ -/* Microdock S/PDIF Left, 1st or 48kHz only */ -#define EMU_SRC_MDOCK_SPDIF_LEFT1 0x0112 -/* Microdock S/PDIF Left, 2nd or 96kHz */ -#define EMU_SRC_MDOCK_SPDIF_LEFT2 0x0113 -/* Microdock S/PDIF Right, 1st or 48kHz only */ -#define EMU_SRC_MDOCK_SPDIF_RIGHT1 0x0116 -/* Microdock S/PDIF Right, 2nd or 96kHz */ -#define EMU_SRC_MDOCK_SPDIF_RIGHT2 0x0117 -/* Microdock ADAT 8 channel in +8 to +f */ -#define EMU_SRC_MDOCK_ADAT 0x0118 + +#define EMU_SRC_MDOCK_SPDIF_LEFT1 0x0112 /* Microdock S/PDIF Left, 1st or 48kHz only */ +#define EMU_SRC_MDOCK_SPDIF_LEFT2 0x0113 /* Microdock S/PDIF Left, 2nd or 96kHz */ +#define EMU_SRC_MDOCK_SPDIF_RIGHT1 0x0116 /* Microdock S/PDIF Right, 1st or 48kHz only */ +#define EMU_SRC_MDOCK_SPDIF_RIGHT2 0x0117 /* Microdock S/PDIF Right, 2nd or 96kHz */ +#define EMU_SRC_MDOCK_ADAT 0x0118 /* Microdock ADAT 8 channel in +8 to +f */ /* 0x600 and 0x700 no used */ @@ -1642,14 +1606,26 @@ enum { EMU_MODEL_EMU0404, }; +// Chip-o-logy: +// - All SB Live! cards use EMU10K1 chips +// - All SB Audigy cards use CA* chips, termed "emu10k2" by the driver +// - Original Audigy uses CA0100 "Alice" +// - Audigy 2 uses CA0102/CA10200 "Alice2" +// - Has an interface for CA0151 (P16V) "Alice3" +// - Audigy 2 Value uses CA0108/CA10300 "Tina" +// - Approximately a CA0102 with an on-chip CA0151 (P17V) +// - Audigy 2 ZS NB uses CA0109 "Tina2" +// - Cardbus version of CA0108 struct snd_emu_chip_details { u32 vendor; u32 device; u32 subsystem; unsigned char revision; unsigned char emu10k1_chip; /* Original SB Live. Not SB Live 24bit. */ + /* Redundant with emu10k2_chip being unset. */ unsigned char emu10k2_chip; /* Audigy 1 or Audigy 2. */ unsigned char ca0102_chip; /* Audigy 1 or Audigy 2. Not SB Audigy 2 Value. */ + /* Redundant with ca0108_chip being unset. */ unsigned char ca0108_chip; /* Audigy 2 Value */ unsigned char ca_cardbus_chip; /* Audigy 2 ZS Notebook */ unsigned char ca0151_chip; /* P16V */ @@ -1659,8 +1635,8 @@ struct snd_emu_chip_details { unsigned char ac97_chip; /* Has an AC97 chip: 1 = mandatory, 2 = optional */ unsigned char ecard; /* APS EEPROM */ unsigned char emu_model; /* EMU model type */ - unsigned char spi_dac; /* SPI interface for DAC */ - unsigned char i2c_adc; /* I2C interface for ADC */ + unsigned char spi_dac; /* SPI interface for DAC; requires ca0108_chip */ + unsigned char i2c_adc; /* I2C interface for ADC; requires ca0108_chip */ unsigned char adc_1361t; /* Use Philips 1361T ADC */ unsigned char invert_shared_spdif; /* analog/digital switch inverted */ const char *driver; @@ -1734,9 +1710,9 @@ struct snd_emu10k1 { void *synth; int (*get_synth_voice)(struct snd_emu10k1 *emu); - spinlock_t reg_lock; - spinlock_t emu_lock; - spinlock_t voice_lock; + spinlock_t reg_lock; // high-level driver lock + spinlock_t emu_lock; // low-level i/o lock + spinlock_t voice_lock; // voice allocator lock spinlock_t spi_lock; /* serialises access to spi port */ spinlock_t i2c_lock; /* serialises access to i2c port */ diff --git a/include/uapi/sound/emu10k1.h b/include/uapi/sound/emu10k1.h index c2414bd5aecd..33e5228f5d8c 100644 --- a/include/uapi/sound/emu10k1.h +++ b/include/uapi/sound/emu10k1.h @@ -111,6 +111,9 @@ #define CC_REG_NONZERO C_00000100 /* FX buses */ +// These are arbitrary mappings; our DSP code simply expects +// the config files to route the channels this way. +// The numbers are documented in {audigy,sb-live}-mixer.rst. #define FXBUS_PCM_LEFT 0x00 #define FXBUS_PCM_RIGHT 0x01 #define FXBUS_PCM_LEFT_REAR 0x02 diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 672af4b9597b..b8163f26004a 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -68,17 +68,6 @@ static const struct pci_device_id snd_emu10k1_ids[] = { { 0, } }; -/* - * Audigy 2 Value notes: - * A_IOCFG Input (GPIO) - * 0x400 = Front analog jack plugged in. (Green socket) - * 0x1000 = Read analog jack plugged in. (Black socket) - * 0x2000 = Center/LFE analog jack plugged in. (Orange socket) - * A_IOCFG Output (GPIO) - * 0x60 = Sound out of front Left. - * Win sets it to 0xXX61 - */ - MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids); static int snd_card_emu10k1_probe(struct pci_dev *pci, diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index c37df604d470..39b63c4ca1df 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -162,6 +162,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir) outl(0, emu->port + INTE); snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); + + /* disable stop on loop end */ snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); @@ -660,13 +662,14 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, return -EIO; /* The FPGA is a Xilinx Spartan IIE XC2S50E */ + /* On E-MU 0404b it is a Xilinx Spartan III XC3S50 */ /* GPIO7 -> FPGA PGMN * GPIO6 -> FPGA CCLK * GPIO5 -> FPGA DIN * FPGA CONFIG OFF -> FPGA PGMN */ spin_lock_irqsave(&emu->emu_lock, flags); - outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 1uS. */ + outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */ write_post = inw(emu->port + A_GPIO); udelay(100); outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */ @@ -782,7 +785,7 @@ static void emu1010_firmware_work(struct work_struct *work) } else if (!reg && emu->emu1010.last_reg) { /* Audio Dock removed */ dev_info(emu->card->dev, "emu1010: Audio Dock detached\n"); - /* Unmute all */ + /* The hardware auto-mutes all, so we unmute again */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); } @@ -794,29 +797,6 @@ static void emu1010_firmware_work(struct work_struct *work) } /* - * EMU-1010 - details found out from this driver, official MS Win drivers, - * testing the card: - * - * Audigy2 (aka Alice2): - * --------------------- - * * communication over PCI - * * conversion of 32-bit data coming over EMU32 links from HANA FPGA - * to 2 x 16-bit, using internal DSP instructions - * * slave mode, clock supplied by HANA - * * linked to HANA using: - * 32 x 32-bit serial EMU32 output channels - * 16 x EMU32 input channels - * (?) x I2S I/O channels (?) - * - * FPGA (aka HANA): - * --------------- - * * provides all (?) physical inputs and outputs of the card - * (ADC, DAC, SPDIF I/O, ADAT I/O, etc.) - * * provides clock signal for the card and Alice2 - * * two crystals - for 44.1kHz and 48kHz multiples - * * provides internal routing of signal sources to signal destinations - * * inputs/outputs to Alice2 - see above - * * Current status of the driver: * ---------------------------- * * only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz) @@ -884,11 +864,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg); /* Optical -> ADAT I/O */ - /* 0 : SPDIF - * 1 : ADAT - */ emu->emu1010.optical_in = 1; /* IN_ADAT */ - emu->emu1010.optical_out = 1; /* IN_ADAT */ + emu->emu1010.optical_out = 1; /* OUT_ADAT */ tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) | (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0); snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp); @@ -1279,6 +1256,15 @@ static const struct snd_emu_chip_details emu_chip_details[] = { * AC97: STAC9750 * CA0151: None */ + /* + * A_IOCFG Input (GPIO) + * 0x400 = Front analog jack plugged in. (Green socket) + * 0x1000 = Rear analog jack plugged in. (Black socket) + * 0x2000 = Center/LFE analog jack plugged in. (Orange socket) + * A_IOCFG Output (GPIO) + * 0x60 = Sound out of front Left. + * Win sets it to 0xXX61 + */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102, .driver = "Audigy2", .name = "SB Audigy 2 Value [SB0400]", .id = "Audigy2", @@ -1327,6 +1313,9 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .spi_dac = 1, .i2c_adc = 1, .spk71 = 1} , + /* This is MAEM8950 "Mana" */ + /* Attach MicroDock[M] to make it an E-MU 1616[m]. */ + /* Does NOT support sync daughter card (obviously). */ /* Tested by James@superbug.co.uk 4th Nov 2007. */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102, .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]", @@ -1337,7 +1326,10 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .spk71 = 1 , .emu_model = EMU_MODEL_EMU1616}, /* Tested by James@superbug.co.uk 4th Nov 2007. */ - /* This is MAEM8960, 0202 is MAEM 8980 */ + /* This is MAEM8960 "Hana3", 0202 is MAEM8980 */ + /* Attach 0202 daughter card to make it an E-MU 1212m, OR a + * MicroDock[M] to make it an E-MU 1616[m]. */ + /* Does NOT support sync daughter card. */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102, .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM8960]", .id = "EMU1010", @@ -1347,6 +1339,11 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 new revision */ /* Tested by Maxim Kachur 17th Oct 2012. */ /* This is MAEM8986, 0202 is MAEM8980 */ + /* Attach 0202 daughter card to make it an E-MU 1212m, OR a + * MicroDockM to make it an E-MU 1616m. The non-m + * version was never sold with this card, but should + * still work. */ + /* Does NOT support sync daughter card. */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40071102, .driver = "Audigy2", .name = "E-mu 1010 PCIe [MAEM8986]", .id = "EMU1010", @@ -1355,7 +1352,10 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .spk71 = 1, .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 PCIe */ /* Tested by James@superbug.co.uk 8th July 2005. */ - /* This is MAEM8810, 0202 is MAEM8820 */ + /* This is MAEM8810 "Hana", 0202 is MAEM8820 "Hamoa" */ + /* Attach 0202 daughter card to make it an E-MU 1212m, OR an + * AudioDock[M] to make it an E-MU 1820[m]. */ + /* Supports sync daughter card. */ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102, .driver = "Audigy2", .name = "E-mu 1010 [MAEM8810]", .id = "EMU1010", @@ -1363,7 +1363,9 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .ca0102_chip = 1, .spk71 = 1, .emu_model = EMU_MODEL_EMU1010}, /* EMU 1010 old revision */ - /* EMU0404b */ + /* This is MAEM8852 "HanaLiteLite" */ + /* Supports sync daughter card. */ + /* Tested by oswald.buddenhagen@gmx.de Mar 2023. */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102, .driver = "Audigy2", .name = "E-mu 0404b PCI [MAEM8852]", .id = "EMU0404", @@ -1371,6 +1373,8 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .ca0108_chip = 1, .spk71 = 1, .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 new revision */ + /* This is MAEM8850 "HanaLite" */ + /* Supports sync daughter card. */ /* Tested by James@superbug.co.uk 20-3-2007. */ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102, .driver = "Audigy2", .name = "E-mu 0404 [MAEM8850]", @@ -1380,6 +1384,7 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .spk71 = 1, .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */ /* EMU0404 PCIe */ + /* Does NOT support sync daughter card. */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40051102, .driver = "Audigy2", .name = "E-mu 0404 PCIe [MAEM8984]", .id = "EMU0404", @@ -1387,7 +1392,6 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .ca0108_chip = 1, .spk71 = 1, .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 PCIe ver_03 */ - /* Note that all E-mu cards require kernel 2.6 or newer. */ {.vendor = 0x1102, .device = 0x0008, .driver = "Audigy2", .name = "SB Audigy 2 Value [Unknown]", .id = "Audigy2", @@ -1468,6 +1472,8 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .spdif_bug = 1, .adc_1361t = 1, /* 24 bit capture instead of 16bit */ .ac97_chip = 1} , + /* Audigy 2 Platinum EX */ + /* Win driver sets A_IOCFG output to 0x1c00 */ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102, .driver = "Audigy2", .name = "Audigy 2 Platinum EX [SB0280]", .id = "Audigy2", @@ -1488,6 +1494,8 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .spdif_bug = 1, .invert_shared_spdif = 1, /* digital/analog switch swapped */ .ac97_chip = 1} , + /* Audigy 2 Platinum */ + /* Win driver sets A_IOCFG output to 0xa00 */ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102, .driver = "Audigy2", .name = "SB Audigy 2 Platinum [SB0240P]", .id = "Audigy2", @@ -1593,6 +1601,9 @@ static const struct snd_emu_chip_details emu_chip_details[] = { .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , + /* SB Live! Platinum */ + /* Win driver sets A_IOCFG output to 0 */ + /* Tested by Jonathan Dowland Apr 2023. */ {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102, .driver = "EMU10K1", .name = "SB Live! Platinum [CT4760P]", .id = "Live", diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 6a51aed59238..510b776f856d 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -1187,8 +1187,8 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl } /* - * Used for emu1010 - conversion from 32-bit capture inputs from HANA - * to 2 x 16-bit registers in audigy - their values are read via DMA. + * Used for emu1010 - conversion from 32-bit capture inputs from the FPGA + * to 2 x 16-bit registers in Audigy - their values are read via DMA. * Conversion is performed by Audigy DSP instructions of FX8010. */ static int snd_emu10k1_audigy_dsp_convert_32_to_2x16( @@ -1330,8 +1330,9 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) #define A_ADD_VOLUME_IN(var,vol,input) \ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) - /* emu1212 DSP 0 and DSP 1 Capture */ if (emu->card_capabilities->emu_model) { + /* EMU1010 DSP 0 and DSP 1 Capture */ + // The 24 MSB hold the actual value. We implicitly discard the 16 LSB. if (emu->card_capabilities->ca0108_chip) { /* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */ A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001); @@ -1636,8 +1637,11 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) #endif if (emu->card_capabilities->emu_model) { + /* Capture 16 channels of S32_LE sound. */ if (emu->card_capabilities->ca0108_chip) { dev_info(emu->card->dev, "EMU2 inputs on\n"); + /* Note that the Tina[2] DSPs have 16 more EMU32 inputs which we don't use. */ + for (z = 0; z < 0x10; z++) { snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, @@ -1646,7 +1650,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) } } else { dev_info(emu->card->dev, "EMU inputs on\n"); - /* Capture 16 (originally 8) channels of S32_LE sound */ + /* Note that the Alice2 DSPs have 6 I2S inputs which we don't use. */ /* dev_dbg(emu->card->dev, "emufx.c: gpr=0x%x, tmp=0x%x\n", diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 754d91050af2..3d2ed5e0a59d 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -346,7 +346,7 @@ static const unsigned int emu1616_output_dst[] = { }; /* - * Data destinations - HANA outputs going to Alice2 (audigy) for + * Data destinations - FPGA outputs going to Alice2 (Audigy) for * capture (EMU32 + I2S links) * Each destination has an enum mixer control to choose a data source */ @@ -367,6 +367,7 @@ static const unsigned int emu1010_input_dst[] = { EMU_DST_ALICE2_EMU32_D, EMU_DST_ALICE2_EMU32_E, EMU_DST_ALICE2_EMU32_F, + /* These exist only on rev1 EMU1010 cards. */ EMU_DST_ALICE_I2S0_LEFT, EMU_DST_ALICE_I2S0_RIGHT, EMU_DST_ALICE_I2S1_LEFT, @@ -708,7 +709,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol, /* 44100 */ /* Mute all */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE ); - /* Default fallback clock 48kHz */ + /* Default fallback clock 44.1kHz */ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_44_1K ); /* Word Clock source, Internal 44.1kHz x1 */ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index c2749ad59e10..a91ea0f863d3 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -326,7 +326,6 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, } else snd_emu10k1_ptr_write(emu, FXRT, voice, snd_emu10k1_compose_send_routing(send_routing)); - /* Stop CA */ /* Assumption that PT is already 0 so no harm overwriting */ snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); @@ -480,9 +479,6 @@ static int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream) start_addr = epcm->start_addr; end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); - /* - * the kX driver leaves some space between voices - */ channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK; snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, @@ -1218,9 +1214,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream) runtime->hw.rate_min = runtime->hw.rate_max = 48000; spin_lock_irq(&emu->reg_lock); if (emu->card_capabilities->emu_model) { - /* Nb. of channels has been increased to 16 */ /* TODO - * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 @@ -1231,13 +1225,14 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream) * Need to add mixer control to fix sample rate * * There are 32 mono channels of 16bits each. - * 24bit Audio uses 2x channels over 16bit - * 96kHz uses 2x channels over 48kHz - * 192kHz uses 4x channels over 48kHz - * So, for 48kHz 24bit, one has 16 channels - * for 96kHz 24bit, one has 8 channels - * for 192kHz 24bit, one has 4 channels - * + * 24bit Audio uses 2x channels over 16bit, + * 96kHz uses 2x channels over 48kHz, + * 192kHz uses 4x channels over 48kHz. + * So, for 48kHz 24bit, one has 16 channels, + * for 96kHz 24bit, one has 8 channels, + * for 192kHz 24bit, one has 4 channels. + * 1010rev2 and 1616(m) cards have double that, + * but we don't exceed 16 channels anyway. */ #if 1 switch (emu->emu1010.internal_clock) { @@ -1459,11 +1454,12 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st nval[idx / 32] |= 1 << (idx % 32); bits++; } - + + // Check that the number of requested channels is a power of two + // not bigger than the number of available channels. for (idx = 0; idx < nefxb; idx++) if (1 << idx == bits) break; - if (idx >= nefxb) return -EINVAL; diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index 42b06f2e5552..c60573f14ea8 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -314,7 +314,6 @@ void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenu unsigned int val; spin_lock_irqsave(&emu->emu_lock, flags); - /* voice interrupt */ if (voicenum >= 32) { outl(CLIEH << 16, emu->port + PTR); val = inl(emu->port + DATA); @@ -334,7 +333,6 @@ void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicen unsigned int val; spin_lock_irqsave(&emu->emu_lock, flags); - /* voice interrupt */ if (voicenum >= 32) { outl(CLIEH << 16, emu->port + PTR); val = inl(emu->port + DATA); @@ -353,7 +351,6 @@ void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum) unsigned long flags; spin_lock_irqsave(&emu->emu_lock, flags); - /* voice interrupt */ if (voicenum >= 32) { outl(CLIPH << 16, emu->port + PTR); voicenum = 1 << (voicenum - 32); @@ -371,7 +368,6 @@ void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned i unsigned int val; spin_lock_irqsave(&emu->emu_lock, flags); - /* voice interrupt */ if (voicenum >= 32) { outl(HLIEH << 16, emu->port + PTR); val = inl(emu->port + DATA); @@ -391,7 +387,6 @@ void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned unsigned int val; spin_lock_irqsave(&emu->emu_lock, flags); - /* voice interrupt */ if (voicenum >= 32) { outl(HLIEH << 16, emu->port + PTR); val = inl(emu->port + DATA); @@ -410,7 +405,6 @@ void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int unsigned long flags; spin_lock_irqsave(&emu->emu_lock, flags); - /* voice interrupt */ if (voicenum >= 32) { outl(HLIPH << 16, emu->port + PTR); voicenum = 1 << (voicenum - 32); @@ -428,7 +422,6 @@ void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voice unsigned int sol; spin_lock_irqsave(&emu->emu_lock, flags); - /* voice interrupt */ if (voicenum >= 32) { outl(SOLEH << 16, emu->port + PTR); sol = inl(emu->port + DATA); @@ -448,7 +441,6 @@ void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voi unsigned int sol; spin_lock_irqsave(&emu->emu_lock, flags); - /* voice interrupt */ if (voicenum >= 32) { outl(SOLEH << 16, emu->port + PTR); sol = inl(emu->port + DATA); diff --git a/sound/pci/emu10k1/p16v.h b/sound/pci/emu10k1/p16v.h index 3cdafa311617..9d429ad1feff 100644 --- a/sound/pci/emu10k1/p16v.h +++ b/sound/pci/emu10k1/p16v.h @@ -64,7 +64,7 @@ */ /********************************************************************************************************/ -/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers */ +/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers */ /********************************************************************************************************/ /* The sample rate of the SPDIF outputs is set by modifying a register in the EMU10K2 PTR register A_SPDIF_SAMPLERATE. diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h index 3a6568346fad..d4ada1c430c8 100644 --- a/sound/pci/emu10k1/p17v.h +++ b/sound/pci/emu10k1/p17v.h @@ -6,8 +6,8 @@ */ /******************************************************************************/ -/* Audigy2Value Tina (P17V) pointer-offset register set, - * accessed through the PTR20 and DATA24 registers */ +/* Audigy2Value Tina (P17V) pointer-offset register set, */ +/* accessed through the PTR2 and DATA2 registers */ /******************************************************************************/ /* 00 - 07: Not used */ -- cgit v1.2.3 From 6815f5359aa5d02d1a936221812ab46624c39283 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 22 Apr 2023 18:10:16 +0200 Subject: ALSA: emu10k1: fix lineup of EMU_HANA_* defines The bit values are supposed to be internally indented by one step relative to the register addresses. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230422161021.1143903-3-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 7786d5807679..35a60bdcf641 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1072,34 +1072,34 @@ #define EMU_HANA_DOCK_LEDS_3_MANUAL_SIGNAL 0x20 /* Manual Signal detection */ #define EMU_HANA_ADC_PADS 0x10 /* 0000xxx 3 bit Audio Dock ADC 14dB pads */ -#define EMU_HANA_DOCK_ADC_PAD1 0x01 /* 14dB Attenuation on Audio Dock ADC 1 */ -#define EMU_HANA_DOCK_ADC_PAD2 0x02 /* 14dB Attenuation on Audio Dock ADC 2 */ -#define EMU_HANA_DOCK_ADC_PAD3 0x04 /* 14dB Attenuation on Audio Dock ADC 3 */ -#define EMU_HANA_0202_ADC_PAD1 0x08 /* 14dB Attenuation on 0202 ADC 1 */ +#define EMU_HANA_DOCK_ADC_PAD1 0x01 /* 14dB Attenuation on Audio Dock ADC 1 */ +#define EMU_HANA_DOCK_ADC_PAD2 0x02 /* 14dB Attenuation on Audio Dock ADC 2 */ +#define EMU_HANA_DOCK_ADC_PAD3 0x04 /* 14dB Attenuation on Audio Dock ADC 3 */ +#define EMU_HANA_0202_ADC_PAD1 0x08 /* 14dB Attenuation on 0202 ADC 1 */ #define EMU_HANA_DOCK_MISC 0x11 /* 0xxxxxx 6 bit Audio Dock misc bits */ -#define EMU_HANA_DOCK_DAC1_MUTE 0x01 /* DAC 1 Mute */ -#define EMU_HANA_DOCK_DAC2_MUTE 0x02 /* DAC 2 Mute */ -#define EMU_HANA_DOCK_DAC3_MUTE 0x04 /* DAC 3 Mute */ -#define EMU_HANA_DOCK_DAC4_MUTE 0x08 /* DAC 4 Mute */ +#define EMU_HANA_DOCK_DAC1_MUTE 0x01 /* DAC 1 Mute */ +#define EMU_HANA_DOCK_DAC2_MUTE 0x02 /* DAC 2 Mute */ +#define EMU_HANA_DOCK_DAC3_MUTE 0x04 /* DAC 3 Mute */ +#define EMU_HANA_DOCK_DAC4_MUTE 0x08 /* DAC 4 Mute */ #define EMU_HANA_DOCK_PHONES_192_DAC1 0x00 /* DAC 1 Headphones source at 192kHz */ #define EMU_HANA_DOCK_PHONES_192_DAC2 0x10 /* DAC 2 Headphones source at 192kHz */ #define EMU_HANA_DOCK_PHONES_192_DAC3 0x20 /* DAC 3 Headphones source at 192kHz */ #define EMU_HANA_DOCK_PHONES_192_DAC4 0x30 /* DAC 4 Headphones source at 192kHz */ #define EMU_HANA_MIDI_OUT 0x12 /* 00xxxxx 5 bit Source for each MIDI out port */ -#define EMU_HANA_MIDI_OUT_0202 0x01 /* 0202 MIDI from Alice 2. 0 = A, 1 = B */ -#define EMU_HANA_MIDI_OUT_DOCK1 0x02 /* Audio Dock MIDI1 front, from Alice 2. 0 = A, 1 = B */ -#define EMU_HANA_MIDI_OUT_DOCK2 0x04 /* Audio Dock MIDI2 rear, from Alice 2. 0 = A, 1 = B */ -#define EMU_HANA_MIDI_OUT_SYNC2 0x08 /* Sync card. Not the actual MIDI out jack. 0 = A, 1 = B */ -#define EMU_HANA_MIDI_OUT_LOOP 0x10 /* 0 = bits (3:0) normal. 1 = MIDI loopback enabled. */ +#define EMU_HANA_MIDI_OUT_0202 0x01 /* 0202 MIDI from Alice 2. 0 = A, 1 = B */ +#define EMU_HANA_MIDI_OUT_DOCK1 0x02 /* Audio Dock MIDI1 front, from Alice 2. 0 = A, 1 = B */ +#define EMU_HANA_MIDI_OUT_DOCK2 0x04 /* Audio Dock MIDI2 rear, from Alice 2. 0 = A, 1 = B */ +#define EMU_HANA_MIDI_OUT_SYNC2 0x08 /* Sync card. Not the actual MIDI out jack. 0 = A, 1 = B */ +#define EMU_HANA_MIDI_OUT_LOOP 0x10 /* 0 = bits (3:0) normal. 1 = MIDI loopback enabled. */ #define EMU_HANA_DAC_PADS 0x13 /* 00xxxxx 5 bit DAC 14dB attenuation pads */ -#define EMU_HANA_DOCK_DAC_PAD1 0x01 /* 14dB Attenuation on AudioDock DAC 1. Left and Right */ -#define EMU_HANA_DOCK_DAC_PAD2 0x02 /* 14dB Attenuation on AudioDock DAC 2. Left and Right */ -#define EMU_HANA_DOCK_DAC_PAD3 0x04 /* 14dB Attenuation on AudioDock DAC 3. Left and Right */ -#define EMU_HANA_DOCK_DAC_PAD4 0x08 /* 14dB Attenuation on AudioDock DAC 4. Left and Right */ -#define EMU_HANA_0202_DAC_PAD1 0x10 /* 14dB Attenuation on 0202 DAC 1. Left and Right */ +#define EMU_HANA_DOCK_DAC_PAD1 0x01 /* 14dB Attenuation on AudioDock DAC 1. Left and Right */ +#define EMU_HANA_DOCK_DAC_PAD2 0x02 /* 14dB Attenuation on AudioDock DAC 2. Left and Right */ +#define EMU_HANA_DOCK_DAC_PAD3 0x04 /* 14dB Attenuation on AudioDock DAC 3. Left and Right */ +#define EMU_HANA_DOCK_DAC_PAD4 0x08 /* 14dB Attenuation on AudioDock DAC 4. Left and Right */ +#define EMU_HANA_0202_DAC_PAD1 0x10 /* 14dB Attenuation on 0202 DAC 1. Left and Right */ /* 0x14 - 0x1f Unused R/W registers */ @@ -1108,8 +1108,8 @@ /* Reading the register resets it. */ #define EMU_HANA_OPTION_CARDS 0x21 /* 000xxxx 4 bits Presence of option cards */ -#define EMU_HANA_OPTION_HAMOA 0x01 /* Hamoa (analog I/O) card present */ -#define EMU_HANA_OPTION_SYNC 0x02 /* Sync card present */ +#define EMU_HANA_OPTION_HAMOA 0x01 /* Hamoa (analog I/O) card present */ +#define EMU_HANA_OPTION_SYNC 0x02 /* Sync card present */ #define EMU_HANA_OPTION_DOCK_ONLINE 0x04 /* Audio/Micro dock present and FPGA configured */ #define EMU_HANA_OPTION_DOCK_OFFLINE 0x08 /* Audio/Micro dock present and FPGA not configured */ @@ -1123,8 +1123,8 @@ #define EMU_DOCK_MINOR_REV 0x26 /* 0000xxx 3 bit Audio Dock FPGA Minor rev */ #define EMU_DOCK_BOARD_ID 0x27 /* 00000xx 2 bits Audio Dock ID pins */ -#define EMU_DOCK_BOARD_ID0 0x00 /* ID bit 0 */ -#define EMU_DOCK_BOARD_ID1 0x03 /* ID bit 1 */ +#define EMU_DOCK_BOARD_ID0 0x00 /* ID bit 0 */ +#define EMU_DOCK_BOARD_ID1 0x03 /* ID bit 1 */ #define EMU_HANA_WC_SPDIF_HI 0x28 /* 0xxxxxx 6 bit SPDIF IN Word clock, upper 6 bits */ #define EMU_HANA_WC_SPDIF_LO 0x29 /* 0xxxxxx 6 bit SPDIF IN Word clock, lower 6 bits */ -- cgit v1.2.3 From a062b1032adae9ad89afb8dca299ef3ce9aca566 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 22 Apr 2023 18:10:17 +0200 Subject: ALSA: emu10k1: eliminate some unused defines One might be mislead to think that these mean anything. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230422161021.1143903-4-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 35a60bdcf641..931f3016d60d 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -25,13 +25,9 @@ /* ------------------- DEFINES -------------------- */ #define EMUPAGESIZE 4096 -#define MAXREQVOICES 8 #define MAXPAGES0 4096 /* 32 bit mode */ #define MAXPAGES1 8192 /* 31 bit mode */ -#define RESERVED 0 -#define NUM_MIDI 16 #define NUM_G 64 /* use all channels */ -#define NUM_FXSENDS 4 #define NUM_EFX_PLAYBACK 16 /* FIXME? - according to the OSS driver the EMU10K1 needs a 29 bit DMA mask */ @@ -39,7 +35,6 @@ #define AUDIGY_DMA_MASK 0xffffffffUL /* 32bit mode */ #define TMEMSIZE 256*1024 -#define TMEMSIZEREG 4 #define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL)) @@ -283,7 +278,6 @@ /* before the new rate is guaranteed accurate. */ #define TIMER_RATE_MASK 0x000003ff /* Timer interrupt rate in sample periods */ /* 0 == 1024 periods, [1..4] are not useful */ -#define TIMER_RATE 0x0a00001a #define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ -- cgit v1.2.3 From ac9219d93a983b0e146878695beb64ea624747d4 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 22 Apr 2023 18:10:18 +0200 Subject: ALSA: emu10k1: remove some bogus defines Firstly, remove the FXWC_* defines - the comment on FXWC implies that the relevant defines are the (A_)EXTOUT_* ones. It's unclear where this came from - it was in the initial ALSA import, but neither the driver from Creative nor kX-project have these defines. Secondly, remove A_HR, which made plain no sense (was unused, and clashed with FXRT). Amends commit cbb7d8f9b7b ("emu10k1: Update registers defines for the Audigy 2/emu10k2.5"). Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230422161021.1143903-5-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 15 --------------- sound/pci/emu10k1/emupcm.c | 1 - 2 files changed, 16 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 931f3016d60d..6927fac1aa5f 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -471,7 +471,6 @@ #define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */ #define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */ -#define A_HR 0x0b /* High Resolution. 24bit playback from host to DSP. */ #define MAPA 0x0c /* Cache map A */ #define MAPB 0x0d /* Cache map B */ @@ -626,20 +625,6 @@ /* is 16bit, 48KHz only. All 32 channels can be enabled */ /* simultaneously. */ -#define FXWC_DEFAULTROUTE_C (1<<0) /* left emu out? */ -#define FXWC_DEFAULTROUTE_B (1<<1) /* right emu out? */ -#define FXWC_DEFAULTROUTE_A (1<<12) -#define FXWC_DEFAULTROUTE_D (1<<13) -#define FXWC_ADCLEFT (1<<18) -#define FXWC_CDROMSPDIFLEFT (1<<18) -#define FXWC_ADCRIGHT (1<<19) -#define FXWC_CDROMSPDIFRIGHT (1<<19) -#define FXWC_MIC (1<<20) -#define FXWC_ZOOMLEFT (1<<20) -#define FXWC_ZOOMRIGHT (1<<21) -#define FXWC_SPDIFLEFT (1<<22) /* 0x00400000 */ -#define FXWC_SPDIFRIGHT (1<<23) /* 0x00800000 */ - #define A_TBLSZ 0x43 /* Effects Tank Internal Table Size. Only low byte or register used */ #define TCBS 0x44 /* Tank cache buffer size register */ diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index a91ea0f863d3..cc07bf4c6eb4 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1745,7 +1745,6 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device) * to these */ - /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */ if (emu->audigy) { emu->efx_voices_mask[0] = 0; if (emu->card_capabilities->emu_model) -- cgit v1.2.3 From 145ec1fd00a875d5afc54f52295472e2a898e35c Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 22 Apr 2023 18:10:19 +0200 Subject: ALSA: emu10k1: pull in some register definitions from kX-project For documentation purposes and later use. Some pre-existing but (mostly) unused definitions were renamed for consistency. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230422161021.1143903-6-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 78 ++++++++++++++++++++++++++++------------ sound/pci/emu10k1/emu10k1_main.c | 2 +- 2 files changed, 56 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 6927fac1aa5f..0d288cc618c1 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -61,8 +61,8 @@ /* the relevant bits and zero to the other bits */ #define IPR_P16V 0x80000000 /* Bit set when the CA0151 P16V chip wishes to interrupt */ -#define IPR_GPIOMSG 0x20000000 /* GPIO message interrupt (RE'd, still not sure - which INTE bits enable it) */ +#define IPR_WATERMARK_REACHED 0x40000000 +#define IPR_A_GPIO 0x20000000 /* GPIO input pin change */ /* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ #define IPR_A_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */ @@ -122,10 +122,14 @@ /* behavior and possibly random segfaults and */ /* lockups if enabled. */ +#define INTE_A_GPIOENABLE 0x00040000 /* Enable GPIO input change interrupts */ + /* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ #define INTE_A_MIDITXENABLE2 0x00020000 /* Enable MIDI transmit-buffer-empty interrupts */ #define INTE_A_MIDIRXENABLE2 0x00010000 /* Enable MIDI receive-buffer-empty interrupts */ +#define INTE_A_SPDIF_BUFFULL_ENABLE 0x00008000 +#define INTE_A_SPDIF_HALFBUFFULL_ENABLE 0x00004000 #define INTE_SAMPLERATETRACKER 0x00002000 /* Enable sample rate tracker interrupts */ /* NOTE: This bit must always be enabled */ @@ -146,9 +150,10 @@ #define WC 0x10 /* Wall Clock register */ #define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */ #define WC_SAMPLECOUNTER 0x14060010 -#define WC_CURRENTCHANNEL 0x0000003F /* Channel [0..63] currently being serviced */ +#define WC_CURRENTCHANNEL_MASK 0x0000003F /* Channel [0..63] currently being serviced */ /* NOTE: Each channel takes 1/64th of a sample */ /* period to be serviced. */ +#define WC_CURRENTCHANNEL 0x06000010 #define HCFG 0x14 /* Hardware config register */ /* NOTE: There is no reason to use the legacy */ @@ -276,7 +281,7 @@ /* NOTE: After the rate is changed, a maximum */ /* of 1024 sample periods should be allowed */ /* before the new rate is guaranteed accurate. */ -#define TIMER_RATE_MASK 0x000003ff /* Timer interrupt rate in sample periods */ +#define TIMER_RATE_MASK 0x03ff /* Timer interrupt rate in sample periods */ /* 0 == 1024 periods, [1..4] are not useful */ #define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ @@ -425,7 +430,7 @@ #define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */ #define CCCA_RESONANCE 0xf0000000 /* Lowpass filter resonance (Q) height */ -#define CCCA_INTERPROMMASK 0x0e000000 /* Selects passband of interpolation ROM */ +#define CCCA_INTERPROM_MASK 0x0e000000 /* Selects passband of interpolation ROM */ /* 1 == full band, 7 == lowpass */ /* ROM 0 is used when pitch shifting downward or less */ /* then 3 semitones upward. Increasingly higher ROM */ @@ -487,7 +492,7 @@ /* 0x8000-n == 666*n usec delay */ #define ATKHLDV 0x11 /* Volume envelope hold and attack register */ -#define ATKHLDV_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDV_PHASE0_MASK 0x00008000 /* 0 = Begin attack phase */ #define ATKHLDV_HOLDTIME_MASK 0x00007f00 /* Envelope hold time (127-n == n*88.2msec) */ #define ATKHLDV_ATTACKTIME_MASK 0x0000007f /* Envelope attack time, log encoded */ /* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */ @@ -510,7 +515,7 @@ /* 0x8000-n == 666*n usec delay */ #define ATKHLDM 0x15 /* Modulation envelope hold and attack register */ -#define ATKHLDM_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDM_PHASE0_MASK 0x00008000 /* 0 = Begin attack phase */ #define ATKHLDM_HOLDTIME 0x00007f00 /* Envelope hold time (127-n == n*42msec) */ #define ATKHLDM_ATTACKTIME 0x0000007f /* Envelope attack time, log encoded */ /* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */ @@ -839,16 +844,15 @@ #define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */ #define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */ -/* Extended Hardware Control */ -#define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */ -#define A_SAMPLE_RATE 0x76 /* Various sample rate settings. */ -#define A_SAMPLE_RATE_NOT_USED 0x0ffc111e /* Bits that are not used and cannot be set. */ -#define A_SAMPLE_RATE_UNKNOWN 0xf0030001 /* Bits that can be set, but have unknown use. */ +#define A_EHC 0x76 /* Extended Hardware Control */ + +#define A_SPDIF_SAMPLERATE A_EHC /* Set the sample rate of SPDIF output */ #define A_SPDIF_RATE_MASK 0x000000e0 /* Any other values for rates, just use 48000 */ -#define A_SPDIF_48000 0x00000000 +#define A_SPDIF_48000 0x00000000 /* kX calls this BYPASS */ #define A_SPDIF_192000 0x00000020 #define A_SPDIF_96000 0x00000040 #define A_SPDIF_44100 0x00000080 +#define A_SPDIF_MUTED 0x000000c0 #define A_I2S_CAPTURE_RATE_MASK 0x00000e00 /* This sets the capture PCM rate, but it is */ #define A_I2S_CAPTURE_48000 0x00000000 /* unclear if this sets the ADC rate as well. */ @@ -856,17 +860,29 @@ #define A_I2S_CAPTURE_96000 0x00000400 #define A_I2S_CAPTURE_44100 0x00000800 -#define A_PCM_RATE_MASK 0x0000e000 /* This sets the playback PCM rate on the P16V */ -#define A_PCM_48000 0x00000000 -#define A_PCM_192000 0x00002000 -#define A_PCM_96000 0x00004000 -#define A_PCM_44100 0x00008000 +#define A_EHC_SRC48_MASK 0x0000e000 /* This sets the playback PCM rate on the P16V */ +#define A_EHC_SRC48_BYPASS 0x00000000 +#define A_EHC_SRC48_192 0x00002000 +#define A_EHC_SRC48_96 0x00004000 +#define A_EHC_SRC48_44 0x00008000 +#define A_EHC_SRC48_MUTED 0x0000c000 + +#define A_EHC_P17V_TVM 0x00000001 /* Tank virtual memory mode */ +#define A_EHC_P17V_SEL0_MASK 0x00030000 /* Aka A_EHC_P16V_PB_RATE; 00: 48, 01: 44.1, 10: 96, 11: 192 */ +#define A_EHC_P17V_SEL1_MASK 0x000c0000 +#define A_EHC_P17V_SEL2_MASK 0x00300000 +#define A_EHC_P17V_SEL3_MASK 0x00c00000 + +#define A_EHC_ASYNC_BYPASS 0x80000000 #define A_SRT3 0x77 /* I2S0 Sample Rate Tracker Status */ #define A_SRT4 0x78 /* I2S1 Sample Rate Tracker Status */ #define A_SRT5 0x79 /* I2S2 Sample Rate Tracker Status */ /* - default to 0x01080000 on my audigy 2 ZS --rlrevell */ +#define A_SRT_ESTSAMPLERATE 0x001fffff +#define A_SRT_RATELOCKED 0x01000000 + #define A_TTDA 0x7a /* Tank Table DMA Address */ #define A_TTDD 0x7b /* Tank Table DMA Data */ @@ -981,7 +997,7 @@ #define EMU_HANA_WCLOCK_INT_44_1K 0x01 #define EMU_HANA_WCLOCK_HANA_SPDIF_IN 0x02 #define EMU_HANA_WCLOCK_HANA_ADAT_IN 0x03 -#define EMU_HANA_WCLOCK_SYNC_BNCN 0x04 +#define EMU_HANA_WCLOCK_SYNC_BNC 0x04 #define EMU_HANA_WCLOCK_2ND_HANA 0x05 #define EMU_HANA_WCLOCK_SRC_RESERVED 0x06 #define EMU_HANA_WCLOCK_OFF 0x07 /* For testing, forces fallback to DEFCLOCK */ @@ -1010,10 +1026,10 @@ #define EMU_HANA_IRQ_DOCK_LOST 0x08 #define EMU_HANA_SPDIF_MODE 0x0a /* 00xxxxx 5 bits SPDIF MODE */ -#define EMU_HANA_SPDIF_MODE_TX_COMSUMER 0x00 +#define EMU_HANA_SPDIF_MODE_TX_CONSUMER 0x00 #define EMU_HANA_SPDIF_MODE_TX_PRO 0x01 #define EMU_HANA_SPDIF_MODE_TX_NOCOPY 0x02 -#define EMU_HANA_SPDIF_MODE_RX_COMSUMER 0x00 +#define EMU_HANA_SPDIF_MODE_RX_CONSUMER 0x00 #define EMU_HANA_SPDIF_MODE_RX_PRO 0x04 #define EMU_HANA_SPDIF_MODE_RX_NOCOPY 0x08 #define EMU_HANA_SPDIF_MODE_RX_INVALID 0x10 @@ -1025,8 +1041,12 @@ #define EMU_HANA_OPTICAL_OUT_ADAT 0x02 #define EMU_HANA_MIDI_IN 0x0c /* 000000x 1 bit Control MIDI */ -#define EMU_HANA_MIDI_IN_FROM_HAMOA 0x00 /* HAMOA MIDI in to Alice 2 MIDI B */ -#define EMU_HANA_MIDI_IN_FROM_DOCK 0x01 /* Audio Dock MIDI in to Alice 2 MIDI B */ +#define EMU_HANA_MIDI_INA_FROM_HAMOA 0x01 /* HAMOA MIDI in to Alice 2 MIDI A */ +#define EMU_HANA_MIDI_INA_FROM_DOCK1 0x02 /* Audio Dock-1 MIDI in to Alice 2 MIDI A */ +#define EMU_HANA_MIDI_INA_FROM_DOCK2 0x03 /* Audio Dock-2 MIDI in to Alice 2 MIDI A */ +#define EMU_HANA_MIDI_INB_FROM_HAMOA 0x08 /* HAMOA MIDI in to Alice 2 MIDI B */ +#define EMU_HANA_MIDI_INB_FROM_DOCK1 0x10 /* Audio Dock-1 MIDI in to Alice 2 MIDI B */ +#define EMU_HANA_MIDI_INB_FROM_DOCK2 0x18 /* Audio Dock-2 MIDI in to Alice 2 MIDI B */ #define EMU_HANA_DOCK_LEDS_1 0x0d /* 000xxxx 4 bit Audio Dock LEDs */ #define EMU_HANA_DOCK_LEDS_1_MIDI1 0x01 /* MIDI 1 LED on */ @@ -1119,6 +1139,10 @@ /* 0x30 - 0x3f Unused Read only registers */ +// The meaning of this is not clear; kX-project just calls it "lock" in some info-only code. +#define EMU_HANA_LOCK_STS_LO 0x38 /* 0xxxxxx lower 6 bits */ +#define EMU_HANA_LOCK_STS_HI 0x39 /* 0xxxxxx upper 6 bits */ + /************************************************************************************************/ /* EMU1010 Audio Destinations */ /************************************************************************************************/ @@ -1257,8 +1281,12 @@ #define EMU_DST_DOCK_SPDIF_RIGHT2 0x011f /* Audio Dock SPDIF Right, 2nd or 96kHz */ #define EMU_DST_HANA_SPDIF_LEFT1 0x0200 /* Hana SPDIF Left, 1st or 48kHz only */ #define EMU_DST_HANA_SPDIF_LEFT2 0x0202 /* Hana SPDIF Left, 2nd or 96kHz */ +#define EMU_DST_HANA_SPDIF_LEFT3 0x0204 /* Hana SPDIF Left, 3rd or 192kHz */ +#define EMU_DST_HANA_SPDIF_LEFT4 0x0206 /* Hana SPDIF Left, 4th or 192kHz */ #define EMU_DST_HANA_SPDIF_RIGHT1 0x0201 /* Hana SPDIF Right, 1st or 48kHz only */ #define EMU_DST_HANA_SPDIF_RIGHT2 0x0203 /* Hana SPDIF Right, 2nd or 96kHz */ +#define EMU_DST_HANA_SPDIF_RIGHT3 0x0205 /* Hana SPDIF Right, 3rd or 192kHz */ +#define EMU_DST_HANA_SPDIF_RIGHT4 0x0207 /* Hana SPDIF Right, 4th or 192kHz */ #define EMU_DST_HAMOA_DAC_LEFT1 0x0300 /* Hamoa DAC Left, 1st or 48kHz only */ #define EMU_DST_HAMOA_DAC_LEFT2 0x0302 /* Hamoa DAC Left, 2nd or 96kHz */ #define EMU_DST_HAMOA_DAC_LEFT3 0x0304 /* Hamoa DAC Left, 3rd or 192kHz */ @@ -1409,8 +1437,12 @@ #define EMU_SRC_HANA_ADAT 0x0400 /* Hana ADAT 8 channel in +0 to +7 */ #define EMU_SRC_HANA_SPDIF_LEFT1 0x0500 /* Hana SPDIF Left, 1st or 48kHz only */ #define EMU_SRC_HANA_SPDIF_LEFT2 0x0502 /* Hana SPDIF Left, 2nd or 96kHz */ +#define EMU_SRC_HANA_SPDIF_LEFT3 0x0504 /* Hana SPDIF Left, 3rd or 192kHz */ +#define EMU_SRC_HANA_SPDIF_LEFT4 0x0506 /* Hana SPDIF Left, 4th or 192kHz */ #define EMU_SRC_HANA_SPDIF_RIGHT1 0x0501 /* Hana SPDIF Right, 1st or 48kHz only */ #define EMU_SRC_HANA_SPDIF_RIGHT2 0x0503 /* Hana SPDIF Right, 2nd or 96kHz */ +#define EMU_SRC_HANA_SPDIF_RIGHT3 0x0505 /* Hana SPDIF Right, 3rd or 192kHz */ +#define EMU_SRC_HANA_SPDIF_RIGHT4 0x0507 /* Hana SPDIF Right, 4th or 192kHz */ /* Additional inputs for 1616(M)/Microdock */ diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 39b63c4ca1df..007c445d72bf 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1953,7 +1953,7 @@ static const unsigned char saved_regs[] = { 0xff /* end */ }; static const unsigned char saved_regs_audigy[] = { - A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_SAMPLE_RATE, + A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_EHC, A_FXRT2, A_SENDAMOUNTS, A_FXRT1, 0xff /* end */ }; -- cgit v1.2.3 From 2696d5a3b0ec9f242b0220999933b6c78502b1b2 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 22 Apr 2023 18:10:20 +0200 Subject: ALSA: emu10k1: fixup DSP defines Firstly, fix the distribution between public and private headers. Otherwise, some of the already public macros wouldn't actually work, and the SNDRV_EMU10K1_IOCTL_DBG_READ result for Audigy would be useless. Secondly, add condition code registers for Audigy. These are just aliases for selected constant registers, and thus are generation- specific. At least A_CC_REG_ZERO is actually correct ... Finally, shuffle around some defines to more logical places while at it, and fix up some more comments. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230422161021.1143903-7-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 46 ++------------ include/uapi/sound/emu10k1.h | 144 ++++++++++++++++++++++++++++--------------- 2 files changed, 99 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 0d288cc618c1..5958cae819fd 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -704,18 +704,15 @@ #define GPSCS 0x51 /* General Purpose SPDIF channel status register */ +// Corresponding EMU10K1_DBG_* constants are in the public header #define DBG 0x52 #define A_SPSC 0x52 /* S/PDIF Input C Channel Status */ #define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ -#define A_DBG 0x53 -#define A_DBG_SINGLE_STEP 0x00020000 /* Set to zero to start dsp */ -#define A_DBG_ZC 0x40000000 /* zero tram counter */ -#define A_DBG_STEP_ADDR 0x000003ff -#define A_DBG_SATURATION_OCCURED 0x20000000 -#define A_DBG_SATURATION_ADDR 0x0ffc0000 +// Corresponding A_DBG_* constants are in the public header +#define A_DBG 0x53 // NOTE: 0x54,55,56: 64-bit (split over voices 0 & 1) #define SPCS0 0x54 /* SPDIF output Channel Status 0 register */ @@ -908,45 +905,14 @@ #define A_FXRT_CHANNELD 0x3f000000 /* 0x7f: Not used */ -/* Each FX general purpose register is 32 bits in length, all bits are used */ -#define FXGPREGBASE 0x100 /* FX general purpose registers base */ -#define A_FXGPREGBASE 0x400 /* Audigy GPRs, 0x400 to 0x5ff */ - -#define A_TANKMEMCTLREGBASE 0x100 /* Tank memory control registers base - only for Audigy */ -#define A_TANKMEMCTLREG_MASK 0x1f /* only 5 bits used - only for Audigy */ - -/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */ -/* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */ -/* locations are for external TRAM. */ -#define TANKMEMDATAREGBASE 0x200 /* Tank memory data registers base */ -#define TANKMEMDATAREG_MASK 0x000fffff /* 20 bit tank audio data field */ - -/* Combined address field and memory opcode or flag field. 160 locations, last 32 are external */ -#define TANKMEMADDRREGBASE 0x300 /* Tank memory address registers base */ -#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ -#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ -#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ -#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ -#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ -#define MICROCODEBASE 0x400 /* Microcode data base address */ +/* The public header defines the GPR and TRAM base addresses that + * are valid for _both_ CPU and DSP addressing. */ /* Each DSP microcode instruction is mapped into 2 doublewords */ /* NOTE: When writing, always write the LO doubleword first. Reads can be in either order. */ -#define LOWORD_OPX_MASK 0x000ffc00 /* Instruction operand X */ -#define LOWORD_OPY_MASK 0x000003ff /* Instruction operand Y */ -#define HIWORD_OPCODE_MASK 0x00f00000 /* Instruction opcode */ -#define HIWORD_RESULT_MASK 0x000ffc00 /* Instruction result */ -#define HIWORD_OPA_MASK 0x000003ff /* Instruction operand A */ - - -/* Audigy Soundcard have a different instruction format */ +#define MICROCODEBASE 0x400 /* Microcode data base address */ #define A_MICROCODEBASE 0x600 -#define A_LOWORD_OPY_MASK 0x000007ff -#define A_LOWORD_OPX_MASK 0x007ff000 -#define A_HIWORD_OPCODE_MASK 0x0f000000 -#define A_HIWORD_RESULT_MASK 0x007ff000 -#define A_HIWORD_OPA_MASK 0x000007ff /************************************************************************************************/ diff --git a/include/uapi/sound/emu10k1.h b/include/uapi/sound/emu10k1.h index 33e5228f5d8c..c8e131d6da00 100644 --- a/include/uapi/sound/emu10k1.h +++ b/include/uapi/sound/emu10k1.h @@ -43,6 +43,19 @@ #define iINTERP 0x0e /* R = A + (X * (Y - A) >> 31) ; saturation */ #define iSKIP 0x0f /* R = A (cc_reg), X (count), Y (cc_test) */ +#define LOWORD_OPX_MASK 0x000ffc00 /* Instruction operand X */ +#define LOWORD_OPY_MASK 0x000003ff /* Instruction operand Y */ +#define HIWORD_OPCODE_MASK 0x00f00000 /* Instruction opcode */ +#define HIWORD_RESULT_MASK 0x000ffc00 /* Instruction result */ +#define HIWORD_OPA_MASK 0x000003ff /* Instruction operand A */ + +/* Audigy Soundcards have a different instruction format */ +#define A_LOWORD_OPX_MASK 0x007ff000 +#define A_LOWORD_OPY_MASK 0x000007ff +#define A_HIWORD_OPCODE_MASK 0x0f000000 +#define A_HIWORD_RESULT_MASK 0x007ff000 +#define A_HIWORD_OPA_MASK 0x000007ff + /* GPRs */ #define FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x0f */ #define EXTIN(x) (0x10 + (x)) /* x = 0x00 - 0x0f */ @@ -50,6 +63,16 @@ #define FXBUS2(x) (0x30 + (x)) /* x = 0x00 - 0x0f copies of fx buses for capture -> FXWC high 16 bits */ /* NB: 0x31 and 0x32 are shared with Center/LFE on SB live 5.1 */ +#define A_FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x3f FX buses */ +#define A_EXTIN(x) (0x40 + (x)) /* x = 0x00 - 0x0f physical ins */ +#define A_P16VIN(x) (0x50 + (x)) /* x = 0x00 - 0x0f p16v ins (A2 only) "EMU32 inputs" */ +#define A_EXTOUT(x) (0x60 + (x)) /* x = 0x00 - 0x1f physical outs -> A_FXWC1 0x79-7f unknown */ +#define A_FXBUS2(x) (0x80 + (x)) /* x = 0x00 - 0x1f extra outs used for EFX capture -> A_FXWC2 */ +#define A_EMU32OUTH(x) (0xa0 + (x)) /* x = 0x00 - 0x0f "EMU32_OUT_10 - _1F" */ +#define A_EMU32OUTL(x) (0xb0 + (x)) /* x = 0x00 - 0x0f "EMU32_OUT_01 - _0F" */ +#define A3_EMU32IN(x) (0x160 + (x)) /* x = 0x00 - 0x1f "EMU32_IN_00 - _1F" - Only when .device = 0x0008 */ +#define A3_EMU32OUT(x) (0x1E0 + (x)) /* x = 0x00 - 0x1f "EMU32_OUT_00 - _1F" - Only when .device = 0x0008 */ + #define C_00000000 0x40 #define C_00000001 0x41 #define C_00000002 0x42 @@ -78,12 +101,66 @@ #define GPR_NOISE1 0x59 /* noise source */ #define GPR_IRQ 0x5a /* IRQ register */ #define GPR_DBAC 0x5b /* TRAM Delay Base Address Counter */ + +/* Audigy constants */ +#define A_C_00000000 0xc0 +#define A_C_00000001 0xc1 +#define A_C_00000002 0xc2 +#define A_C_00000003 0xc3 +#define A_C_00000004 0xc4 +#define A_C_00000008 0xc5 +#define A_C_00000010 0xc6 +#define A_C_00000020 0xc7 +#define A_C_00000100 0xc8 +#define A_C_00010000 0xc9 +#define A_C_00000800 0xca +#define A_C_10000000 0xcb +#define A_C_20000000 0xcc +#define A_C_40000000 0xcd +#define A_C_80000000 0xce +#define A_C_7fffffff 0xcf +#define A_C_ffffffff 0xd0 +#define A_C_fffffffe 0xd1 +#define A_C_c0000000 0xd2 +#define A_C_4f1bbcdc 0xd3 +#define A_C_5a7ef9db 0xd4 +#define A_C_00100000 0xd5 +#define A_GPR_ACCU 0xd6 /* ACCUM, accumulator */ +#define A_GPR_COND 0xd7 /* CCR, condition register */ +#define A_GPR_NOISE0 0xd8 /* noise source */ +#define A_GPR_NOISE1 0xd9 /* noise source */ +#define A_GPR_IRQ 0xda /* IRQ register */ +#define A_GPR_DBAC 0xdb /* TRAM Delay Base Address Counter - internal */ +#define A_GPR_DBACE 0xde /* TRAM Delay Base Address Counter - external */ + +/* Each FX general purpose register is 32 bits in length, all bits are used */ +#define FXGPREGBASE 0x100 /* FX general purpose registers base */ +#define A_FXGPREGBASE 0x400 /* Audigy GPRs, 0x400 to 0x5ff */ + +#define A_TANKMEMCTLREGBASE 0x100 /* Tank memory control registers base - only for Audigy */ +#define A_TANKMEMCTLREG_MASK 0x1f /* only 5 bits used - only for Audigy */ + +/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */ +/* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */ +/* locations are for external TRAM. */ +#define TANKMEMDATAREGBASE 0x200 /* Tank memory data registers base */ +#define TANKMEMDATAREG_MASK 0x000fffff /* 20 bit tank audio data field */ + +/* Combined address field and memory opcode or flag field. 160 locations, last 32 are external */ +#define TANKMEMADDRREGBASE 0x300 /* Tank memory address registers base */ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ + #define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ #define ITRAM_DATA(x) (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ #define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ #define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ #define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ +#define A_GPR(x) (A_FXGPREGBASE + (x)) #define A_ITRAM_DATA(x) (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0xbf */ #define A_ETRAM_DATA(x) (TANKMEMDATAREGBASE + 0xc0 + (x)) /* x = 0x00 - 0x3f */ #define A_ITRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0xbf */ @@ -91,17 +168,6 @@ #define A_ITRAM_CTL(x) (A_TANKMEMCTLREGBASE + 0x00 + (x)) /* x = 0x00 - 0xbf */ #define A_ETRAM_CTL(x) (A_TANKMEMCTLREGBASE + 0xc0 + (x)) /* x = 0x00 - 0x3f */ -#define A_FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x3f FX buses */ -#define A_EXTIN(x) (0x40 + (x)) /* x = 0x00 - 0x0f physical ins */ -#define A_P16VIN(x) (0x50 + (x)) /* x = 0x00 - 0x0f p16v ins (A2 only) "EMU32 inputs" */ -#define A_EXTOUT(x) (0x60 + (x)) /* x = 0x00 - 0x1f physical outs -> A_FXWC1 0x79-7f unknown */ -#define A_FXBUS2(x) (0x80 + (x)) /* x = 0x00 - 0x1f extra outs used for EFX capture -> A_FXWC2 */ -#define A_EMU32OUTH(x) (0xa0 + (x)) /* x = 0x00 - 0x0f "EMU32_OUT_10 - _1F" - ??? */ -#define A_EMU32OUTL(x) (0xb0 + (x)) /* x = 0x00 - 0x0f "EMU32_OUT_1 - _F" - ??? */ -#define A3_EMU32IN(x) (0x160 + (x)) /* x = 0x00 - 0x3f "EMU32_IN_00 - _3F" - Only when .device = 0x0008 */ -#define A3_EMU32OUT(x) (0x1E0 + (x)) /* x = 0x00 - 0x0f "EMU32_OUT_00 - _3F" - Only when .device = 0x0008 */ -#define A_GPR(x) (A_FXGPREGBASE + (x)) - /* cc_reg constants */ #define CC_REG_NORMALIZED C_00000001 #define CC_REG_BORROW C_00000002 @@ -110,6 +176,13 @@ #define CC_REG_SATURATE C_00000010 #define CC_REG_NONZERO C_00000100 +#define A_CC_REG_NORMALIZED A_C_00000001 +#define A_CC_REG_BORROW A_C_00000002 +#define A_CC_REG_MINUS A_C_00000004 +#define A_CC_REG_ZERO A_C_00000008 +#define A_CC_REG_SATURATE A_C_00000010 +#define A_CC_REG_NONZERO A_C_00000100 + /* FX buses */ // These are arbitrary mappings; our DSP code simply expects // the config files to route the channels this way. @@ -203,38 +276,7 @@ #define A_EXTOUT_ADC_CAP_R 0x17 /* right */ #define A_EXTOUT_MIC_CAP 0x18 /* Mic capture buffer */ -/* Audigy constants */ -#define A_C_00000000 0xc0 -#define A_C_00000001 0xc1 -#define A_C_00000002 0xc2 -#define A_C_00000003 0xc3 -#define A_C_00000004 0xc4 -#define A_C_00000008 0xc5 -#define A_C_00000010 0xc6 -#define A_C_00000020 0xc7 -#define A_C_00000100 0xc8 -#define A_C_00010000 0xc9 -#define A_C_00000800 0xca -#define A_C_10000000 0xcb -#define A_C_20000000 0xcc -#define A_C_40000000 0xcd -#define A_C_80000000 0xce -#define A_C_7fffffff 0xcf -#define A_C_ffffffff 0xd0 -#define A_C_fffffffe 0xd1 -#define A_C_c0000000 0xd2 -#define A_C_4f1bbcdc 0xd3 -#define A_C_5a7ef9db 0xd4 -#define A_C_00100000 0xd5 -#define A_GPR_ACCU 0xd6 /* ACCUM, accumulator */ -#define A_GPR_COND 0xd7 /* CCR, condition register */ -#define A_GPR_NOISE0 0xd8 /* noise source */ -#define A_GPR_NOISE1 0xd9 /* noise source */ -#define A_GPR_IRQ 0xda /* IRQ register */ -#define A_GPR_DBAC 0xdb /* TRAM Delay Base Address Counter - internal */ -#define A_GPR_DBACE 0xde /* TRAM Delay Base Address Counter - external */ - -/* definitions for debug register */ +/* Definitions for debug register. Note that these are for emu10k1 ONLY. */ #define EMU10K1_DBG_ZC 0x80000000 /* zero tram counter */ #define EMU10K1_DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ #define EMU10K1_DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ @@ -243,14 +285,14 @@ #define EMU10K1_DBG_CONDITION_CODE 0x00003e00 /* condition code */ #define EMU10K1_DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ -/* tank memory address line */ -#ifndef __KERNEL__ -#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ -#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ -#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ -#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ -#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ -#endif +/* Definitions for emu10k2 debug register. */ +#define A_DBG_ZC 0x40000000 /* zero tram counter */ +#define A_DBG_SATURATION_OCCURED 0x20000000 +#define A_DBG_SATURATION_ADDR 0x0ffc0000 +#define A_DBG_SINGLE_STEP 0x00020000 /* Set to zero to start dsp */ +#define A_DBG_STEP 0x00010000 +#define A_DBG_CONDITION_CODE 0x0000f800 +#define A_DBG_STEP_ADDR 0x000003ff struct snd_emu10k1_fx8010_info { unsigned int internal_tram_size; /* in samples */ -- cgit v1.2.3 From 8d60d5cabea12cd533e4f216ab3b773390165aac Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sat, 22 Apr 2023 18:10:20 +0200 Subject: ALSA: emu10k1: use high-level I/O functions also during init ... and also use more pre-defined constants on the way (some of which required adjustment). Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230422161021.1143967-1-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 9 +++++---- sound/pci/emu10k1/emu10k1_main.c | 20 ++++++-------------- 2 files changed, 11 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 5958cae819fd..05a09826eef0 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -852,10 +852,11 @@ #define A_SPDIF_MUTED 0x000000c0 #define A_I2S_CAPTURE_RATE_MASK 0x00000e00 /* This sets the capture PCM rate, but it is */ -#define A_I2S_CAPTURE_48000 0x00000000 /* unclear if this sets the ADC rate as well. */ -#define A_I2S_CAPTURE_192000 0x00000200 -#define A_I2S_CAPTURE_96000 0x00000400 -#define A_I2S_CAPTURE_44100 0x00000800 +#define A_I2S_CAPTURE_RATE 0x03090076 /* unclear if this sets the ADC rate as well. */ +#define A_I2S_CAPTURE_48000 0x0 +#define A_I2S_CAPTURE_192000 0x1 +#define A_I2S_CAPTURE_96000 0x2 +#define A_I2S_CAPTURE_44100 0x4 #define A_EHC_SRC48_MASK 0x0000e000 /* This sets the playback PCM rate on the P16V */ #define A_EHC_SRC48_BYPASS 0x00000000 diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index d9199d92ccd3..3abdaf1b9624 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -187,10 +187,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir) } else if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ /* Hacks for Alice3 to work independent of haP16V driver */ /* Setup SRCMulti_I2S SamplingRate */ - tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); - tmp &= 0xfffff1ff; - tmp |= (0x2<<9); - snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp); + snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, 0, A_I2S_CAPTURE_96000); /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */ snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14); @@ -206,25 +203,20 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir) /* Hacks for Alice3 to work independent of haP16V driver */ dev_info(emu->card->dev, "Audigy2 value: Special config.\n"); /* Setup SRCMulti_I2S SamplingRate */ - tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); - tmp &= 0xfffff1ff; - tmp |= (0x2<<9); - snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp); + snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, 0, A_I2S_CAPTURE_96000); /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */ - outl(0x600000, emu->port + 0x20); - outl(0x14, emu->port + 0x24); + snd_emu10k1_ptr20_write(emu, P17V_SRCSel, 0, 0x14); /* Setup SRCMulti Input Audio Enable */ - outl(0x7b0000, emu->port + 0x20); - outl(0xFF000000, emu->port + 0x24); + snd_emu10k1_ptr20_write(emu, P17V_MIXER_I2S_ENABLE, 0, 0xFF000000); /* Setup SPDIF Out Audio Enable */ /* The Audigy 2 Value has a separate SPDIF out, * so no need for a mixer switch */ - outl(0x7a0000, emu->port + 0x20); - outl(0xFF000000, emu->port + 0x24); + snd_emu10k1_ptr20_write(emu, P17V_MIXER_SPDIF_ENABLE, 0, 0xFF000000); + tmp = inw(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */ outw(tmp, emu->port + A_IOCFG); } -- cgit v1.2.3 From 7002cbd625467084f1ef01b6e365e10b51fc4b9f Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Sun, 23 Apr 2023 20:10:02 +0200 Subject: ALSA: emu10k1: use high-level I/O in set_filterQ() This makes the code shorter and more legible. Signed-off-by: Oswald Buddenhagen Link: https://lore.kernel.org/r/20230423181002.1246793-2-oswald.buddenhagen@gmx.de Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 3 ++- sound/pci/emu10k1/emu10k1_callback.c | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 05a09826eef0..8fe80dcee71b 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -429,7 +429,8 @@ #define DSL_LOOPENDADDR 0x18000007 #define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */ -#define CCCA_RESONANCE 0xf0000000 /* Lowpass filter resonance (Q) height */ +#define CCCA_RESONANCE_MASK 0xf0000000 /* Lowpass filter resonance (Q) height */ +#define CCCA_RESONANCE 0x041c0008 #define CCCA_INTERPROM_MASK 0x0e000000 /* Selects passband of interpolation ROM */ /* 1 == full band, 7 == lowpass */ /* ROM 0 is used when pitch shifting downward or less */ diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c index dba1e9fc2eec..c6d152575181 100644 --- a/sound/pci/emu10k1/emu10k1_callback.c +++ b/sound/pci/emu10k1/emu10k1_callback.c @@ -531,8 +531,5 @@ set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp) static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp) { - unsigned int val; - val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE; - val |= (vp->reg.parm.filterQ << 28); - snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val); + snd_emu10k1_ptr_write(hw, CCCA_RESONANCE, vp->ch, vp->reg.parm.filterQ); } -- cgit v1.2.3