diff options
49 files changed, 3235 insertions, 798 deletions
diff --git a/Documentation/devicetree/bindings/sound/alc5623.txt b/Documentation/devicetree/bindings/sound/alc5623.txt deleted file mode 100644 index 26c86c98d671..000000000000 --- a/Documentation/devicetree/bindings/sound/alc5623.txt +++ /dev/null @@ -1,25 +0,0 @@ -ALC5621/ALC5622/ALC5623 audio Codec - -Required properties: - - - compatible: "realtek,alc5623" - - reg: the I2C address of the device. - -Optional properties: - - - add-ctrl: Default register value for Reg-40h, Additional Control - Register. If absent or has the value of 0, the - register is untouched. - - - jack-det-ctrl: Default register value for Reg-5Ah, Jack Detect - Control Register. If absent or has value 0, the - register is untouched. - -Example: - - alc5621: alc5621@1a { - compatible = "alc5621"; - reg = <0x1a>; - add-ctrl = <0x3700>; - jack-det-ctrl = <0x4810>; - }; diff --git a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt deleted file mode 100644 index 7bb0362828ec..000000000000 --- a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.txt +++ /dev/null @@ -1,24 +0,0 @@ -* Broadcom BCM2835 SoC I2S/PCM module - -Required properties: -- compatible: "brcm,bcm2835-i2s" -- reg: Should contain PCM registers location and length. -- clocks: the (PCM) clock to use -- dmas: List of DMA controller phandle and DMA request line ordered pairs. -- dma-names: Identifier string for each DMA request line in the dmas property. - These strings correspond 1:1 with the ordered pairs in dmas. - - One of the DMA channels will be responsible for transmission (should be - named "tx") and one for reception (should be named "rx"). - -Example: - -bcm2835_i2s: i2s@7e203000 { - compatible = "brcm,bcm2835-i2s"; - reg = <0x7e203000 0x24>; - clocks = <&clocks BCM2835_CLOCK_PCM>; - - dmas = <&dma 2>, - <&dma 3>; - dma-names = "tx", "rx"; -}; diff --git a/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.yaml b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.yaml new file mode 100644 index 000000000000..f3cfe92684d0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/brcm,bcm2835-i2s.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/brcm,bcm2835-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom BCM2835 SoC I2S/PCM module + +maintainers: + - Florian Fainelli <florian.fainelli@broadcom.com> + +properties: + compatible: + const: brcm,bcm2835-i2s + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + dmas: + items: + - description: Transmission DMA controller phandle and request line. + - description: Reception DMA controller phandle and request line. + + dma-names: + items: + - const: tx + - const: rx + +required: + - compatible + - reg + - clocks + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/bcm2835.h> + + i2s@7e203000 { + compatible = "brcm,bcm2835-i2s"; + reg = <0x7e203000 0x24>; + clocks = <&clocks BCM2835_CLOCK_PCM>; + dmas = <&dma 2>, <&dma 3>; + dma-names = "tx", "rx"; + }; diff --git a/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml b/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml new file mode 100644 index 000000000000..4da735317e0f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/foursemi,fs2105s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: FourSemi FS2104/5S Digital Audio Amplifier + +maintainers: + - Nick Li <nick.li@foursemi.com> + +description: + The FS2104 is a 15W Inductor-Less, Stereo, Closed-Loop, + Digital Input Class-D Power Amplifier with Enhanced Signal Processing. + The FS2105S is a 30W Inductor-Less, Stereo, Closed-Loop, + Digital Input Class-D Power Amplifier with Enhanced Signal Processing. + +properties: + compatible: + oneOf: + - items: + - enum: + - foursemi,fs2104 + - const: foursemi,fs2105s + - enum: + - foursemi,fs2105s + + reg: + maxItems: 1 + + clocks: + items: + - description: The clock of I2S BCLK + + clock-names: + items: + - const: bclk + + interrupts: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + pvdd-supply: + description: + Regulator for power supply(PVDD in datasheet). + + dvdd-supply: + description: + Regulator for digital supply(DVDD in datasheet). + + reset-gpios: + maxItems: 1 + description: + It's the SDZ pin in datasheet, the pin is active low, + it will power down and reset the chip to shut down state. + + firmware-name: + maxItems: 1 + description: | + The firmware(*.bin) contains: + a. Register initialization settings + b. DSP effect parameters + c. Multi-scene sound effect configurations(optional) + It's gernerated by FourSemi's tuning tool. + +required: + - compatible + - reg + - '#sound-dai-cells' + - pvdd-supply + - dvdd-supply + - reset-gpios + - firmware-name + +allOf: + - $ref: dai-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + audio-codec@68 { + compatible = "foursemi,fs2105s"; + reg = <0x68>; + clocks = <&clocks 18>; + clock-names = "bclk"; + #sound-dai-cells = <0>; + pvdd-supply = <&pvdd_supply>; + dvdd-supply = <&dvdd_supply>; + reset-gpios = <&gpio 18 GPIO_ACTIVE_LOW>; + firmware-name = "fs2105s-btl-2p0-0s.bin"; + pinctrl-names = "default"; + pinctrl-0 = <&fs210x_pins_default>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt deleted file mode 100644 index 2f89db88fd57..000000000000 --- a/Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt +++ /dev/null @@ -1,56 +0,0 @@ -Freescale i.MX audio complex with SGTL5000 codec - -Required properties: - - - compatible : "fsl,imx-audio-sgtl5000" - - - model : The user-visible name of this sound complex - - - ssi-controller : The phandle of the i.MX SSI controller - - - audio-codec : The phandle of the SGTL5000 audio codec - - - audio-routing : A list of the connections between audio components. - Each entry is a pair of strings, the first being the - connection's sink, the second being the connection's - source. Valid names could be power supplies, SGTL5000 - pins, and the jacks on the board: - - Power supplies: - * Mic Bias - - SGTL5000 pins: - * MIC_IN - * LINE_IN - * HP_OUT - * LINE_OUT - - Board connectors: - * Mic Jack - * Line In Jack - * Headphone Jack - * Line Out Jack - * Ext Spk - - - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) - - - mux-ext-port : The external port of the i.MX audio muxer - -Note: The AUDMUX port numbering should start at 1, which is consistent with -hardware manual. - -Example: - -sound { - compatible = "fsl,imx51-babbage-sgtl5000", - "fsl,imx-audio-sgtl5000"; - model = "imx51-babbage-sgtl5000"; - ssi-controller = <&ssi1>; - audio-codec = <&sgtl5000>; - audio-routing = - "MIC_IN", "Mic Jack", - "Mic Jack", "Mic Bias", - "Headphone Jack", "HP_OUT"; - mux-int-port = <1>; - mux-ext-port = <3>; -}; diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml index 14d312f9c345..098f1df62c8c 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wsa883x.yaml @@ -29,6 +29,10 @@ properties: description: GPIO spec for Powerdown/Shutdown line to use (pin SD_N) maxItems: 1 + reset-gpios: + description: Powerdown/Shutdown line to use (pin SD_N) + maxItems: 1 + vdd-supply: description: VDD Supply for the Codec @@ -50,10 +54,15 @@ required: - compatible - reg - vdd-supply - - powerdown-gpios - "#thermal-sensor-cells" - "#sound-dai-cells" +oneOf: + - required: + - powerdown-gpios + - required: + - reset-gpios + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/sound/realtek,alc5623.yaml b/Documentation/devicetree/bindings/sound/realtek,alc5623.yaml new file mode 100644 index 000000000000..683c58c3ef50 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/realtek,alc5623.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/realtek,alc5623.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ALC5621/ALC5623 Audio Codec + +maintainers: + - Mahdi Khosravi <mmk1776@gmail.com> + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - realtek,alc5621 + - realtek,alc5623 + + reg: + maxItems: 1 + + add-ctrl: + description: + Default register value for Reg-40h, Additional Control Register. + If absent or zero, the register is left untouched. + $ref: /schemas/types.yaml#/definitions/uint32 + + jack-det-ctrl: + description: + Default register value for Reg-5Ah, Jack Detect Control Register. + If absent or zero, the register is left untouched. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1a { + compatible = "realtek,alc5623"; + reg = <0x1a>; + add-ctrl = <0x3700>; + jack-det-ctrl = <0x4810>; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 77160cd47f54..a92261b10c52 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -552,6 +552,8 @@ patternProperties: description: FocalTech Systems Co.,Ltd "^forlinx,.*": description: Baoding Forlinx Embedded Technology Co., Ltd. + "^foursemi,.*": + description: Shanghai FourSemi Semiconductor Co.,Ltd. "^freebox,.*": description: Freebox SAS "^freecom,.*": diff --git a/MAINTAINERS b/MAINTAINERS index daf520a13bdf..5f0b02137456 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9573,6 +9573,14 @@ F: lib/tests/memcpy_kunit.c K: \bunsafe_memcpy\b K: \b__NO_FORTIFY\b +FOURSEMI AUDIO AMPLIFIER DRIVER +M: Nick Li <nick.li@foursemi.com> +L: linux-sound@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml +F: sound/soc/codecs/fs-amp-lib.* +F: sound/soc/codecs/fs210x.* + FPGA DFL DRIVERS M: Xu Yilun <yilun.xu@intel.com> R: Tom Rix <trix@redhat.com> diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 1ef13bcdc43f..9472f0a966a2 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -69,6 +69,10 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) * @peripheral_config: peripheral configuration for programming peripheral * for dmaengine transfer * @peripheral_size: peripheral configuration buffer size + * @port_window_size: The length of the register area in words the data need + * to be accessed on the device side. It is only used for devices which is using + * an area instead of a single register to send/receive the data. Typically the + * DMA loops in this area in order to transfer the data. */ struct snd_dmaengine_dai_dma_data { dma_addr_t addr; @@ -80,6 +84,7 @@ struct snd_dmaengine_dai_dma_data { unsigned int flags; void *peripheral_config; size_t peripheral_size; + u32 port_window_size; }; void snd_dmaengine_pcm_set_config_from_dai_data( diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 2caa807c6249..54bfa0cb1085 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -287,51 +287,6 @@ static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm( } /** - * snd_soc_component_init_bias_level() - Initialize COMPONENT DAPM bias level - * @component: The COMPONENT for which to initialize the DAPM bias level - * @level: The DAPM level to initialize to - * - * Initializes the COMPONENT DAPM bias level. See snd_soc_dapm_init_bias_level() - */ -static inline void -snd_soc_component_init_bias_level(struct snd_soc_component *component, - enum snd_soc_bias_level level) -{ - snd_soc_dapm_init_bias_level( - snd_soc_component_get_dapm(component), level); -} - -/** - * snd_soc_component_get_bias_level() - Get current COMPONENT DAPM bias level - * @component: The COMPONENT for which to get the DAPM bias level - * - * Returns: The current DAPM bias level of the COMPONENT. - */ -static inline enum snd_soc_bias_level -snd_soc_component_get_bias_level(struct snd_soc_component *component) -{ - return snd_soc_dapm_get_bias_level( - snd_soc_component_get_dapm(component)); -} - -/** - * snd_soc_component_force_bias_level() - Set the COMPONENT DAPM bias level - * @component: The COMPONENT for which to set the level - * @level: The level to set to - * - * Forces the COMPONENT bias level to a specific state. See - * snd_soc_dapm_force_bias_level(). - */ -static inline int -snd_soc_component_force_bias_level(struct snd_soc_component *component, - enum snd_soc_bias_level level) -{ - return snd_soc_dapm_force_bias_level( - snd_soc_component_get_dapm(component), - level); -} - -/** * snd_soc_dapm_kcontrol_component() - Returns the component associated to a * kcontrol * @kcontrol: The kcontrol diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 0b5c7e6a90c8..2e9196b6ffba 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -722,6 +722,13 @@ struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(struct snd_kcontrol *kco struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(struct snd_kcontrol *kcontrol); int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); +enum snd_soc_bias_level snd_soc_dapm_get_bias_level(struct snd_soc_dapm_context *dapm); +void snd_soc_dapm_init_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); + +// REMOVE ME !! +#define snd_soc_component_force_bias_level(c, l) snd_soc_dapm_force_bias_level(&(c)->dapm, l) +#define snd_soc_component_get_bias_level(c) snd_soc_dapm_get_bias_level(&(c)->dapm) +#define snd_soc_component_init_bias_level(c, l) snd_soc_dapm_init_bias_level(&(c)->dapm, l) #define for_each_dapm_widgets(list, i, widget) \ for ((i) = 0; \ @@ -729,37 +736,6 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_so (i)++) /** - * snd_soc_dapm_init_bias_level() - Initialize DAPM bias level - * @dapm: The DAPM context to initialize - * @level: The DAPM level to initialize to - * - * This function only sets the driver internal state of the DAPM level and will - * not modify the state of the device. Hence it should not be used during normal - * operation, but only to synchronize the internal state to the device state. - * E.g. during driver probe to set the DAPM level to the one corresponding with - * the power-on reset state of the device. - * - * To change the DAPM state of the device use snd_soc_dapm_set_bias_level(). - */ -static inline void snd_soc_dapm_init_bias_level( - struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) -{ - dapm->bias_level = level; -} - -/** - * snd_soc_dapm_get_bias_level() - Get current DAPM bias level - * @dapm: The context for which to get the bias level - * - * Returns: The current bias level of the passed DAPM context. - */ -static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level( - struct snd_soc_dapm_context *dapm) -{ - return dapm->bias_level; -} - -/** * snd_soc_dapm_widget_for_each_path - Iterates over all paths in the * specified direction of a widget * @w: The widget diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h index c3a9efa73d5d..a21f34c0266e 100644 --- a/include/sound/tas2781-dsp.h +++ b/include/sound/tas2781-dsp.h @@ -198,6 +198,14 @@ struct tasdevice_rca { int ncfgs; struct tasdevice_config_info **cfg_info; int profile_cfg_id; + /* + * Since version 0x105, the keyword 'init' was introduced into the + * profile, which is used for chip initialization, particularly to + * store common settings for other non-initialization profiles. + * if (init_profile_id < 0) + * No init profile inside the RCA firmware. + */ + int init_profile_id; }; void tasdevice_select_cfg_blk(void *context, int conf_no, diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h index c9f845b3c523..f3ff6aae09a9 100644 --- a/include/uapi/sound/intel/avs/tokens.h +++ b/include/uapi/sound/intel/avs/tokens.h @@ -133,6 +133,21 @@ enum avs_tplg_token { AVS_TKN_PATH_FE_FMT_ID_U32 = 1902, AVS_TKN_PATH_BE_FMT_ID_U32 = 1903, + /* struct avs_tplg_path_template (conditional) */ + AVS_TKN_CONDPATH_TMPL_ID_U32 = 1801, + AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING = 2002, + AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32 = 2003, + AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING = 2004, + AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32 = 2005, + AVS_TKN_CONDPATH_TMPL_COND_TYPE_U32 = 2006, + AVS_TKN_CONDPATH_TMPL_OVERRIDABLE_BOOL = 2007, + AVS_TKN_CONDPATH_TMPL_PRIORITY_U8 = 2008, + + /* struct avs_tplg_path (conditional) */ + AVS_TKN_CONDPATH_ID_U32 = 1901, + AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32 = 2102, + AVS_TKN_CONDPATH_SINK_PATH_ID_U32 = 2103, + /* struct avs_tplg_pin_format */ AVS_TKN_PIN_FMT_INDEX_U32 = 2201, AVS_TKN_PIN_FMT_IOBS_U32 = 2202, diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 72040964b6fd..f0c17503df42 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -111,6 +111,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data( if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { slave_config->dst_addr = dma_data->addr; slave_config->dst_maxburst = dma_data->maxburst; + slave_config->dst_port_window_size = dma_data->port_window_size; if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK) slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; @@ -119,6 +120,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data( } else { slave_config->src_addr = dma_data->addr; slave_config->src_maxburst = dma_data->maxburst; + slave_config->src_port_window_size = dma_data->port_window_size; if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK) slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 6d7e4725d89c..b8d58d2fe326 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -125,6 +125,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ES7134 imply SND_SOC_ES7241 imply SND_SOC_FRAMER + imply SND_SOC_FS210X imply SND_SOC_GTM601 imply SND_SOC_HDAC_HDMI imply SND_SOC_HDAC_HDA @@ -300,7 +301,6 @@ config SND_SOC_ALL_CODECS imply SND_SOC_LPASS_MACRO_COMMON imply SND_SOC_LPASS_RX_MACRO imply SND_SOC_LPASS_TX_MACRO - imply SND_SOC_WL1273 imply SND_SOC_WM0010 imply SND_SOC_WM1250_EV1 imply SND_SOC_WM2000 @@ -622,6 +622,7 @@ config SND_SOC_AK4619 config SND_SOC_AK4641 tristate depends on I2C + depends on GPIOLIB_LEGACY config SND_SOC_AK4642 tristate "AKM AK4642 CODEC" @@ -1232,6 +1233,21 @@ config SND_SOC_FRAMER To compile this driver as a module, choose M here: the module will be called snd-soc-framer. +config SND_SOC_FS_AMP_LIB + select CRC16 + tristate + +config SND_SOC_FS210X + tristate 'FourSemi FS2104/5S digital audio amplifier' + depends on I2C + select GPIOLIB + select REGMAP_I2C + select SND_SOC_FS_AMP_LIB + help + Enable support for FourSemi FS2104/5S digital audio amplifier. + The FS2104/5S are Inductor-Less, Stereo, Closed-Loop, + Digital Input Class-D Power Amplifiers with Enhanced Signal Processing. + The amplifiers support I2C and I2S/TDM. config SND_SOC_GTM601 tristate 'GTM601 UMTS modem audio codec' @@ -2175,6 +2191,7 @@ config SND_SOC_TLV320AIC3X_SPI config SND_SOC_TLV320DAC33 tristate depends on I2C + depends on GPIOLIB_LEGACY config SND_SOC_TLV320ADCX140 tristate "Texas Instruments TLV320ADCX140 CODEC family" @@ -2229,6 +2246,7 @@ config SND_SOC_UDA1342 config SND_SOC_UDA1380 tristate depends on I2C + depends on GPIOLIB_LEGACY config SND_SOC_WCD_CLASSH tristate @@ -2316,9 +2334,6 @@ config SND_SOC_WCD939X_SDW The WCD9390/9395 is a audio codec IC Integrated in Qualcomm SoCs like SM8650. -config SND_SOC_WL1273 - tristate - config SND_SOC_WM0010 tristate depends on SPI_MASTER diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index a68c3d192a1b..a476d6c45451 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -137,6 +137,8 @@ snd-soc-es8328-spi-y := es8328-spi.o snd-soc-es8375-y := es8375.o snd-soc-es8389-y := es8389.o snd-soc-framer-y := framer-codec.o +snd-soc-fs-amp-lib-y := fs-amp-lib.o +snd-soc-fs210x-y := fs210x.o snd-soc-gtm601-y := gtm601.o snd-soc-hdac-hdmi-y := hdac_hdmi.o snd-soc-hdac-hda-y := hdac_hda.o @@ -348,7 +350,6 @@ snd-soc-wcd938x-y := wcd938x.o snd-soc-wcd938x-sdw-y := wcd938x-sdw.o snd-soc-wcd939x-y := wcd939x.o snd-soc-wcd939x-sdw-y := wcd939x-sdw.o -snd-soc-wl1273-y := wl1273.o snd-soc-wm-adsp-y := wm_adsp.o snd-soc-wm0010-y := wm0010.o snd-soc-wm1250-ev1-y := wm1250-ev1.o @@ -562,6 +563,8 @@ obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o obj-$(CONFIG_SND_SOC_ES8375) += snd-soc-es8375.o obj-$(CONFIG_SND_SOC_ES8389) += snd-soc-es8389.o obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o +obj-$(CONFIG_SND_SOC_FS_AMP_LIB)+= snd-soc-fs-amp-lib.o +obj-$(CONFIG_SND_SOC_FS210X) += snd-soc-fs210x.o obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o @@ -779,7 +782,6 @@ ifdef CONFIG_SND_SOC_WCD939X_SDW # avoid link failure by forcing sdw code built-in when needed obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o endif -obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o diff --git a/sound/soc/codecs/cs48l32-tables.c b/sound/soc/codecs/cs48l32-tables.c index 59eaa9a5029f..8ff3652a010e 100644 --- a/sound/soc/codecs/cs48l32-tables.c +++ b/sound/soc/codecs/cs48l32-tables.c @@ -533,8 +533,6 @@ static const struct regmap_config cs48l32_regmap = { int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32) { cs48l32->regmap = devm_regmap_init_spi(spi, &cs48l32_regmap); - if (IS_ERR(cs48l32->regmap)) - return PTR_ERR(cs48l32->regmap); - return 0; + return PTR_ERR_OR_ZERO(cs48l32->regmap); } diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c index a9822998199f..eb85b71e87f3 100644 --- a/sound/soc/codecs/es8323.c +++ b/sound/soc/codecs/es8323.c @@ -182,13 +182,13 @@ static const struct snd_kcontrol_new es8323_mono_adc_mux_controls = /* Left Mixer */ static const struct snd_kcontrol_new es8323_left_mixer_controls[] = { - SOC_DAPM_SINGLE("Left Playback Switch", SND_SOC_NOPM, 7, 1, 1), + SOC_DAPM_SINGLE("Left Playback Switch", ES8323_DACCONTROL17, 7, 1, 0), SOC_DAPM_SINGLE("Left Bypass Switch", ES8323_DACCONTROL17, 6, 1, 0), }; /* Right Mixer */ static const struct snd_kcontrol_new es8323_right_mixer_controls[] = { - SOC_DAPM_SINGLE("Right Playback Switch", SND_SOC_NOPM, 6, 1, 1), + SOC_DAPM_SINGLE("Right Playback Switch", ES8323_DACCONTROL20, 7, 1, 0), SOC_DAPM_SINGLE("Right Bypass Switch", ES8323_DACCONTROL20, 6, 1, 0), }; @@ -211,8 +211,8 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = { SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1), SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1), - SND_SOC_DAPM_DAC("Right DAC", "Right Playback", SND_SOC_NOPM, 6, 1), - SND_SOC_DAPM_DAC("Left DAC", "Left Playback", SND_SOC_NOPM, 7, 1), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1), SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, &es8323_left_mixer_controls[0], @@ -223,10 +223,10 @@ static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = { SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0), SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0), - SND_SOC_DAPM_PGA("Right Out 2", SND_SOC_NOPM, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA("Left Out 2", SND_SOC_NOPM, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("Right Out 1", SND_SOC_NOPM, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("Left Out 1", SND_SOC_NOPM, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0), SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0), SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 0, NULL, 0), @@ -632,7 +632,6 @@ static int es8323_probe(struct snd_soc_component *component) snd_soc_component_write(component, ES8323_CONTROL2, 0x60); snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00); - snd_soc_component_write(component, ES8323_DACCONTROL17, 0xB8); return 0; } diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c new file mode 100644 index 000000000000..75d8d5082e30 --- /dev/null +++ b/sound/soc/codecs/fs-amp-lib.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// fs-amp-lib.c --- Common library for FourSemi Audio Amplifiers +// +// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. + +#include <linux/crc16.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "fs-amp-lib.h" + +static int fs_get_scene_count(struct fs_amp_lib *amp_lib) +{ + const struct fs_fwm_table *table; + int count; + + if (!amp_lib || !amp_lib->dev) + return -EINVAL; + + table = amp_lib->table[FS_INDEX_SCENE]; + if (!table) + return -EFAULT; + + count = table->size / sizeof(struct fs_scene_index); + if (count < 1 || count > FS_SCENE_COUNT_MAX) { + dev_err(amp_lib->dev, "Invalid scene count: %d\n", count); + return -ERANGE; + } + + return count; +} + +static void fs_get_fwm_string(struct fs_amp_lib *amp_lib, + int offset, const char **pstr) +{ + const struct fs_fwm_table *table; + + if (!amp_lib || !amp_lib->dev || !pstr) + return; + + table = amp_lib->table[FS_INDEX_STRING]; + if (table && offset > 0 && offset < table->size + sizeof(*table)) + *pstr = (char *)table + offset; + else + *pstr = NULL; +} + +static void fs_get_scene_reg(struct fs_amp_lib *amp_lib, + int offset, struct fs_amp_scene *scene) +{ + const struct fs_fwm_table *table; + + if (!amp_lib || !amp_lib->dev || !scene) + return; + + table = amp_lib->table[FS_INDEX_REG]; + if (table && offset > 0 && offset < table->size + sizeof(*table)) + scene->reg = (struct fs_reg_table *)((char *)table + offset); + else + scene->reg = NULL; +} + +static void fs_get_scene_model(struct fs_amp_lib *amp_lib, + int offset, struct fs_amp_scene *scene) +{ + const struct fs_fwm_table *table; + const char *ptr; + + if (!amp_lib || !amp_lib->dev || !scene) + return; + + table = amp_lib->table[FS_INDEX_MODEL]; + ptr = (char *)table; + if (table && offset > 0 && offset < table->size + sizeof(*table)) + scene->model = (struct fs_file_table *)(ptr + offset); + else + scene->model = NULL; +} + +static void fs_get_scene_effect(struct fs_amp_lib *amp_lib, + int offset, struct fs_amp_scene *scene) +{ + const struct fs_fwm_table *table; + const char *ptr; + + if (!amp_lib || !amp_lib->dev || !scene) + return; + + table = amp_lib->table[FS_INDEX_EFFECT]; + ptr = (char *)table; + if (table && offset > 0 && offset < table->size + sizeof(*table)) + scene->effect = (struct fs_file_table *)(ptr + offset); + else + scene->effect = NULL; +} + +static int fs_parse_scene_tables(struct fs_amp_lib *amp_lib) +{ + const struct fs_scene_index *scene_index; + const struct fs_fwm_table *table; + struct fs_amp_scene *scene; + int idx, count; + + if (!amp_lib || !amp_lib->dev) + return -EINVAL; + + count = fs_get_scene_count(amp_lib); + if (count <= 0) + return -EFAULT; + + scene = devm_kzalloc(amp_lib->dev, count * sizeof(*scene), GFP_KERNEL); + if (!scene) + return -ENOMEM; + + amp_lib->scene_count = count; + amp_lib->scene = scene; + + table = amp_lib->table[FS_INDEX_SCENE]; + scene_index = (struct fs_scene_index *)table->buf; + + for (idx = 0; idx < count; idx++) { + fs_get_fwm_string(amp_lib, scene_index->name, &scene->name); + if (!scene->name) + scene->name = devm_kasprintf(amp_lib->dev, + GFP_KERNEL, "S%d", idx); + dev_dbg(amp_lib->dev, "scene.%d name: %s\n", idx, scene->name); + fs_get_scene_reg(amp_lib, scene_index->reg, scene); + fs_get_scene_model(amp_lib, scene_index->model, scene); + fs_get_scene_effect(amp_lib, scene_index->effect, scene); + scene++; + scene_index++; + } + + return 0; +} + +static int fs_parse_all_tables(struct fs_amp_lib *amp_lib) +{ + const struct fs_fwm_table *table; + const struct fs_fwm_index *index; + const char *ptr; + int idx, count; + int ret; + + if (!amp_lib || !amp_lib->dev || !amp_lib->hdr) + return -EINVAL; + + /* Parse all fwm tables */ + table = (struct fs_fwm_table *)amp_lib->hdr->params; + index = (struct fs_fwm_index *)table->buf; + count = table->size / sizeof(*index); + + for (idx = 0; idx < count; idx++, index++) { + if (index->type >= FS_INDEX_MAX) + return -ERANGE; + ptr = (char *)table + (int)index->offset; + amp_lib->table[index->type] = (struct fs_fwm_table *)ptr; + } + + /* Parse all scene tables */ + ret = fs_parse_scene_tables(amp_lib); + if (ret) + dev_err(amp_lib->dev, "Failed to parse scene: %d\n", ret); + + return ret; +} + +static int fs_verify_firmware(struct fs_amp_lib *amp_lib) +{ + const struct fs_fwm_header *hdr; + int crcsum; + + if (!amp_lib || !amp_lib->dev || !amp_lib->hdr) + return -EINVAL; + + hdr = amp_lib->hdr; + + /* Verify the crcsum code */ + crcsum = crc16(0x0000, (const char *)&hdr->crc_size, hdr->crc_size); + if (crcsum != hdr->crc16) { + dev_err(amp_lib->dev, "Failed to checksum: %x-%x\n", + crcsum, hdr->crc16); + return -EFAULT; + } + + /* Verify the devid(chip_type) */ + if (amp_lib->devid != LO_U16(hdr->chip_type)) { + dev_err(amp_lib->dev, "DEVID dismatch: %04X#%04X\n", + amp_lib->devid, hdr->chip_type); + return -EINVAL; + } + + return 0; +} + +static void fs_print_firmware_info(struct fs_amp_lib *amp_lib) +{ + const struct fs_fwm_header *hdr; + const char *pro_name = NULL; + const char *dev_name = NULL; + + if (!amp_lib || !amp_lib->dev || !amp_lib->hdr) + return; + + hdr = amp_lib->hdr; + + fs_get_fwm_string(amp_lib, hdr->project, &pro_name); + fs_get_fwm_string(amp_lib, hdr->device, &dev_name); + + dev_info(amp_lib->dev, "Project: %s Device: %s\n", + pro_name ? pro_name : "null", + dev_name ? dev_name : "null"); + + dev_info(amp_lib->dev, "Date: %04d%02d%02d-%02d%02d\n", + hdr->date.year, hdr->date.month, hdr->date.day, + hdr->date.hour, hdr->date.minute); +} + +int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name) +{ + const struct firmware *cont; + struct fs_fwm_header *hdr; + int ret; + + if (!amp_lib || !amp_lib->dev || !name) + return -EINVAL; + + ret = request_firmware(&cont, name, amp_lib->dev); + if (ret) { + dev_err(amp_lib->dev, "Failed to request %s: %d\n", name, ret); + return ret; + } + + dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size); + + hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL); + release_firmware(cont); + if (!hdr) + return -ENOMEM; + + amp_lib->hdr = hdr; + ret = fs_verify_firmware(amp_lib); + if (ret) { + amp_lib->hdr = NULL; + return ret; + } + + ret = fs_parse_all_tables(amp_lib); + if (ret) { + amp_lib->hdr = NULL; + return ret; + } + + fs_print_firmware_info(amp_lib); + + return 0; +} +EXPORT_SYMBOL_GPL(fs_amp_load_firmware); + +MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>"); +MODULE_DESCRIPTION("FourSemi audio amplifier library"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/fs-amp-lib.h b/sound/soc/codecs/fs-amp-lib.h new file mode 100644 index 000000000000..4a77c7b383cd --- /dev/null +++ b/sound/soc/codecs/fs-amp-lib.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * fs-amp-lib.h --- Common library for FourSemi Audio Amplifiers + * + * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. + */ + +#ifndef __FS_AMP_LIB_H__ +#define __FS_AMP_LIB_H__ + +#define HI_U16(a) (((a) >> 8) & 0xFF) +#define LO_U16(a) ((a) & 0xFF) +#define FS_TABLE_NAME_LEN (4) +#define FS_SCENE_COUNT_MAX (16) +#define FS_CMD_DELAY_MS_MAX (100) /* 100ms */ + +#define FS_CMD_DELAY (0xFF) +#define FS_CMD_BURST (0xFE) +#define FS_CMD_UPDATE (0xFD) + +#define FS_SOC_ENUM_EXT(xname, xhandler_info, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = xhandler_info, \ + .get = xhandler_get, .put = xhandler_put \ +} + +enum fs_index_type { + FS_INDEX_INFO = 0, + FS_INDEX_STCOEF, + FS_INDEX_SCENE, + FS_INDEX_MODEL, + FS_INDEX_REG, + FS_INDEX_EFFECT, + FS_INDEX_STRING, + FS_INDEX_WOOFER, + FS_INDEX_MAX, +}; + +#pragma pack(push, 1) + +struct fs_reg_val { + u8 reg; + u16 val; +}; + +struct fs_reg_bits { + u8 cmd; /* FS_CMD_UPDATE */ + u8 reg; + u16 val; + u16 mask; +}; + +struct fs_cmd_pkg { + union { + u8 cmd; + struct fs_reg_val regv; + struct fs_reg_bits regb; + }; +}; + +struct fs_fwm_index { + /* Index type */ + u16 type; + /* Offset address starting from the end of header */ + u16 offset; +}; + +struct fs_fwm_table { + char name[FS_TABLE_NAME_LEN]; + u16 size; /* size of buf */ + u8 buf[]; +}; + +struct fs_scene_index { + /* Offset address(scene name) in string table */ + u16 name; + /* Offset address(scene reg) in register table */ + u16 reg; + /* Offset address(scene model) in model table */ + u16 model; + /* Offset address(scene effect) in effect table */ + u16 effect; +}; + +struct fs_reg_table { + u16 size; /* size of buf */ + u8 buf[]; +}; + +struct fs_file_table { + u16 name; + u16 size; /* size of buf */ + u8 buf[]; +}; + +struct fs_fwm_date { + u32 year:12; + u32 month:4; + u32 day:5; + u32 hour:5; + u32 minute:6; +}; + +struct fs_fwm_header { + u16 version; + u16 project; /* Offset address(project name) in string table */ + u16 device; /* Offset address(device name) in string table */ + struct fs_fwm_date date; + u16 crc16; + u16 crc_size; /* Starting position for CRC checking */ + u16 chip_type; + u16 addr; /* 7-bit i2c address */ + u16 spkid; + u16 rsvd[6]; + u8 params[]; +}; + +#pragma pack(pop) + +struct fs_i2s_srate { + u32 srate; /* Sample rate */ + u16 i2ssr; /* Value of Bit field[I2SSR] */ +}; + +struct fs_pll_div { + unsigned int bclk; /* Rate of bit clock */ + u16 pll1; + u16 pll2; + u16 pll3; +}; + +struct fs_amp_scene { + const char *name; + const struct fs_reg_table *reg; + const struct fs_file_table *model; + const struct fs_file_table *effect; +}; + +struct fs_amp_lib { + const struct fs_fwm_header *hdr; + const struct fs_fwm_table *table[FS_INDEX_MAX]; + struct fs_amp_scene *scene; + struct device *dev; + int scene_count; + u16 devid; +}; + +int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name); + +#endif // __FS_AMP_LIB_H__ diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c new file mode 100644 index 000000000000..b4f51ee7235a --- /dev/null +++ b/sound/soc/codecs/fs210x.c @@ -0,0 +1,1583 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// fs210x.c -- Driver for the FS2104/5S Audio Amplifier +// +// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/workqueue.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "fs210x.h" +#include "fs-amp-lib.h" + +#define FS210X_DEFAULT_FWM_NAME "fs210x_fwm.bin" +#define FS210X_DEFAULT_DAI_NAME "fs210x-aif" +#define FS2105S_DEVICE_ID 0x20 /* FS2105S */ +#define FS210X_DEVICE_ID 0x45 /* FS2104 */ +#define FS210X_REG_MAX 0xF8 +#define FS210X_INIT_SCENE 0 +#define FS210X_DEFAULT_SCENE 1 +#define FS210X_START_DELAY_MS 5 +#define FS210X_FAULT_CHECK_INTERVAL_MS 2000 +#define FS2105S_RATES (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000) +#define FS210X_RATES (SNDRV_PCM_RATE_16000 | FS2105S_RATES) +#define FS210X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define FS210X_NUM_SUPPLIES ARRAY_SIZE(fs210x_supply_names) + +static const char *const fs210x_supply_names[] = { + "pvdd", + "dvdd", +}; + +struct fs210x_platform_data { + const char *fwm_name; +}; + +struct fs210x_priv { + struct i2c_client *i2c; + struct device *dev; + struct regmap *regmap; + struct fs210x_platform_data pdata; + struct regulator_bulk_data supplies[FS210X_NUM_SUPPLIES]; + struct gpio_desc *gpio_sdz; + struct delayed_work start_work; + struct delayed_work fault_check_work; + struct fs_amp_lib amp_lib; + const struct fs_amp_scene *cur_scene; + struct clk *clk_bclk; + /* + * @lock: Mutex ensuring exclusive access for critical device operations + * + * This lock serializes access between the following actions: + * - Device initialization procedures(probe) + * - Enable/disable device(DAPM event) + * - Suspend/resume device(PM) + * - Runtime scene switching(control) + * - Scheduling/execution of delayed works items(delayed works) + */ + struct mutex lock; + unsigned int check_interval_ms; + unsigned int bclk; + unsigned int srate; + int scene_id; + u16 devid; + bool is_inited; + bool is_suspended; + bool is_bclk_on; + bool is_playing; +}; + +static const unsigned int fs2105s_rates[] = { + 32000, 44100, 48000, 88200, 96000 +}; + +static const struct snd_pcm_hw_constraint_list fs2105s_constraints = { + .count = ARRAY_SIZE(fs2105s_rates), + .list = fs2105s_rates, +}; + +static const unsigned int fs210x_rates[] = { + 16000, 32000, 44100, 48000, 88200, 96000 +}; + +static const struct snd_pcm_hw_constraint_list fs210x_constraints = { + .count = ARRAY_SIZE(fs210x_rates), + .list = fs210x_rates, +}; + +static const struct fs_pll_div fs210x_pll_div[] = { + /* bclk, pll1, pll2, pll3 */ + { 512000, 0x006C, 0x0120, 0x0001 }, + { 768000, 0x016C, 0x00C0, 0x0001 }, + { 1024000, 0x016C, 0x0090, 0x0001 }, + { 1536000, 0x016C, 0x0060, 0x0001 }, + { 2048000, 0x016C, 0x0090, 0x0002 }, + { 2304000, 0x016C, 0x0080, 0x0002 }, + { 3072000, 0x016C, 0x0090, 0x0003 }, + { 4096000, 0x016C, 0x0090, 0x0004 }, + { 4608000, 0x016C, 0x0080, 0x0004 }, + { 6144000, 0x016C, 0x0090, 0x0006 }, + { 8192000, 0x016C, 0x0090, 0x0008 }, + { 9216000, 0x016C, 0x0090, 0x0009 }, + { 12288000, 0x016C, 0x0090, 0x000C }, + { 16384000, 0x016C, 0x0090, 0x0010 }, + { 18432000, 0x016C, 0x0090, 0x0012 }, + { 24576000, 0x016C, 0x0090, 0x0018 }, + { 1411200, 0x016C, 0x0060, 0x0001 }, + { 2116800, 0x016C, 0x0080, 0x0002 }, + { 2822400, 0x016C, 0x0090, 0x0003 }, + { 4233600, 0x016C, 0x0080, 0x0004 }, + { 5644800, 0x016C, 0x0090, 0x0006 }, + { 8467200, 0x016C, 0x0090, 0x0009 }, + { 11289600, 0x016C, 0x0090, 0x000C }, + { 16934400, 0x016C, 0x0090, 0x0012 }, + { 22579200, 0x016C, 0x0090, 0x0018 }, + { 2000000, 0x017C, 0x0093, 0x0002 }, +}; + +static int fs210x_bclk_set(struct fs210x_priv *fs210x, bool on) +{ + int ret = 0; + + if (!fs210x || !fs210x->dev) + return -EINVAL; + + if ((fs210x->is_bclk_on ^ on) == 0) + return 0; + + if (on) { + clk_set_rate(fs210x->clk_bclk, fs210x->bclk); + ret = clk_prepare_enable(fs210x->clk_bclk); + fs210x->is_bclk_on = true; + fsleep(2000); /* >= 2ms */ + } else { + clk_disable_unprepare(fs210x->clk_bclk); + fs210x->is_bclk_on = false; + } + + return ret; +} + +static int fs210x_reg_write(struct fs210x_priv *fs210x, + u8 reg, u16 val) +{ + int ret; + + ret = regmap_write(fs210x->regmap, reg, val); + if (ret) { + dev_err(fs210x->dev, "Failed to write %02Xh: %d\n", reg, ret); + return ret; + } + + return 0; +} + +static int fs210x_reg_read(struct fs210x_priv *fs210x, + u8 reg, u16 *pval) +{ + unsigned int val; + int ret; + + ret = regmap_read(fs210x->regmap, reg, &val); + if (ret) { + dev_err(fs210x->dev, "Failed to read %02Xh: %d\n", reg, ret); + return ret; + } + + *pval = (u16)val; + + return 0; +} + +static int fs210x_reg_update_bits(struct fs210x_priv *fs210x, + u8 reg, u16 mask, u16 val) +{ + int ret; + + ret = regmap_update_bits(fs210x->regmap, reg, mask, val); + if (ret) { + dev_err(fs210x->dev, "Failed to update %02Xh: %d\n", reg, ret); + return ret; + } + + return 0; +} + +static int fs210x_reg_bulk_write(struct fs210x_priv *fs210x, + u8 reg, const void *val, u32 size) +{ + int ret; + + ret = regmap_bulk_write(fs210x->regmap, reg, val, size / 2); + if (ret) { + dev_err(fs210x->dev, "Failed to bulk write %02Xh: %d\n", + reg, ret); + return ret; + } + + return 0; +} + +static inline int fs210x_write_reg_val(struct fs210x_priv *fs210x, + const struct fs_reg_val *regv) +{ + return fs210x_reg_write(fs210x, regv->reg, regv->val); +} + +static inline int fs210x_write_reg_bits(struct fs210x_priv *fs210x, + const struct fs_reg_bits *regu) +{ + return fs210x_reg_update_bits(fs210x, + regu->reg, + regu->mask, + regu->val); +} + +static inline int fs210x_set_cmd_pkg(struct fs210x_priv *fs210x, + const struct fs_cmd_pkg *pkg, + unsigned int *offset) +{ + int delay_us; + + if (pkg->cmd >= 0x00 && pkg->cmd <= FS210X_REG_MAX) { + *offset = sizeof(pkg->regv); + return fs210x_write_reg_val(fs210x, &pkg->regv); + } else if (pkg->cmd == FS_CMD_UPDATE) { + *offset = sizeof(pkg->regb); + return fs210x_write_reg_bits(fs210x, &pkg->regb); + } else if (pkg->cmd == FS_CMD_DELAY) { + if (pkg->regv.val > FS_CMD_DELAY_MS_MAX) + return -EOPNOTSUPP; + delay_us = pkg->regv.val * 1000; /* ms -> us */ + fsleep(delay_us); + *offset = sizeof(pkg->regv); + return 0; + } + + dev_err(fs210x->dev, "Invalid pkg cmd: %d\n", pkg->cmd); + + return -EOPNOTSUPP; +} + +static int fs210x_reg_write_table(struct fs210x_priv *fs210x, + const struct fs_reg_table *reg) +{ + const struct fs_cmd_pkg *pkg; + unsigned int index, offset; + int ret; + + if (!fs210x || !fs210x->dev) + return -EINVAL; + + if (!reg || reg->size == 0) + return -EFAULT; + + for (index = 0; index < reg->size; index += offset) { + pkg = (struct fs_cmd_pkg *)(reg->buf + index); + ret = fs210x_set_cmd_pkg(fs210x, pkg, &offset); + if (ret) { + dev_err(fs210x->dev, "Failed to set cmd pkg: %02X-%d\n", + pkg->cmd, ret); + return ret; + } + } + + if (index != reg->size) { + dev_err(fs210x->dev, "Invalid reg table size: %d-%d\n", + index, reg->size); + return -EFAULT; + } + + return 0; +} + +static int fs210x_dev_play(struct fs210x_priv *fs210x) +{ + int ret; + + if (!fs210x->is_inited) + return -EFAULT; + + if (fs210x->is_playing) + return 0; + + ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL, + FS210X_11H_DPS_PLAY); + if (!ret) + fs210x->is_playing = true; + + fsleep(10000); /* >= 10ms */ + + return ret; +} + +static int fs210x_dev_stop(struct fs210x_priv *fs210x) +{ + int ret; + + if (!fs210x->is_inited) + return -EFAULT; + + if (!fs210x->is_playing) + return 0; + + ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL, + FS210X_11H_DPS_PWDN); + fs210x->is_playing = false; + + fsleep(30000); /* >= 30ms */ + + return ret; +} + +static int fs210x_set_reg_table(struct fs210x_priv *fs210x, + const struct fs_amp_scene *scene) +{ + const struct fs_amp_scene *cur_scene; + const struct fs_reg_table *reg; + + if (!fs210x || !fs210x->dev || !scene) + return -EINVAL; + + cur_scene = fs210x->cur_scene; + if (!scene->reg || cur_scene == scene) { + dev_dbg(fs210x->dev, "Skip writing reg table\n"); + return 0; + } + + reg = scene->reg; + dev_dbg(fs210x->dev, "reg table size: %d\n", reg->size); + + return fs210x_reg_write_table(fs210x, reg); +} + +static int fs210x_set_woofer_table(struct fs210x_priv *fs210x) +{ + const struct fs_file_table *woofer; + const struct fs_fwm_table *table; + int ret; + + if (!fs210x || !fs210x->dev) + return -EINVAL; + + /* NOTE: fs2105s has woofer ram only */ + if (fs210x->devid != FS2105S_DEVICE_ID) + return 0; + + table = fs210x->amp_lib.table[FS_INDEX_WOOFER]; + if (!table) { + dev_dbg(fs210x->dev, "Skip writing woofer table\n"); + return 0; + } + + woofer = (struct fs_file_table *)table->buf; + dev_dbg(fs210x->dev, "woofer table size: %d\n", woofer->size); + /* Unit of woofer data is u32(4 bytes) */ + if (woofer->size == 0 || (woofer->size & 0x3)) { + dev_err(fs210x->dev, "Invalid woofer size: %d\n", + woofer->size); + return -EINVAL; + } + + ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA, + FS2105S_46H_CAM_BURST_W); + ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL, + woofer->buf, woofer->size); + + return ret; +} + +static int fs210x_set_effect_table(struct fs210x_priv *fs210x, + const struct fs_amp_scene *scene) +{ + const struct fs_amp_scene *cur_scene; + const struct fs_file_table *effect; + int half_size; + int ret; + + if (!fs210x || !fs210x->dev || !scene) + return -EINVAL; + + cur_scene = fs210x->cur_scene; + if (!scene->effect || cur_scene == scene) { + dev_dbg(fs210x->dev, "Skip writing effect table\n"); + return 0; + } + + effect = scene->effect; + dev_dbg(fs210x->dev, "effect table size: %d\n", effect->size); + + /* Unit of effect data is u32(4 bytes), 2 channels */ + if (effect->size == 0 || (effect->size & 0x7)) { + dev_err(fs210x->dev, "Invalid effect size: %d\n", + effect->size); + return -EINVAL; + } + + half_size = effect->size / 2; + + /* Left channel */ + ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA, + FS210X_46H_CAM_BURST_L); + ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL, + effect->buf, half_size); + if (ret) + return ret; + + /* Right channel */ + ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA, + FS210X_46H_CAM_BURST_R); + ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL, + effect->buf + half_size, half_size); + + return ret; +} + +static int fs210x_access_dsp_ram(struct fs210x_priv *fs210x, bool enable) +{ + int ret; + + if (!fs210x || !fs210x->dev) + return -EINVAL; + + if (enable) { + ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL, + FS210X_11H_DPS_HIZ); + ret |= fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY, + FS210X_0BH_ACCKEY_ON); + } else { + ret = fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY, + FS210X_0BH_ACCKEY_OFF); + ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL, + FS210X_11H_DPS_PWDN); + } + + fsleep(10000); /* >= 10ms */ + + return ret; +} + +static int fs210x_write_dsp_effect(struct fs210x_priv *fs210x, + const struct fs_amp_scene *scene, + int scene_id) +{ + int ret; + + if (!fs210x || !scene) + return -EINVAL; + + ret = fs210x_access_dsp_ram(fs210x, true); + if (ret) { + dev_err(fs210x->dev, "Failed to access dsp: %d\n", ret); + goto tag_exit; + } + + ret = fs210x_set_effect_table(fs210x, scene); + if (ret) { + dev_err(fs210x->dev, "Failed to set effect: %d\n", ret); + goto tag_exit; + } + + if (scene_id == FS210X_INIT_SCENE) + ret = fs210x_set_woofer_table(fs210x); + +tag_exit: + fs210x_reg_write(fs210x, FS210X_46H_DACEQA, + FS210X_46H_CAM_CLEAR); + fs210x_access_dsp_ram(fs210x, false); + + return ret; +} + +static int fs210x_check_scene(struct fs210x_priv *fs210x, + int scene_id, bool *skip_set) +{ + struct fs_amp_lib *amp_lib; + + if (!fs210x || !skip_set) + return -EINVAL; + + amp_lib = &fs210x->amp_lib; + if (amp_lib->scene_count == 0 || !amp_lib->scene) { + dev_err(fs210x->dev, "There's no scene data\n"); + return -EINVAL; + } + + if (scene_id < 0 || scene_id >= amp_lib->scene_count) { + dev_err(fs210x->dev, "Invalid scene_id: %d\n", scene_id); + return -EINVAL; + } + + if (fs210x->scene_id == scene_id) { + dev_dbg(fs210x->dev, "Skip to set same scene\n"); + return 0; + } + + *skip_set = false; + + return 0; +} + +static int fs210x_set_scene(struct fs210x_priv *fs210x, int scene_id) +{ + const struct fs_amp_scene *scene; + bool skip_set = true; + bool is_playing; + int ret; + + if (!fs210x || !fs210x->dev) + return -EINVAL; + + ret = fs210x_check_scene(fs210x, scene_id, &skip_set); + if (ret || skip_set) + return ret; + + scene = fs210x->amp_lib.scene + scene_id; + dev_info(fs210x->dev, "Switch scene.%d: %s\n", + scene_id, scene->name); + + is_playing = fs210x->is_playing; + if (is_playing) + fs210x_dev_stop(fs210x); + + ret = fs210x_set_reg_table(fs210x, scene); + if (ret) { + dev_err(fs210x->dev, "Failed to set reg: %d\n", ret); + return ret; + } + + ret = fs210x_write_dsp_effect(fs210x, scene, scene_id); + if (ret) { + dev_err(fs210x->dev, "Failed to write ram: %d\n", ret); + return ret; + } + + fs210x->cur_scene = scene; + fs210x->scene_id = scene_id; + + if (is_playing) + fs210x_dev_play(fs210x); + + return 0; +} + +static int fs210x_init_chip(struct fs210x_priv *fs210x) +{ + int scene_id; + int ret; + + regcache_cache_bypass(fs210x->regmap, true); + + if (!fs210x->gpio_sdz) { + /* Gpio is not found, i2c reset */ + ret = fs210x_reg_write(fs210x, FS210X_10H_PWRCTRL, + FS210X_10H_I2C_RESET); + if (ret) + goto tag_power_down; + } else { + /* gpio reset, deactivate */ + gpiod_set_value_cansleep(fs210x->gpio_sdz, 0); + } + + fsleep(10000); /* >= 10ms */ + + /* Backup scene id */ + scene_id = fs210x->scene_id; + fs210x->scene_id = -1; + + /* Init registers/RAM by init scene */ + ret = fs210x_set_scene(fs210x, FS210X_INIT_SCENE); + if (ret) + goto tag_power_down; + + /* + * If the firmware has effect scene(s), + * we load effect scene by default scene or scene_id + */ + if (fs210x->amp_lib.scene_count > 1) { + if (scene_id < FS210X_DEFAULT_SCENE) + scene_id = FS210X_DEFAULT_SCENE; + ret = fs210x_set_scene(fs210x, scene_id); + if (ret) + goto tag_power_down; + } + +tag_power_down: + /* Power down the device */ + ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL, + FS210X_11H_DPS_PWDN); + fsleep(10000); /* >= 10ms */ + + regcache_cache_bypass(fs210x->regmap, false); + if (!ret) { + regcache_mark_dirty(fs210x->regmap); + regcache_sync(fs210x->regmap); + fs210x->is_inited = true; + } + + return ret; +} + +static int fs210x_set_i2s_params(struct fs210x_priv *fs210x) +{ + const struct fs_i2s_srate params[] = { + { 16000, 0x3 }, + { 32000, 0x7 }, + { 44100, 0x8 }, + { 48000, 0x9 }, + { 88200, 0xA }, + { 96000, 0xB }, + }; + u16 val; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(params); i++) { + if (params[i].srate != fs210x->srate) + continue; + val = params[i].i2ssr << FS210X_17H_I2SSR_SHIFT; + ret = fs210x_reg_update_bits(fs210x, + FS210X_17H_I2SCTRL, + FS210X_17H_I2SSR_MASK, + val); + return ret; + } + + dev_err(fs210x->dev, "Invalid sample rate: %d\n", fs210x->srate); + + return -EINVAL; +} + +static int fs210x_get_pll_div(struct fs210x_priv *fs210x, + const struct fs_pll_div **pll_div) +{ + int i; + + if (!fs210x || !pll_div) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(fs210x_pll_div); i++) { + if (fs210x_pll_div[i].bclk != fs210x->bclk) + continue; + *pll_div = fs210x_pll_div + i; + return 0; + } + + dev_err(fs210x->dev, "No PLL table for bclk: %d\n", fs210x->bclk); + + return -EFAULT; +} + +static int fs210x_set_hw_params(struct fs210x_priv *fs210x) +{ + const struct fs_pll_div *pll_div; + int ret; + + ret = fs210x_set_i2s_params(fs210x); + if (ret) { + dev_err(fs210x->dev, "Failed to set i2s params: %d\n", ret); + return ret; + } + + /* Set pll params */ + ret = fs210x_get_pll_div(fs210x, &pll_div); + if (ret) + return ret; + + ret = fs210x_reg_write(fs210x, FS210X_A1H_PLLCTRL1, pll_div->pll1); + ret |= fs210x_reg_write(fs210x, FS210X_A2H_PLLCTRL2, pll_div->pll2); + ret |= fs210x_reg_write(fs210x, FS210X_A3H_PLLCTRL3, pll_div->pll3); + + return ret; +} + +static int fs210x_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + const struct snd_pcm_hw_constraint_list *list; + struct fs210x_priv *fs210x; + int ret; + + fs210x = snd_soc_component_get_drvdata(dai->component); + if (!fs210x) { + pr_err("dai_startup: fs210x is null\n"); + return -EINVAL; + } + + if (!substream->runtime) + return 0; + + ret = snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, + FS210X_FORMATS); + if (ret < 0) { + dev_err(fs210x->dev, + "Failed to set hw param format: %d\n", ret); + return ret; + } + + if (fs210x->devid == FS2105S_DEVICE_ID) + list = &fs2105s_constraints; + else + list = &fs210x_constraints; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + list); + if (ret < 0) { + dev_err(fs210x->dev, + "Failed to set hw param rate: %d\n", ret); + return ret; + } + + return 0; +} + +static int fs210x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fs210x_priv *fs210x; + + fs210x = snd_soc_component_get_drvdata(dai->component); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Only supports consumer mode */ + break; + default: + dev_err(fs210x->dev, "Only supports consumer mode\n"); + return -EINVAL; + } + + return 0; +} + +static int fs210x_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct fs210x_priv *fs210x; + int chn_num; + int ret; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + fs210x = snd_soc_component_get_drvdata(dai->component); + + fs210x->srate = params_rate(params); + fs210x->bclk = snd_soc_params_to_bclk(params); + chn_num = params_channels(params); + if (chn_num == 1) /* mono */ + fs210x->bclk *= 2; /* I2S bus has 2 channels */ + + /* The FS2105S can't support 16kHz sample rate. */ + if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000) + return -EOPNOTSUPP; + + mutex_lock(&fs210x->lock); + ret = fs210x_set_hw_params(fs210x); + mutex_unlock(&fs210x->lock); + if (ret) + dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret); + + return ret; +} + +static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct fs210x_priv *fs210x; + unsigned long delay; + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + fs210x = snd_soc_component_get_drvdata(dai->component); + + mutex_lock(&fs210x->lock); + + if (!fs210x->is_inited || fs210x->is_suspended) { + mutex_unlock(&fs210x->lock); + return 0; + } + + mutex_unlock(&fs210x->lock); + + if (mute) { + cancel_delayed_work_sync(&fs210x->fault_check_work); + cancel_delayed_work_sync(&fs210x->start_work); + } else { + delay = msecs_to_jiffies(fs210x->check_interval_ms); + schedule_delayed_work(&fs210x->fault_check_work, delay); + } + + return 0; +} + +static int fs210x_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct fs210x_priv *fs210x; + + fs210x = snd_soc_component_get_drvdata(dai->component); + + mutex_lock(&fs210x->lock); + + if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) { + mutex_unlock(&fs210x->lock); + return 0; + } + + mutex_unlock(&fs210x->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* + * According to the power up/down sequence of FS210x, + * it requests the I2S clock has been present + * and stable(>= 2ms) before playing. + */ + schedule_delayed_work(&fs210x->start_work, + msecs_to_jiffies(FS210X_START_DELAY_MS)); + break; + + default: + break; + } + + return 0; +} + +static void fs210x_start_work(struct work_struct *work) +{ + struct fs210x_priv *fs210x; + int ret; + + fs210x = container_of(work, struct fs210x_priv, start_work.work); + + mutex_lock(&fs210x->lock); + + ret = fs210x_dev_play(fs210x); + if (ret) + dev_err(fs210x->dev, "Failed to start playing: %d\n", ret); + + mutex_unlock(&fs210x->lock); +} + +static void fs210x_fault_check_work(struct work_struct *work) +{ + struct fs210x_priv *fs210x; + u16 status; + int ret; + + fs210x = container_of(work, struct fs210x_priv, fault_check_work.work); + + mutex_lock(&fs210x->lock); + + if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) { + mutex_unlock(&fs210x->lock); + return; + } + + ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status); + mutex_unlock(&fs210x->lock); + if (ret) + return; + + if (!(status & FS210X_05H_PVDD_MASK)) + dev_err(fs210x->dev, "PVDD fault\n"); + if (status & FS210X_05H_OCDL_MASK) + dev_err(fs210x->dev, "OC detected\n"); + if (status & FS210X_05H_UVDL_MASK) + dev_err(fs210x->dev, "UV detected\n"); + if (status & FS210X_05H_OVDL_MASK) + dev_err(fs210x->dev, "OV detected\n"); + if (status & FS210X_05H_OTPDL_MASK) + dev_err(fs210x->dev, "OT detected\n"); + if (status & FS210X_05H_OCRDL_MASK) + dev_err(fs210x->dev, "OCR detected\n"); + if (status & FS210X_05H_OCLDL_MASK) + dev_err(fs210x->dev, "OCL detected\n"); + if (status & FS210X_05H_DCRDL_MASK) + dev_err(fs210x->dev, "DCR detected\n"); + if (status & FS210X_05H_DCLDL_MASK) + dev_err(fs210x->dev, "DCL detected\n"); + if (status & FS210X_05H_SRDL_MASK) + dev_err(fs210x->dev, "SR detected\n"); + if (status & FS210X_05H_OTWDL_MASK) + dev_err(fs210x->dev, "OTW detected\n"); + if (!(status & FS210X_05H_AMPS_MASK)) + dev_dbg(fs210x->dev, "Amplifier unready\n"); + if (!(status & FS210X_05H_PLLS_MASK)) + dev_err(fs210x->dev, "PLL unlock\n"); + if (!(status & FS210X_05H_ANAS_MASK)) + dev_err(fs210x->dev, "Analog power fault\n"); + + schedule_delayed_work(&fs210x->fault_check_work, + msecs_to_jiffies(fs210x->check_interval_ms)); +} + +static int fs210x_get_drvdata_from_kctrl(struct snd_kcontrol *kctrl, + struct fs210x_priv **fs210x) +{ + struct snd_soc_component *cmpnt; + + if (!kctrl) { + pr_err("fs210x: kcontrol is null\n"); + return -EINVAL; + } + + cmpnt = snd_soc_kcontrol_component(kctrl); + if (!cmpnt) { + pr_err("fs210x: component is null\n"); + return -EINVAL; + } + + *fs210x = snd_soc_component_get_drvdata(cmpnt); + + return 0; +} + +static int fs210x_effect_scene_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + const struct fs_amp_scene *scene; + struct fs210x_priv *fs210x; + const char *name = "N/A"; + int idx, count; + int ret; + + ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x); + if (ret || !fs210x->dev) { + pr_err("scene_effect_info: fs210x is null\n"); + return -EINVAL; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */ + if (count < 1) { + uinfo->value.enumerated.items = 0; + return 0; + } + + uinfo->value.enumerated.items = count; + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + idx = uinfo->value.enumerated.item; + scene = fs210x->amp_lib.scene + idx + 1; + if (scene->name) + name = scene->name; + + strscpy(uinfo->value.enumerated.name, name, strlen(name) + 1); + + return 0; +} + +static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct fs210x_priv *fs210x; + int index; + int ret; + + ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x); + if (ret || !fs210x->dev) { + pr_err("scene_effect_get: fs210x is null\n"); + return -EINVAL; + } + + /* The id of effect scene is from 1 to N. */ + if (fs210x->scene_id < 1) + return -EINVAL; + + mutex_lock(&fs210x->lock); + /* + * FS210x has scene(s) as below: + * init scene: id = 0 + * effect scene(s): id = 1~N (optional) + * effect_index = scene_id - 1 + */ + index = fs210x->scene_id - 1; + ucontrol->value.integer.value[0] = index; + mutex_unlock(&fs210x->lock); + + return 0; +} + +static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct fs210x_priv *fs210x; + int scene_id, scene_count; + bool is_changed = false; + int ret; + + ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x); + if (ret || !fs210x->dev) { + pr_err("scene_effect_put: fs210x is null\n"); + return -EINVAL; + } + + mutex_lock(&fs210x->lock); + + /* + * FS210x has scene(s) as below: + * init scene: id = 0 (It's set in fs210x_init_chip() only) + * effect scene(s): id = 1~N (optional) + * scene_id = effect_index + 1. + */ + scene_id = ucontrol->value.integer.value[0] + 1; + scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */ + if (scene_id < 1 || scene_id > scene_count) { + mutex_unlock(&fs210x->lock); + return -ERANGE; + } + + if (scene_id != fs210x->scene_id) + is_changed = true; + + if (fs210x->is_suspended) { + fs210x->scene_id = scene_id; + mutex_unlock(&fs210x->lock); + return is_changed; + } + + ret = fs210x_set_scene(fs210x, scene_id); + if (ret) + dev_err(fs210x->dev, "Failed to set scene: %d\n", ret); + + mutex_unlock(&fs210x->lock); + + if (!ret && is_changed) + return 1; + + return ret; +} + +static int fs210x_playback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt); + int ret = 0; + + mutex_lock(&fs210x->lock); + + if (fs210x->is_suspended) { + mutex_unlock(&fs210x->lock); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * If there is no bclk for us to set the clock output, + * we will enable the device(start_work) in dai trigger. + */ + if (!fs210x->clk_bclk) + break; + fs210x_bclk_set(fs210x, true); + ret = fs210x_dev_play(fs210x); + break; + case SND_SOC_DAPM_POST_PMD: + ret = fs210x_dev_stop(fs210x); + fs210x_bclk_set(fs210x, false); + break; + default: + break; + } + + mutex_unlock(&fs210x->lock); + + return ret; +} + +static const struct snd_soc_dai_ops fs210x_dai_ops = { + .startup = fs210x_dai_startup, + .set_fmt = fs210x_dai_set_fmt, + .hw_params = fs210x_dai_hw_params, + .mute_stream = fs210x_dai_mute, + .trigger = fs210x_dai_trigger, +}; + +static const struct snd_soc_dai_driver fs210x_dai = { + .name = FS210X_DEFAULT_DAI_NAME, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = FS210X_RATES, + .formats = FS210X_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = FS210X_RATES, + .formats = FS210X_FORMATS, + }, + .ops = &fs210x_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, +}; + +static const DECLARE_TLV_DB_SCALE(fs2105s_vol_tlv, -9709, 19, 1); +static const DECLARE_TLV_DB_SCALE(fs210x_vol_tlv, -13357, 19, 1); + +static const struct snd_kcontrol_new fs2105s_vol_control[] = { + SOC_DOUBLE_R_TLV("PCM Playback Volume", + FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL, + 7, 0x1FF, 0, fs2105s_vol_tlv), +}; + +static const struct snd_kcontrol_new fs210x_vol_control[] = { + SOC_DOUBLE_R_TLV("PCM Playback Volume", + FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL, + 6, 0x2BF, 0, fs210x_vol_tlv), +}; + +static const struct snd_kcontrol_new fs210x_controls[] = { + SOC_DOUBLE("DAC Mute Switch", FS210X_30H_DACCTRL, 4, 8, 1, 0), + SOC_DOUBLE("DAC Fade Switch", FS210X_30H_DACCTRL, 5, 9, 1, 0), +}; + +static const struct snd_kcontrol_new fs210x_scene_control[] = { + FS_SOC_ENUM_EXT("Effect Scene", + fs210x_effect_scene_info, + fs210x_effect_scene_get, + fs210x_effect_scene_put), +}; + +static const struct snd_soc_dapm_widget fs210x_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0, + fs210x_playback_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_OUTPUT("OUTR"), + SND_SOC_DAPM_INPUT("SDO"), +}; + +static const struct snd_soc_dapm_route fs210x_dapm_routes[] = { + { "OUTL", NULL, "AIF IN" }, + { "OUTR", NULL, "AIF IN" }, + { "AIF OUT", NULL, "SDO" }, +}; + +static int fs210x_add_mixer_controls(struct fs210x_priv *fs210x, + struct snd_soc_component *cmpnt) +{ + const struct snd_kcontrol_new *kctrl; + int count; + int ret; + + if (!fs210x || !cmpnt) + return -EINVAL; + + if (fs210x->devid == FS2105S_DEVICE_ID) { + kctrl = fs2105s_vol_control; + count = ARRAY_SIZE(fs2105s_vol_control); + } else { + kctrl = fs210x_vol_control; + count = ARRAY_SIZE(fs210x_vol_control); + } + + ret = snd_soc_add_component_controls(cmpnt, kctrl, count); + if (ret) + return ret; + + /* + * If the firmware has no scene or only init scene, + * we skip adding this mixer control. + */ + if (fs210x->amp_lib.scene_count < 2) + return 0; + + kctrl = fs210x_scene_control; + count = ARRAY_SIZE(fs210x_scene_control); + + return snd_soc_add_component_controls(cmpnt, kctrl, count); +} + +static int fs210x_probe(struct snd_soc_component *cmpnt) +{ + struct fs210x_priv *fs210x; + int ret; + + fs210x = snd_soc_component_get_drvdata(cmpnt); + if (!fs210x || !fs210x->dev) + return -EINVAL; + + fs210x->amp_lib.dev = fs210x->dev; + fs210x->amp_lib.devid = fs210x->devid; + + ret = fs_amp_load_firmware(&fs210x->amp_lib, fs210x->pdata.fwm_name); + if (ret) + return ret; + + ret = fs210x_add_mixer_controls(fs210x, cmpnt); + if (ret) + return ret; + + mutex_lock(&fs210x->lock); + ret = fs210x_init_chip(fs210x); + mutex_unlock(&fs210x->lock); + + return ret; +} + +static void fs210x_remove(struct snd_soc_component *cmpnt) +{ + struct fs210x_priv *fs210x; + + fs210x = snd_soc_component_get_drvdata(cmpnt); + if (!fs210x || !fs210x->dev) + return; + + cancel_delayed_work_sync(&fs210x->start_work); + cancel_delayed_work_sync(&fs210x->fault_check_work); +} + +#ifdef CONFIG_PM +static int fs210x_suspend(struct snd_soc_component *cmpnt) +{ + struct fs210x_priv *fs210x; + int ret; + + fs210x = snd_soc_component_get_drvdata(cmpnt); + if (!fs210x || !fs210x->dev) + return -EINVAL; + + regcache_cache_only(fs210x->regmap, true); + + mutex_lock(&fs210x->lock); + fs210x->cur_scene = NULL; + fs210x->is_inited = false; + fs210x->is_playing = false; + fs210x->is_suspended = true; + + gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ + fsleep(30000); /* >= 30ms */ + mutex_unlock(&fs210x->lock); + + cancel_delayed_work_sync(&fs210x->start_work); + cancel_delayed_work_sync(&fs210x->fault_check_work); + + ret = regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies); + if (ret) { + dev_err(fs210x->dev, "Failed to suspend: %d\n", ret); + return ret; + } + + return 0; +} + +static int fs210x_resume(struct snd_soc_component *cmpnt) +{ + struct fs210x_priv *fs210x; + int ret; + + fs210x = snd_soc_component_get_drvdata(cmpnt); + if (!fs210x || !fs210x->dev) + return -EINVAL; + + ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies); + if (ret) { + dev_err(fs210x->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + mutex_lock(&fs210x->lock); + + fs210x->is_suspended = false; + ret = fs210x_init_chip(fs210x); + + mutex_unlock(&fs210x->lock); + + return ret; +} +#else +#define fs210x_suspend NULL +#define fs210x_resume NULL +#endif // CONFIG_PM + +static bool fs210x_volatile_registers(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FS210X_00H_STATUS ... FS210X_0FH_I2CADDR: + case FS210X_ABH_INTSTAT: + case FS210X_ACH_INTSTATR: + return true; + default: + return false; + } +} + +static const struct snd_soc_component_driver fs210x_soc_component_dev = { + .probe = fs210x_probe, + .remove = fs210x_remove, + .suspend = fs210x_suspend, + .resume = fs210x_resume, + .controls = fs210x_controls, + .num_controls = ARRAY_SIZE(fs210x_controls), + .dapm_widgets = fs210x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(fs210x_dapm_widgets), + .dapm_routes = fs210x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(fs210x_dapm_routes), +}; + +static const struct regmap_config fs210x_regmap = { + .reg_bits = 8, + .val_bits = 16, + .max_register = FS210X_REG_MAX, + .val_format_endian = REGMAP_ENDIAN_BIG, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = fs210x_volatile_registers, +}; + +static int fs210x_detect_device(struct fs210x_priv *fs210x) +{ + u16 devid; + int ret; + + ret = fs210x_reg_read(fs210x, FS210X_03H_DEVID, &devid); + if (ret) + return ret; + + fs210x->devid = HI_U16(devid); + + switch (fs210x->devid) { + case FS210X_DEVICE_ID: + dev_info(fs210x->dev, "FS2104 detected\n"); + break; + case FS2105S_DEVICE_ID: + dev_info(fs210x->dev, "FS2105S detected\n"); + break; + default: + dev_err(fs210x->dev, "DEVID: 0x%04X dismatch\n", devid); + return -ENODEV; + } + + return 0; +} + +static int fs210x_parse_dts(struct fs210x_priv *fs210x, + struct fs210x_platform_data *pdata) +{ + struct device_node *node = fs210x->dev->of_node; + int i, ret; + + if (!node) + return 0; + + ret = of_property_read_string(node, "firmware-name", &pdata->fwm_name); + if (ret) + pdata->fwm_name = FS210X_DEFAULT_FWM_NAME; + + fs210x->gpio_sdz = devm_gpiod_get_optional(fs210x->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(fs210x->gpio_sdz)) + return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->gpio_sdz), + "Failed to get reset-gpios\n"); + + for (i = 0; i < FS210X_NUM_SUPPLIES; i++) + fs210x->supplies[i].supply = fs210x_supply_names[i]; + + ret = devm_regulator_bulk_get(fs210x->dev, + ARRAY_SIZE(fs210x->supplies), + fs210x->supplies); + if (ret) + return dev_err_probe(fs210x->dev, ret, + "Failed to get supplies\n"); + + return 0; +} + +static void fs210x_deinit(struct fs210x_priv *fs210x) +{ + gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */ + fsleep(10000); /* >= 10ms */ + + regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies); +} + +static int fs210x_init(struct fs210x_priv *fs210x) +{ + int ret; + + ret = fs210x_parse_dts(fs210x, &fs210x->pdata); + if (ret) + return ret; + + fs210x->clk_bclk = devm_clk_get_optional(fs210x->dev, "bclk"); + if (IS_ERR(fs210x->clk_bclk)) + return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->clk_bclk), + "Failed to get bclk\n"); + + ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies); + if (ret) + return dev_err_probe(fs210x->dev, ret, + "Failed to enable supplies\n"); + + /* Make sure the SDZ pin is pulled down enough time. */ + fsleep(10000); /* >= 10ms */ + gpiod_set_value_cansleep(fs210x->gpio_sdz, 0); /* Deactivate */ + fsleep(10000); /* >= 10ms */ + + ret = fs210x_detect_device(fs210x); + if (ret) { + fs210x_deinit(fs210x); + return ret; + } + + fs210x->scene_id = -1; /* Invalid scene */ + fs210x->cur_scene = NULL; + fs210x->is_playing = false; + fs210x->is_inited = false; + fs210x->is_suspended = false; + fs210x->check_interval_ms = FS210X_FAULT_CHECK_INTERVAL_MS; + + INIT_DELAYED_WORK(&fs210x->fault_check_work, fs210x_fault_check_work); + INIT_DELAYED_WORK(&fs210x->start_work, fs210x_start_work); + mutex_init(&fs210x->lock); + + return 0; +} + +static int fs210x_register_snd_component(struct fs210x_priv *fs210x) +{ + struct snd_soc_dai_driver *dai_drv; + static int instance_id; + int ret; + + dai_drv = devm_kmemdup(fs210x->dev, &fs210x_dai, + sizeof(fs210x_dai), GFP_KERNEL); + if (!dai_drv) + return -ENOMEM; + + dai_drv->name = devm_kasprintf(fs210x->dev, + GFP_KERNEL, "%s-%d", + dai_drv->name, instance_id); + instance_id++; + + if (fs210x->devid == FS2105S_DEVICE_ID) { + dai_drv->playback.rates = FS2105S_RATES; + dai_drv->capture.rates = FS2105S_RATES; + } + + ret = snd_soc_register_component(fs210x->dev, + &fs210x_soc_component_dev, + dai_drv, 1); + return ret; +} + +static ssize_t check_interval_ms_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct fs210x_priv *fs210x = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", fs210x->check_interval_ms); +} + +static ssize_t check_interval_ms_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct fs210x_priv *fs210x = dev_get_drvdata(dev); + int ret; + + ret = kstrtouint(buf, 10, &fs210x->check_interval_ms); + if (ret) + return -EINVAL; + + return (ssize_t)count; +} + +static DEVICE_ATTR_RW(check_interval_ms); + +static struct attribute *fs210x_attrs[] = { + &dev_attr_check_interval_ms.attr, + NULL, +}; + +static struct attribute_group fs210x_attr_group = { + .attrs = fs210x_attrs, +}; + +static int fs210x_i2c_probe(struct i2c_client *client) +{ + struct fs210x_priv *fs210x; + int ret; + + fs210x = devm_kzalloc(&client->dev, sizeof(*fs210x), GFP_KERNEL); + if (!fs210x) + return -ENOMEM; + + fs210x->i2c = client; + fs210x->dev = &client->dev; + i2c_set_clientdata(client, fs210x); + + fs210x->regmap = devm_regmap_init_i2c(client, &fs210x_regmap); + if (IS_ERR(fs210x->regmap)) + return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->regmap), + "Failed to get regmap\n"); + + ret = fs210x_init(fs210x); + if (ret) + return ret; + + ret = devm_device_add_group(fs210x->dev, &fs210x_attr_group); + if (ret) { + fs210x_deinit(fs210x); + return dev_err_probe(fs210x->dev, ret, + "Failed to create sysfs group\n"); + } + + ret = fs210x_register_snd_component(fs210x); + if (ret) { + fs210x_deinit(fs210x); + return dev_err_probe(fs210x->dev, ret, + "Failed to register component\n"); + } + + return 0; +} + +static void fs210x_i2c_remove(struct i2c_client *client) +{ + struct fs210x_priv *fs210x = i2c_get_clientdata(client); + + snd_soc_unregister_component(fs210x->dev); + fs210x_deinit(fs210x); +} + +static const struct i2c_device_id fs210x_i2c_id[] = { + { "fs2104" }, + { "fs2105s" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, fs210x_i2c_id); + +static const struct of_device_id fs210x_of_match[] = { + { .compatible = "foursemi,fs2105s", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fs210x_of_match); + +static struct i2c_driver fs210x_i2c_driver = { + .driver = { + .name = "fs210x", + .of_match_table = fs210x_of_match, + }, + .id_table = fs210x_i2c_id, + .probe = fs210x_i2c_probe, + .remove = fs210x_i2c_remove, +}; + +module_i2c_driver(fs210x_i2c_driver); + +MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>"); +MODULE_DESCRIPTION("FS2104/5S Audio Amplifier Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/fs210x.h b/sound/soc/codecs/fs210x.h new file mode 100644 index 000000000000..78e1760332ca --- /dev/null +++ b/sound/soc/codecs/fs210x.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * fs210x.h -- Driver for the FS2104/5S Audio Amplifier + * + * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd. + */ + +#ifndef __FS210X_H__ +#define __FS210X_H__ + +#define FS210X_00H_STATUS 0x00 +#define FS210X_03H_DEVID 0x03 +#define FS210X_05H_ANASTAT 0x05 +#define FS210X_06H_DIGSTAT 0x06 +#define FS210X_0BH_ACCKEY 0x0B +#define FS210X_0FH_I2CADDR 0x0F +#define FS210X_10H_PWRCTRL 0x10 +#define FS210X_11H_SYSCTRL 0x11 +#define FS210X_17H_I2SCTRL 0x17 +#define FS210X_30H_DACCTRL 0x30 +#define FS210X_39H_LVOLCTRL 0x39 +#define FS210X_3AH_RVOLCTRL 0x3A +#define FS210X_42H_DACEQWL 0x42 +#define FS210X_46H_DACEQA 0x46 +#define FS210X_A1H_PLLCTRL1 0xA1 +#define FS210X_A2H_PLLCTRL2 0xA2 +#define FS210X_A3H_PLLCTRL3 0xA3 +#define FS210X_ABH_INTSTAT 0xAB +#define FS210X_ACH_INTSTATR 0xAC + +#define FS210X_05H_PVDD_SHIFT 14 +#define FS210X_05H_PVDD_MASK BIT(14) +#define FS210X_05H_OCDL_SHIFT 13 +#define FS210X_05H_OCDL_MASK BIT(13) +#define FS210X_05H_UVDL_SHIFT 12 +#define FS210X_05H_UVDL_MASK BIT(12) +#define FS210X_05H_OVDL_SHIFT 11 +#define FS210X_05H_OVDL_MASK BIT(11) +#define FS210X_05H_OTPDL_SHIFT 10 +#define FS210X_05H_OTPDL_MASK BIT(10) +#define FS210X_05H_OCRDL_SHIFT 9 +#define FS210X_05H_OCRDL_MASK BIT(9) +#define FS210X_05H_OCLDL_SHIFT 8 +#define FS210X_05H_OCLDL_MASK BIT(8) +#define FS210X_05H_DCRDL_SHIFT 7 +#define FS210X_05H_DCRDL_MASK BIT(7) +#define FS210X_05H_DCLDL_SHIFT 6 +#define FS210X_05H_DCLDL_MASK BIT(6) +#define FS210X_05H_SRDL_SHIFT 5 +#define FS210X_05H_SRDL_MASK BIT(5) +#define FS210X_05H_OTWDL_SHIFT 4 +#define FS210X_05H_OTWDL_MASK BIT(4) +#define FS210X_05H_AMPS_SHIFT 3 +#define FS210X_05H_AMPS_MASK BIT(3) +#define FS210X_05H_PLLS_SHIFT 1 +#define FS210X_05H_PLLS_MASK BIT(1) +#define FS210X_05H_ANAS_SHIFT 0 +#define FS210X_05H_ANAS_MASK BIT(0) +#define FS210X_17H_I2SSR_SHIFT 12 +#define FS210X_17H_I2SSR_MASK GENMASK(15, 12) +#define FS210X_30H_RMUTE_SHIFT 8 +#define FS210X_30H_LMUTE_SHIFT 4 + +#define FS210X_0BH_ACCKEY_ON 0x0091 +#define FS210X_0BH_ACCKEY_OFF 0x0000 +#define FS210X_10H_I2C_RESET 0x0002 +#define FS210X_11H_DPS_HIZ 0x0100 +#define FS210X_11H_DPS_PWDN 0x0000 +#define FS210X_11H_DPS_PLAY 0x0300 +#define FS210X_46H_CAM_BURST_L 0x8000 +#define FS210X_46H_CAM_BURST_R 0x8200 +#define FS2105S_46H_CAM_BURST_W 0x8400 +#define FS210X_46H_CAM_CLEAR 0x0000 + +#endif /* __FS210X_H__ */ diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index dcddc28e8856..e3f9b03df3aa 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -256,6 +256,161 @@ static const struct reg_sequence rt1320_vc_blind_write[] = { { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, }; +static const struct reg_sequence rt1321_blind_write[] = { + { 0x0000c003, 0xf0 }, + { 0x0000c01b, 0xfc }, + { 0x0000c5c3, 0xf2 }, + { 0x0000c5c2, 0x00 }, + { 0x0000c5c1, 0x10 }, + { 0x0000c5c0, 0x04 }, + { 0x0000c5c7, 0x03 }, + { 0x0000c5c6, 0x10 }, + { 0x0000c526, 0x47 }, + { 0x0000c5c4, 0x12 }, + { 0x0000c5c5, 0x60 }, + { 0x0000c520, 0x10 }, + { 0x0000c521, 0x32 }, + { 0x0000c5c7, 0x00 }, + { 0x0000c5c8, 0x03 }, + { 0x0000c5d3, 0x08 }, + { 0x0000c5d2, 0x0a }, + { 0x0000c5d1, 0x49 }, + { 0x0000c5d0, 0x0f }, + { 0x0000c580, 0x10 }, + { 0x0000c581, 0x32 }, + { 0x0000c582, 0x01 }, + { 0x0000cb00, 0x03 }, + { 0x0000cb02, 0x52 }, + { 0x0000cb04, 0x80 }, + { 0x0000cb0b, 0x01 }, + { 0x0000c682, 0x60 }, + { 0x0000c019, 0x10 }, + { 0x0000c5f0, 0x01 }, + { 0x0000c5f7, 0x22 }, + { 0x0000c5f6, 0x22 }, + { 0x0000c057, 0x51 }, + { 0x0000c054, 0x55 }, + { 0x0000c053, 0x55 }, + { 0x0000c052, 0x55 }, + { 0x0000c051, 0x01 }, + { 0x0000c050, 0x15 }, + { 0x0000c060, 0x99 }, + { 0x0000c030, 0x55 }, + { 0x0000c061, 0x55 }, + { 0x0000c063, 0x55 }, + { 0x0000c065, 0xa5 }, + { 0x0000c06b, 0x0a }, + { 0x0000ca05, 0xd6 }, + { 0x0000ca07, 0x07 }, + { 0x0000ca25, 0xd6 }, + { 0x0000ca27, 0x07 }, + { 0x0000cd00, 0x05 }, + { 0x0000c604, 0x40 }, + { 0x0000c609, 0x40 }, + { 0x0000c046, 0xf7 }, + { 0x0000c045, 0xff }, + { 0x0000c044, 0xff }, + { 0x0000c043, 0xff }, + { 0x0000c042, 0xff }, + { 0x0000c041, 0xff }, + { 0x0000c040, 0xff }, + { 0x0000c049, 0xff }, + { 0x0000c028, 0x3f }, + { 0x0000c020, 0x3f }, + { 0x0000c032, 0x13 }, + { 0x0000c033, 0x01 }, + { 0x0000cc10, 0x01 }, + { 0x0000dc20, 0x03 }, + { 0x0000de03, 0x05 }, + { 0x0000dc00, 0x00 }, + { 0x0000c700, 0xf0 }, + { 0x0000c701, 0x13 }, + { 0x0000c900, 0xc3 }, + { 0x0000c570, 0x08 }, + { 0x0000c086, 0x02 }, + { 0x0000c085, 0x7f }, + { 0x0000c084, 0x00 }, + { 0x0000c081, 0xff }, + { 0x0000f084, 0x0f }, + { 0x0000f083, 0xff }, + { 0x0000f082, 0xff }, + { 0x0000f081, 0xff }, + { 0x0000f080, 0xff }, + { 0x20003003, 0x3f }, + { 0x20005818, 0x81 }, + { 0x20009018, 0x81 }, + { 0x2000301c, 0x81 }, + { 0x0000c003, 0xc0 }, + { 0x0000c047, 0x80 }, + { 0x0000d541, 0x80 }, + { 0x0000d487, 0x0b }, + { 0x0000d487, 0x3b }, + { 0x0000d486, 0xc3 }, + { 0x0000d470, 0x89 }, + { 0x0000d471, 0x3a }, + { 0x0000d472, 0x1d }, + { 0x0000d478, 0xff }, + { 0x0000d479, 0x20 }, + { 0x0000d47a, 0x10 }, + { 0x0000d73c, 0xb7 }, + { 0x0000d73d, 0xd7 }, + { 0x0000d73e, 0x00 }, + { 0x0000d73f, 0x10 }, + { 0x3fc2dfc3, 0x00 }, + { 0x3fc2dfc2, 0x00 }, + { 0x3fc2dfc1, 0x00 }, + { 0x3fc2dfc0, 0x07 }, + { 0x3fc2dfc7, 0x00 }, + { 0x3fc2dfc6, 0x00 }, + { 0x3fc2dfc5, 0x00 }, + { 0x3fc2dfc4, 0x01 }, + { 0x3fc2df83, 0x00 }, + { 0x3fc2df82, 0x00 }, + { 0x3fc2df81, 0x00 }, + { 0x3fc2df80, 0x00 }, + { 0x0000d541, 0x40 }, + { 0x0000d486, 0x43 }, + { 0x1000db00, 0x03 }, + { 0x1000db01, 0x00 }, + { 0x1000db02, 0x10 }, + { 0x1000db03, 0x00 }, + { 0x1000db04, 0x00 }, + { 0x1000db05, 0x45 }, + { 0x1000db06, 0x12 }, + { 0x1000db07, 0x09 }, + { 0x1000db08, 0x00 }, + { 0x1000db09, 0x00 }, + { 0x1000db0a, 0x00 }, + { 0x1000db0b, 0x13 }, + { 0x1000db0c, 0x09 }, + { 0x1000db0d, 0x00 }, + { 0x1000db0e, 0x00 }, + { 0x1000db0f, 0x00 }, + { 0x0000d540, 0x21 }, + { 0x41000189, 0x00 }, + { 0x4100018a, 0x00 }, + { 0x41001988, 0x00 }, + { 0x41081400, 0x09 }, + { 0x40801508, 0x03 }, + { 0x40801588, 0x03 }, + { 0x40801809, 0x00 }, + { 0x4080180a, 0x00 }, + { 0x4080180b, 0x00 }, + { 0x4080180c, 0x00 }, + { 0x40801b09, 0x00 }, + { 0x40801b0a, 0x00 }, + { 0x40801b0b, 0x00 }, + { 0x40801b0c, 0x00 }, + { 0x0000d714, 0x17 }, + { 0x20009012, 0x00 }, + { 0x0000dd0b, 0x0d }, + { 0x0000dd0a, 0xff }, + { 0x0000dd09, 0x0d }, + { 0x0000dd08, 0xff }, + { 0x0000d172, 0x2a }, + { 0x41001988, 0x03 }, +}; + static const struct reg_default rt1320_reg_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, @@ -340,10 +495,19 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg) case 0xf717 ... 0xf719: case 0xf720 ... 0xf723: case 0x1000cd91 ... 0x1000cd96: + case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER: case 0x1000f008: case 0x1000f021: + case 0x2000300f: + case 0x2000301c: + case 0x2000900f: + case 0x20009018: + case 0x3fc29d80 ... 0x3fc29d83: case 0x3fe2e000 ... 0x3fe2e003: case 0x3fc2ab80 ... 0x3fc2abd4: + case 0x3fc2bfc0 ... 0x3fc2bfc8: + case 0x3fc2d300 ... 0x3fc2d354: + case 0x3fc2dfc0 ... 0x3fc2dfc8: /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0): case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01): @@ -394,6 +558,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) case 0xc560: case 0xc5b5 ... 0xc5b7: case 0xc5fc ... 0xc5ff: + case 0xc680 ... 0xc683: case 0xc820: case 0xc900: case 0xc920: @@ -412,7 +577,7 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) case 0xd4e5 ... 0xd4e6: case 0xd4e8 ... 0xd4ff: case 0xd530: - case 0xd540: + case 0xd540 ... 0xd541: case 0xd543: case 0xdb58 ... 0xdb5f: case 0xdb60 ... 0xdb63: @@ -429,13 +594,20 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg) case 0xf01e: case 0xf717 ... 0xf719: case 0xf720 ... 0xf723: - case 0x10000000 ... 0x10007fff: + case 0x10000000 ... 0x10008fff: case 0x1000c000 ... 0x1000dfff: case 0x1000f008: case 0x1000f021: + case 0x2000300f: + case 0x2000301c: + case 0x2000900f: + case 0x20009018: case 0x3fc2ab80 ... 0x3fc2abd4: + case 0x3fc2b780: case 0x3fc2bf80 ... 0x3fc2bf83: - case 0x3fc2bfc0 ... 0x3fc2bfc7: + case 0x3fc2bfc0 ... 0x3fc2bfc8: + case 0x3fc2d300 ... 0x3fc2d354: + case 0x3fc2dfc0 ... 0x3fc2dfc8: case 0x3fe2e000 ... 0x3fe2e003: case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0): case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0): @@ -560,7 +732,7 @@ static int rt1320_read_prop(struct sdw_slave *slave) static int rt1320_pde_transition_delay(struct rt1320_sdw_priv *rt1320, unsigned char func, unsigned char entity, unsigned char ps) { - unsigned int delay = 1000, val; + unsigned int delay = 2000, val; pm_runtime_mark_last_busy(&rt1320->sdw_slave->dev); @@ -591,24 +763,44 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) struct sdw_slave *slave = rt1320->sdw_slave; const struct firmware *patch; const char *filename; - unsigned int addr, val; + unsigned int addr, val, min_addr, max_addr; const unsigned char *ptr; int ret, i; - if (rt1320->version_id <= RT1320_VB) - filename = RT1320_VAB_MCU_PATCH; - else - filename = RT1320_VC_MCU_PATCH; + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + if (rt1320->version_id <= RT1320_VB) + filename = RT1320_VAB_MCU_PATCH; + else + filename = RT1320_VC_MCU_PATCH; + min_addr = 0x10007000; + max_addr = 0x10007fff; + break; + case RT1321_DEV_ID: + filename = RT1321_VA_MCU_PATCH; + min_addr = 0x10008000; + max_addr = 0x10008fff; + break; + default: + dev_err(&slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return; + } /* load the patch code here */ ret = request_firmware(&patch, filename, &slave->dev); if (ret) { dev_err(&slave->dev, "%s: Failed to load %s firmware", __func__, filename); regmap_write(rt1320->regmap, 0xc598, 0x00); - regmap_write(rt1320->regmap, 0x10007000, 0x67); - regmap_write(rt1320->regmap, 0x10007001, 0x80); - regmap_write(rt1320->regmap, 0x10007002, 0x00); - regmap_write(rt1320->regmap, 0x10007003, 0x00); + regmap_write(rt1320->regmap, min_addr, 0x67); + regmap_write(rt1320->regmap, min_addr + 0x1, 0x80); + regmap_write(rt1320->regmap, min_addr + 0x2, 0x00); + regmap_write(rt1320->regmap, min_addr + 0x3, 0x00); + if (rt1320->dev_id == RT1321_DEV_ID) { + regmap_write(rt1320->regmap, 0xd73c, 0x67); + regmap_write(rt1320->regmap, 0xd73d, 0x80); + regmap_write(rt1320->regmap, 0xd73e, 0x00); + regmap_write(rt1320->regmap, 0xd73f, 0x00); + } } else { ptr = (const unsigned char *)patch->data; if ((patch->size % 8) == 0) { @@ -618,7 +810,7 @@ static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320) val = (ptr[i + 4] & 0xff) | (ptr[i + 5] & 0xff) << 8 | (ptr[i + 6] & 0xff) << 16 | (ptr[i + 7] & 0xff) << 24; - if (addr > 0x10007fff || addr < 0x10007000) { + if (addr > max_addr || addr < min_addr) { dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr); goto _exit_; } @@ -688,6 +880,28 @@ static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320) } } +static void rt1321_preset(struct rt1320_sdw_priv *rt1320) +{ + unsigned int i, reg, val, delay; + + for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) { + reg = rt1321_blind_write[i].reg; + val = rt1321_blind_write[i].def; + delay = rt1321_blind_write[i].delay_us; + + if (reg == 0x3fc2dfc3) + rt1320_load_mcu_patch(rt1320); + + regmap_write(rt1320->regmap, reg, val); + + if (delay) + usleep_range(delay, delay + 1000); + + if (reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0)) + rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, val); + } +} + static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) { struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); @@ -714,6 +928,9 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) if (rt1320->version_id < 0) { regmap_read(rt1320->regmap, RT1320_DEV_VERSION_ID_1, &val); rt1320->version_id = val; + regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val); + regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp); + rt1320->dev_id = (val << 8) | tmp; } regmap_read(rt1320->regmap, @@ -722,16 +939,25 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) /* initialization write */ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) { - if (rt1320->version_id < RT1320_VC) - rt1320_vab_preset(rt1320); - else - rt1320_vc_preset(rt1320); + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + if (rt1320->version_id < RT1320_VC) + rt1320_vab_preset(rt1320); + else + rt1320_vc_preset(rt1320); + break; + case RT1321_DEV_ID: + rt1321_preset(rt1320); + break; + default: + dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + } regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0), FUNCTION_NEEDS_INITIALIZATION); } - if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA) { + if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA && rt1320->dev_id == RT1320_DEV_ID) { regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0); regmap_read(rt1320->regmap, RT1320_HIFI_VER_0, &val); @@ -751,7 +977,7 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 3); } - dev_dbg(dev, "%s version_id=%d\n", __func__, rt1320->version_id); + dev_dbg(dev, "%s version_id=%d, dev_id=0x%x\n", __func__, rt1320->version_id, rt1320->dev_id); if (rt1320->first_hw_init) { regcache_cache_bypass(rt1320->regmap, false); @@ -894,12 +1120,20 @@ _dmic_vol_: /* check all channels */ for (i = 0; i < p->count; i++) { - if (i < 2) { + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + if (i < 2) { + reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01); + regmap_read(rt1320->mbq_regmap, reg_base + i, ®value[i]); + } else { + reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01); + regmap_read(rt1320->mbq_regmap, reg_base + i - 2, ®value[i]); + } + break; + case RT1321_DEV_ID: reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01); regmap_read(rt1320->mbq_regmap, reg_base + i, ®value[i]); - } else { - reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01); - regmap_read(rt1320->mbq_regmap, reg_base + i - 2, ®value[i]); + break; } gain_val[i] = ucontrol->value.integer.value[i]; @@ -916,12 +1150,20 @@ _dmic_vol_: return 0; for (i = 0; i < p->count; i++) { - if (i < 2) { + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + if (i < 2) { + reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01); + err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]); + } else { + reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01); + err = regmap_write(rt1320->mbq_regmap, reg_base + i - 2, gain_val[i]); + } + break; + case RT1321_DEV_ID: reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01); err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]); - } else { - reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01); - err = regmap_write(rt1320->mbq_regmap, reg_base + i - 2, gain_val[i]); + break; } if (err < 0) @@ -966,12 +1208,20 @@ _dmic_vol_: /* check all channels */ for (i = 0; i < p->count; i++) { - if (i < 2) { + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + if (i < 2) { + reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01); + regmap_read(rt1320->mbq_regmap, reg_base + i, ®value); + } else { + reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01); + regmap_read(rt1320->mbq_regmap, reg_base + i - 2, ®value); + } + break; + case RT1321_DEV_ID: reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01); regmap_read(rt1320->mbq_regmap, reg_base + i, ®value); - } else { - reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01); - regmap_read(rt1320->mbq_regmap, reg_base + i - 2, ®value); + break; } ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); @@ -989,14 +1239,26 @@ static int rt1320_set_fu_capture_ctl(struct rt1320_sdw_priv *rt1320) for (i = 0; i < ARRAY_SIZE(rt1320->fu_mixer_mute); i++) { ch_mute = (rt1320->fu_dapm_mute || rt1320->fu_mixer_mute[i]) ? 0x01 : 0x00; - if (i < 2) + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + if (i < 2) + err = regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, + RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); + else + err = regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, + RT1320_SDCA_CTL_FU_MUTE, CH_01) + i - 2, ch_mute); + break; + case RT1321_DEV_ID: err = regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); - else - err = regmap_write(rt1320->regmap, - SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, - RT1320_SDCA_CTL_FU_MUTE, CH_01) + i - 2, ch_mute); + break; + default: + dev_err(&rt1320->sdw_slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } if (err < 0) return err; } @@ -1217,10 +1479,20 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, if (dai->id == RT1320_AIF1) port_config.num = 4; else if (dai->id == RT1320_AIF2) { - dmic_port_config[0].ch_mask = BIT(0) | BIT(1); - dmic_port_config[0].num = 8; - dmic_port_config[1].ch_mask = BIT(0) | BIT(1); - dmic_port_config[1].num = 10; + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + dmic_port_config[0].ch_mask = BIT(0) | BIT(1); + dmic_port_config[0].num = 8; + dmic_port_config[1].ch_mask = BIT(0) | BIT(1); + dmic_port_config[1].num = 10; + break; + case RT1321_DEV_ID: + dmic_port_config[0].ch_mask = BIT(0) | BIT(1); + dmic_port_config[0].num = 8; + break; + default: + return -EINVAL; + } } else return -EINVAL; } @@ -1228,10 +1500,21 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, if (dai->id == RT1320_AIF1) retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config, &port_config, 1, sdw_stream); - else if (dai->id == RT1320_AIF2) - retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config, + else if (dai->id == RT1320_AIF2) { + switch (rt1320->dev_id) { + case RT1320_DEV_ID: + retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config, dmic_port_config, 2, sdw_stream); - else + break; + case RT1321_DEV_ID: + retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config, + dmic_port_config, 1, sdw_stream); + break; + default: + dev_err(dai->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id); + return -EINVAL; + } + } else return -EINVAL; if (retval) { dev_err(dai->dev, "%s: Unable to configure port\n", __func__); @@ -1273,9 +1556,11 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream, regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); - regmap_write(rt1320->regmap, - SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), - sampling_rate); + + if (rt1320->dev_id == RT1320_DEV_ID) + regmap_write(rt1320->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); } return 0; @@ -1469,6 +1754,7 @@ static int rt1320_sdw_remove(struct sdw_slave *slave) static const struct sdw_device_id rt1320_id[] = { SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x0, 0), SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1321, 0x3, 0x1, 0), {}, }; MODULE_DEVICE_TABLE(sdw, rt1320_id); diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h index 23b321aee6a9..a6d90e259dc9 100644 --- a/sound/soc/codecs/rt1320-sdw.h +++ b/sound/soc/codecs/rt1320-sdw.h @@ -14,8 +14,16 @@ #include <linux/soundwire/sdw_registers.h> #include <sound/soc.h> +#define RT1320_DEV_ID 0x6981 +#define RT1321_DEV_ID 0x7045 + /* imp-defined registers */ #define RT1320_DEV_VERSION_ID_1 0xc404 +#define RT1320_DEV_ID_1 0xc405 +#define RT1320_DEV_ID_0 0xc406 + +#define RT1321_PATCH_MAIN_VER 0x1000cffe +#define RT1321_PATCH_BETA_VER 0x1000cfff #define RT1320_KR0_STATUS_CNT 0x1000f008 #define RT1320_KR0_INT_READY 0x1000f021 @@ -86,6 +94,7 @@ enum rt1320_version_id { #define RT1320_VER_B_ID 0x07392238 #define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin" #define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin" +#define RT1321_VA_MCU_PATCH "realtek/rt1320/rt1321-patch-code-va.bin" struct rt1320_sdw_priv { struct snd_soc_component *component; @@ -96,6 +105,7 @@ struct rt1320_sdw_priv { bool hw_init; bool first_hw_init; int version_id; + unsigned int dev_id; bool fu_dapm_mute; bool fu_mixer_mute[4]; }; diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index 582b47d69278..4d8a12b13015 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -63,15 +63,14 @@ static bool rt721_sdca_volatile_register(struct device *dev, unsigned int reg) static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int reg) { switch (reg) { - case 0x0900007: + case 0x0900004 ... 0x0900009: case 0x0a00005: case 0x0c00005: case 0x0d00014: case 0x0310100: - case 0x2000001: - case 0x2000002: - case 0x2000003: + case 0x2000000 ... 0x2000003: case 0x2000013: + case 0x200002c: case 0x200003c: case 0x2000046: case 0x5810000: @@ -134,6 +133,8 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re { switch (reg) { case 0x0310100: + case 0x0900005: + case 0x0900009: case 0x0a00005: case 0x0c00005: case 0x0d00014: @@ -141,6 +142,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re case 0x200000d: case 0x2000019: case 0x2000020: + case 0x200002c: case 0x2000030: case 0x2000046: case 0x2000067: diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index c9c1e608ddb7..8baf56237624 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -180,6 +180,16 @@ static struct tasdevice_config_info *tasdevice_add_config( dev_err(tas_priv->dev, "add conf: Out of boundary\n"); goto out; } + /* If in the RCA bin file are several profiles with the + * keyword "init", init_profile_id only store the last + * init profile id. + */ + if (strnstr(&config_data[config_offset], "init", 64)) { + tas_priv->rcabin.init_profile_id = + tas_priv->rcabin.ncfgs - 1; + dev_dbg(tas_priv->dev, "%s: init profile id = %d\n", + __func__, tas_priv->rcabin.init_profile_id); + } config_offset += 64; } @@ -283,6 +293,8 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw) int i; rca = &(tas_priv->rcabin); + /* Initialize to none */ + rca->init_profile_id = -1; fw_hdr = &(rca->fw_hdr); if (!fmw || !fmw->data) { dev_err(tas_priv->dev, "Failed to read %s\n", diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 676130f4cf3e..e4ca43e006db 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1641,6 +1641,12 @@ static void tasdevice_fw_ready(const struct firmware *fmw, tasdevice_prmg_load(tas_priv, 0); tas_priv->cur_prog = 0; + /* Init common setting for different audio profiles */ + if (tas_priv->rcabin.init_profile_id >= 0) + tasdevice_select_cfg_blk(tas_priv, + tas_priv->rcabin.init_profile_id, + TASDEVICE_BIN_BLK_PRE_POWER_UP); + #ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C if (tas_priv->name_prefix) acoustic_debugfs_node = devm_kasprintf(tas_priv->dev, diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c deleted file mode 100644 index 737ca82cf976..000000000000 --- a/sound/soc/codecs/wl1273.c +++ /dev/null @@ -1,500 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ALSA SoC WL1273 codec driver - * - * Author: Matti Aaltonen, <matti.j.aaltonen@nokia.com> - * - * Copyright: (C) 2010, 2011 Nokia Corporation - */ - -#include <linux/mfd/wl1273-core.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> -#include <sound/initval.h> - -#include "wl1273.h" - -enum wl1273_mode { WL1273_MODE_BT, WL1273_MODE_FM_RX, WL1273_MODE_FM_TX }; - -/* codec private data */ -struct wl1273_priv { - enum wl1273_mode mode; - struct wl1273_core *core; - unsigned int channels; -}; - -static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core, - int rate, int width) -{ - struct device *dev = &core->client->dev; - int r = 0; - u16 mode; - - dev_dbg(dev, "rate: %d\n", rate); - dev_dbg(dev, "width: %d\n", width); - - mutex_lock(&core->lock); - - mode = core->i2s_mode & ~WL1273_IS2_WIDTH & ~WL1273_IS2_RATE; - - switch (rate) { - case 48000: - mode |= WL1273_IS2_RATE_48K; - break; - case 44100: - mode |= WL1273_IS2_RATE_44_1K; - break; - case 32000: - mode |= WL1273_IS2_RATE_32K; - break; - case 22050: - mode |= WL1273_IS2_RATE_22_05K; - break; - case 16000: - mode |= WL1273_IS2_RATE_16K; - break; - case 12000: - mode |= WL1273_IS2_RATE_12K; - break; - case 11025: - mode |= WL1273_IS2_RATE_11_025; - break; - case 8000: - mode |= WL1273_IS2_RATE_8K; - break; - default: - dev_err(dev, "Sampling rate: %d not supported\n", rate); - r = -EINVAL; - goto out; - } - - switch (width) { - case 16: - mode |= WL1273_IS2_WIDTH_32; - break; - case 20: - mode |= WL1273_IS2_WIDTH_40; - break; - case 24: - mode |= WL1273_IS2_WIDTH_48; - break; - case 25: - mode |= WL1273_IS2_WIDTH_50; - break; - case 30: - mode |= WL1273_IS2_WIDTH_60; - break; - case 32: - mode |= WL1273_IS2_WIDTH_64; - break; - case 40: - mode |= WL1273_IS2_WIDTH_80; - break; - case 48: - mode |= WL1273_IS2_WIDTH_96; - break; - case 64: - mode |= WL1273_IS2_WIDTH_128; - break; - default: - dev_err(dev, "Data width: %d not supported\n", width); - r = -EINVAL; - goto out; - } - - dev_dbg(dev, "WL1273_I2S_DEF_MODE: 0x%04x\n", WL1273_I2S_DEF_MODE); - dev_dbg(dev, "core->i2s_mode: 0x%04x\n", core->i2s_mode); - dev_dbg(dev, "mode: 0x%04x\n", mode); - - if (core->i2s_mode != mode) { - r = core->write(core, WL1273_I2S_MODE_CONFIG_SET, mode); - if (r) - goto out; - - core->i2s_mode = mode; - r = core->write(core, WL1273_AUDIO_ENABLE, - WL1273_AUDIO_ENABLE_I2S); - if (r) - goto out; - } -out: - mutex_unlock(&core->lock); - - return r; -} - -static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core, - int channel_number) -{ - struct device *dev = &core->client->dev; - int r = 0; - - dev_dbg(dev, "%s\n", __func__); - - mutex_lock(&core->lock); - - if (core->channel_number == channel_number) - goto out; - - if (channel_number == 1 && core->mode == WL1273_MODE_RX) - r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO); - else if (channel_number == 1 && core->mode == WL1273_MODE_TX) - r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO); - else if (channel_number == 2 && core->mode == WL1273_MODE_RX) - r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO); - else if (channel_number == 2 && core->mode == WL1273_MODE_TX) - r = core->write(core, WL1273_MONO_SET, WL1273_TX_STEREO); - else - r = -EINVAL; -out: - mutex_unlock(&core->lock); - - return r; -} - -static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); - - ucontrol->value.enumerated.item[0] = wl1273->mode; - - return 0; -} - -/* - * TODO: Implement the audio routing in the driver. Now this control - * only indicates the setting that has been done elsewhere (in the user - * space). - */ -static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; - -static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); - - if (wl1273->mode == ucontrol->value.enumerated.item[0]) - return 0; - - /* Do not allow changes while stream is running */ - if (snd_soc_component_active(component)) - return -EPERM; - - if (ucontrol->value.enumerated.item[0] >= ARRAY_SIZE(wl1273_audio_route)) - return -EINVAL; - - wl1273->mode = ucontrol->value.enumerated.item[0]; - - return 1; -} - -static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route); - -static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); - - dev_dbg(component->dev, "%s: enter.\n", __func__); - - ucontrol->value.enumerated.item[0] = wl1273->core->audio_mode; - - return 0; -} - -static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); - int val, r = 0; - - dev_dbg(component->dev, "%s: enter.\n", __func__); - - val = ucontrol->value.enumerated.item[0]; - if (wl1273->core->audio_mode == val) - return 0; - - r = wl1273->core->set_audio(wl1273->core, val); - if (r < 0) - return r; - - return 1; -} - -static const char * const wl1273_audio_strings[] = { "Digital", "Analog" }; - -static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings); - -static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); - - dev_dbg(component->dev, "%s: enter.\n", __func__); - - ucontrol->value.integer.value[0] = wl1273->core->volume; - - return 0; -} - -static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); - int r; - - dev_dbg(component->dev, "%s: enter.\n", __func__); - - r = wl1273->core->set_volume(wl1273->core, - ucontrol->value.integer.value[0]); - if (r) - return r; - - return 1; -} - -static const struct snd_kcontrol_new wl1273_controls[] = { - SOC_ENUM_EXT("Codec Mode", wl1273_enum, - snd_wl1273_get_audio_route, snd_wl1273_set_audio_route), - SOC_ENUM_EXT("Audio Switch", wl1273_audio_enum, - snd_wl1273_fm_audio_get, snd_wl1273_fm_audio_put), - SOC_SINGLE_EXT("Volume", 0, 0, WL1273_MAX_VOLUME, 0, - snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put), -}; - -static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = { - SND_SOC_DAPM_INPUT("RX"), - - SND_SOC_DAPM_OUTPUT("TX"), -}; - -static const struct snd_soc_dapm_route wl1273_dapm_routes[] = { - { "Capture", NULL, "RX" }, - - { "TX", NULL, "Playback" }, -}; - -static int wl1273_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); - - switch (wl1273->mode) { - case WL1273_MODE_BT: - snd_pcm_hw_constraint_single(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, 8000); - snd_pcm_hw_constraint_single(substream->runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, 1); - break; - case WL1273_MODE_FM_RX: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - pr_err("Cannot play in RX mode.\n"); - return -EINVAL; - } - break; - case WL1273_MODE_FM_TX: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - pr_err("Cannot capture in TX mode.\n"); - return -EINVAL; - } - break; - default: - return -EINVAL; - } - - return 0; -} - -static int wl1273_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(dai->component); - struct wl1273_core *core = wl1273->core; - unsigned int rate, width, r; - - if (params_width(params) != 16) { - dev_err(dai->dev, "%d bits/sample not supported\n", - params_width(params)); - return -EINVAL; - } - - rate = params_rate(params); - width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; - - if (wl1273->mode == WL1273_MODE_BT) { - if (rate != 8000) { - pr_err("Rate %d not supported.\n", params_rate(params)); - return -EINVAL; - } - - if (params_channels(params) != 1) { - pr_err("Only mono supported.\n"); - return -EINVAL; - } - - return 0; - } - - if (wl1273->mode == WL1273_MODE_FM_TX && - substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - pr_err("Only playback supported with TX.\n"); - return -EINVAL; - } - - if (wl1273->mode == WL1273_MODE_FM_RX && - substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - pr_err("Only capture supported with RX.\n"); - return -EINVAL; - } - - if (wl1273->mode != WL1273_MODE_FM_RX && - wl1273->mode != WL1273_MODE_FM_TX) { - pr_err("Unexpected mode: %d.\n", wl1273->mode); - return -EINVAL; - } - - r = snd_wl1273_fm_set_i2s_mode(core, rate, width); - if (r) - return r; - - wl1273->channels = params_channels(params); - r = snd_wl1273_fm_set_channel_number(core, wl1273->channels); - if (r) - return r; - - return 0; -} - -static const struct snd_soc_dai_ops wl1273_dai_ops = { - .startup = wl1273_startup, - .hw_params = wl1273_hw_params, -}; - -static struct snd_soc_dai_driver wl1273_dai = { - .name = "wl1273-fm", - .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE}, - .ops = &wl1273_dai_ops, -}; - -/* Audio interface format for the soc_card driver */ -int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt) -{ - struct wl1273_priv *wl1273; - - if (component == NULL || fmt == NULL) - return -EINVAL; - - wl1273 = snd_soc_component_get_drvdata(component); - - switch (wl1273->mode) { - case WL1273_MODE_FM_RX: - case WL1273_MODE_FM_TX: - *fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBP_CFP; - - break; - case WL1273_MODE_BT: - *fmt = SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBP_CFP; - - break; - default: - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL_GPL(wl1273_get_format); - -static int wl1273_probe(struct snd_soc_component *component) -{ - struct wl1273_core **core = component->dev->platform_data; - struct wl1273_priv *wl1273; - - dev_dbg(component->dev, "%s.\n", __func__); - - if (!core) { - dev_err(component->dev, "Platform data is missing.\n"); - return -EINVAL; - } - - wl1273 = kzalloc(sizeof(struct wl1273_priv), GFP_KERNEL); - if (!wl1273) - return -ENOMEM; - - wl1273->mode = WL1273_MODE_BT; - wl1273->core = *core; - - snd_soc_component_set_drvdata(component, wl1273); - - return 0; -} - -static void wl1273_remove(struct snd_soc_component *component) -{ - struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); - - dev_dbg(component->dev, "%s\n", __func__); - kfree(wl1273); -} - -static const struct snd_soc_component_driver soc_component_dev_wl1273 = { - .probe = wl1273_probe, - .remove = wl1273_remove, - .controls = wl1273_controls, - .num_controls = ARRAY_SIZE(wl1273_controls), - .dapm_widgets = wl1273_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets), - .dapm_routes = wl1273_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes), - .idle_bias_on = 1, - .use_pmdown_time = 1, - .endianness = 1, -}; - -static int wl1273_platform_probe(struct platform_device *pdev) -{ - return devm_snd_soc_register_component(&pdev->dev, - &soc_component_dev_wl1273, - &wl1273_dai, 1); -} - -MODULE_ALIAS("platform:wl1273-codec"); - -static struct platform_driver wl1273_platform_driver = { - .driver = { - .name = "wl1273-codec", - }, - .probe = wl1273_platform_probe, -}; - -module_platform_driver(wl1273_platform_driver); - -MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>"); -MODULE_DESCRIPTION("ASoC WL1273 codec driver"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wl1273.h b/sound/soc/codecs/wl1273.h deleted file mode 100644 index 66c312fa7eee..000000000000 --- a/sound/soc/codecs/wl1273.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * sound/soc/codec/wl1273.h - * - * ALSA SoC WL1273 codec driver - * - * Copyright (C) Nokia Corporation - * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com> - */ - -#ifndef __WL1273_CODEC_H__ -#define __WL1273_CODEC_H__ - -int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt); - -#endif /* End of __WL1273_CODEC_H__ */ diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index bc584b17bf28..b28398aa9e48 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -106,33 +106,33 @@ struct wm8994_priv { int vss_ena[3]; int enh_eq_ena[3]; - /* Platform dependant DRC configuration */ + /* Platform dependent DRC configuration */ const char **drc_texts; int drc_cfg[WM8994_NUM_DRC]; struct soc_enum drc_enum; - /* Platform dependant ReTune mobile configuration */ + /* Platform dependent ReTune mobile configuration */ int num_retune_mobile_texts; const char **retune_mobile_texts; int retune_mobile_cfg[WM8994_NUM_EQ]; struct soc_enum retune_mobile_enum; - /* Platform dependant MBC configuration */ + /* Platform dependent MBC configuration */ int mbc_cfg; const char **mbc_texts; struct soc_enum mbc_enum; - /* Platform dependant VSS configuration */ + /* Platform dependent VSS configuration */ int vss_cfg; const char **vss_texts; struct soc_enum vss_enum; - /* Platform dependant VSS HPF configuration */ + /* Platform dependent VSS HPF configuration */ int vss_hpf_cfg; const char **vss_hpf_texts; struct soc_enum vss_hpf_enum; - /* Platform dependant enhanced EQ configuration */ + /* Platform dependent enhanced EQ configuration */ int enh_eq_cfg; const char **enh_eq_texts; struct soc_enum enh_eq_enum; diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 459b39998307..ee2040782532 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -77,7 +77,7 @@ struct wm8996_priv { int rx_rate[WM8996_AIFS]; int bclk_rate[WM8996_AIFS]; - /* Platform dependant ReTune mobile configuration */ + /* Platform dependent ReTune mobile configuration */ int num_retune_mobile_texts; const char **retune_mobile_texts; int retune_mobile_cfg[2]; diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 188363b03b93..ca4520ade79a 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -14,6 +14,7 @@ #include <linux/printk.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/slab.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_registers.h> @@ -468,6 +469,7 @@ struct wsa883x_priv { struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WSA883X_MAX_SWR_PORTS]; struct gpio_desc *sd_n; + struct reset_control *sd_reset; bool port_prepared[WSA883X_MAX_SWR_PORTS]; bool port_enable[WSA883X_MAX_SWR_PORTS]; int active_ports; @@ -1546,6 +1548,46 @@ static const struct hwmon_chip_info wsa883x_hwmon_chip_info = { .info = wsa883x_hwmon_info, }; +static void wsa883x_reset_assert(void *data) +{ + struct wsa883x_priv *wsa883x = data; + + if (wsa883x->sd_reset) + reset_control_assert(wsa883x->sd_reset); + else + gpiod_direction_output(wsa883x->sd_n, 1); +} + +static void wsa883x_reset_deassert(struct wsa883x_priv *wsa883x) +{ + if (wsa883x->sd_reset) + reset_control_deassert(wsa883x->sd_reset); + else + gpiod_direction_output(wsa883x->sd_n, 0); +} + +static int wsa883x_get_reset(struct device *dev, struct wsa883x_priv *wsa883x) +{ + wsa883x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(wsa883x->sd_reset)) + return dev_err_probe(dev, PTR_ERR(wsa883x->sd_reset), + "Failed to get reset\n"); + /* + * if sd_reset: NULL, so use the backwards compatible way for powerdown-gpios, + * which does not handle sharing GPIO properly. + */ + if (!wsa883x->sd_reset) { + wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_FLAGS_BIT_NONEXCLUSIVE | + GPIOD_OUT_HIGH); + if (IS_ERR(wsa883x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa883x->sd_n), + "Shutdown Control GPIO not found\n"); + } + + return 0; +} + static int wsa883x_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) { @@ -1566,13 +1608,9 @@ static int wsa883x_probe(struct sdw_slave *pdev, if (ret) return dev_err_probe(dev, ret, "Failed to enable vdd regulator\n"); - wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown", - GPIOD_FLAGS_BIT_NONEXCLUSIVE | GPIOD_OUT_HIGH); - if (IS_ERR(wsa883x->sd_n)) { - ret = dev_err_probe(dev, PTR_ERR(wsa883x->sd_n), - "Shutdown Control GPIO not found\n"); + ret = wsa883x_get_reset(dev, wsa883x); + if (ret) goto err; - } dev_set_drvdata(dev, wsa883x); wsa883x->slave = pdev; @@ -1595,11 +1633,14 @@ static int wsa883x_probe(struct sdw_slave *pdev, pdev->prop.simple_clk_stop_capable = true; pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; - gpiod_direction_output(wsa883x->sd_n, 0); + + wsa883x_reset_deassert(wsa883x); + ret = devm_add_action_or_reset(dev, wsa883x_reset_assert, wsa883x); + if (ret) + return ret; wsa883x->regmap = devm_regmap_init_sdw(pdev, &wsa883x_regmap_config); if (IS_ERR(wsa883x->regmap)) { - gpiod_direction_output(wsa883x->sd_n, 1); ret = dev_err_probe(dev, PTR_ERR(wsa883x->regmap), "regmap_init failed\n"); goto err; diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index d0367b21f775..cac064a60349 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1244,7 +1244,6 @@ static struct regmap_config fsl_sai_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .fast_io = true, .max_register = FSL_SAI_RMR, .reg_defaults = fsl_sai_reg_defaults_ofs0, diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index e0357d257c6c..3c47c8de04b7 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -64,7 +64,7 @@ static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context) header.p.header_high.part.done = 0; sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full); - /* write 1 to clear status register */; + /* write 1 to clear status register */ isr.part.done_interrupt = 1; sst_shim_write64(drv->shim, SST_ISRX, isr.full); spin_unlock(&drv->ipc_spin_lock); diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index e8e6b1c7fc90..7aa20fcf1a33 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -115,6 +115,55 @@ avs_path_find_variant(struct avs_dev *adev, return NULL; } +static struct avs_tplg_path *avs_condpath_find_variant(struct avs_dev *adev, + struct avs_tplg_path_template *template, + struct avs_path *source, + struct avs_path *sink) +{ + struct avs_tplg_path *variant; + + list_for_each_entry(variant, &template->path_list, node) { + if (variant->source_path_id == source->template->id && + variant->sink_path_id == sink->template->id) + return variant; + } + + return NULL; +} + +static bool avs_tplg_path_template_id_equal(struct avs_tplg_path_template_id *id, + struct avs_tplg_path_template_id *id2) +{ + return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name); +} + +static struct avs_path *avs_condpath_find_match(struct avs_dev *adev, + struct avs_tplg_path_template *template, + struct avs_path *path, int dir) +{ + struct avs_tplg_path_template_id *id, *id2; + + if (dir) { + id = &template->source; + id2 = &template->sink; + } else { + id = &template->sink; + id2 = &template->source; + } + + /* Check whether this path is either source or sink of condpath template. */ + if (id->id != path->template->owner->id || + strcmp(id->tplg_name, path->template->owner->owner->name)) + return NULL; + + /* Unidirectional condpaths are allowed. */ + if (avs_tplg_path_template_id_equal(id, id2)) + return path; + + /* Now find the counterpart. */ + return avs_path_find_path(adev, id2->tplg_name, id2->id); +} + static struct acpi_nhlt_config * avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t); @@ -1051,6 +1100,10 @@ static int avs_path_init(struct avs_dev *adev, struct avs_path *path, path->dma_id = dma_id; INIT_LIST_HEAD(&path->ppl_list); INIT_LIST_HEAD(&path->node); + INIT_LIST_HEAD(&path->source_list); + INIT_LIST_HEAD(&path->sink_list); + INIT_LIST_HEAD(&path->source_node); + INIT_LIST_HEAD(&path->sink_node); /* create all the pipelines */ list_for_each_entry(tppl, &template->ppl_list, node) { @@ -1134,12 +1187,129 @@ err: return ERR_PTR(ret); } +static void avs_condpath_free(struct avs_dev *adev, struct avs_path *path) +{ + int ret; + + list_del(&path->source_node); + list_del(&path->sink_node); + + ret = avs_path_reset(path); + if (ret < 0) + dev_err(adev->dev, "reset condpath failed: %d\n", ret); + + ret = avs_path_unbind(path); + if (ret < 0) + dev_err(adev->dev, "unbind condpath failed: %d\n", ret); + + avs_path_free_unlocked(path); +} + +static struct avs_path *avs_condpath_create(struct avs_dev *adev, + struct avs_tplg_path *template, + struct avs_path *source, + struct avs_path *sink) +{ + struct avs_path *path; + int ret; + + path = avs_path_create_unlocked(adev, 0, template); + if (IS_ERR(path)) + return path; + + ret = avs_path_bind(path); + if (ret) + goto err_bind; + + ret = avs_path_reset(path); + if (ret) + goto err_reset; + + path->source = source; + path->sink = sink; + list_add_tail(&path->source_node, &source->source_list); + list_add_tail(&path->sink_node, &sink->sink_list); + + return path; + +err_reset: + avs_path_unbind(path); +err_bind: + avs_path_free_unlocked(path); + return ERR_PTR(ret); +} + +static int avs_condpaths_walk(struct avs_dev *adev, struct avs_path *path, int dir) +{ + struct avs_soc_component *acomp; + struct avs_path *source, *sink; + struct avs_path **other; + + if (dir) { + source = path; + other = &sink; + } else { + sink = path; + other = &source; + } + + list_for_each_entry(acomp, &adev->comp_list, node) { + for (int i = 0; i < acomp->tplg->num_condpath_tmpls; i++) { + struct avs_tplg_path_template *template; + struct avs_tplg_path *variant; + struct avs_path *cpath; + + template = &acomp->tplg->condpath_tmpls[i]; + + /* Do not create unidirectional condpaths twice. */ + if (avs_tplg_path_template_id_equal(&template->source, + &template->sink) && dir) + continue; + + *other = avs_condpath_find_match(adev, template, path, dir); + if (!*other) + continue; + + variant = avs_condpath_find_variant(adev, template, source, sink); + if (!variant) + continue; + + cpath = avs_condpath_create(adev, variant, source, sink); + if (IS_ERR(cpath)) + return PTR_ERR(cpath); + } + } + + return 0; +} + +/* Caller responsible for holding adev->path_mutex. */ +static int avs_condpaths_walk_all(struct avs_dev *adev, struct avs_path *path) +{ + int ret; + + ret = avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_CAPTURE); + if (ret) + return ret; + + return avs_condpaths_walk(adev, path, SNDRV_PCM_STREAM_PLAYBACK); +} + void avs_path_free(struct avs_path *path) { + struct avs_path *cpath, *csave; struct avs_dev *adev = path->owner; mutex_lock(&adev->path_mutex); + + /* Free all condpaths this path spawned. */ + list_for_each_entry_safe(cpath, csave, &path->source_list, source_node) + avs_condpath_free(path->owner, cpath); + list_for_each_entry_safe(cpath, csave, &path->sink_list, sink_node) + avs_condpath_free(path->owner, cpath); + avs_path_free_unlocked(path); + mutex_unlock(&adev->path_mutex); } @@ -1150,6 +1320,7 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, { struct avs_tplg_path *variant; struct avs_path *path; + int ret; variant = avs_path_find_variant(adev, template, fe_params, be_params); if (!variant) { @@ -1163,7 +1334,16 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, mutex_lock(&adev->comp_list_mutex); path = avs_path_create_unlocked(adev, dma_id, variant); + if (IS_ERR(path)) + goto exit; + + ret = avs_condpaths_walk_all(adev, path); + if (ret) { + avs_path_free_unlocked(path); + path = ERR_PTR(ret); + } +exit: mutex_unlock(&adev->comp_list_mutex); mutex_unlock(&adev->path_mutex); @@ -1286,6 +1466,42 @@ int avs_path_reset(struct avs_path *path) return 0; } +static int avs_condpath_pause(struct avs_dev *adev, struct avs_path *cpath) +{ + struct avs_path_pipeline *ppl; + int ret; + + if (cpath->state == AVS_PPL_STATE_PAUSED) + return 0; + + list_for_each_entry_reverse(ppl, &cpath->ppl_list, node) { + ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_PAUSED); + if (ret) { + dev_err(adev->dev, "pause cpath failed: %d\n", ret); + cpath->state = AVS_PPL_STATE_INVALID; + return AVS_IPC_RET(ret); + } + } + + cpath->state = AVS_PPL_STATE_PAUSED; + return 0; +} + +static void avs_condpaths_pause(struct avs_dev *adev, struct avs_path *path) +{ + struct avs_path *cpath; + + mutex_lock(&adev->path_mutex); + + /* If either source or sink stops, so do the attached conditional paths. */ + list_for_each_entry(cpath, &path->source_list, source_node) + avs_condpath_pause(adev, cpath); + list_for_each_entry(cpath, &path->sink_list, sink_node) + avs_condpath_pause(adev, cpath); + + mutex_unlock(&adev->path_mutex); +} + int avs_path_pause(struct avs_path *path) { struct avs_path_pipeline *ppl; @@ -1295,6 +1511,8 @@ int avs_path_pause(struct avs_path *path) if (path->state == AVS_PPL_STATE_PAUSED) return 0; + avs_condpaths_pause(adev, path); + list_for_each_entry_reverse(ppl, &path->ppl_list, node) { ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_PAUSED); @@ -1309,6 +1527,50 @@ int avs_path_pause(struct avs_path *path) return 0; } +static int avs_condpath_run(struct avs_dev *adev, struct avs_path *cpath, int trigger) +{ + struct avs_path_pipeline *ppl; + int ret; + + if (cpath->state == AVS_PPL_STATE_RUNNING) + return 0; + + list_for_each_entry(ppl, &cpath->ppl_list, node) { + if (ppl->template->cfg->trigger != trigger) + continue; + + ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, AVS_PPL_STATE_RUNNING); + if (ret) { + dev_err(adev->dev, "run cpath failed: %d\n", ret); + cpath->state = AVS_PPL_STATE_INVALID; + return AVS_IPC_RET(ret); + } + } + + cpath->state = AVS_PPL_STATE_RUNNING; + return 0; +} + +static void avs_condpaths_run(struct avs_dev *adev, struct avs_path *path, int trigger) +{ + struct avs_path *cpath; + + mutex_lock(&adev->path_mutex); + + /* Run conditional paths only if source and sink are both running. */ + list_for_each_entry(cpath, &path->source_list, source_node) + if (cpath->source->state == AVS_PPL_STATE_RUNNING && + cpath->sink->state == AVS_PPL_STATE_RUNNING) + avs_condpath_run(adev, cpath, trigger); + + list_for_each_entry(cpath, &path->sink_list, sink_node) + if (cpath->source->state == AVS_PPL_STATE_RUNNING && + cpath->sink->state == AVS_PPL_STATE_RUNNING) + avs_condpath_run(adev, cpath, trigger); + + mutex_unlock(&adev->path_mutex); +} + int avs_path_run(struct avs_path *path, int trigger) { struct avs_path_pipeline *ppl; @@ -1332,5 +1594,10 @@ int avs_path_run(struct avs_path *path, int trigger) } path->state = AVS_PPL_STATE_RUNNING; + + /* Granular pipeline triggering not intended for conditional paths. */ + if (trigger == AVS_TPLG_TRIGGER_AUTO) + avs_condpaths_run(adev, path, trigger); + return 0; } diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h index c65ed84aa853..ceb89971a902 100644 --- a/sound/soc/intel/avs/path.h +++ b/sound/soc/intel/avs/path.h @@ -13,11 +13,24 @@ #include "avs.h" #include "topology.h" +#define AVS_COND_TYPE_NONE 0 +#define AVS_COND_TYPE_AECREF 1 + struct avs_path { u32 dma_id; struct list_head ppl_list; u32 state; + /* condpath navigation for standard paths */ + struct list_head source_list; + struct list_head sink_list; + + /* conditional path fields */ + struct avs_path *source; + struct avs_path *sink; + struct list_head source_node; + struct list_head sink_node; + struct avs_tplg_path *template; struct avs_dev *owner; /* device path management */ diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index f2e4ad8b8e14..dfe8cf505381 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1387,6 +1387,27 @@ static const struct avs_tplg_token_parser path_parsers[] = { }, }; +static const struct avs_tplg_token_parser condpath_parsers[] = { + { + .token = AVS_TKN_CONDPATH_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path, id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path, source_path_id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_CONDPATH_SINK_PATH_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path, sink_path_id), + .parse = avs_parse_word_token, + }, +}; + static struct avs_tplg_path * avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner, struct snd_soc_tplg_vendor_array *tuples, u32 block_size, @@ -1454,6 +1475,39 @@ static const struct avs_tplg_token_parser path_tmpl_parsers[] = { }, }; +static const struct avs_tplg_token_parser condpath_tmpl_parsers[] = { + { + .token = AVS_TKN_CONDPATH_TMPL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path_template, id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING, + .type = SND_SOC_TPLG_TUPLE_TYPE_STRING, + .offset = offsetof(struct avs_tplg_path_template, source.tplg_name), + .parse = avs_parse_string_token, + }, + { + .token = AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path_template, source.id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING, + .type = SND_SOC_TPLG_TUPLE_TYPE_STRING, + .offset = offsetof(struct avs_tplg_path_template, sink.tplg_name), + .parse = avs_parse_string_token, + }, + { + .token = AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path_template, sink.id), + .parse = avs_parse_word_token, + }, +}; + static int parse_path_template(struct snd_soc_component *comp, struct snd_soc_tplg_vendor_array *tuples, u32 block_size, struct avs_tplg_path_template *template, @@ -1524,6 +1578,56 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o return template; } +static int avs_tplg_parse_condpath_templates(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, + u32 block_size) +{ + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg = acomp->tplg; + int ret, i; + + ret = parse_dictionary_header(comp, tuples, (void **)&tplg->condpath_tmpls, + &tplg->num_condpath_tmpls, + sizeof(*tplg->condpath_tmpls), + AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32); + if (ret) + return ret; + + block_size -= le32_to_cpu(tuples->size); + /* With header parsed, move on to parsing entries. */ + tuples = avs_tplg_vendor_array_next(tuples); + + for (i = 0; i < tplg->num_condpath_tmpls; i++) { + struct avs_tplg_path_template *template; + u32 esize; + + template = &tplg->condpath_tmpls[i]; + template->owner = tplg; /* Used when building sysfs hierarchy. */ + INIT_LIST_HEAD(&template->path_list); + INIT_LIST_HEAD(&template->node); + + ret = avs_tplg_vendor_entry_size(tuples, block_size, + AVS_TKN_CONDPATH_TMPL_ID_U32, &esize); + if (ret) + return ret; + + ret = parse_path_template(comp, tuples, esize, template, + condpath_tmpl_parsers, + ARRAY_SIZE(condpath_tmpl_parsers), + condpath_parsers, + ARRAY_SIZE(condpath_parsers)); + if (ret < 0) { + dev_err(comp->dev, "parse condpath_tmpl: %d failed: %d\n", i, ret); + return ret; + } + + block_size -= esize; + tuples = avs_tplg_vendor_array_at(tuples, esize); + } + + return 0; +} + static const struct avs_tplg_token_parser mod_init_config_parsers[] = { { .token = AVS_TKN_INIT_CONFIG_ID_U32, @@ -1891,6 +1995,12 @@ static int avs_manifest(struct snd_soc_component *comp, int index, return ret; } + /* Condpaths dictionary. */ + ret = avs_tplg_parse_condpath_templates(comp, tuples, + has_init_config ? offset : remaining); + if (ret < 0) + return ret; + if (!has_init_config) return 0; diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h index f5601a4e3ec8..1e83fccf2ea2 100644 --- a/sound/soc/intel/avs/topology.h +++ b/sound/soc/intel/avs/topology.h @@ -33,6 +33,7 @@ struct avs_tplg { u32 num_pplcfgs; struct avs_tplg_binding *bindings; u32 num_bindings; + struct avs_tplg_path_template *condpath_tmpls; u32 num_condpath_tmpls; struct avs_tplg_init_config *init_configs; u32 num_init_configs; @@ -155,6 +156,10 @@ struct avs_tplg_path_template { struct snd_soc_dapm_widget *w; + /* Conditional path. */ + struct avs_tplg_path_template_id source; + struct avs_tplg_path_template_id sink; + struct list_head path_list; struct avs_tplg *owner; @@ -176,6 +181,9 @@ struct avs_tplg_path { /* Path format requirements. */ struct avs_audio_format *fe_fmt; struct avs_audio_format *be_fmt; + /* Condpath path-variant requirements. */ + u32 source_path_id; + u32 sink_path_id; struct list_head ppl_list; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 7d6a3586cdd5..3d6d7bc05b87 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -159,7 +159,7 @@ static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd) { int ret; - ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, + ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, &mt8173_rt5650_hdmi_jack); if (ret) return ret; diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 3388e076ccc9..983f3b91119a 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -378,7 +378,7 @@ static int mt8183_da7219_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd) snd_soc_card_get_drvdata(rtd->card); int ret; - ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, + ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, &priv->hdmi_jack); if (ret) return ret; diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index 497a9043be7b..0bc1f11e17aa 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -383,7 +383,7 @@ mt8183_mt6358_ts3a227_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd) snd_soc_card_get_drvdata(rtd->card); int ret; - ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, + ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, &priv->hdmi_jack); if (ret) return ret; diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366.c b/sound/soc/mediatek/mt8186/mt8186-mt6366.c index 43546012cf61..45df69809cba 100644 --- a/sound/soc/mediatek/mt8186/mt8186-mt6366.c +++ b/sound/soc/mediatek/mt8186/mt8186-mt6366.c @@ -362,7 +362,7 @@ static int mt8186_mt6366_rt1019_rt5682s_hdmi_init(struct snd_soc_pcm_runtime *rt return ret; } - ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, jack); + ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack); if (ret) { dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index ea814a0f726d..c6e7461e8f76 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -250,14 +250,14 @@ enum mt8188_jacks { static struct snd_soc_jack_pin mt8188_hdmi_jack_pins[] = { { .pin = "HDMI", - .mask = SND_JACK_LINEOUT, + .mask = SND_JACK_AVOUT, }, }; static struct snd_soc_jack_pin mt8188_dp_jack_pins[] = { { .pin = "DP", - .mask = SND_JACK_LINEOUT, + .mask = SND_JACK_AVOUT, }, }; @@ -638,7 +638,7 @@ static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd) int ret = 0; ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack", - SND_JACK_LINEOUT, jack, + SND_JACK_AVOUT, jack, mt8188_hdmi_jack_pins, ARRAY_SIZE(mt8188_hdmi_jack_pins)); if (ret) { @@ -663,7 +663,7 @@ static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; int ret = 0; - ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_LINEOUT, + ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_AVOUT, jack, mt8188_dp_jack_pins, ARRAY_SIZE(mt8188_dp_jack_pins)); if (ret) { diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index bf483a8fb34a..91c57765ab57 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -368,7 +368,7 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd) snd_soc_rtd_to_codec(rtd, 0)->component; int ret; - ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, jack); + ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack); if (ret) { dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c index e57391c213e7..7b96c843a14a 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -360,7 +360,7 @@ static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) snd_soc_rtd_to_codec(rtd, 0)->component; int ret; - ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT, jack); + ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_AVOUT, jack); if (ret) return ret; @@ -375,7 +375,7 @@ static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd) snd_soc_rtd_to_codec(rtd, 0)->component; int ret; - ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, jack); + ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_AVOUT, jack); if (ret) return ret; diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index e026f9912a6d..e54abcd39f79 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -3,7 +3,7 @@ menu "PXA" config SND_PXA2XX_SOC tristate "SoC Audio for the Intel PXA2xx chip" - depends on ARCH_PXA || COMPILE_TEST + depends on ARCH_PXA || (COMPILE_TEST && GPIOLIB_LEGACY) select SND_PXA2XX_LIB help Say Y or M if you want to add support for codecs attached to @@ -26,7 +26,7 @@ config SND_PXA2XX_SOC_I2S config SND_PXA_SOC_SSP tristate "Soc Audio via PXA2xx/PXA3xx SSP ports" - depends on PLAT_PXA + depends on ARCH_PXA select PXA_SSP select SND_PXA2XX_LIB diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a37d44cd04c6..6782a0d6cd47 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1000,6 +1000,25 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, EXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level); /** + * snd_soc_dapm_init_bias_level() - Initialize DAPM bias level + * @dapm: The DAPM context to initialize + * @level: The DAPM level to initialize to + * + * This function only sets the driver internal state of the DAPM level and will + * not modify the state of the device. Hence it should not be used during normal + * operation, but only to synchronize the internal state to the device state. + * E.g. during driver probe to set the DAPM level to the one corresponding with + * the power-on reset state of the device. + * + * To change the DAPM state of the device use snd_soc_dapm_set_bias_level(). + */ +void snd_soc_dapm_init_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) +{ + dapm->bias_level = level; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_init_bias_level); + +/** * snd_soc_dapm_set_bias_level - set the bias level for the system * @dapm: DAPM context * @level: level to configure @@ -1037,6 +1056,18 @@ out: return ret; } +/** + * snd_soc_dapm_get_bias_level() - Get current DAPM bias level + * @dapm: The context for which to get the bias level + * + * Returns: The current bias level of the passed DAPM context. + */ +enum snd_soc_bias_level snd_soc_dapm_get_bias_level(struct snd_soc_dapm_context *dapm) +{ + return dapm->bias_level; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_bias_level); + static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *kcontrolw, const struct snd_kcontrol_new *kcontrol_new, diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index f00b381cec3b..1757539a6a28 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -382,7 +382,7 @@ static int imx_probe(struct snd_sof_dev *sdev) imx_unregister_action, sdev); if (ret) - return dev_err_probe(sdev->dev, ret, "failed to add devm action\n"); + return ret; common->ipc_handle = dev_get_drvdata(&common->ipc_dev->dev); if (!common->ipc_handle) |
