diff options
| author | Takashi Iwai <tiwai@suse.de> | 2017-02-20 21:43:40 +0100 | 
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2017-02-20 21:43:40 +0100 | 
| commit | 4e25d30c8ddeae6ad0b68440aacadaacb14f8538 (patch) | |
| tree | 08fe4ac15aec508b370262ad653c83a91be247b5 | |
| parent | d2bb390a2081a36ffe906724d2848d846f2aeb29 (diff) | |
| parent | 141dee78c40ac2c43aa4ff306688d625e1c731de (diff) | |
Merge tag 'asoc-v4.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v4.11
Another release that's mainly focused on drivers rather than core
changes, highlights include:
 - A huge batch of updates to the Intel drivers, mainly around
   DisplayPort and HDMI with some additional board support too.
 - Channel mapping support for HDMI.
 - Support for AllWinner A31 and A33, Everest Semiconductor ES8328,
   Nuvoton NAU8540.
147 files changed, 5466 insertions, 1521 deletions
| diff --git a/Documentation/devicetree/bindings/misc/atmel-ssc.txt b/Documentation/devicetree/bindings/misc/atmel-ssc.txt index efc98ea1f23d..f8629bb73945 100644 --- a/Documentation/devicetree/bindings/misc/atmel-ssc.txt +++ b/Documentation/devicetree/bindings/misc/atmel-ssc.txt @@ -24,6 +24,8 @@ Optional properties:         this parameter to choose where the clock from.       - By default the clock is from TK pin, if the clock from RK pin, this         property is needed. +  - #sound-dai-cells: Should contain <0>. +     - This property makes the SSC into an automatically registered DAI.  Examples:  - PDC transfer: diff --git a/Documentation/devicetree/bindings/sound/axentia,tse850-pcm5142.txt b/Documentation/devicetree/bindings/sound/axentia,tse850-pcm5142.txt index 5b9b38f578bb..fdb25b492514 100644 --- a/Documentation/devicetree/bindings/sound/axentia,tse850-pcm5142.txt +++ b/Documentation/devicetree/bindings/sound/axentia,tse850-pcm5142.txt @@ -2,8 +2,7 @@ Devicetree bindings for the Axentia TSE-850 audio complex  Required properties:    - compatible: "axentia,tse850-pcm5142" -  - axentia,ssc-controller: The phandle of the atmel SSC controller used as -    cpu dai. +  - axentia,cpu-dai: The phandle of the cpu dai.    - axentia,audio-codec: The phandle of the PCM5142 codec.    - axentia,add-gpios: gpio specifier that controls the mixer.    - axentia,loop1-gpios: gpio specifier that controls loop relays on channel 1. @@ -43,6 +42,12 @@ the PCM5142 codec.  Example: +	&ssc0 { +		#sound-dai-cells = <0>; + +		status = "okay"; +	}; +  	&i2c {  		codec: pcm5142@4c {  			compatible = "ti,pcm5142"; @@ -77,7 +82,7 @@ Example:  	sound {  		compatible = "axentia,tse850-pcm5142"; -		axentia,ssc-controller = <&ssc0>; +		axentia,cpu-dai = <&ssc0>;  		axentia,audio-codec = <&codec>;  		axentia,add-gpios = <&pioA 8 GPIO_ACTIVE_LOW>; diff --git a/Documentation/devicetree/bindings/sound/es8328.txt b/Documentation/devicetree/bindings/sound/es8328.txt index 30ea8a318ae9..33fbf058c997 100644 --- a/Documentation/devicetree/bindings/sound/es8328.txt +++ b/Documentation/devicetree/bindings/sound/es8328.txt @@ -4,7 +4,7 @@ This device supports both I2C and SPI.  Required properties: -  - compatible : "everest,es8328" +  - compatible  : Should be "everest,es8328" or "everest,es8388"    - DVDD-supply : Regulator providing digital core supply voltage 1.8 - 3.6V    - AVDD-supply : Regulator providing analog supply voltage 3.3V    - PVDD-supply : Regulator providing digital IO supply voltage 1.8 - 3.6V diff --git a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt index 3e623a724e55..9800a560e0c2 100644 --- a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt +++ b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt @@ -4,6 +4,7 @@ Required properties:  - compatible = "mediatek,mt2701-audio";  - reg: register location and size  - interrupts: Should contain AFE interrupt +- power-domains: should define the power domain  - clock-names: should have these clock names:  		"infra_sys_audio_clk",  		"top_audio_mux1_sel", @@ -58,6 +59,7 @@ Example:  		      <0 0x112A0000 0 0x20000>;  		interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,  			     <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>; +		power-domains = <&scpsys MT2701_POWER_DOMAIN_IFR_MSC>;  		clocks = <&infracfg CLK_INFRA_AUDIO>,  			 <&topckgen CLK_TOP_AUD_MUX1_SEL>,  			 <&topckgen CLK_TOP_AUD_MUX2_SEL>, diff --git a/Documentation/devicetree/bindings/sound/nau8540.txt b/Documentation/devicetree/bindings/sound/nau8540.txt new file mode 100644 index 000000000000..307a76528320 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nau8540.txt @@ -0,0 +1,16 @@ +NAU85L40 audio CODEC + +This device supports I2C only. + +Required properties: + +  - compatible : "nuvoton,nau8540" + +  - reg : the I2C address of the device. + +Example: + +codec: nau8540@1c { +       compatible = "nuvoton,nau8540"; +       reg = <0x1c>; +}; diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3288-hdmi-analog.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3288-hdmi-analog.txt new file mode 100644 index 000000000000..2539e1d68107 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,rk3288-hdmi-analog.txt @@ -0,0 +1,36 @@ +ROCKCHIP RK3288 with HDMI and analog audio + +Required properties: +- compatible: "rockchip,rk3288-hdmi-analog" +- rockchip,model: The user-visible name of this sound complex +- rockchip,i2s-controller: The phandle of the Rockchip I2S controller that's +  connected to the CODEC +- rockchip,audio-codec: The phandle of the analog audio codec. +- rockchip,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. For this driver the first string should always be +		    "Analog". + +Optionnal properties: +- rockchip,hp-en-gpios = The phandle of the GPIO that power up/down the +  headphone (when the analog output is an headphone). +- rockchip,hp-det-gpios = The phandle of the GPIO that detects the headphone +  (when the analog output is an headphone). +- pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt + +Example: + +sound { +	compatible = "rockchip,rockchip-audio-es8388"; +	rockchip,model = "Analog audio output"; +	rockchip,i2s-controller = <&i2s>; +	rockchip,audio-codec = <&es8388>; +	rockchip,routing = "Analog", "LOUT2", +			   "Analog", "ROUT2"; +	rockchip,hp-en-gpios = <&gpio8 0 GPIO_ACTIVE_HIGH>; +	rockchip,hp-det-gpios = <&gpio7 7 GPIO_ACTIVE_HIGH>; +	pinctrl-names = "default"; +	pinctrl-0 = <&headphone>; +}; + diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt index 7b526ec64991..f4adc58f82ba 100644 --- a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt +++ b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt @@ -7,6 +7,7 @@ Required properties:  - compatible: should be one of the followings     - "allwinner,sun4i-a10-i2s" +   - "allwinner,sun6i-a31-i2s"  - reg: physical base address of the controller and length of memory mapped    region.  - interrupts: should contain the I2S interrupt. @@ -19,6 +20,10 @@ Required properties:     - "mod" : module clock for the I2S controller  - #sound-dai-cells : Must be equal to 0 +Required properties for the following compatibles: +	- "allwinner,sun6i-a31-i2s" +- resets: phandle to the reset line for this codec +  Example:  i2s0: i2s@01c22400 { diff --git a/Documentation/devicetree/bindings/sound/sun8i-a33-codec.txt b/Documentation/devicetree/bindings/sound/sun8i-a33-codec.txt new file mode 100644 index 000000000000..399b1b4bae22 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/sun8i-a33-codec.txt @@ -0,0 +1,63 @@ +Allwinner SUN8I audio codec +------------------------------------ + +On Sun8i-A33 SoCs, the audio is separated in different parts: +	  - A DAI driver. It uses the "sun4i-i2s" driver which is +	  documented here: +	  Documentation/devicetree/bindings/sound/sun4i-i2s.txt +	  - An analog part of the codec which is handled as PRCM registers. +	  See Documentation/devicetree/bindings/sound/sun8i-codec-analog.txt +	  - An digital part of the codec which is documented in this current +	  binding documentation. +	  - And finally, an audio card which links all the above components. +	  The simple-audio card will be used. +	  See Documentation/devicetree/bindings/sound/simple-card.txt + +This bindings documentation exposes Sun8i codec (digital part). + +Required properties: +- compatible: must be "allwinner,sun8i-a33-codec" +- reg: must contain the registers location and length +- interrupts: must contain the codec interrupt +- clocks: a list of phandle + clock-specifer pairs, one for each entry +  in clock-names. +- clock-names: should contain followings: +   - "bus": the parent APB clock for this controller +   - "mod": the parent module clock + +Here is an example to add a sound card and the codec binding on sun8i SoCs that +are similar to A33 using simple-card: + +	sound { +		compatible = "simple-audio-card"; +		simple-audio-card,name = "sun8i-a33-audio"; +		simple-audio-card,format = "i2s"; +		simple-audio-card,frame-master = <&link_codec>; +		simple-audio-card,bitclock-master = <&link_codec>; +		simple-audio-card,mclk-fs = <512>; +		simple-audio-card,aux-devs = <&codec_analog>; +		simple-audio-card,routing = +				"Left DAC", "Digital Left DAC", +				"Right DAC", "Digital Right DAC"; + +		simple-audio-card,cpu { +			sound-dai = <&dai>; +		}; + +		link_codec: simple-audio-card,codec { +			sound-dai = <&codec>; +		}; + +	soc@01c00000 { +		[...] + +		audio-codec@1c22e00 { +			#sound-dai-cells = <0>; +			compatible = "allwinner,sun8i-a33-codec"; +			reg = <0x01c22e00 0x400>; +			interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; +			clocks = <&ccu CLK_BUS_CODEC>, <&ccu CLK_AC_DIG>; +			clock-names = "bus", "mod"; +		}; +	}; + diff --git a/Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt b/Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt index 0230c4d20506..fe0a65e6d629 100644 --- a/Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt +++ b/Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt @@ -10,6 +10,7 @@ Required properties:    - compatible		: should be one of the following:      - "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC      - "allwinner,sun6i-a31-spdif": for the Allwinner A31 SoC +    - "allwinner,sun8i-h3-spdif": for the Allwinner H3 SoC    - reg			: Offset and length of the register set for the device. diff --git a/Documentation/devicetree/bindings/sound/zte,zx-i2s.txt b/Documentation/devicetree/bindings/sound/zte,zx-i2s.txt index 7e5aa6f6b5a1..292ad5083704 100644 --- a/Documentation/devicetree/bindings/sound/zte,zx-i2s.txt +++ b/Documentation/devicetree/bindings/sound/zte,zx-i2s.txt @@ -1,10 +1,12 @@  ZTE ZX296702 I2S controller  Required properties: - - compatible : Must be "zte,zx296702-i2s" + - compatible : Must be one of: +	"zte,zx296718-i2s", "zte,zx296702-i2s" +	"zte,zx296702-i2s"   - reg : Must contain I2S core's registers location and length   - clocks : Pairs of phandle and specifier referencing the controller's clocks. - - clock-names: "tx" for the clock to the I2S interface. + - clock-names: "wclk" for the wclk, "pclk" for the pclk to the I2S interface.   - dmas: Pairs of phandle and specifier for the DMA channel that is used by     the core. The core expects two dma channels for transmit.   - dma-names : Must be "tx" and "rx" @@ -16,12 +18,12 @@ please check:  	* dma/dma.txt  Example: -	i2s0: i2s0@0b005000 { +	i2s0: i2s@b005000 {  		#sound-dai-cells = <0>; -		compatible = "zte,zx296702-i2s"; +		compatible = "zte,zx296718-i2s", "zte,zx296702-i2s";  		reg = <0x0b005000 0x1000>; -		clocks = <&lsp0clk ZX296702_I2S0_DIV>; -		clock-names = "tx"; +		clocks = <&audiocrm AUDIO_I2S0_WCLK>, <&audiocrm AUDIO_I2S0_PCLK>; +		clock-names = "wclk", "pclk";  		interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;  		dmas = <&dma 5>, <&dma 6>;  		dma-names = "tx", "rx"; diff --git a/arch/arm/mach-s3c64xx/dev-audio.c b/arch/arm/mach-s3c64xx/dev-audio.c index b57783371d52..247dcc0b691e 100644 --- a/arch/arm/mach-s3c64xx/dev-audio.c +++ b/arch/arm/mach-s3c64xx/dev-audio.c @@ -106,9 +106,7 @@ static struct s3c_audio_pdata i2sv4_pdata = {  	.dma_playback = DMACH_HSI_I2SV40_TX,  	.dma_capture = DMACH_HSI_I2SV40_RX,  	.type = { -		.i2s = { -			.quirks = QUIRK_PRI_6CHAN, -		}, +		.quirks = QUIRK_PRI_6CHAN,  	},  }; diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 0516ecda54d3..b2a0340f277e 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -20,6 +20,8 @@  #include <linux/of.h> +#include "../../sound/soc/atmel/atmel_ssc_dai.h" +  /* Serialize access to ssc_list and user count */  static DEFINE_SPINLOCK(user_lock);  static LIST_HEAD(ssc_list); @@ -145,6 +147,49 @@ static inline const struct atmel_ssc_platform_data * __init  		platform_get_device_id(pdev)->driver_data;  } +#ifdef CONFIG_SND_ATMEL_SOC_SSC +static int ssc_sound_dai_probe(struct ssc_device *ssc) +{ +	struct device_node *np = ssc->pdev->dev.of_node; +	int ret; +	int id; + +	ssc->sound_dai = false; + +	if (!of_property_read_bool(np, "#sound-dai-cells")) +		return 0; + +	id = of_alias_get_id(np, "ssc"); +	if (id < 0) +		return id; + +	ret = atmel_ssc_set_audio(id); +	ssc->sound_dai = !ret; + +	return ret; +} + +static void ssc_sound_dai_remove(struct ssc_device *ssc) +{ +	if (!ssc->sound_dai) +		return; + +	atmel_ssc_put_audio(of_alias_get_id(ssc->pdev->dev.of_node, "ssc")); +} +#else +static inline int ssc_sound_dai_probe(struct ssc_device *ssc) +{ +	if (of_property_read_bool(ssc->pdev->dev.of_node, "#sound-dai-cells")) +		return -ENOTSUPP; + +	return 0; +} + +static inline void ssc_sound_dai_remove(struct ssc_device *ssc) +{ +} +#endif +  static int ssc_probe(struct platform_device *pdev)  {  	struct resource *regs; @@ -204,6 +249,9 @@ static int ssc_probe(struct platform_device *pdev)  	dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n",  			ssc->regs, ssc->irq); +	if (ssc_sound_dai_probe(ssc)) +		dev_err(&pdev->dev, "failed to auto-setup ssc for audio\n"); +  	return 0;  } @@ -211,6 +259,8 @@ static int ssc_remove(struct platform_device *pdev)  {  	struct ssc_device *ssc = platform_get_drvdata(pdev); +	ssc_sound_dai_remove(ssc); +  	spin_lock(&user_lock);  	list_del(&ssc->list);  	spin_unlock(&user_lock); diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 38eabf65f19d..2705a66b770b 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -248,6 +248,7 @@ struct detailed_timing {  # define DRM_ELD_AUD_SYNCH_DELAY_MAX	0xfa	/* 500 ms */  #define DRM_ELD_SPEAKER			7 +# define DRM_ELD_SPEAKER_MASK		0x7f  # define DRM_ELD_SPEAKER_RLRC		(1 << 6)  # define DRM_ELD_SPEAKER_FLRC		(1 << 5)  # define DRM_ELD_SPEAKER_RC		(1 << 4) @@ -414,6 +415,18 @@ static inline int drm_eld_size(const uint8_t *eld)  }  /** + * drm_eld_get_spk_alloc - Get speaker allocation + * @eld: pointer to an ELD memory structure + * + * The returned value is the speakers mask. User has to use %DRM_ELD_SPEAKER + * field definitions to identify speakers. + */ +static inline u8 drm_eld_get_spk_alloc(const uint8_t *eld) +{ +	return eld[DRM_ELD_SPEAKER] & DRM_ELD_SPEAKER_MASK; +} + +/**   * drm_eld_get_conn_type - Get device type hdmi/dp connected   * @eld: pointer to an ELD memory structure   * diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index 7c0f6549898b..fdb545101ede 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h @@ -20,6 +20,7 @@ struct ssc_device {  	int			user;  	int			irq;  	bool			clk_from_rk_pin; +	bool			sound_dai;  };  struct ssc_device * __must_check ssc_request(unsigned int ssc_num); diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index 15bf56ee8af7..90641a5daaf0 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -18,7 +18,7 @@  extern void s3c64xx_ac97_setup_gpio(int); -struct samsung_i2s { +struct samsung_i2s_type {  /* If the Primary DAI has 5.1 Channels */  #define QUIRK_PRI_6CHAN		(1 << 0)  /* If the I2S block has a Stereo Overlay Channel */ @@ -47,7 +47,5 @@ struct s3c_audio_pdata {  	void *dma_capture;  	void *dma_play_sec;  	void *dma_capture_mic; -	union { -		struct samsung_i2s i2s; -	} type; +	struct samsung_i2s_type type;  }; diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 1c8f9e1ef2a5..67be2445941a 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -71,6 +71,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)   * @slave_id: Slave requester id for the DMA channel.   * @filter_data: Custom DMA channel filter data, this will usually be used when   * requesting the DMA channel. + * @chan_name: Custom channel name to use when requesting DMA channel.   * @fifo_size: FIFO size of the DAI controller in bytes   * @flags: PCM_DAI flags, only SND_DMAENGINE_PCM_DAI_FLAG_PACK for now   */ @@ -80,6 +81,7 @@ struct snd_dmaengine_dai_dma_data {  	u32 maxburst;  	unsigned int slave_id;  	void *filter_data; +	const char *chan_name;  	unsigned int fifo_size;  	unsigned int flags;  }; @@ -105,6 +107,10 @@ void snd_dmaengine_pcm_set_config_from_dai_data(   * playback.   */  #define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3) +/* + * The PCM streams have custom channel names specified. + */ +#define SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME BIT(4)  /**   * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 64e90ca9ad32..af58d2362975 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -34,11 +34,12 @@ int asoc_simple_card_set_dailink_name(struct device *dev,  int asoc_simple_card_parse_card_name(struct snd_soc_card *card,  				     char *prefix); -#define asoc_simple_card_parse_clk_cpu(node, dai_link, simple_dai)		\ -	asoc_simple_card_parse_clk(node, dai_link->cpu_of_node, simple_dai) -#define asoc_simple_card_parse_clk_codec(node, dai_link, simple_dai)		\ -	asoc_simple_card_parse_clk(node, dai_link->codec_of_node, simple_dai) -int asoc_simple_card_parse_clk(struct device_node *node, +#define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai)		\ +	asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai) +#define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai)	\ +	asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai) +int asoc_simple_card_parse_clk(struct device *dev, +			       struct device_node *node,  			       struct device_node *dai_of_node,  			       struct asoc_simple_dai *simple_dai); diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 200e1f04c166..58acd00cae19 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -256,6 +256,9 @@ struct snd_soc_dai_driver {  	int (*resume)(struct snd_soc_dai *dai);  	/* compress dai */  	int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num); +	/* Optional Callback used at pcm creation*/ +	int (*pcm_new)(struct snd_soc_pcm_runtime *rtd, +		       struct snd_soc_dai *dai);  	/* DAI is also used for the control bus */  	bool bus_control; diff --git a/include/sound/soc.h b/include/sound/soc.h index b86168a21d56..cdfb55f7aede 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -497,6 +497,8 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream);  int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,  	unsigned int dai_fmt); +int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour); +  /* Utility functions to get clock rates from various things */  int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);  int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); @@ -507,9 +509,6 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms);  int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,  	const struct snd_pcm_hardware *hw); -int snd_soc_platform_trigger(struct snd_pcm_substream *substream, -		int cmd, struct snd_soc_platform *platform); -  int soc_dai_hw_params(struct snd_pcm_substream *substream,  		      struct snd_pcm_hw_params *params,  		      struct snd_soc_dai *dai); @@ -785,6 +784,10 @@ struct snd_soc_component_driver {  	int (*suspend)(struct snd_soc_component *);  	int (*resume)(struct snd_soc_component *); +	/* pcm creation and destruction */ +	int (*pcm_new)(struct snd_soc_pcm_runtime *); +	void (*pcm_free)(struct snd_pcm *); +  	/* DT */  	int (*of_xlate_dai_name)(struct snd_soc_component *component,  				 struct of_phandle_args *args, @@ -859,6 +862,8 @@ struct snd_soc_component {  	void (*remove)(struct snd_soc_component *);  	int (*suspend)(struct snd_soc_component *);  	int (*resume)(struct snd_soc_component *); +	int (*pcm_new)(struct snd_soc_pcm_runtime *); +	void (*pcm_free)(struct snd_pcm *);  	/* machine specific init */  	int (*init)(struct snd_soc_component *component); @@ -941,20 +946,11 @@ struct snd_soc_platform_driver {  	int (*pcm_new)(struct snd_soc_pcm_runtime *);  	void (*pcm_free)(struct snd_pcm *); -	/* -	 * For platform caused delay reporting. -	 * Optional. -	 */ -	snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *, -		struct snd_soc_dai *); -  	/* platform stream pcm ops */  	const struct snd_pcm_ops *ops;  	/* platform stream compress ops */  	const struct snd_compr_ops *compr_ops; - -	int (*bespoke_trigger)(struct snd_pcm_substream *, int);  };  struct snd_soc_dai_link_component { @@ -1099,6 +1095,8 @@ struct snd_soc_card {  	const char *name;  	const char *long_name;  	const char *driver_name; +	char dmi_longname[80]; +  	struct device *dev;  	struct snd_card *snd_card;  	struct module *owner; @@ -1647,37 +1645,21 @@ static inline struct snd_soc_platform *snd_soc_kcontrol_platform(  int snd_soc_util_init(void);  void snd_soc_util_exit(void); -#define snd_soc_of_parse_card_name(card, propname) \ -	snd_soc_of_parse_card_name_from_node(card, NULL, propname) -int snd_soc_of_parse_card_name_from_node(struct snd_soc_card *card, -					 struct device_node *np, -					 const char *propname); -#define snd_soc_of_parse_audio_simple_widgets(card, propname)\ -	snd_soc_of_parse_audio_simple_widgets_from_node(card, NULL, propname) -int snd_soc_of_parse_audio_simple_widgets_from_node(struct snd_soc_card *card, -						    struct device_node *np, -						    const char *propname); - +int snd_soc_of_parse_card_name(struct snd_soc_card *card, +			       const char *propname); +int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, +					  const char *propname);  int snd_soc_of_parse_tdm_slot(struct device_node *np,  			      unsigned int *tx_mask,  			      unsigned int *rx_mask,  			      unsigned int *slots,  			      unsigned int *slot_width); -#define snd_soc_of_parse_audio_prefix(card, codec_conf, of_node, propname) \ -	snd_soc_of_parse_audio_prefix_from_node(card, NULL, codec_conf, \ -						of_node, propname) -void snd_soc_of_parse_audio_prefix_from_node(struct snd_soc_card *card, -				   struct device_node *np, +void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card,  				   struct snd_soc_codec_conf *codec_conf,  				   struct device_node *of_node,  				   const char *propname); - -#define snd_soc_of_parse_audio_routing(card, propname) \ -	snd_soc_of_parse_audio_routing_from_node(card, NULL, propname) -int snd_soc_of_parse_audio_routing_from_node(struct snd_soc_card *card, -					     struct device_node *np, -					     const char *propname); - +int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, +				   const char *propname);  unsigned int snd_soc_of_parse_daifmt(struct device_node *np,  				     const char *prefix,  				     struct device_node **bitclkmaster, diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 3be051ab5533..c96d7a7a36af 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -128,14 +128,17 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,  {  	struct hdac_stream *hstream = &stream->hstream;  	struct hdac_bus *bus = &ebus->bus; +	u32 val; +	int mask = AZX_PPCTL_PROCEN(hstream->index);  	spin_lock_irq(&bus->reg_lock); -	if (decouple) -		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, -				AZX_PPCTL_PROCEN(hstream->index)); -	else -		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, -					AZX_PPCTL_PROCEN(hstream->index), 0); +	val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask; + +	if (decouple && !val) +		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask); +	else if (!decouple && val) +		snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0); +  	stream->decoupled = decouple;  	spin_unlock_irq(&bus->reg_lock);  } diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 504c7cd7f58a..818b052377f3 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -670,13 +670,10 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,  {  	int status;  	uint64_t size; -	struct snd_dma_buffer *dma_buffer;  	struct page *pg;  	struct snd_pcm_runtime *runtime;  	struct audio_substream_data *rtd; -	dma_buffer = &substream->dma_buffer; -  	runtime = substream->runtime;  	rtd = runtime->private_data; diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c index ac6a814c8ecf..a72c7d642026 100644 --- a/sound/soc/atmel/tse850-pcm5142.c +++ b/sound/soc/atmel/tse850-pcm5142.c @@ -51,11 +51,7 @@  #include <sound/soc.h>  #include <sound/pcm_params.h> -#include "atmel_ssc_dai.h" -  struct tse850_priv { -	int ssc_id; -  	struct gpio_desc *add;  	struct gpio_desc *loop1;  	struct gpio_desc *loop2; @@ -329,23 +325,20 @@ static int tse850_dt_init(struct platform_device *pdev)  {  	struct device_node *np = pdev->dev.of_node;  	struct device_node *codec_np, *cpu_np; -	struct snd_soc_card *card = &tse850_card;  	struct snd_soc_dai_link *dailink = &tse850_dailink; -	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);  	if (!np) {  		dev_err(&pdev->dev, "only device tree supported\n");  		return -EINVAL;  	} -	cpu_np = of_parse_phandle(np, "axentia,ssc-controller", 0); +	cpu_np = of_parse_phandle(np, "axentia,cpu-dai", 0);  	if (!cpu_np) { -		dev_err(&pdev->dev, "failed to get dai and pcm info\n"); +		dev_err(&pdev->dev, "failed to get cpu dai\n");  		return -EINVAL;  	}  	dailink->cpu_of_node = cpu_np;  	dailink->platform_of_node = cpu_np; -	tse850->ssc_id = of_alias_get_id(cpu_np, "ssc");  	of_node_put(cpu_np);  	codec_np = of_parse_phandle(np, "axentia,audio-codec", 0); @@ -415,23 +408,14 @@ static int tse850_probe(struct platform_device *pdev)  		return ret;  	} -	ret = atmel_ssc_set_audio(tse850->ssc_id); -	if (ret != 0) { -		dev_err(dev, -			"failed to set SSC %d for audio\n", tse850->ssc_id); -		goto err_disable_ana; -	} -  	ret = snd_soc_register_card(card);  	if (ret) {  		dev_err(dev, "snd_soc_register_card failed\n"); -		goto err_put_audio; +		goto err_disable_ana;  	}  	return 0; -err_put_audio: -	atmel_ssc_put_audio(tse850->ssc_id);  err_disable_ana:  	regulator_disable(tse850->ana);  	return ret; @@ -443,7 +427,6 @@ static int tse850_remove(struct platform_device *pdev)  	struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);  	snd_soc_unregister_card(card); -	atmel_ssc_put_audio(tse850->ssc_id);  	regulator_disable(tse850->ana);  	return 0; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9e1718a8cb1c..e49e9da7f1f6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -45,7 +45,7 @@ config SND_SOC_ALL_CODECS  	select SND_SOC_ALC5623 if I2C  	select SND_SOC_ALC5632 if I2C  	select SND_SOC_BT_SCO -	select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC +	select SND_SOC_CQ0093VC  	select SND_SOC_CS35L32 if I2C  	select SND_SOC_CS35L33 if I2C  	select SND_SOC_CS35L34 if I2C @@ -95,6 +95,7 @@ config SND_SOC_ALL_CODECS  	select SND_SOC_MAX9877 if I2C  	select SND_SOC_MC13783 if MFD_MC13XXX  	select SND_SOC_ML26124 if I2C +	select SND_SOC_NAU8540 if I2C  	select SND_SOC_NAU8810 if I2C  	select SND_SOC_NAU8825 if I2C  	select SND_SOC_HDMI_CODEC @@ -117,8 +118,8 @@ config SND_SOC_ALL_CODECS  	select SND_SOC_RT5651 if I2C  	select SND_SOC_RT5659 if I2C  	select SND_SOC_RT5660 if I2C -	select SND_SOC_RT5665 if I2C  	select SND_SOC_RT5663 if I2C +	select SND_SOC_RT5665 if I2C  	select SND_SOC_RT5670 if I2C  	select SND_SOC_RT5677 if I2C && SPI_MASTER  	select SND_SOC_SGTL5000 if I2C @@ -525,14 +526,16 @@ config SND_SOC_HDMI_CODEC  	select HDMI  config SND_SOC_ES8328 -	tristate "Everest Semi ES8328 CODEC" +	tristate  config SND_SOC_ES8328_I2C -	tristate +	tristate "Everest Semi ES8328 CODEC (I2C)" +	depends on I2C  	select SND_SOC_ES8328  config SND_SOC_ES8328_SPI -	tristate +	tristate "Everest Semi ES8328 CODEC (SPI)" +	depends on SPI_MASTER  	select SND_SOC_ES8328  config SND_SOC_GTM601 @@ -668,8 +671,8 @@ config SND_SOC_RL6231  	default y if SND_SOC_RT5651=y  	default y if SND_SOC_RT5659=y  	default y if SND_SOC_RT5660=y -	default y if SND_SOC_RT5665=y  	default y if SND_SOC_RT5663=y +	default y if SND_SOC_RT5665=y  	default y if SND_SOC_RT5670=y  	default y if SND_SOC_RT5677=y  	default m if SND_SOC_RT5514=m @@ -679,8 +682,8 @@ config SND_SOC_RL6231  	default m if SND_SOC_RT5651=m  	default m if SND_SOC_RT5659=m  	default m if SND_SOC_RT5660=m -	default m if SND_SOC_RT5665=m  	default m if SND_SOC_RT5663=m +	default m if SND_SOC_RT5665=m  	default m if SND_SOC_RT5670=m  	default m if SND_SOC_RT5677=m @@ -728,10 +731,10 @@ config SND_SOC_RT5659  config SND_SOC_RT5660  	tristate -config SND_SOC_RT5665 +config SND_SOC_RT5663  	tristate -config SND_SOC_RT5663 +config SND_SOC_RT5665  	tristate  config SND_SOC_RT5670 @@ -1105,6 +1108,10 @@ config SND_SOC_MC13783  config SND_SOC_ML26124  	tristate +config SND_SOC_NAU8540 +       tristate "Nuvoton Technology Corporation NAU85L40 CODEC" +       depends on I2C +  config SND_SOC_NAU8810  	tristate "Nuvoton Technology Corporation NAU88C10 CODEC"  	depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7e1dad79610b..1796cb987e71 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -90,6 +90,7 @@ snd-soc-mc13783-objs := mc13783.o  snd-soc-ml26124-objs := ml26124.o  snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o  snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o +snd-soc-nau8540-objs := nau8540.o  snd-soc-nau8810-objs := nau8810.o  snd-soc-nau8825-objs := nau8825.o  snd-soc-hdmi-codec-objs := hdmi-codec.o @@ -118,8 +119,8 @@ snd-soc-rt5645-objs := rt5645.o  snd-soc-rt5651-objs := rt5651.o  snd-soc-rt5659-objs := rt5659.o  snd-soc-rt5660-objs := rt5660.o -snd-soc-rt5665-objs := rt5665.o  snd-soc-rt5663-objs := rt5663.o +snd-soc-rt5665-objs := rt5665.o  snd-soc-rt5670-objs := rt5670.o  snd-soc-rt5677-objs := rt5677.o  snd-soc-rt5677-spi-objs := rt5677-spi.o @@ -318,6 +319,7 @@ obj-$(CONFIG_SND_SOC_MC13783)	+= snd-soc-mc13783.o  obj-$(CONFIG_SND_SOC_ML26124)	+= snd-soc-ml26124.o  obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o  obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o +obj-$(CONFIG_SND_SOC_NAU8540)   += snd-soc-nau8540.o  obj-$(CONFIG_SND_SOC_NAU8810)   += snd-soc-nau8810.o  obj-$(CONFIG_SND_SOC_NAU8825)   += snd-soc-nau8825.o  obj-$(CONFIG_SND_SOC_HDMI_CODEC)	+= snd-soc-hdmi-codec.o @@ -346,8 +348,8 @@ obj-$(CONFIG_SND_SOC_RT5645)	+= snd-soc-rt5645.o  obj-$(CONFIG_SND_SOC_RT5651)	+= snd-soc-rt5651.o  obj-$(CONFIG_SND_SOC_RT5659)	+= snd-soc-rt5659.o  obj-$(CONFIG_SND_SOC_RT5660)	+= snd-soc-rt5660.o -obj-$(CONFIG_SND_SOC_RT5665)	+= snd-soc-rt5665.o  obj-$(CONFIG_SND_SOC_RT5663)	+= snd-soc-rt5663.o +obj-$(CONFIG_SND_SOC_RT5665)	+= snd-soc-rt5665.o  obj-$(CONFIG_SND_SOC_RT5670)	+= snd-soc-rt5670.o  obj-$(CONFIG_SND_SOC_RT5677)	+= snd-soc-rt5677.o  obj-$(CONFIG_SND_SOC_RT5677_SPI)	+= snd-soc-rt5677-spi.o diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index b36511d965c8..2c1bd2763864 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -65,7 +65,6 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,  {  	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);  	struct adau *adau = snd_soc_codec_get_drvdata(codec); -	int ret;  	if (SND_SOC_DAPM_EVENT_ON(event)) {  		adau->pll_regs[5] = 1; @@ -78,7 +77,7 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,  	}  	/* The PLL register is 6 bytes long and can only be written at once. */ -	ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL, +	regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,  			adau->pll_regs, ARRAY_SIZE(adau->pll_regs));  	if (SND_SOC_DAPM_EVENT_ON(event)) { diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 2609f95b7d19..23ab9646c351 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -189,7 +189,7 @@ static int ak4642_lout_event(struct snd_soc_dapm_widget *w,  	case SND_SOC_DAPM_POST_PMU:  	case SND_SOC_DAPM_POST_PMD:  		/* Power save mode OFF */ -		mdelay(300); +		msleep(300);  		snd_soc_update_bits(codec, SG_SL2, LOPS, 0);  		break;  	} diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 56707860657c..1822e3b3de80 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -192,6 +192,7 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];  #define ARIZONA_DSP_ROUTES(name) \  	{ name, NULL, name " Preloader"}, \  	{ name " Preloader", NULL, "SYSCLK" }, \ +	{ name " Preload", NULL, name " Preloader"}, \  	{ name, NULL, name " Aux 1" }, \  	{ name, NULL, name " Aux 2" }, \  	{ name, NULL, name " Aux 3" }, \ diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 73559ae864b6..47e6fddef92b 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -173,6 +173,9 @@ SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),  SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),  SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), +WM_ADSP2_PRELOAD_SWITCH("DSP2", 2), +WM_ADSP2_PRELOAD_SWITCH("DSP3", 3), +  ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE), @@ -1121,7 +1124,10 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)  	priv->core.arizona->dapm = dapm; -	arizona_init_spk(codec); +	ret = arizona_init_spk(codec); +	if (ret < 0) +		return ret; +  	arizona_init_gpio(codec);  	arizona_init_mono(codec);  	arizona_init_notifiers(codec); diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index c69e97654fc6..d256ebf9e309 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -1634,7 +1634,8 @@ static const struct snd_soc_dapm_widget da7218_dapm_widgets[] = {  			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),  	/* DAI */ -	SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0), +	SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7218_DAI_TDM_CTRL, +			     DA7218_DAI_OE_SHIFT, DA7218_NO_INVERT),  	SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),  	/* Output Mixers */ diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c index 2d05b5d3a6ce..318ab28c5351 100644 --- a/sound/soc/codecs/es8328-i2c.c +++ b/sound/soc/codecs/es8328-i2c.c @@ -20,12 +20,14 @@  static const struct i2c_device_id es8328_id[] = {  	{ "es8328", 0 }, +	{ "es8388", 0 },  	{ }  };  MODULE_DEVICE_TABLE(i2c, es8328_id);  static const struct of_device_id es8328_of_match[] = {  	{ .compatible = "everest,es8328", }, +	{ .compatible = "everest,es8388", },  	{ }  };  MODULE_DEVICE_TABLE(of, es8328_of_match); diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 37722194b107..3f84fbd071e2 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -589,9 +589,21 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,  	u8 dac_mode = 0;  	u8 adc_mode = 0; -	/* set master/slave audio interface */ -	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM) +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBM_CFM: +		/* Master serial port mode, with BCLK generated automatically */ +		snd_soc_update_bits(codec, ES8328_MASTERMODE, +				    ES8328_MASTERMODE_MSC, +				    ES8328_MASTERMODE_MSC); +		break; +	case SND_SOC_DAIFMT_CBS_CFS: +		/* Slave serial port mode */ +		snd_soc_update_bits(codec, ES8328_MASTERMODE, +				    ES8328_MASTERMODE_MSC, 0); +		break; +	default:  		return -EINVAL; +	}  	/* interface format */  	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -620,10 +632,6 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,  	snd_soc_update_bits(codec, ES8328_ADCCONTROL4,  			ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode); -	/* Master serial port mode, with BCLK generated automatically */ -	snd_soc_update_bits(codec, ES8328_MASTERMODE, -			ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC); -  	return 0;  } diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 0c6228a0bf95..78fca8acd3ec 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -42,10 +42,15 @@  #define HDA_MAX_CONNECTIONS     32  #define HDA_MAX_CVTS		3 +#define HDA_MAX_PORTS		3  #define ELD_MAX_SIZE    256  #define ELD_FIXED_BYTES	20 +#define ELD_VER_CEA_861D 2 +#define ELD_VER_PARTIAL 31 +#define ELD_MAX_MNL     16 +  struct hdac_hdmi_cvt_params {  	unsigned int channels_min;  	unsigned int channels_max; @@ -77,43 +82,180 @@ struct hdac_hdmi_eld {  struct hdac_hdmi_pin {  	struct list_head head;  	hda_nid_t nid; +	bool mst_capable; +	struct hdac_hdmi_port *ports; +	int num_ports; +	struct hdac_ext_device *edev; +}; + +struct hdac_hdmi_port { +	struct list_head head; +	int id; +	struct hdac_hdmi_pin *pin;  	int num_mux_nids;  	hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];  	struct hdac_hdmi_eld eld; -	struct hdac_ext_device *edev; -	int repoll_count; -	struct delayed_work work; -	struct mutex lock; -	bool chmap_set; -	unsigned char chmap[8]; /* ALSA API channel-map */ -	int channels; /* current number of channels */ +	const char *jack_pin; +	struct snd_soc_dapm_context *dapm; +	const char *output_pin;  };  struct hdac_hdmi_pcm {  	struct list_head head;  	int pcm_id; -	struct hdac_hdmi_pin *pin; +	struct list_head port_list;  	struct hdac_hdmi_cvt *cvt; -	struct snd_jack *jack; +	struct snd_soc_jack *jack; +	int stream_tag; +	int channels; +	int format; +	bool chmap_set; +	unsigned char chmap[8]; /* ALSA API channel-map */ +	struct mutex lock; +	int jack_event;  }; -struct hdac_hdmi_dai_pin_map { +struct hdac_hdmi_dai_port_map {  	int dai_id; -	struct hdac_hdmi_pin *pin; +	struct hdac_hdmi_port *port;  	struct hdac_hdmi_cvt *cvt;  };  struct hdac_hdmi_priv { -	struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS]; +	struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS];  	struct list_head pin_list;  	struct list_head cvt_list;  	struct list_head pcm_list;  	int num_pin;  	int num_cvt; +	int num_ports;  	struct mutex pin_mutex;  	struct hdac_chmap chmap;  }; +static struct hdac_hdmi_pcm * +hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi, +			   struct hdac_hdmi_cvt *cvt) +{ +	struct hdac_hdmi_pcm *pcm = NULL; + +	list_for_each_entry(pcm, &hdmi->pcm_list, head) { +		if (pcm->cvt == cvt) +			break; +	} + +	return pcm; +} + +static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, +		struct hdac_hdmi_port *port, bool is_connect) +{ +	struct hdac_ext_device *edev = port->pin->edev; + +	if (is_connect) +		snd_soc_dapm_enable_pin(port->dapm, port->jack_pin); +	else +		snd_soc_dapm_disable_pin(port->dapm, port->jack_pin); + +	if (is_connect) { +		/* +		 * Report Jack connect event when a device is connected +		 * for the first time where same PCM is attached to multiple +		 * ports. +		 */ +		if (pcm->jack_event == 0) { +			dev_dbg(&edev->hdac.dev, +					"jack report for pcm=%d\n", +					pcm->pcm_id); +			snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT, +						SND_JACK_AVOUT); +		} +		pcm->jack_event++; +	} else { +		/* +		 * Report Jack disconnect event when a device is disconnected +		 * is the only last connected device when same PCM is attached +		 * to multiple ports. +		 */ +		if (pcm->jack_event == 1) +			snd_soc_jack_report(pcm->jack, 0, SND_JACK_AVOUT); +		if (pcm->jack_event > 0) +			pcm->jack_event--; +	} + +	snd_soc_dapm_sync(port->dapm); +} + +/* MST supported verbs */ +/* + * Get the no devices that can be connected to a port on the Pin widget. + */ +static int hdac_hdmi_get_port_len(struct hdac_ext_device *hdac, hda_nid_t nid) +{ +	unsigned int caps; +	unsigned int type, param; + +	caps = get_wcaps(&hdac->hdac, nid); +	type = get_wcaps_type(caps); + +	if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN)) +		return 0; + +	param = snd_hdac_read_parm_uncached(&hdac->hdac, nid, +					AC_PAR_DEVLIST_LEN); +	if (param == -1) +		return param; + +	return param & AC_DEV_LIST_LEN_MASK; +} + +/* + * Get the port entry select on the pin. Return the port entry + * id selected on the pin. Return 0 means the first port entry + * is selected or MST is not supported. + */ +static int hdac_hdmi_port_select_get(struct hdac_ext_device *hdac, +					struct hdac_hdmi_port *port) +{ +	return snd_hdac_codec_read(&hdac->hdac, port->pin->nid, +				0, AC_VERB_GET_DEVICE_SEL, 0); +} + +/* + * Sets the selected port entry for the configuring Pin widget verb. + * returns error if port set is not equal to port get otherwise success + */ +static int hdac_hdmi_port_select_set(struct hdac_ext_device *hdac, +					struct hdac_hdmi_port *port) +{ +	int num_ports; + +	if (!port->pin->mst_capable) +		return 0; + +	/* AC_PAR_DEVLIST_LEN is 0 based. */ +	num_ports = hdac_hdmi_get_port_len(hdac, port->pin->nid); + +	if (num_ports < 0) +		return -EIO; +	/* +	 * Device List Length is a 0 based integer value indicating the +	 * number of sink device that a MST Pin Widget can support. +	 */ +	if (num_ports + 1  < port->id) +		return 0; + +	snd_hdac_codec_write(&hdac->hdac, port->pin->nid, 0, +			AC_VERB_SET_DEVICE_SEL, port->id); + +	if (port->id != hdac_hdmi_port_select_get(hdac, port)) +		return -EIO; + +	dev_dbg(&hdac->hdac.dev, "Selected the port=%d\n", port->id); + +	return 0; +} +  static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,  						int pcm_idx)  { @@ -173,99 +315,6 @@ format_constraint:  } - /* HDMI ELD routines */ -static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec, -				hda_nid_t nid, int byte_index) -{ -	unsigned int val; - -	val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD, -							byte_index); - -	dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n", -					byte_index, val); - -	return val; -} - -static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid) -{ -	return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, -						 AC_DIPSIZE_ELD_BUF); -} - -/* - * This function queries the ELD size and ELD data and fills in the buffer - * passed by user - */ -static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid, -			     unsigned char *buf, int *eld_size) -{ -	int i, size, ret = 0; - -	/* -	 * ELD size is initialized to zero in caller function. If no errors and -	 * ELD is valid, actual eld_size is assigned. -	 */ - -	size = hdac_hdmi_get_eld_size(codec, nid); -	if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) { -		dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size); -		return -ERANGE; -	} - -	/* set ELD buffer */ -	for (i = 0; i < size; i++) { -		unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i); -		/* -		 * Graphics driver might be writing to ELD buffer right now. -		 * Just abort. The caller will repoll after a while. -		 */ -		if (!(val & AC_ELDD_ELD_VALID)) { -			dev_err(&codec->dev, -				"HDMI: invalid ELD data byte %d\n", i); -			ret = -EINVAL; -			goto error; -		} -		val &= AC_ELDD_ELD_DATA; -		/* -		 * The first byte cannot be zero. This can happen on some DVI -		 * connections. Some Intel chips may also need some 250ms delay -		 * to return non-zero ELD data, even when the graphics driver -		 * correctly writes ELD content before setting ELD_valid bit. -		 */ -		if (!val && !i) { -			dev_err(&codec->dev, "HDMI: 0 ELD data\n"); -			ret = -EINVAL; -			goto error; -		} -		buf[i] = val; -	} - -	*eld_size = size; -error: -	return ret; -} - -static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac, -				hda_nid_t cvt_nid, hda_nid_t pin_nid, -				u32 stream_tag, int format) -{ -	unsigned int val; - -	dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n", -			cvt_nid, pin_nid, stream_tag, format); - -	val = (stream_tag << 4); - -	snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, -				AC_VERB_SET_CHANNEL_STREAMID, val); -	snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, -				AC_VERB_SET_STREAM_FORMAT, format); - -	return 0; -} -  static void  hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,  				int packet_index, int byte_index) @@ -291,13 +340,14 @@ struct dp_audio_infoframe {  };  static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, -				hda_nid_t cvt_nid, hda_nid_t pin_nid) +		   struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port)  {  	uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];  	struct hdmi_audio_infoframe frame; +	struct hdac_hdmi_pin *pin = port->pin;  	struct dp_audio_infoframe dp_ai;  	struct hdac_hdmi_priv *hdmi = hdac->private_data; -	struct hdac_hdmi_pin *pin; +	struct hdac_hdmi_cvt *cvt = pcm->cvt;  	u8 *dip;  	int ret;  	int i; @@ -305,21 +355,16 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,  	u8 conn_type;  	int channels, ca; -	list_for_each_entry(pin, &hdmi->pin_list, head) { -		if (pin->nid == pin_nid) -			break; -	} - -	ca = snd_hdac_channel_allocation(&hdac->hdac, pin->eld.info.spk_alloc, -			pin->channels, pin->chmap_set, true, pin->chmap); +	ca = snd_hdac_channel_allocation(&hdac->hdac, port->eld.info.spk_alloc, +			pcm->channels, pcm->chmap_set, true, pcm->chmap);  	channels = snd_hdac_get_active_channels(ca); -	hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt_nid, channels); +	hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt->nid, channels);  	snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, -				pin->channels, pin->chmap, pin->chmap_set); +				pcm->channels, pcm->chmap, pcm->chmap_set); -	eld_buf = pin->eld.eld_buffer; +	eld_buf = port->eld.eld_buffer;  	conn_type = drm_eld_get_conn_type(eld_buf);  	switch (conn_type) { @@ -353,75 +398,50 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,  	}  	/* stop infoframe transmission */ -	hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); -	snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, +	hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0); +	snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,  			AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE);  	/*  Fill infoframe. Index auto-incremented */ -	hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); +	hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0);  	if (conn_type == DRM_ELD_CONN_TYPE_HDMI) {  		for (i = 0; i < sizeof(buffer); i++) -			snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, +			snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,  				AC_VERB_SET_HDMI_DIP_DATA, buffer[i]);  	} else {  		for (i = 0; i < sizeof(dp_ai); i++) -			snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, +			snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,  				AC_VERB_SET_HDMI_DIP_DATA, dip[i]);  	}  	/* Start infoframe */ -	hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); -	snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, +	hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0); +	snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,  			AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST);  	return 0;  } -static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev, -		struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state) +static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai, +		unsigned int tx_mask, unsigned int rx_mask, +		int slots, int slot_width)  { -	/* Power up pin widget */ -	if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin->nid, -						pwr_state)) -		snd_hdac_codec_write(&edev->hdac, dai_map->pin->nid, 0, -			AC_VERB_SET_POWER_STATE, pwr_state); - -	/* Power up converter */ -	if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt->nid, -						pwr_state)) -		snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0, -			AC_VERB_SET_POWER_STATE, pwr_state); -} +	struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); +	struct hdac_hdmi_priv *hdmi = edev->private_data; +	struct hdac_hdmi_dai_port_map *dai_map; +	struct hdac_hdmi_pcm *pcm; -static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream, -				struct snd_soc_dai *dai) -{ -	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); -	struct hdac_hdmi_priv *hdmi = hdac->private_data; -	struct hdac_hdmi_dai_pin_map *dai_map; -	struct hdac_hdmi_pin *pin; -	struct hdac_ext_dma_params *dd; -	int ret; +	dev_dbg(&edev->hdac.dev, "%s: strm_tag: %d\n", __func__, tx_mask);  	dai_map = &hdmi->dai_map[dai->id]; -	pin = dai_map->pin; - -	dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); -	dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n", -			dd->stream_tag,	dd->format); -	mutex_lock(&pin->lock); -	pin->channels = substream->runtime->channels; +	pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); -	ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid, -						dai_map->pin->nid); -	mutex_unlock(&pin->lock); -	if (ret < 0) -		return ret; +	if (pcm) +		pcm->stream_tag = (tx_mask << 4); -	return hdac_hdmi_setup_stream(hdac, dai_map->cvt->nid, -			dai_map->pin->nid, dd->stream_tag, dd->format); +	return 0;  }  static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, @@ -429,101 +449,41 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,  {  	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);  	struct hdac_hdmi_priv *hdmi = hdac->private_data; -	struct hdac_hdmi_dai_pin_map *dai_map; -	struct hdac_hdmi_pin *pin; -	struct hdac_ext_dma_params *dd; +	struct hdac_hdmi_dai_port_map *dai_map; +	struct hdac_hdmi_port *port; +	struct hdac_hdmi_pcm *pcm; +	int format;  	dai_map = &hdmi->dai_map[dai->id]; -	pin = dai_map->pin; +	port = dai_map->port; -	if (!pin) +	if (!port)  		return -ENODEV; -	if ((!pin->eld.monitor_present) || (!pin->eld.eld_valid)) { -		dev_err(&hdac->hdac.dev, "device is not configured for this pin: %d\n", -								pin->nid); +	if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) { +		dev_err(&hdac->hdac.dev, +			"device is not configured for this pin:port%d:%d\n", +					port->pin->nid, port->id);  		return -ENODEV;  	} -	dd = snd_soc_dai_get_dma_data(dai, substream); -	if (!dd) { -		dd = kzalloc(sizeof(*dd), GFP_KERNEL); -		if (!dd) -			return -ENOMEM; -	} - -	dd->format = snd_hdac_calc_stream_format(params_rate(hparams), +	format = snd_hdac_calc_stream_format(params_rate(hparams),  			params_channels(hparams), params_format(hparams),  			24, 0); -	snd_soc_dai_set_dma_data(dai, substream, (void *)dd); - -	return 0; -} - -static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream, -		struct snd_soc_dai *dai) -{ -	struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai); -	struct hdac_ext_dma_params *dd; -	struct hdac_hdmi_priv *hdmi = edev->private_data; -	struct hdac_hdmi_dai_pin_map *dai_map; - -	dai_map = &hdmi->dai_map[dai->id]; - -	dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); - -	if (dd) { -		snd_soc_dai_set_dma_data(dai, substream, NULL); -		kfree(dd); -	} - -	return 0; -} - -static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev, -		struct hdac_hdmi_dai_pin_map *dai_map) -{ -	/* Enable transmission */ -	snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0, -			AC_VERB_SET_DIGI_CONVERT_1, 1); - -	/* Category Code (CC) to zero */ -	snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0, -			AC_VERB_SET_DIGI_CONVERT_2, 0); -} - -static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac, -		struct hdac_hdmi_dai_pin_map *dai_map) -{ -	int mux_idx; -	struct hdac_hdmi_pin *pin = dai_map->pin; - -	for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) { -		if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) { -			snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, -					AC_VERB_SET_CONNECT_SEL, mux_idx); -			break; -		} -	} - -	if (mux_idx == pin->num_mux_nids) +	pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); +	if (!pcm)  		return -EIO; -	/* Enable out path for this pin widget */ -	snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, -			AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - -	hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0); - -	snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, -			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); +	pcm->format = format; +	pcm->channels = params_channels(hparams);  	return 0;  } -static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac, -					struct hdac_hdmi_pin *pin) +static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *hdac, +					struct hdac_hdmi_pin *pin, +					struct hdac_hdmi_port *port)  {  	if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {  		dev_warn(&hdac->hdac.dev, @@ -532,51 +492,60 @@ static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,  		return -EINVAL;  	} -	pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid, -			pin->mux_nids, HDA_MAX_CONNECTIONS); -	if (pin->num_mux_nids == 0) -		dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n", -								pin->nid); +	if (hdac_hdmi_port_select_set(hdac, port) < 0) +		return -EIO; -	dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n", -			pin->num_mux_nids, pin->nid); +	port->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid, +			port->mux_nids, HDA_MAX_CONNECTIONS); +	if (port->num_mux_nids == 0) +		dev_warn(&hdac->hdac.dev, +			"No connections found for pin:port %d:%d\n", +						pin->nid, port->id); + +	dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin:port %d:%d\n", +			port->num_mux_nids, pin->nid, port->id); -	return pin->num_mux_nids; +	return port->num_mux_nids;  }  /* - * Query pcm list and return pin widget to which stream is routed. + * Query pcm list and return port to which stream is routed.   * - * Also query connection list of the pin, to validate the cvt to pin map. + * Also query connection list of the pin, to validate the cvt to port map.   * - * Same stream rendering to multiple pins simultaneously can be done - * possibly, but not supported for now in driver. So return the first pin + * Same stream rendering to multiple ports simultaneously can be done + * possibly, but not supported for now in driver. So return the first port   * connected.   */ -static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt( +static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(  			struct hdac_ext_device *edev,  			struct hdac_hdmi_priv *hdmi,  			struct hdac_hdmi_cvt *cvt)  {  	struct hdac_hdmi_pcm *pcm; -	struct hdac_hdmi_pin *pin = NULL; +	struct hdac_hdmi_port *port = NULL;  	int ret, i;  	list_for_each_entry(pcm, &hdmi->pcm_list, head) {  		if (pcm->cvt == cvt) { -			pin = pcm->pin; -			break; -		} -	} - -	if (pin) { -		ret = hdac_hdmi_query_pin_connlist(edev, pin); -		if (ret < 0) -			return NULL; - -		for (i = 0; i < pin->num_mux_nids; i++) { -			if (pin->mux_nids[i] == cvt->nid) -				return pin; +			if (list_empty(&pcm->port_list)) +				continue; + +			list_for_each_entry(port, &pcm->port_list, head) { +				mutex_lock(&pcm->lock); +				ret = hdac_hdmi_query_port_connlist(edev, +							port->pin, port); +				mutex_unlock(&pcm->lock); +				if (ret < 0) +					continue; + +				for (i = 0; i < port->num_mux_nids; i++) { +					if (port->mux_nids[i] == cvt->nid && +						port->eld.monitor_present && +						port->eld.eld_valid) +						return port; +				} +			}  		}  	} @@ -593,67 +562,42 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,  {  	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);  	struct hdac_hdmi_priv *hdmi = hdac->private_data; -	struct hdac_hdmi_dai_pin_map *dai_map; +	struct hdac_hdmi_dai_port_map *dai_map;  	struct hdac_hdmi_cvt *cvt; -	struct hdac_hdmi_pin *pin; +	struct hdac_hdmi_port *port;  	int ret;  	dai_map = &hdmi->dai_map[dai->id];  	cvt = dai_map->cvt; -	pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt); +	port = hdac_hdmi_get_port_from_cvt(hdac, hdmi, cvt);  	/*  	 * To make PA and other userland happy.  	 * userland scans devices so returning error does not help.  	 */ -	if (!pin) +	if (!port)  		return 0; - -	if ((!pin->eld.monitor_present) || -			(!pin->eld.eld_valid)) { +	if ((!port->eld.monitor_present) || +			(!port->eld.eld_valid)) {  		dev_warn(&hdac->hdac.dev, -			"Failed: monitor present? %d ELD valid?: %d for pin: %d\n", -			pin->eld.monitor_present, pin->eld.eld_valid, pin->nid); +			"Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n", +			port->eld.monitor_present, port->eld.eld_valid, +			port->pin->nid, port->id);  		return 0;  	} -	dai_map->pin = pin; - -	hdac_hdmi_enable_cvt(hdac, dai_map); -	ret = hdac_hdmi_enable_pin(hdac, dai_map); -	if (ret < 0) -		return ret; +	dai_map->port = port;  	ret = hdac_hdmi_eld_limit_formats(substream->runtime, -				pin->eld.eld_buffer); +				port->eld.eld_buffer);  	if (ret < 0)  		return ret;  	return snd_pcm_hw_constraint_eld(substream->runtime, -				pin->eld.eld_buffer); -} - -static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd, -		struct snd_soc_dai *dai) -{ -	struct hdac_hdmi_dai_pin_map *dai_map; -	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); -	struct hdac_hdmi_priv *hdmi = hdac->private_data; -	int ret; - -	dai_map = &hdmi->dai_map[dai->id]; -	if (cmd == SNDRV_PCM_TRIGGER_RESUME) { -		ret = hdac_hdmi_enable_pin(hdac, dai_map); -		if (ret < 0) -			return ret; - -		return hdac_hdmi_playback_prepare(substream, dai); -	} - -	return 0; +				port->eld.eld_buffer);  }  static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, @@ -661,29 +605,23 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,  {  	struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);  	struct hdac_hdmi_priv *hdmi = hdac->private_data; -	struct hdac_hdmi_dai_pin_map *dai_map; +	struct hdac_hdmi_dai_port_map *dai_map; +	struct hdac_hdmi_pcm *pcm;  	dai_map = &hdmi->dai_map[dai->id]; -	if (dai_map->pin) { -		snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0, -				AC_VERB_SET_CHANNEL_STREAMID, 0); -		snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0, -				AC_VERB_SET_STREAM_FORMAT, 0); - -		hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3); - -		snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, -			AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - -		mutex_lock(&dai_map->pin->lock); -		dai_map->pin->chmap_set = false; -		memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap)); -		dai_map->pin->channels = 0; -		mutex_unlock(&dai_map->pin->lock); +	pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); -		dai_map->pin = NULL; +	if (pcm) { +		mutex_lock(&pcm->lock); +		pcm->chmap_set = false; +		memset(pcm->chmap, 0, sizeof(pcm->chmap)); +		pcm->channels = 0; +		mutex_unlock(&pcm->lock);  	} + +	if (dai_map->port) +		dai_map->port = NULL;  }  static int @@ -716,10 +654,11 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)  }  static int hdac_hdmi_fill_widget_info(struct device *dev, -				struct snd_soc_dapm_widget *w, -				enum snd_soc_dapm_type id, void *priv, -				const char *wname, const char *stream, -				struct snd_kcontrol_new *wc, int numkc) +		struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id, +		void *priv, const char *wname, const char *stream, +		struct snd_kcontrol_new *wc, int numkc, +		int (*event)(struct snd_soc_dapm_widget *, +		struct snd_kcontrol *, int), unsigned short event_flags)  {  	w->id = id;  	w->name = devm_kstrdup(dev, wname, GFP_KERNEL); @@ -732,6 +671,8 @@ static int hdac_hdmi_fill_widget_info(struct device *dev,  	w->kcontrol_news = wc;  	w->num_kcontrols = numkc;  	w->priv = priv; +	w->event = event; +	w->event_flags = event_flags;  	return 0;  } @@ -748,30 +689,175 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,  }  static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, -					struct hdac_hdmi_pin *pin) +					struct hdac_hdmi_port *port)  {  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct hdac_hdmi_pcm *pcm = NULL; +	struct hdac_hdmi_port *p;  	list_for_each_entry(pcm, &hdmi->pcm_list, head) { -		if (pcm->pin == pin) -			return pcm; +		if (list_empty(&pcm->port_list)) +			continue; + +		list_for_each_entry(p, &pcm->port_list, head) { +			if (p->id == port->id && port->pin == p->pin) +				return pcm; +		}  	}  	return NULL;  } +static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev, +			     hda_nid_t nid, unsigned int pwr_state) +{ +	if (get_wcaps(&edev->hdac, nid) & AC_WCAP_POWER) { +		if (!snd_hdac_check_power_state(&edev->hdac, nid, pwr_state)) +			snd_hdac_codec_write(&edev->hdac, nid, 0, +				AC_VERB_SET_POWER_STATE, pwr_state); +	} +} + +static void hdac_hdmi_set_amp(struct hdac_ext_device *edev, +				   hda_nid_t nid, int val) +{ +	if (get_wcaps(&edev->hdac, nid) & AC_WCAP_OUT_AMP) +		snd_hdac_codec_write(&edev->hdac, nid, 0, +					AC_VERB_SET_AMP_GAIN_MUTE, val); +} + + +static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w, +					struct snd_kcontrol *kc, int event) +{ +	struct hdac_hdmi_port *port = w->priv; +	struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); +	struct hdac_hdmi_pcm *pcm; + +	dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", +			__func__, w->name, event); + +	pcm = hdac_hdmi_get_pcm(edev, port); +	if (!pcm) +		return -EIO; + +	/* set the device if pin is mst_capable */ +	if (hdac_hdmi_port_select_set(edev, port) < 0) +		return -EIO; + +	switch (event) { +	case SND_SOC_DAPM_PRE_PMU: +		hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D0); + +		/* Enable out path for this pin widget */ +		snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0, +				AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + +		hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_UNMUTE); + +		return hdac_hdmi_setup_audio_infoframe(edev, pcm, port); + +	case SND_SOC_DAPM_POST_PMD: +		hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_MUTE); + +		/* Disable out path for this pin widget */ +		snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0, +				AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + +		hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D3); +		break; + +	} + +	return 0; +} + +static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, +					struct snd_kcontrol *kc, int event) +{ +	struct hdac_hdmi_cvt *cvt = w->priv; +	struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); +	struct hdac_hdmi_priv *hdmi = edev->private_data; +	struct hdac_hdmi_pcm *pcm; + +	dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", +			__func__, w->name, event); + +	pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt); +	if (!pcm) +		return -EIO; + +	switch (event) { +	case SND_SOC_DAPM_PRE_PMU: +		hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D0); + +		/* Enable transmission */ +		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, +			AC_VERB_SET_DIGI_CONVERT_1, 1); + +		/* Category Code (CC) to zero */ +		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, +			AC_VERB_SET_DIGI_CONVERT_2, 0); + +		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, +				AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag); +		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, +				AC_VERB_SET_STREAM_FORMAT, pcm->format); +		break; + +	case SND_SOC_DAPM_POST_PMD: +		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, +				AC_VERB_SET_CHANNEL_STREAMID, 0); +		snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, +				AC_VERB_SET_STREAM_FORMAT, 0); + +		hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D3); +		break; + +	} + +	return 0; +} + +static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, +					struct snd_kcontrol *kc, int event) +{ +	struct hdac_hdmi_port *port = w->priv; +	struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev); +	int mux_idx; + +	dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n", +			__func__, w->name, event); + +	if (!kc) +		kc  = w->kcontrols[0]; + +	mux_idx = dapm_kcontrol_get_value(kc); + +	/* set the device if pin is mst_capable */ +	if (hdac_hdmi_port_select_set(edev, port) < 0) +		return -EIO; + +	if (mux_idx > 0) { +		snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0, +			AC_VERB_SET_CONNECT_SEL, (mux_idx - 1)); +	} + +	return 0; +} +  /*   * Based on user selection, map the PINs with the PCMs.   */ -static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol, +static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,  		struct snd_ctl_elem_value *ucontrol)  {  	int ret; +	struct hdac_hdmi_port *p, *p_next;  	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;  	struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);  	struct snd_soc_dapm_context *dapm = w->dapm; -	struct hdac_hdmi_pin *pin = w->priv; +	struct hdac_hdmi_port *port = w->priv;  	struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct hdac_hdmi_pcm *pcm = NULL; @@ -781,26 +867,35 @@ static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,  	if (ret < 0)  		return ret; +	if (port == NULL) +		return -EINVAL; +  	mutex_lock(&hdmi->pin_mutex);  	list_for_each_entry(pcm, &hdmi->pcm_list, head) { -		if (pcm->pin == pin) -			pcm->pin = NULL; +		if (list_empty(&pcm->port_list)) +			continue; -		/* -		 * Jack status is not reported during device probe as the -		 * PCMs are not registered by then. So report it here. -		 */ -		if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) { -			pcm->pin = pin; -			if (pin->eld.monitor_present && pin->eld.eld_valid) { -				dev_dbg(&edev->hdac.dev, -					"jack report for pcm=%d\n", -					pcm->pcm_id); +		list_for_each_entry_safe(p, p_next, &pcm->port_list, head) { +			if (p == port && p->id == port->id && +					p->pin == port->pin) { +				hdac_hdmi_jack_report(pcm, port, false); +				list_del(&p->head); +			} +		} +	} -				snd_jack_report(pcm->jack, SND_JACK_AVOUT); +	/* +	 * Jack status is not reported during device probe as the +	 * PCMs are not registered by then. So report it here. +	 */ +	list_for_each_entry(pcm, &hdmi->pcm_list, head) { +		if (!strcmp(cvt_name, pcm->cvt->name)) { +			list_add_tail(&port->head, &pcm->port_list); +			if (port->eld.monitor_present && port->eld.eld_valid) { +				hdac_hdmi_jack_report(pcm, port, true); +				mutex_unlock(&hdmi->pin_mutex); +				return ret;  			} -			mutex_unlock(&hdmi->pin_mutex); -			return ret;  		}  	}  	mutex_unlock(&hdmi->pin_mutex); @@ -817,12 +912,13 @@ static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,   * care of selecting the right one and leaving all other inputs selected to   * "NONE"   */ -static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev, -				struct hdac_hdmi_pin *pin, +static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, +				struct hdac_hdmi_port *port,  				struct snd_soc_dapm_widget *widget,  				const char *widget_name)  {  	struct hdac_hdmi_priv *hdmi = edev->private_data; +	struct hdac_hdmi_pin *pin = port->pin;  	struct snd_kcontrol_new *kc;  	struct hdac_hdmi_cvt *cvt;  	struct soc_enum *se; @@ -841,7 +937,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,  	if (!se)  		return -ENOMEM; -	sprintf(kc_name, "Pin %d Input", pin->nid); +	sprintf(kc_name, "Pin %d port %d Input", pin->nid, port->id);  	kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL);  	if (!kc->name)  		return -ENOMEM; @@ -850,7 +946,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,  	kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;  	kc->access = 0;  	kc->info = snd_soc_info_enum_double; -	kc->put = hdac_hdmi_set_pin_mux; +	kc->put = hdac_hdmi_set_pin_port_mux;  	kc->get = snd_soc_dapm_get_enum_double;  	se->reg = SND_SOC_NOPM; @@ -878,7 +974,9 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,  		return -ENOMEM;  	return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget, -			snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1); +			snd_soc_dapm_mux, port, widget_name, NULL, kc, 1, +			hdac_hdmi_pin_mux_widget_event, +			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG);  }  /* Add cvt <- input <- mux route map */ @@ -889,10 +987,10 @@ static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	const struct snd_kcontrol_new *kc;  	struct soc_enum *se; -	int mux_index = hdmi->num_cvt + hdmi->num_pin; +	int mux_index = hdmi->num_cvt + hdmi->num_ports;  	int i, j; -	for (i = 0; i < hdmi->num_pin; i++) { +	for (i = 0; i < hdmi->num_ports; i++) {  		kc = widgets[mux_index].kcontrol_news;  		se = (struct soc_enum *)kc->private_value;  		for (j = 0; j < hdmi->num_cvt; j++) { @@ -911,17 +1009,18 @@ static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,  /*   * Widgets are added in the below sequence   *	Converter widgets for num converters enumerated - *	Pin widgets for num pins enumerated - *	Pin mux widgets to represent connenction list of pin widget + *	Pin-port widgets for num ports for Pins enumerated + *	Pin-port mux widgets to represent connenction list of pin widget   * - * Total widgets elements = num_cvt + num_pin + num_pin; + * For each port, one Mux and One output widget is added + * Total widgets elements = num_cvt + (num_ports * 2);   *   * Routes are added as below: - *	pin mux -> pin (based on num_pins) - *	cvt -> "Input sel control" -> pin_mux + *	pin-port mux -> pin (based on num_ports) + *	cvt -> "Input sel control" -> pin-port_mux   *   * Total route elements: - *	num_pins + (pin_muxes * num_cvt) + *	num_ports + (pin_muxes * num_cvt)   */  static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)  { @@ -933,14 +1032,14 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)  	char widget_name[NAME_SIZE];  	struct hdac_hdmi_cvt *cvt;  	struct hdac_hdmi_pin *pin; -	int ret, i = 0, num_routes = 0; +	int ret, i = 0, num_routes = 0, j;  	if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))  		return -EINVAL; -	widgets = devm_kzalloc(dapm->dev, -		(sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)), -		GFP_KERNEL); +	widgets = devm_kzalloc(dapm->dev, (sizeof(*widgets) * +				((2 * hdmi->num_ports) + hdmi->num_cvt)), +				GFP_KERNEL);  	if (!widgets)  		return -ENOMEM; @@ -949,37 +1048,50 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)  	list_for_each_entry(cvt, &hdmi->cvt_list, head) {  		sprintf(widget_name, "Converter %d", cvt->nid);  		ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], -			snd_soc_dapm_aif_in, &cvt->nid, -			widget_name, dai_drv[i].playback.stream_name, NULL, 0); +			snd_soc_dapm_aif_in, cvt, +			widget_name, dai_drv[i].playback.stream_name, NULL, 0, +			hdac_hdmi_cvt_output_widget_event, +			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);  		if (ret < 0)  			return ret;  		i++;  	}  	list_for_each_entry(pin, &hdmi->pin_list, head) { -		sprintf(widget_name, "hif%d Output", pin->nid); -		ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], -				snd_soc_dapm_output, &pin->nid, -				widget_name, NULL, NULL, 0); -		if (ret < 0) -			return ret; -		i++; +		for (j = 0; j < pin->num_ports; j++) { +			sprintf(widget_name, "hif%d-%d Output", +				pin->nid, pin->ports[j].id); +			ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], +					snd_soc_dapm_output, &pin->ports[j], +					widget_name, NULL, NULL, 0, +					hdac_hdmi_pin_output_widget_event, +					SND_SOC_DAPM_PRE_PMU | +					SND_SOC_DAPM_POST_PMD); +			if (ret < 0) +				return ret; +			pin->ports[j].output_pin = widgets[i].name; +			i++; +		}  	}  	/* DAPM widgets to represent the connection list to pin widget */  	list_for_each_entry(pin, &hdmi->pin_list, head) { -		sprintf(widget_name, "Pin %d Mux", pin->nid); -		ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i], -							widget_name); -		if (ret < 0) -			return ret; -		i++; +		for (j = 0; j < pin->num_ports; j++) { +			sprintf(widget_name, "Pin%d-Port%d Mux", +				pin->nid, pin->ports[j].id); +			ret = hdac_hdmi_create_pin_port_muxs(edev, +						&pin->ports[j], &widgets[i], +						widget_name); +			if (ret < 0) +				return ret; +			i++; -		/* For cvt to pin_mux mapping */ -		num_routes += hdmi->num_cvt; +			/* For cvt to pin_mux mapping */ +			num_routes += hdmi->num_cvt; -		/* For pin_mux to pin mapping */ -		num_routes++; +			/* For pin_mux to pin mapping */ +			num_routes++; +		}  	}  	route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes), @@ -990,20 +1102,22 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)  	i = 0;  	/* Add pin <- NULL <- mux route map */  	list_for_each_entry(pin, &hdmi->pin_list, head) { -		int sink_index = i + hdmi->num_cvt; -		int src_index = sink_index + hdmi->num_pin; +		for (j = 0; j < pin->num_ports; j++) { +			int sink_index = i + hdmi->num_cvt; +			int src_index = sink_index + pin->num_ports * +						hdmi->num_pin; -		hdac_hdmi_fill_route(&route[i], +			hdac_hdmi_fill_route(&route[i],  				widgets[sink_index].name, NULL,  				widgets[src_index].name, NULL); -		i++; - +			i++; +		}  	}  	hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i);  	snd_soc_dapm_new_controls(dapm, widgets, -		((2 * hdmi->num_pin) + hdmi->num_cvt)); +		((2 * hdmi->num_ports) + hdmi->num_cvt));  	snd_soc_dapm_add_routes(dapm, route, num_routes);  	snd_soc_dapm_new_widgets(dapm->card); @@ -1015,7 +1129,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)  static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)  {  	struct hdac_hdmi_priv *hdmi = edev->private_data; -	struct hdac_hdmi_dai_pin_map *dai_map; +	struct hdac_hdmi_dai_port_map *dai_map;  	struct hdac_hdmi_cvt *cvt;  	int dai_id = 0; @@ -1059,132 +1173,149 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)  	return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);  } -static void hdac_hdmi_parse_eld(struct hdac_ext_device *edev, -			struct hdac_hdmi_pin *pin) +static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev, +			struct hdac_hdmi_port *port)  { -	pin->eld.info.spk_alloc = pin->eld.eld_buffer[DRM_ELD_SPEAKER]; +	unsigned int ver, mnl; + +	ver = (port->eld.eld_buffer[DRM_ELD_VER] & DRM_ELD_VER_MASK) +						>> DRM_ELD_VER_SHIFT; + +	if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) { +		dev_err(&edev->hdac.dev, "HDMI: Unknown ELD version %d\n", ver); +		return -EINVAL; +	} + +	mnl = (port->eld.eld_buffer[DRM_ELD_CEA_EDID_VER_MNL] & +		DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; + +	if (mnl > ELD_MAX_MNL) { +		dev_err(&edev->hdac.dev, "HDMI: MNL Invalid %d\n", mnl); +		return -EINVAL; +	} + +	port->eld.info.spk_alloc = port->eld.eld_buffer[DRM_ELD_SPEAKER]; + +	return 0;  } -static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) +static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, +				    struct hdac_hdmi_port *port)  {  	struct hdac_ext_device *edev = pin->edev;  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct hdac_hdmi_pcm *pcm; -	int val; +	int size = 0; +	int port_id = -1; -	pin->repoll_count = repoll; +	if (!hdmi) +		return; -	pm_runtime_get_sync(&edev->hdac.dev); -	val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0, -					AC_VERB_GET_PIN_SENSE, 0); +	/* +	 * In case of non MST pin, get_eld info API expectes port +	 * to be -1. +	 */ +	mutex_lock(&hdmi->pin_mutex); +	port->eld.monitor_present = false; -	dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n", -						val, pin->nid); +	if (pin->mst_capable) +		port_id = port->id; +	size = snd_hdac_acomp_get_eld(&edev->hdac, pin->nid, port_id, +				&port->eld.monitor_present, +				port->eld.eld_buffer, +				ELD_MAX_SIZE); -	mutex_lock(&hdmi->pin_mutex); -	pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE); -	pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV); +	if (size > 0) { +		size = min(size, ELD_MAX_SIZE); +		if (hdac_hdmi_parse_eld(edev, port) < 0) +			size = -EINVAL; +	} -	pcm = hdac_hdmi_get_pcm(edev, pin); +	if (size > 0) { +		port->eld.eld_valid = true; +		port->eld.eld_size = size; +	} else { +		port->eld.eld_valid = false; +		port->eld.eld_size = 0; +	} -	if (!pin->eld.monitor_present || !pin->eld.eld_valid) { +	pcm = hdac_hdmi_get_pcm(edev, port); -		dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n", -						__func__, pin->nid); +	if (!port->eld.monitor_present || !port->eld.eld_valid) { + +		dev_err(&edev->hdac.dev, "%s: disconnect for pin:port %d:%d\n", +						__func__, pin->nid, port->id);  		/*  		 * PCMs are not registered during device probe, so don't  		 * report jack here. It will be done in usermode mux  		 * control select.  		 */ -		if (pcm) { -			dev_dbg(&edev->hdac.dev, -				"jack report for pcm=%d\n", pcm->pcm_id); - -			snd_jack_report(pcm->jack, 0); -		} +		if (pcm) +			hdac_hdmi_jack_report(pcm, port, false);  		mutex_unlock(&hdmi->pin_mutex); -		goto put_hdac_device; +		return;  	} -	if (pin->eld.monitor_present && pin->eld.eld_valid) { -		/* TODO: use i915 component for reading ELD later */ -		if (hdac_hdmi_get_eld(&edev->hdac, pin->nid, -				pin->eld.eld_buffer, -				&pin->eld.eld_size) == 0) { +	if (port->eld.monitor_present && port->eld.eld_valid) { +		if (pcm) +			hdac_hdmi_jack_report(pcm, port, true); -			if (pcm) { -				dev_dbg(&edev->hdac.dev, -					"jack report for pcm=%d\n", -					pcm->pcm_id); - -				snd_jack_report(pcm->jack, SND_JACK_AVOUT); -			} -			hdac_hdmi_parse_eld(edev, pin); +		print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, +			  port->eld.eld_buffer, port->eld.eld_size, false); -			print_hex_dump_debug("ELD: ", -					DUMP_PREFIX_OFFSET, 16, 1, -					pin->eld.eld_buffer, pin->eld.eld_size, -					true); -		} else { -			pin->eld.monitor_present = false; -			pin->eld.eld_valid = false; - -			if (pcm) { -				dev_dbg(&edev->hdac.dev, -					"jack report for pcm=%d\n", -					pcm->pcm_id); - -				snd_jack_report(pcm->jack, 0); -			} -		}  	} -  	mutex_unlock(&hdmi->pin_mutex); - -	/* -	 * Sometimes the pin_sense may present invalid monitor -	 * present and eld_valid. If ELD data is not valid, loop few -	 * more times to get correct pin sense and valid ELD. -	 */ -	if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll) -		schedule_delayed_work(&pin->work, msecs_to_jiffies(300)); - -put_hdac_device: -	pm_runtime_put_sync(&edev->hdac.dev);  } -static void hdac_hdmi_repoll_eld(struct work_struct *work) +static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi, +				struct hdac_hdmi_pin *pin)  { -	struct hdac_hdmi_pin *pin = -		container_of(to_delayed_work(work), struct hdac_hdmi_pin, work); +	struct hdac_hdmi_port *ports; +	int max_ports = HDA_MAX_PORTS; +	int i; + +	/* +	 * FIXME: max_port may vary for each platform, so pass this as +	 * as driver data or query from i915 interface when this API is +	 * implemented. +	 */ -	/* picked from legacy HDA driver */ -	if (pin->repoll_count++ > 6) -		pin->repoll_count = 0; +	ports = kcalloc(max_ports, sizeof(*ports), GFP_KERNEL); +	if (!ports) +		return -ENOMEM; -	hdac_hdmi_present_sense(pin, pin->repoll_count); +	for (i = 0; i < max_ports; i++) { +		ports[i].id = i; +		ports[i].pin = pin; +	} +	pin->ports = ports; +	pin->num_ports = max_ports; +	return 0;  }  static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)  {  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct hdac_hdmi_pin *pin; +	int ret;  	pin = kzalloc(sizeof(*pin), GFP_KERNEL);  	if (!pin)  		return -ENOMEM;  	pin->nid = nid; +	pin->mst_capable = false; +	pin->edev = edev; +	ret = hdac_hdmi_add_ports(hdmi, pin); +	if (ret < 0) +		return ret;  	list_add_tail(&pin->head, &hdmi->pin_list);  	hdmi->num_pin++; - -	pin->edev = edev; -	mutex_init(&pin->lock); -	INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld); +	hdmi->num_ports += pin->num_ports;  	return 0;  } @@ -1233,9 +1364,7 @@ static struct snd_soc_dai_ops hdmi_dai_ops = {  	.startup = hdac_hdmi_pcm_open,  	.shutdown = hdac_hdmi_pcm_close,  	.hw_params = hdac_hdmi_set_hw_params, -	.prepare = hdac_hdmi_playback_prepare, -	.trigger = hdac_hdmi_trigger, -	.hw_free = hdac_hdmi_playback_cleanup, +	.set_tdm_slot = hdac_hdmi_set_tdm_slot,  };  /* @@ -1372,13 +1501,16 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)  {  	struct hdac_ext_device *edev = aptr;  	struct hdac_hdmi_priv *hdmi = edev->private_data; -	struct hdac_hdmi_pin *pin; +	struct hdac_hdmi_pin *pin = NULL; +	struct hdac_hdmi_port *hport = NULL;  	struct snd_soc_codec *codec = edev->scodec; +	int i;  	/* Don't know how this mapping is derived */  	hda_nid_t pin_nid = port + 0x04; -	dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid); +	dev_dbg(&edev->hdac.dev, "%s: for pin:%d port=%d\n", __func__, +							pin_nid, pipe);  	/*  	 * skip notification during system suspend (but not in runtime PM); @@ -1394,9 +1526,29 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)  		return;  	list_for_each_entry(pin, &hdmi->pin_list, head) { -		if (pin->nid == pin_nid) -			hdac_hdmi_present_sense(pin, 1); +		if (pin->nid != pin_nid) +			continue; + +		/* In case of non MST pin, pipe is -1 */ +		if (pipe == -1) { +			pin->mst_capable = false; +			/* if not MST, default is port[0] */ +			hport = &pin->ports[0]; +			goto out; +		} else { +			for (i = 0; i < pin->num_ports; i++) { +				pin->mst_capable = true; +				if (pin->ports[i].id == pipe) { +					hport = &pin->ports[i]; +					goto out; +				} +			} +		}  	} + +out: +	if (pin && hport) +		hdac_hdmi_present_sense(pin, hport);  }  static struct i915_audio_component_audio_ops aops = { @@ -1416,13 +1568,130 @@ static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,  	return NULL;  } -int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) +/* create jack pin kcontrols */ +static int create_fill_jack_kcontrols(struct snd_soc_card *card, +				    struct hdac_ext_device *edev) +{ +	struct hdac_hdmi_pin *pin; +	struct snd_kcontrol_new *kc; +	char kc_name[NAME_SIZE], xname[NAME_SIZE]; +	char *name; +	int i = 0, j; +	struct snd_soc_codec *codec = edev->scodec; +	struct hdac_hdmi_priv *hdmi = edev->private_data; + +	kc = devm_kcalloc(codec->dev, hdmi->num_ports, +				sizeof(*kc), GFP_KERNEL); + +	if (!kc) +		return -ENOMEM; + +	list_for_each_entry(pin, &hdmi->pin_list, head) { +		for (j = 0; j < pin->num_ports; j++) { +			snprintf(xname, sizeof(xname), "hif%d-%d Jack", +						pin->nid, pin->ports[j].id); +			name = devm_kstrdup(codec->dev, xname, GFP_KERNEL); +			if (!name) +				return -ENOMEM; +			snprintf(kc_name, sizeof(kc_name), "%s Switch", xname); +			kc[i].name = devm_kstrdup(codec->dev, kc_name, +							GFP_KERNEL); +			if (!kc[i].name) +				return -ENOMEM; + +			kc[i].private_value = (unsigned long)name; +			kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; +			kc[i].access = 0; +			kc[i].info = snd_soc_dapm_info_pin_switch; +			kc[i].put = snd_soc_dapm_put_pin_switch; +			kc[i].get = snd_soc_dapm_get_pin_switch; +			i++; +		} +	} + +	return snd_soc_add_card_controls(card, kc, i); +} + +int hdac_hdmi_jack_port_init(struct snd_soc_codec *codec, +			struct snd_soc_dapm_context *dapm) +{ +	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); +	struct hdac_hdmi_priv *hdmi = edev->private_data; +	struct hdac_hdmi_pin *pin; +	struct snd_soc_dapm_widget *widgets; +	struct snd_soc_dapm_route *route; +	char w_name[NAME_SIZE]; +	int i = 0, j, ret; + +	widgets = devm_kcalloc(dapm->dev, hdmi->num_ports, +				sizeof(*widgets), GFP_KERNEL); + +	if (!widgets) +		return -ENOMEM; + +	route = devm_kcalloc(dapm->dev, hdmi->num_ports, +				sizeof(*route), GFP_KERNEL); +	if (!route) +		return -ENOMEM; + +	/* create Jack DAPM widget */ +	list_for_each_entry(pin, &hdmi->pin_list, head) { +		for (j = 0; j < pin->num_ports; j++) { +			snprintf(w_name, sizeof(w_name), "hif%d-%d Jack", +						pin->nid, pin->ports[j].id); + +			ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], +					snd_soc_dapm_spk, NULL, +					w_name, NULL, NULL, 0, NULL, 0); +			if (ret < 0) +				return ret; + +			pin->ports[j].jack_pin = widgets[i].name; +			pin->ports[j].dapm = dapm; + +			/* add to route from Jack widget to output */ +			hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin, +					NULL, pin->ports[j].output_pin, NULL); + +			i++; +		} +	} + +	/* Add Route from Jack widget to the output widget */ +	ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports); +	if (ret < 0) +		return ret; + +	ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports); +	if (ret < 0) +		return ret; + +	ret = snd_soc_dapm_new_widgets(dapm->card); +	if (ret < 0) +		return ret; + +	/* Add Jack Pin switch Kcontrol */ +	ret = create_fill_jack_kcontrols(dapm->card, edev); + +	if (ret < 0) +		return ret; + +	/* default set the Jack Pin switch to OFF */ +	list_for_each_entry(pin, &hdmi->pin_list, head) { +		for (j = 0; j < pin->num_ports; j++) +			snd_soc_dapm_disable_pin(pin->ports[j].dapm, +						pin->ports[j].jack_pin); +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init); + +int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, +				struct snd_soc_jack *jack)  { -	char jack_name[NAME_SIZE];  	struct snd_soc_codec *codec = dai->codec;  	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); -	struct snd_soc_dapm_context *dapm = -		snd_soc_component_get_dapm(&codec->component);  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct hdac_hdmi_pcm *pcm;  	struct snd_pcm *snd_pcm; @@ -1437,7 +1706,10 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)  		return -ENOMEM;  	pcm->pcm_id = device;  	pcm->cvt = hdmi->dai_map[dai->id].cvt; - +	pcm->jack_event = 0; +	pcm->jack = jack; +	mutex_init(&pcm->lock); +	INIT_LIST_HEAD(&pcm->port_list);  	snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device);  	if (snd_pcm) {  		err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); @@ -1452,20 +1724,40 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)  	list_add_tail(&pcm->head, &hdmi->pcm_list); -	sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device); - -	return snd_jack_new(dapm->card->snd_card, jack_name, -		SND_JACK_AVOUT,	&pcm->jack, true, false); +	return 0;  }  EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); +static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev, +			struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) +{ +	int i; +	struct hdac_hdmi_pin *pin; + +	list_for_each_entry(pin, &hdmi->pin_list, head) { +		if (detect_pin_caps) { + +			if (hdac_hdmi_get_port_len(edev, pin->nid)  == 0) +				pin->mst_capable = false; +			else +				pin->mst_capable = true; +		} + +		for (i = 0; i < pin->num_ports; i++) { +			if (!pin->mst_capable && i > 0) +				continue; + +			hdac_hdmi_present_sense(pin, &pin->ports[i]); +		} +	} +} +  static int hdmi_codec_probe(struct snd_soc_codec *codec)  {  	struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct snd_soc_dapm_context *dapm =  		snd_soc_component_get_dapm(&codec->component); -	struct hdac_hdmi_pin *pin;  	struct hdac_ext_link *hlink = NULL;  	int ret; @@ -1495,9 +1787,7 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)  		return ret;  	} -	list_for_each_entry(pin, &hdmi->pin_list, head) -		hdac_hdmi_present_sense(pin, 1); - +	hdac_hdmi_present_sense_all_pins(edev, hdmi, true);  	/* Imp: Store the card pointer in hda_codec */  	edev->card = dapm->card->snd_card; @@ -1545,7 +1835,6 @@ static void hdmi_codec_complete(struct device *dev)  {  	struct hdac_ext_device *edev = to_hda_ext_device(dev);  	struct hdac_hdmi_priv *hdmi = edev->private_data; -	struct hdac_hdmi_pin *pin;  	struct hdac_device *hdac = &edev->hdac;  	/* Power up afg */ @@ -1558,10 +1847,10 @@ static void hdmi_codec_complete(struct device *dev)  	/*  	 * As the ELD notify callback request is not entertained while the  	 * device is in suspend state. Need to manually check detection of -	 * all pins here. +	 * all pins here. pin capablity change is not support, so use the +	 * already set pin caps.  	 */ -	list_for_each_entry(pin, &hdmi->pin_list, head) -		hdac_hdmi_present_sense(pin, 1); +	hdac_hdmi_present_sense_all_pins(edev, hdmi, false);  	pm_runtime_put_sync(&edev->hdac.dev);  } @@ -1582,13 +1871,8 @@ static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,  	struct hdac_ext_device *edev = to_ehdac_device(hdac);  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); -	struct hdac_hdmi_pin *pin = pcm->pin; - -	/* chmap is already set to 0 in caller */ -	if (!pin) -		return; -	memcpy(chmap, pin->chmap, ARRAY_SIZE(pin->chmap)); +	memcpy(chmap, pcm->chmap, ARRAY_SIZE(pcm->chmap));  }  static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, @@ -1597,14 +1881,18 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,  	struct hdac_ext_device *edev = to_ehdac_device(hdac);  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); -	struct hdac_hdmi_pin *pin = pcm->pin; - -	mutex_lock(&pin->lock); -	pin->chmap_set = true; -	memcpy(pin->chmap, chmap, ARRAY_SIZE(pin->chmap)); -	if (prepared) -		hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid, pin->nid); -	mutex_unlock(&pin->lock); +	struct hdac_hdmi_port *port; + +	if (list_empty(&pcm->port_list)) +		return; + +	mutex_lock(&pcm->lock); +	pcm->chmap_set = true; +	memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); +	list_for_each_entry(port, &pcm->port_list, head) +		if (prepared) +			hdac_hdmi_setup_audio_infoframe(edev, pcm, port); +	mutex_unlock(&pcm->lock);  }  static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) @@ -1612,9 +1900,11 @@ static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)  	struct hdac_ext_device *edev = to_ehdac_device(hdac);  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); -	struct hdac_hdmi_pin *pin = pcm->pin; -	return pin ? true:false; +	if (list_empty(&pcm->port_list)) +		return false; + +	return true;  }  static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) @@ -1622,12 +1912,20 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)  	struct hdac_ext_device *edev = to_ehdac_device(hdac);  	struct hdac_hdmi_priv *hdmi = edev->private_data;  	struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); -	struct hdac_hdmi_pin *pin = pcm->pin; +	struct hdac_hdmi_port *port; -	if (!pin || !pin->eld.eld_valid) +	if (list_empty(&pcm->port_list))  		return 0; -	return pin->eld.info.spk_alloc; +	port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head); + +	if (!port) +		return 0; + +	if (!port || !port->eld.eld_valid) +		return 0; + +	return port->eld.info.spk_alloc;  }  static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) @@ -1700,12 +1998,19 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)  	struct hdac_hdmi_pin *pin, *pin_next;  	struct hdac_hdmi_cvt *cvt, *cvt_next;  	struct hdac_hdmi_pcm *pcm, *pcm_next; +	struct hdac_hdmi_port *port; +	int i;  	snd_soc_unregister_codec(&edev->hdac.dev);  	list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) {  		pcm->cvt = NULL; -		pcm->pin = NULL; +		if (list_empty(&pcm->port_list)) +			continue; + +		list_for_each_entry(port, &pcm->port_list, head) +			port = NULL; +  		list_del(&pcm->head);  		kfree(pcm);  	} @@ -1717,6 +2022,9 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)  	}  	list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) { +		for (i = 0; i < pin->num_ports; i++) +			pin->ports[i].pin = NULL; +		kfree(pin->ports);  		list_del(&pin->head);  		kfree(pin);  	} @@ -1819,6 +2127,7 @@ static const struct hda_device_id hdmi_list[] = {  	HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),  	HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),  	HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0), +	HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", 0),  	{}  }; diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h index 8dfd1e0b57b3..dfc3a9cf7199 100644 --- a/sound/soc/codecs/hdac_hdmi.h +++ b/sound/soc/codecs/hdac_hdmi.h @@ -1,6 +1,9 @@  #ifndef __HDAC_HDMI_H__  #define __HDAC_HDMI_H__ -int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm); +int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm, +				struct snd_soc_jack *jack); +int hdac_hdmi_jack_port_init(struct snd_soc_codec *codec, +			struct snd_soc_dapm_context *dapm);  #endif /* __HDAC_HDMI_H__ */ diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 90b5948e0ff3..8c5ae1fc23a9 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -18,6 +18,7 @@  #include <sound/pcm.h>  #include <sound/pcm_params.h>  #include <sound/soc.h> +#include <sound/tlv.h>  #include <sound/pcm_drm_eld.h>  #include <sound/hdmi-codec.h>  #include <sound/pcm_iec958.h> @@ -31,8 +32,261 @@ struct hdmi_device {  };  #define pos_to_hdmi_device(pos)	container_of((pos), struct hdmi_device, list)  LIST_HEAD(hdmi_device_list); +static DEFINE_MUTEX(hdmi_mutex);  #define DAI_NAME_SIZE 16 + +#define HDMI_CODEC_CHMAP_IDX_UNKNOWN  -1 + +struct hdmi_codec_channel_map_table { +	unsigned char map;	/* ALSA API channel map position */ +	unsigned long spk_mask;		/* speaker position bit mask */ +}; + +/* + * CEA speaker placement for HDMI 1.4: + * + *  FL  FLC   FC   FRC   FR   FRW + * + *                                  LFE + * + *  RL  RLC   RC   RRC   RR + * + *  Speaker placement has to be extended to support HDMI 2.0 + */ +enum hdmi_codec_cea_spk_placement { +	FL  = BIT(0),	/* Front Left           */ +	FC  = BIT(1),	/* Front Center         */ +	FR  = BIT(2),	/* Front Right          */ +	FLC = BIT(3),	/* Front Left Center    */ +	FRC = BIT(4),	/* Front Right Center   */ +	RL  = BIT(5),	/* Rear Left            */ +	RC  = BIT(6),	/* Rear Center          */ +	RR  = BIT(7),	/* Rear Right           */ +	RLC = BIT(8),	/* Rear Left Center     */ +	RRC = BIT(9),	/* Rear Right Center    */ +	LFE = BIT(10),	/* Low Frequency Effect */ +}; + +/* + * cea Speaker allocation structure + */ +struct hdmi_codec_cea_spk_alloc { +	const int ca_id; +	unsigned int n_ch; +	unsigned long mask; +}; + +/* Channel maps  stereo HDMI */ +const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = { +	{ .channels = 2, +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, +	{ } +}; + +/* Channel maps for multi-channel playbacks, up to 8 n_ch */ +const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = { +	{ .channels = 2, /* CA_ID 0x00 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, +	{ .channels = 4, /* CA_ID 0x01 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_NA } }, +	{ .channels = 4, /* CA_ID 0x02 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FC } }, +	{ .channels = 4, /* CA_ID 0x03 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_FC } }, +	{ .channels = 6, /* CA_ID 0x04 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, +	{ .channels = 6, /* CA_ID 0x05 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, +	{ .channels = 6, /* CA_ID 0x06 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, +	{ .channels = 6, /* CA_ID 0x07 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, +	{ .channels = 6, /* CA_ID 0x08 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, +	{ .channels = 6, /* CA_ID 0x09 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, +	{ .channels = 6, /* CA_ID 0x0A */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, +	{ .channels = 6, /* CA_ID 0x0B */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, +	{ .channels = 8, /* CA_ID 0x0C */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, +	{ .channels = 8, /* CA_ID 0x0D */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, +	{ .channels = 8, /* CA_ID 0x0E */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, +	{ .channels = 8, /* CA_ID 0x0F */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, +	{ .channels = 8, /* CA_ID 0x10 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, +	{ .channels = 8, /* CA_ID 0x11 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, +	{ .channels = 8, /* CA_ID 0x12 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, +	{ .channels = 8, /* CA_ID 0x13 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, +		   SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, +	{ .channels = 8, /* CA_ID 0x14 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x15 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x16 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x17 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x18 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x19 */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x1A */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x1B */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x1C */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x1D */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x1E */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ .channels = 8, /* CA_ID 0x1F */ +	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, +		   SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, +		   SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, +	{ } +}; + +/* + * hdmi_codec_channel_alloc: speaker configuration available for CEA + * + * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct + * The preceding ones have better chances to be selected by + * hdmi_codec_get_ch_alloc_table_idx(). + */ +static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = { +	{ .ca_id = 0x00, .n_ch = 2, +	  .mask = FL | FR}, +	/* 2.1 */ +	{ .ca_id = 0x01, .n_ch = 4, +	  .mask = FL | FR | LFE}, +	/* Dolby Surround */ +	{ .ca_id = 0x02, .n_ch = 4, +	  .mask = FL | FR | FC }, +	/* surround51 */ +	{ .ca_id = 0x0b, .n_ch = 6, +	  .mask = FL | FR | LFE | FC | RL | RR}, +	/* surround40 */ +	{ .ca_id = 0x08, .n_ch = 6, +	  .mask = FL | FR | RL | RR }, +	/* surround41 */ +	{ .ca_id = 0x09, .n_ch = 6, +	  .mask = FL | FR | LFE | RL | RR }, +	/* surround50 */ +	{ .ca_id = 0x0a, .n_ch = 6, +	  .mask = FL | FR | FC | RL | RR }, +	/* 6.1 */ +	{ .ca_id = 0x0f, .n_ch = 8, +	  .mask = FL | FR | LFE | FC | RL | RR | RC }, +	/* surround71 */ +	{ .ca_id = 0x13, .n_ch = 8, +	  .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC }, +	/* others */ +	{ .ca_id = 0x03, .n_ch = 8, +	  .mask = FL | FR | LFE | FC }, +	{ .ca_id = 0x04, .n_ch = 8, +	  .mask = FL | FR | RC}, +	{ .ca_id = 0x05, .n_ch = 8, +	  .mask = FL | FR | LFE | RC }, +	{ .ca_id = 0x06, .n_ch = 8, +	  .mask = FL | FR | FC | RC }, +	{ .ca_id = 0x07, .n_ch = 8, +	  .mask = FL | FR | LFE | FC | RC }, +	{ .ca_id = 0x0c, .n_ch = 8, +	  .mask = FL | FR | RC | RL | RR }, +	{ .ca_id = 0x0d, .n_ch = 8, +	  .mask = FL | FR | LFE | RL | RR | RC }, +	{ .ca_id = 0x0e, .n_ch = 8, +	  .mask = FL | FR | FC | RL | RR | RC }, +	{ .ca_id = 0x10, .n_ch = 8, +	  .mask = FL | FR | RL | RR | RLC | RRC }, +	{ .ca_id = 0x11, .n_ch = 8, +	  .mask = FL | FR | LFE | RL | RR | RLC | RRC }, +	{ .ca_id = 0x12, .n_ch = 8, +	  .mask = FL | FR | FC | RL | RR | RLC | RRC }, +	{ .ca_id = 0x14, .n_ch = 8, +	  .mask = FL | FR | FLC | FRC }, +	{ .ca_id = 0x15, .n_ch = 8, +	  .mask = FL | FR | LFE | FLC | FRC }, +	{ .ca_id = 0x16, .n_ch = 8, +	  .mask = FL | FR | FC | FLC | FRC }, +	{ .ca_id = 0x17, .n_ch = 8, +	  .mask = FL | FR | LFE | FC | FLC | FRC }, +	{ .ca_id = 0x18, .n_ch = 8, +	  .mask = FL | FR | RC | FLC | FRC }, +	{ .ca_id = 0x19, .n_ch = 8, +	  .mask = FL | FR | LFE | RC | FLC | FRC }, +	{ .ca_id = 0x1a, .n_ch = 8, +	  .mask = FL | FR | RC | FC | FLC | FRC }, +	{ .ca_id = 0x1b, .n_ch = 8, +	  .mask = FL | FR | LFE | RC | FC | FLC | FRC }, +	{ .ca_id = 0x1c, .n_ch = 8, +	  .mask = FL | FR | RL | RR | FLC | FRC }, +	{ .ca_id = 0x1d, .n_ch = 8, +	  .mask = FL | FR | LFE | RL | RR | FLC | FRC }, +	{ .ca_id = 0x1e, .n_ch = 8, +	  .mask = FL | FR | FC | RL | RR | FLC | FRC }, +	{ .ca_id = 0x1f, .n_ch = 8, +	  .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC }, +}; +  struct hdmi_codec_priv {  	struct hdmi_codec_pdata hcd;  	struct snd_soc_dai_driver *daidrv; @@ -41,6 +295,8 @@ struct hdmi_codec_priv {  	struct snd_pcm_substream *current_stream;  	struct snd_pcm_hw_constraint_list ratec;  	uint8_t eld[MAX_ELD_BYTES]; +	struct snd_pcm_chmap *chmap_info; +	unsigned int chmap_idx;  };  static const struct snd_soc_dapm_widget hdmi_widgets[] = { @@ -79,6 +335,83 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,  	return 0;  } +static unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc) +{ +	int i; +	const unsigned long hdmi_codec_eld_spk_alloc_bits[] = { +		[0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR, +		[4] = RC, [5] = FLC | FRC, [6] = RLC | RRC, +	}; +	unsigned long spk_mask = 0; + +	for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) { +		if (spk_alloc & (1 << i)) +			spk_mask |= hdmi_codec_eld_spk_alloc_bits[i]; +	} + +	return spk_mask; +} + +void hdmi_codec_eld_chmap(struct hdmi_codec_priv *hcp) +{ +	u8 spk_alloc; +	unsigned long spk_mask; + +	spk_alloc = drm_eld_get_spk_alloc(hcp->eld); +	spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); + +	/* Detect if only stereo supported, else return 8 channels mappings */ +	if ((spk_mask & ~(FL | FR)) && hcp->chmap_info->max_channels > 2) +		hcp->chmap_info->chmap = hdmi_codec_8ch_chmaps; +	else +		hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; +} + +static int hdmi_codec_get_ch_alloc_table_idx(struct hdmi_codec_priv *hcp, +					     unsigned char channels) +{ +	int i; +	u8 spk_alloc; +	unsigned long spk_mask; +	const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc; + +	spk_alloc = drm_eld_get_spk_alloc(hcp->eld); +	spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); + +	for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) { +		/* If spk_alloc == 0, HDMI is unplugged return stereo config*/ +		if (!spk_alloc && cap->ca_id == 0) +			return i; +		if (cap->n_ch != channels) +			continue; +		if (!(cap->mask == (spk_mask & cap->mask))) +			continue; +		return i; +	} + +	return -EINVAL; +} +static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol, +			      struct snd_ctl_elem_value *ucontrol) +{ +	unsigned const char *map; +	unsigned int i; +	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); +	struct hdmi_codec_priv *hcp = info->private_data; + +	map = info->chmap[hcp->chmap_idx].map; + +	for (i = 0; i < info->max_channels; i++) { +		if (hcp->chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN) +			ucontrol->value.integer.value[i] = 0; +		else +			ucontrol->value.integer.value[i] = map[i]; +	} + +	return 0; +} + +  static const struct snd_kcontrol_new hdmi_controls[] = {  	{  		.access = SNDRV_CTL_ELEM_ACCESS_READ | @@ -140,6 +473,8 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,  			if (ret)  				return ret;  		} +		/* Select chmap supported */ +		hdmi_codec_eld_chmap(hcp);  	}  	return 0;  } @@ -153,6 +488,7 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,  	WARN_ON(hcp->current_stream != substream); +	hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;  	hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);  	mutex_lock(&hcp->current_stream_lock); @@ -173,7 +509,7 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,  			.dig_subframe = { 0 },  		}  	}; -	int ret; +	int ret, idx;  	dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,  		params_width(params), params_rate(params), @@ -200,6 +536,17 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,  	hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;  	hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; +	/* Select a channel allocation that matches with ELD and pcm channels */ +	idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels); +	if (idx < 0) { +		dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", +			idx); +		hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; +		return idx; +	} +	hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id; +	hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id; +  	hp.sample_width = params_width(params);  	hp.sample_rate = params_rate(params);  	hp.channels = params_channels(params); @@ -328,6 +675,32 @@ static const struct snd_soc_dai_ops hdmi_dai_ops = {  			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\  			 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) +static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, +			      struct snd_soc_dai *dai) +{ +	struct snd_soc_dai_driver *drv = dai->driver; +	struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); +	int ret; + +	dev_dbg(dai->dev, "%s()\n", __func__); + +	ret =  snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK, +				      NULL, drv->playback.channels_max, 0, +				      &hcp->chmap_info); +	if (ret < 0) +		return ret; + +	/* override handlers */ +	hcp->chmap_info->private_data = hcp; +	hcp->chmap_info->kctl->get = hdmi_codec_chmap_ctl_get; + +	/* default chmap supported is stereo */ +	hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; +	hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + +	return 0; +} +  static struct snd_soc_dai_driver hdmi_i2s_dai = {  	.id = DAI_ID_I2S,  	.playback = { @@ -339,6 +712,7 @@ static struct snd_soc_dai_driver hdmi_i2s_dai = {  		.sig_bits = 24,  	},  	.ops = &hdmi_dai_ops, +	.pcm_new = hdmi_codec_pcm_new,  };  static const struct snd_soc_dai_driver hdmi_spdif_dai = { @@ -351,6 +725,7 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {  		.formats = SPDIF_FORMATS,  	},  	.ops = &hdmi_dai_ops, +	.pcm_new = hdmi_codec_pcm_new,  };  static char hdmi_dai_name[][DAI_NAME_SIZE] = { @@ -420,6 +795,7 @@ static int hdmi_codec_probe(struct platform_device *pdev)  		return -ENOMEM;  	hd = NULL; +	mutex_lock(&hdmi_mutex);  	list_for_each(pos, &hdmi_device_list) {  		struct hdmi_device *tmp = pos_to_hdmi_device(pos); @@ -431,13 +807,16 @@ static int hdmi_codec_probe(struct platform_device *pdev)  	if (!hd) {  		hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL); -		if (!hd) +		if (!hd) { +			mutex_unlock(&hdmi_mutex);  			return -ENOMEM; +		}  		hd->dev = dev->parent;  		list_add_tail(&hd->list, &hdmi_device_list);  	} +	mutex_unlock(&hdmi_mutex);  	if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) {  		dev_err(dev, "too many hdmi codec are deteced\n"); @@ -479,7 +858,25 @@ static int hdmi_codec_probe(struct platform_device *pdev)  static int hdmi_codec_remove(struct platform_device *pdev)  { -	snd_soc_unregister_codec(&pdev->dev); +	struct device *dev = &pdev->dev; +	struct list_head *pos; +	struct hdmi_codec_priv *hcp; + +	mutex_lock(&hdmi_mutex); +	list_for_each(pos, &hdmi_device_list) { +		struct hdmi_device *tmp = pos_to_hdmi_device(pos); + +		if (tmp->dev == dev->parent) { +			list_del(pos); +			break; +		} +	} +	mutex_unlock(&hdmi_mutex); + +	hcp = dev_get_drvdata(dev); +	kfree(hcp->chmap_info); +	snd_soc_unregister_codec(dev); +  	return 0;  } diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 584aab83e478..66828480d484 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2456,7 +2456,7 @@ static int max98090_probe(struct snd_soc_codec *codec)  	if (err) {  		micbias = M98090_MBVSEL_2V8;  		dev_info(codec->dev, "use default 2.8v micbias\n"); -	} else if (micbias < M98090_MBVSEL_2V2 || micbias > M98090_MBVSEL_2V8) { +	} else if (micbias > M98090_MBVSEL_2V8) {  		dev_err(codec->dev, "micbias out of range 0x%x\n", micbias);  		micbias = M98090_MBVSEL_2V8;  	} diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 42e2e407e287..6cdf15ab46de 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -309,7 +309,6 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,  	struct snd_soc_codec *codec = codec_dai->codec;  	struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);  	u8 iface1A = 0, iface1B = 0; -	int ret;  	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {  	case SND_SOC_DAIFMT_CBM_CFM: @@ -346,8 +345,8 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,  		return -EINVAL;  	} -	ret = regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); -	ret = regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B); +	regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); +	regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);  	return 0;  } diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c new file mode 100644 index 000000000000..9e8f0f4aa51a --- /dev/null +++ b/sound/soc/codecs/nau8540.c @@ -0,0 +1,835 @@ +/* + * NAU85L40 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: John Hsu <KCHSU0@nuvoton.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/of_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include "nau8540.h" + + +#define NAU_FREF_MAX 13500000 +#define NAU_FVCO_MAX 100000000 +#define NAU_FVCO_MIN 90000000 + +/* the maximum frequency of CLK_ADC */ +#define CLK_ADC_MAX 6144000 + +/* scaling for mclk from sysclk_src output */ +static const struct nau8540_fll_attr mclk_src_scaling[] = { +       { 1, 0x0 }, +       { 2, 0x2 }, +       { 4, 0x3 }, +       { 8, 0x4 }, +       { 16, 0x5 }, +       { 32, 0x6 }, +       { 3, 0x7 }, +       { 6, 0xa }, +       { 12, 0xb }, +       { 24, 0xc }, +}; + +/* ratio for input clk freq */ +static const struct nau8540_fll_attr fll_ratio[] = { +       { 512000, 0x01 }, +       { 256000, 0x02 }, +       { 128000, 0x04 }, +       { 64000, 0x08 }, +       { 32000, 0x10 }, +       { 8000, 0x20 }, +       { 4000, 0x40 }, +}; + +static const struct nau8540_fll_attr fll_pre_scalar[] = { +       { 1, 0x0 }, +       { 2, 0x1 }, +       { 4, 0x2 }, +       { 8, 0x3 }, +}; + +/* over sampling rate */ +static const struct nau8540_osr_attr osr_adc_sel[] = { +       { 32, 3 },      /* OSR 32, SRC 1/8 */ +       { 64, 2 },      /* OSR 64, SRC 1/4 */ +       { 128, 1 },     /* OSR 128, SRC 1/2 */ +       { 256, 0 },     /* OSR 256, SRC 1 */ +}; + +static const struct reg_default nau8540_reg_defaults[] = { +       {NAU8540_REG_POWER_MANAGEMENT, 0x0000}, +       {NAU8540_REG_CLOCK_CTRL, 0x0000}, +       {NAU8540_REG_CLOCK_SRC, 0x0000}, +       {NAU8540_REG_FLL1, 0x0001}, +       {NAU8540_REG_FLL2, 0x3126}, +       {NAU8540_REG_FLL3, 0x0008}, +       {NAU8540_REG_FLL4, 0x0010}, +       {NAU8540_REG_FLL5, 0xC000}, +       {NAU8540_REG_FLL6, 0x6000}, +       {NAU8540_REG_FLL_VCO_RSV, 0xF13C}, +       {NAU8540_REG_PCM_CTRL0, 0x000B}, +       {NAU8540_REG_PCM_CTRL1, 0x3010}, +       {NAU8540_REG_PCM_CTRL2, 0x0800}, +       {NAU8540_REG_PCM_CTRL3, 0x0000}, +       {NAU8540_REG_PCM_CTRL4, 0x000F}, +       {NAU8540_REG_ALC_CONTROL_1, 0x0000}, +       {NAU8540_REG_ALC_CONTROL_2, 0x700B}, +       {NAU8540_REG_ALC_CONTROL_3, 0x0022}, +       {NAU8540_REG_ALC_CONTROL_4, 0x1010}, +       {NAU8540_REG_ALC_CONTROL_5, 0x1010}, +       {NAU8540_REG_NOTCH_FIL1_CH1, 0x0000}, +       {NAU8540_REG_NOTCH_FIL2_CH1, 0x0000}, +       {NAU8540_REG_NOTCH_FIL1_CH2, 0x0000}, +       {NAU8540_REG_NOTCH_FIL2_CH2, 0x0000}, +       {NAU8540_REG_NOTCH_FIL1_CH3, 0x0000}, +       {NAU8540_REG_NOTCH_FIL2_CH3, 0x0000}, +       {NAU8540_REG_NOTCH_FIL1_CH4, 0x0000}, +       {NAU8540_REG_NOTCH_FIL2_CH4, 0x0000}, +       {NAU8540_REG_HPF_FILTER_CH12, 0x0000}, +       {NAU8540_REG_HPF_FILTER_CH34, 0x0000}, +       {NAU8540_REG_ADC_SAMPLE_RATE, 0x0002}, +       {NAU8540_REG_DIGITAL_GAIN_CH1, 0x0400}, +       {NAU8540_REG_DIGITAL_GAIN_CH2, 0x0400}, +       {NAU8540_REG_DIGITAL_GAIN_CH3, 0x0400}, +       {NAU8540_REG_DIGITAL_GAIN_CH4, 0x0400}, +       {NAU8540_REG_DIGITAL_MUX, 0x00E4}, +       {NAU8540_REG_GPIO_CTRL, 0x0000}, +       {NAU8540_REG_MISC_CTRL, 0x0000}, +       {NAU8540_REG_I2C_CTRL, 0xEFFF}, +       {NAU8540_REG_VMID_CTRL, 0x0000}, +       {NAU8540_REG_MUTE, 0x0000}, +       {NAU8540_REG_ANALOG_ADC1, 0x0011}, +       {NAU8540_REG_ANALOG_ADC2, 0x0020}, +       {NAU8540_REG_ANALOG_PWR, 0x0000}, +       {NAU8540_REG_MIC_BIAS, 0x0004}, +       {NAU8540_REG_REFERENCE, 0x0000}, +       {NAU8540_REG_FEPGA1, 0x0000}, +       {NAU8540_REG_FEPGA2, 0x0000}, +       {NAU8540_REG_FEPGA3, 0x0101}, +       {NAU8540_REG_FEPGA4, 0x0101}, +       {NAU8540_REG_PWR, 0x0000}, +}; + +static bool nau8540_readable_reg(struct device *dev, unsigned int reg) +{ +       switch (reg) { +       case NAU8540_REG_POWER_MANAGEMENT ... NAU8540_REG_FLL_VCO_RSV: +       case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4: +       case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5: +       case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ADC_SAMPLE_RATE: +       case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX: +       case NAU8540_REG_P2P_CH1 ... NAU8540_REG_I2C_CTRL: +       case NAU8540_REG_I2C_DEVICE_ID: +       case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE: +       case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR: +               return true; +       default: +               return false; +       } + +} + +static bool nau8540_writeable_reg(struct device *dev, unsigned int reg) +{ +       switch (reg) { +       case NAU8540_REG_SW_RESET ... NAU8540_REG_FLL_VCO_RSV: +       case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4: +       case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5: +       case NAU8540_REG_NOTCH_FIL1_CH1 ... NAU8540_REG_ADC_SAMPLE_RATE: +       case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX: +       case NAU8540_REG_GPIO_CTRL ... NAU8540_REG_I2C_CTRL: +       case NAU8540_REG_RST: +       case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE: +       case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR: +               return true; +       default: +               return false; +       } +} + +static bool nau8540_volatile_reg(struct device *dev, unsigned int reg) +{ +       switch (reg) { +       case NAU8540_REG_SW_RESET: +       case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ALC_STATUS: +       case NAU8540_REG_P2P_CH1 ... NAU8540_REG_PEAK_CH4: +       case NAU8540_REG_I2C_DEVICE_ID: +       case NAU8540_REG_RST: +               return true; +       default: +               return false; +       } +} + + +static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -12800, 3600); +static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600); + +static const struct snd_kcontrol_new nau8540_snd_controls[] = { +       SOC_SINGLE_TLV("Mic1 Volume", NAU8540_REG_DIGITAL_GAIN_CH1, +               0, 0x520, 0, adc_vol_tlv), +       SOC_SINGLE_TLV("Mic2 Volume", NAU8540_REG_DIGITAL_GAIN_CH2, +               0, 0x520, 0, adc_vol_tlv), +       SOC_SINGLE_TLV("Mic3 Volume", NAU8540_REG_DIGITAL_GAIN_CH3, +               0, 0x520, 0, adc_vol_tlv), +       SOC_SINGLE_TLV("Mic4 Volume", NAU8540_REG_DIGITAL_GAIN_CH4, +               0, 0x520, 0, adc_vol_tlv), + +       SOC_SINGLE_TLV("Frontend PGA1 Volume", NAU8540_REG_FEPGA3, +               0, 0x25, 0, fepga_gain_tlv), +       SOC_SINGLE_TLV("Frontend PGA2 Volume", NAU8540_REG_FEPGA3, +               8, 0x25, 0, fepga_gain_tlv), +       SOC_SINGLE_TLV("Frontend PGA3 Volume", NAU8540_REG_FEPGA4, +               0, 0x25, 0, fepga_gain_tlv), +       SOC_SINGLE_TLV("Frontend PGA4 Volume", NAU8540_REG_FEPGA4, +               8, 0x25, 0, fepga_gain_tlv), +}; + +static const char * const adc_channel[] = { +       "ADC channel 1", "ADC channel 2", "ADC channel 3", "ADC channel 4" +}; +static SOC_ENUM_SINGLE_DECL( +       digital_ch4_enum, NAU8540_REG_DIGITAL_MUX, 6, adc_channel); + +static const struct snd_kcontrol_new digital_ch4_mux = +       SOC_DAPM_ENUM("Digital CH4 Select", digital_ch4_enum); + +static SOC_ENUM_SINGLE_DECL( +       digital_ch3_enum, NAU8540_REG_DIGITAL_MUX, 4, adc_channel); + +static const struct snd_kcontrol_new digital_ch3_mux = +       SOC_DAPM_ENUM("Digital CH3 Select", digital_ch3_enum); + +static SOC_ENUM_SINGLE_DECL( +       digital_ch2_enum, NAU8540_REG_DIGITAL_MUX, 2, adc_channel); + +static const struct snd_kcontrol_new digital_ch2_mux = +       SOC_DAPM_ENUM("Digital CH2 Select", digital_ch2_enum); + +static SOC_ENUM_SINGLE_DECL( +       digital_ch1_enum, NAU8540_REG_DIGITAL_MUX, 0, adc_channel); + +static const struct snd_kcontrol_new digital_ch1_mux = +       SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum); + +static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = { +       SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0), +       SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0), + +       SND_SOC_DAPM_INPUT("MIC1"), +       SND_SOC_DAPM_INPUT("MIC2"), +       SND_SOC_DAPM_INPUT("MIC3"), +       SND_SOC_DAPM_INPUT("MIC4"), + +       SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0), +       SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0), +       SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0), +       SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0), + +       SND_SOC_DAPM_ADC("ADC1", NULL, +               NAU8540_REG_POWER_MANAGEMENT, 0, 0), +       SND_SOC_DAPM_ADC("ADC2", NULL, +               NAU8540_REG_POWER_MANAGEMENT, 1, 0), +       SND_SOC_DAPM_ADC("ADC3", NULL, +               NAU8540_REG_POWER_MANAGEMENT, 2, 0), +       SND_SOC_DAPM_ADC("ADC4", NULL, +               NAU8540_REG_POWER_MANAGEMENT, 3, 0), + +       SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0), +       SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0), +       SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0), +       SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0), + +       SND_SOC_DAPM_MUX("Digital CH4 Mux", +               SND_SOC_NOPM, 0, 0, &digital_ch4_mux), +       SND_SOC_DAPM_MUX("Digital CH3 Mux", +               SND_SOC_NOPM, 0, 0, &digital_ch3_mux), +       SND_SOC_DAPM_MUX("Digital CH2 Mux", +               SND_SOC_NOPM, 0, 0, &digital_ch2_mux), +       SND_SOC_DAPM_MUX("Digital CH1 Mux", +               SND_SOC_NOPM, 0, 0, &digital_ch1_mux), + +       SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route nau8540_dapm_routes[] = { +       {"Frontend PGA1", NULL, "MIC1"}, +       {"Frontend PGA2", NULL, "MIC2"}, +       {"Frontend PGA3", NULL, "MIC3"}, +       {"Frontend PGA4", NULL, "MIC4"}, + +       {"ADC1", NULL, "Frontend PGA1"}, +       {"ADC2", NULL, "Frontend PGA2"}, +       {"ADC3", NULL, "Frontend PGA3"}, +       {"ADC4", NULL, "Frontend PGA4"}, + +       {"ADC CH1", NULL, "ADC1"}, +       {"ADC CH2", NULL, "ADC2"}, +       {"ADC CH3", NULL, "ADC3"}, +       {"ADC CH4", NULL, "ADC4"}, + +       {"ADC1", NULL, "MICBIAS1"}, +       {"ADC2", NULL, "MICBIAS1"}, +       {"ADC3", NULL, "MICBIAS2"}, +       {"ADC4", NULL, "MICBIAS2"}, + +       {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"}, +       {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"}, +       {"Digital CH1 Mux", "ADC channel 3", "ADC CH3"}, +       {"Digital CH1 Mux", "ADC channel 4", "ADC CH4"}, + +       {"Digital CH2 Mux", "ADC channel 1", "ADC CH1"}, +       {"Digital CH2 Mux", "ADC channel 2", "ADC CH2"}, +       {"Digital CH2 Mux", "ADC channel 3", "ADC CH3"}, +       {"Digital CH2 Mux", "ADC channel 4", "ADC CH4"}, + +       {"Digital CH3 Mux", "ADC channel 1", "ADC CH1"}, +       {"Digital CH3 Mux", "ADC channel 2", "ADC CH2"}, +       {"Digital CH3 Mux", "ADC channel 3", "ADC CH3"}, +       {"Digital CH3 Mux", "ADC channel 4", "ADC CH4"}, + +       {"Digital CH4 Mux", "ADC channel 1", "ADC CH1"}, +       {"Digital CH4 Mux", "ADC channel 2", "ADC CH2"}, +       {"Digital CH4 Mux", "ADC channel 3", "ADC CH3"}, +       {"Digital CH4 Mux", "ADC channel 4", "ADC CH4"}, + +       {"AIFTX", NULL, "Digital CH1 Mux"}, +       {"AIFTX", NULL, "Digital CH2 Mux"}, +       {"AIFTX", NULL, "Digital CH3 Mux"}, +       {"AIFTX", NULL, "Digital CH4 Mux"}, +}; + +static int nau8540_clock_check(struct nau8540 *nau8540, int rate, int osr) +{ +       int osrate; + +       if (osr >= ARRAY_SIZE(osr_adc_sel)) +               return -EINVAL; +       osrate = osr_adc_sel[osr].osr; + +       if (rate * osr > CLK_ADC_MAX) { +               dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n"); +               return -EINVAL; +       } + +       return 0; +} + +static int nau8540_hw_params(struct snd_pcm_substream *substream, +       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ +       struct snd_soc_codec *codec = dai->codec; +       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec); +       unsigned int val_len = 0, osr; + +       /* CLK_ADC = OSR * FS +        * ADC clock frequency is defined as Over Sampling Rate (OSR) +        * multiplied by the audio sample rate (Fs). Note that the OSR and Fs +        * values must be selected such that the maximum frequency is less +        * than 6.144 MHz. +        */ +       regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr); +       osr &= NAU8540_ADC_OSR_MASK; +       if (nau8540_clock_check(nau8540, params_rate(params), osr)) +               return -EINVAL; +       regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC, +               NAU8540_CLK_ADC_SRC_MASK, +               osr_adc_sel[osr].clk_src << NAU8540_CLK_ADC_SRC_SFT); + +       switch (params_width(params)) { +       case 16: +               val_len |= NAU8540_I2S_DL_16; +               break; +       case 20: +               val_len |= NAU8540_I2S_DL_20; +               break; +       case 24: +               val_len |= NAU8540_I2S_DL_24; +               break; +       case 32: +               val_len |= NAU8540_I2S_DL_32; +               break; +       default: +               return -EINVAL; +       } + +       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0, +               NAU8540_I2S_DL_MASK, val_len); + +       return 0; +} + +static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ +       struct snd_soc_codec *codec = dai->codec; +       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec); +       unsigned int ctrl1_val = 0, ctrl2_val = 0; + +       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +       case SND_SOC_DAIFMT_CBM_CFM: +               ctrl2_val |= NAU8540_I2S_MS_MASTER; +               break; +       case SND_SOC_DAIFMT_CBS_CFS: +               break; +       default: +               return -EINVAL; +       } + +       switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +       case SND_SOC_DAIFMT_NB_NF: +               break; +       case SND_SOC_DAIFMT_IB_NF: +               ctrl1_val |= NAU8540_I2S_BP_INV; +               break; +       default: +               return -EINVAL; +       } + +       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +       case SND_SOC_DAIFMT_I2S: +               ctrl1_val |= NAU8540_I2S_DF_I2S; +               break; +       case SND_SOC_DAIFMT_LEFT_J: +               ctrl1_val |= NAU8540_I2S_DF_LEFT; +               break; +       case SND_SOC_DAIFMT_RIGHT_J: +               ctrl1_val |= NAU8540_I2S_DF_RIGTH; +               break; +       case SND_SOC_DAIFMT_DSP_A: +               ctrl1_val |= NAU8540_I2S_DF_PCM_AB; +               break; +       case SND_SOC_DAIFMT_DSP_B: +               ctrl1_val |= NAU8540_I2S_DF_PCM_AB; +               ctrl1_val |= NAU8540_I2S_PCMB_EN; +               break; +       default: +               return -EINVAL; +       } + +       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0, +               NAU8540_I2S_DL_MASK | NAU8540_I2S_DF_MASK | +               NAU8540_I2S_BP_INV | NAU8540_I2S_PCMB_EN, ctrl1_val); +       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, +               NAU8540_I2S_MS_MASK | NAU8540_I2S_DO12_OE, ctrl2_val); +       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, +               NAU8540_I2S_DO34_OE, 0); + +       return 0; +} + +/** + * nau8540_set_tdm_slot - configure DAI TX TDM. + * @dai: DAI + * @tx_mask: bitmask representing active TX slots. Ex. + *                 0xf for normal 4 channel TDM. + *                 0xf0 for shifted 4 channel TDM + * @rx_mask: no used. + * @slots: Number of slots in use. + * @slot_width: Width in bits for each slot. + * + * Configures a DAI for TDM operation. Only support 4 slots TDM. + */ +static int nau8540_set_tdm_slot(struct snd_soc_dai *dai, +       unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ +       struct snd_soc_codec *codec = dai->codec; +       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec); +       unsigned int ctrl2_val = 0, ctrl4_val = 0; + +       if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf))) +               return -EINVAL; + +       ctrl4_val |= (NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN); +       if (tx_mask & 0xf0) { +               ctrl2_val = 4 * slot_width; +               ctrl4_val |= (tx_mask >> 4); +       } else { +               ctrl4_val |= tx_mask; +       } +       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL4, +               NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN | +               NAU8540_TDM_TX_MASK, ctrl4_val); +       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, +               NAU8540_I2S_DO12_OE, NAU8540_I2S_DO12_OE); +       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, +               NAU8540_I2S_DO34_OE | NAU8540_I2S_TSLOT_L_MASK, +               NAU8540_I2S_DO34_OE | ctrl2_val); + +       return 0; +} + + +static const struct snd_soc_dai_ops nau8540_dai_ops = { +       .hw_params = nau8540_hw_params, +       .set_fmt = nau8540_set_fmt, +       .set_tdm_slot = nau8540_set_tdm_slot, +}; + +#define NAU8540_RATES SNDRV_PCM_RATE_8000_48000 +#define NAU8540_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ +        | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver nau8540_dai = { +       .name = "nau8540-hifi", +       .capture = { +               .stream_name = "Capture", +               .channels_min = 1, +               .channels_max = 4, +               .rates = NAU8540_RATES, +               .formats = NAU8540_FORMATS, +       }, +       .ops = &nau8540_dai_ops, +}; + +/** + * nau8540_calc_fll_param - Calculate FLL parameters. + * @fll_in: external clock provided to codec. + * @fs: sampling rate. + * @fll_param: Pointer to structure of FLL parameters. + * + * Calculate FLL parameters to configure codec. + * + * Returns 0 for success or negative error code. + */ +static int nau8540_calc_fll_param(unsigned int fll_in, +       unsigned int fs, struct nau8540_fll *fll_param) +{ +       u64 fvco, fvco_max; +       unsigned int fref, i, fvco_sel; + +       /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing +        * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. +        * FREF = freq_in / NAU8540_FLL_REF_DIV_MASK +        */ +       for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { +               fref = fll_in / fll_pre_scalar[i].param; +               if (fref <= NAU_FREF_MAX) +                       break; +       } +       if (i == ARRAY_SIZE(fll_pre_scalar)) +               return -EINVAL; +       fll_param->clk_ref_div = fll_pre_scalar[i].val; + +       /* Choose the FLL ratio based on FREF */ +       for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { +               if (fref >= fll_ratio[i].param) +                       break; +       } +       if (i == ARRAY_SIZE(fll_ratio)) +               return -EINVAL; +       fll_param->ratio = fll_ratio[i].val; + +       /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. +        * FDCO must be within the 90MHz - 124MHz or the FFL cannot be +        * guaranteed across the full range of operation. +        * FDCO = freq_out * 2 * mclk_src_scaling +        */ +       fvco_max = 0; +       fvco_sel = ARRAY_SIZE(mclk_src_scaling); +       for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { +               fvco = 256 * fs * 2 * mclk_src_scaling[i].param; +               if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && +                       fvco_max < fvco) { +                       fvco_max = fvco; +                       fvco_sel = i; +               } +       } +       if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel) +               return -EINVAL; +       fll_param->mclk_src = mclk_src_scaling[fvco_sel].val; + +       /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional +        * input based on FDCO, FREF and FLL ratio. +        */ +       fvco = div_u64(fvco_max << 16, fref * fll_param->ratio); +       fll_param->fll_int = (fvco >> 16) & 0x3FF; +       fll_param->fll_frac = fvco & 0xFFFF; +       return 0; +} + +static void nau8540_fll_apply(struct regmap *regmap, +       struct nau8540_fll *fll_param) +{ +       regmap_update_bits(regmap, NAU8540_REG_CLOCK_SRC, +               NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK, +               NAU8540_CLK_SRC_MCLK | fll_param->mclk_src); +       regmap_update_bits(regmap, NAU8540_REG_FLL1, +               NAU8540_FLL_RATIO_MASK, fll_param->ratio); +       /* FLL 16-bit fractional input */ +       regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac); +       /* FLL 10-bit integer input */ +       regmap_update_bits(regmap, NAU8540_REG_FLL3, +               NAU8540_FLL_INTEGER_MASK, fll_param->fll_int); +       /* FLL pre-scaler */ +       regmap_update_bits(regmap, NAU8540_REG_FLL4, +               NAU8540_FLL_REF_DIV_MASK, +               fll_param->clk_ref_div << NAU8540_FLL_REF_DIV_SFT); +       regmap_update_bits(regmap, NAU8540_REG_FLL5, +               NAU8540_FLL_CLK_SW_MASK, NAU8540_FLL_CLK_SW_REF); +       regmap_update_bits(regmap, +               NAU8540_REG_FLL6, NAU8540_DCO_EN, 0); +       if (fll_param->fll_frac) { +               regmap_update_bits(regmap, NAU8540_REG_FLL5, +                       NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN | +                       NAU8540_FLL_FTR_SW_MASK, +                       NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN | +                       NAU8540_FLL_FTR_SW_FILTER); +               regmap_update_bits(regmap, NAU8540_REG_FLL6, +                       NAU8540_SDM_EN, NAU8540_SDM_EN); +       } else { +               regmap_update_bits(regmap, NAU8540_REG_FLL5, +                       NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN | +                       NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU); +               regmap_update_bits(regmap, +                       NAU8540_REG_FLL6, NAU8540_SDM_EN, 0); +       } +} + +/* freq_out must be 256*Fs in order to achieve the best performance */ +static int nau8540_set_pll(struct snd_soc_codec *codec, int pll_id, int source, +               unsigned int freq_in, unsigned int freq_out) +{ +       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec); +       struct nau8540_fll fll_param; +       int ret, fs; + +       switch (pll_id) { +       case NAU8540_CLK_FLL_MCLK: +               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3, +                       NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_MCLK); +               break; + +       case NAU8540_CLK_FLL_BLK: +               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3, +                       NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_BLK); +               break; + +       case NAU8540_CLK_FLL_FS: +               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3, +                       NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_FS); +               break; + +       default: +               dev_err(nau8540->dev, "Invalid clock id (%d)\n", pll_id); +               return -EINVAL; +       } +       dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n", +               freq_out, pll_id); + +       fs = freq_out / 256; +       ret = nau8540_calc_fll_param(freq_in, fs, &fll_param); +       if (ret < 0) { +               dev_err(nau8540->dev, "Unsupported input clock %d\n", freq_in); +               return ret; +       } +       dev_dbg(nau8540->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", +               fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac, +               fll_param.fll_int, fll_param.clk_ref_div); + +       nau8540_fll_apply(nau8540->regmap, &fll_param); +       mdelay(2); +       regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC, +               NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO); + +       return 0; +} + +static int nau8540_set_sysclk(struct snd_soc_codec *codec, +       int clk_id, int source, unsigned int freq, int dir) +{ +       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec); + +       switch (clk_id) { +       case NAU8540_CLK_DIS: +       case NAU8540_CLK_MCLK: +               regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC, +                       NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_MCLK); +               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6, +                       NAU8540_DCO_EN, 0); +               break; + +       case NAU8540_CLK_INTERNAL: +               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6, +                       NAU8540_DCO_EN, NAU8540_DCO_EN); +               regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC, +                       NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO); +               break; + +       default: +               dev_err(nau8540->dev, "Invalid clock id (%d)\n", clk_id); +               return -EINVAL; +       } + +       dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n", +               freq, clk_id); + +       return 0; +} + +static void nau8540_reset_chip(struct regmap *regmap) +{ +       regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00); +       regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00); +} + +static void nau8540_init_regs(struct nau8540 *nau8540) +{ +       struct regmap *regmap = nau8540->regmap; + +       /* Enable Bias/VMID/VMID Tieoff */ +       regmap_update_bits(regmap, NAU8540_REG_VMID_CTRL, +               NAU8540_VMID_EN | NAU8540_VMID_SEL_MASK, +               NAU8540_VMID_EN | (0x2 << NAU8540_VMID_SEL_SFT)); +       regmap_update_bits(regmap, NAU8540_REG_REFERENCE, +               NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN, +               NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN); +       mdelay(2); +       regmap_update_bits(regmap, NAU8540_REG_MIC_BIAS, +               NAU8540_PU_PRE, NAU8540_PU_PRE); +       regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL, +               NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN, +               NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN); +       /* ADC OSR selection, CLK_ADC = Fs * OSR */ +       regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE, +               NAU8540_ADC_OSR_MASK, NAU8540_ADC_OSR_64); +} + +static int __maybe_unused nau8540_suspend(struct snd_soc_codec *codec) +{ +       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec); + +       regcache_cache_only(nau8540->regmap, true); +       regcache_mark_dirty(nau8540->regmap); + +       return 0; +} + +static int __maybe_unused nau8540_resume(struct snd_soc_codec *codec) +{ +       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec); + +       regcache_cache_only(nau8540->regmap, false); +       regcache_sync(nau8540->regmap); + +       return 0; +} + +static struct snd_soc_codec_driver nau8540_codec_driver = { +       .set_sysclk = nau8540_set_sysclk, +       .set_pll = nau8540_set_pll, +       .suspend = nau8540_suspend, +       .resume = nau8540_resume, +       .suspend_bias_off = true, + +       .component_driver = { +               .controls = nau8540_snd_controls, +               .num_controls = ARRAY_SIZE(nau8540_snd_controls), +               .dapm_widgets = nau8540_dapm_widgets, +               .num_dapm_widgets = ARRAY_SIZE(nau8540_dapm_widgets), +               .dapm_routes = nau8540_dapm_routes, +               .num_dapm_routes = ARRAY_SIZE(nau8540_dapm_routes), +       }, +}; + +static const struct regmap_config nau8540_regmap_config = { +       .val_bits = 16, +       .reg_bits = 16, + +       .max_register = NAU8540_REG_MAX, +       .readable_reg = nau8540_readable_reg, +       .writeable_reg = nau8540_writeable_reg, +       .volatile_reg = nau8540_volatile_reg, + +       .cache_type = REGCACHE_RBTREE, +       .reg_defaults = nau8540_reg_defaults, +       .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults), +}; + +static int nau8540_i2c_probe(struct i2c_client *i2c, +       const struct i2c_device_id *id) +{ +       struct device *dev = &i2c->dev; +       struct nau8540 *nau8540 = dev_get_platdata(dev); +       int ret, value; + +       if (!nau8540) { +               nau8540 = devm_kzalloc(dev, sizeof(*nau8540), GFP_KERNEL); +               if (!nau8540) +                       return -ENOMEM; +       } +       i2c_set_clientdata(i2c, nau8540); + +       nau8540->regmap = devm_regmap_init_i2c(i2c, &nau8540_regmap_config); +       if (IS_ERR(nau8540->regmap)) +               return PTR_ERR(nau8540->regmap); +       ret = regmap_read(nau8540->regmap, NAU8540_REG_I2C_DEVICE_ID, &value); +       if (ret < 0) { +               dev_err(dev, "Failed to read device id from the NAU85L40: %d\n", +                       ret); +               return ret; +       } + +       nau8540->dev = dev; +       nau8540_reset_chip(nau8540->regmap); +       nau8540_init_regs(nau8540); + +       return snd_soc_register_codec(dev, +               &nau8540_codec_driver, &nau8540_dai, 1); +} + +static int nau8540_i2c_remove(struct i2c_client *client) +{ +       snd_soc_unregister_codec(&client->dev); +       return 0; +} + + +static const struct i2c_device_id nau8540_i2c_ids[] = { +       { "nau8540", 0 }, +       { } +}; +MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id nau8540_of_ids[] = { +       { .compatible = "nuvoton,nau8540", }, +       {} +}; +MODULE_DEVICE_TABLE(of, nau8540_of_ids); +#endif + +static struct i2c_driver nau8540_i2c_driver = { +       .driver = { +               .name = "nau8540", +               .of_match_table = of_match_ptr(nau8540_of_ids), +       }, +       .probe = nau8540_i2c_probe, +       .remove = nau8540_i2c_remove, +       .id_table = nau8540_i2c_ids, +}; +module_i2c_driver(nau8540_i2c_driver); + +MODULE_DESCRIPTION("ASoC NAU85L40 driver"); +MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h new file mode 100644 index 000000000000..d06e65188cd5 --- /dev/null +++ b/sound/soc/codecs/nau8540.h @@ -0,0 +1,222 @@ +/* + * NAU85L40 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: John Hsu <KCHSU0@nuvoton.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __NAU8540_H__ +#define __NAU8540_H__ + +#define NAU8540_REG_SW_RESET                   0x00 +#define NAU8540_REG_POWER_MANAGEMENT   0x01 +#define NAU8540_REG_CLOCK_CTRL         0x02 +#define NAU8540_REG_CLOCK_SRC                  0x03 +#define NAU8540_REG_FLL1                       0x04 +#define NAU8540_REG_FLL2                       0x05 +#define NAU8540_REG_FLL3                       0x06 +#define NAU8540_REG_FLL4                       0x07 +#define NAU8540_REG_FLL5                       0x08 +#define NAU8540_REG_FLL6                       0x09 +#define NAU8540_REG_FLL_VCO_RSV                0x0A +#define NAU8540_REG_PCM_CTRL0                  0x10 +#define NAU8540_REG_PCM_CTRL1                  0x11 +#define NAU8540_REG_PCM_CTRL2                  0x12 +#define NAU8540_REG_PCM_CTRL3                  0x13 +#define NAU8540_REG_PCM_CTRL4                  0x14 +#define NAU8540_REG_ALC_CONTROL_1              0x20 +#define NAU8540_REG_ALC_CONTROL_2              0x21 +#define NAU8540_REG_ALC_CONTROL_3              0x22 +#define NAU8540_REG_ALC_CONTROL_4              0x23 +#define NAU8540_REG_ALC_CONTROL_5              0x24 +#define NAU8540_REG_ALC_GAIN_CH12              0x2D +#define NAU8540_REG_ALC_GAIN_CH34              0x2E +#define NAU8540_REG_ALC_STATUS         0x2F +#define NAU8540_REG_NOTCH_FIL1_CH1             0x30 +#define NAU8540_REG_NOTCH_FIL2_CH1             0x31 +#define NAU8540_REG_NOTCH_FIL1_CH2             0x32 +#define NAU8540_REG_NOTCH_FIL2_CH2             0x33 +#define NAU8540_REG_NOTCH_FIL1_CH3             0x34 +#define NAU8540_REG_NOTCH_FIL2_CH3             0x35 +#define NAU8540_REG_NOTCH_FIL1_CH4             0x36 +#define NAU8540_REG_NOTCH_FIL2_CH4             0x37 +#define NAU8540_REG_HPF_FILTER_CH12            0x38 +#define NAU8540_REG_HPF_FILTER_CH34            0x39 +#define NAU8540_REG_ADC_SAMPLE_RATE            0x3A +#define NAU8540_REG_DIGITAL_GAIN_CH1           0x40 +#define NAU8540_REG_DIGITAL_GAIN_CH2           0x41 +#define NAU8540_REG_DIGITAL_GAIN_CH3           0x42 +#define NAU8540_REG_DIGITAL_GAIN_CH4           0x43 +#define NAU8540_REG_DIGITAL_MUX                0x44 +#define NAU8540_REG_P2P_CH1                    0x48 +#define NAU8540_REG_P2P_CH2                    0x49 +#define NAU8540_REG_P2P_CH3                    0x4A +#define NAU8540_REG_P2P_CH4                    0x4B +#define NAU8540_REG_PEAK_CH1                   0x4C +#define NAU8540_REG_PEAK_CH2                   0x4D +#define NAU8540_REG_PEAK_CH3                   0x4E +#define NAU8540_REG_PEAK_CH4                   0x4F +#define NAU8540_REG_GPIO_CTRL                  0x50 +#define NAU8540_REG_MISC_CTRL                  0x51 +#define NAU8540_REG_I2C_CTRL                   0x52 +#define NAU8540_REG_I2C_DEVICE_ID              0x58 +#define NAU8540_REG_RST                        0x5A +#define NAU8540_REG_VMID_CTRL                  0x60 +#define NAU8540_REG_MUTE                       0x61 +#define NAU8540_REG_ANALOG_ADC1                0x64 +#define NAU8540_REG_ANALOG_ADC2                0x65 +#define NAU8540_REG_ANALOG_PWR         0x66 +#define NAU8540_REG_MIC_BIAS                   0x67 +#define NAU8540_REG_REFERENCE                  0x68 +#define NAU8540_REG_FEPGA1                     0x69 +#define NAU8540_REG_FEPGA2                     0x6A +#define NAU8540_REG_FEPGA3                     0x6B +#define NAU8540_REG_FEPGA4                     0x6C +#define NAU8540_REG_PWR                        0x6D +#define NAU8540_REG_MAX                        NAU8540_REG_PWR + + +/* POWER_MANAGEMENT (0x01) */ +#define NAU8540_ADC4_EN                (0x1 << 3) +#define NAU8540_ADC3_EN                (0x1 << 2) +#define NAU8540_ADC2_EN                (0x1 << 1) +#define NAU8540_ADC1_EN                0x1 + +/* CLOCK_CTRL (0x02) */ +#define NAU8540_CLK_ADC_EN             (0x1 << 15) +#define NAU8540_CLK_I2S_EN             (0x1 << 1) + +/* CLOCK_SRC (0x03) */ +#define NAU8540_CLK_SRC_SFT            15 +#define NAU8540_CLK_SRC_MASK           (1 << NAU8540_CLK_SRC_SFT) +#define NAU8540_CLK_SRC_VCO            (1 << NAU8540_CLK_SRC_SFT) +#define NAU8540_CLK_SRC_MCLK           (0 << NAU8540_CLK_SRC_SFT) +#define NAU8540_CLK_ADC_SRC_SFT        6 +#define NAU8540_CLK_ADC_SRC_MASK       (0x3 << NAU8540_CLK_ADC_SRC_SFT) +#define NAU8540_CLK_MCLK_SRC_MASK      0xf + +/* FLL1 (0x04) */ +#define NAU8540_FLL_RATIO_MASK 0x7f + +/* FLL3 (0x06) */ +#define NAU8540_FLL_CLK_SRC_SFT        10 +#define NAU8540_FLL_CLK_SRC_MASK       (0x3 << NAU8540_FLL_CLK_SRC_SFT) +#define NAU8540_FLL_CLK_SRC_MCLK       (0 << NAU8540_FLL_CLK_SRC_SFT) +#define NAU8540_FLL_CLK_SRC_BLK        (0x2 << NAU8540_FLL_CLK_SRC_SFT) +#define NAU8540_FLL_CLK_SRC_FS         (0x3 << NAU8540_FLL_CLK_SRC_SFT) +#define NAU8540_FLL_INTEGER_MASK       0x3ff + +/* FLL4 (0x07) */ +#define NAU8540_FLL_REF_DIV_SFT        10 +#define NAU8540_FLL_REF_DIV_MASK       (0x3 << NAU8540_FLL_REF_DIV_SFT) + +/* FLL5 (0x08) */ +#define NAU8540_FLL_PDB_DAC_EN (0x1 << 15) +#define NAU8540_FLL_LOOP_FTR_EN        (0x1 << 14) +#define NAU8540_FLL_CLK_SW_MASK        (0x1 << 13) +#define NAU8540_FLL_CLK_SW_N2          (0x1 << 13) +#define NAU8540_FLL_CLK_SW_REF (0x0 << 13) +#define NAU8540_FLL_FTR_SW_MASK        (0x1 << 12) +#define NAU8540_FLL_FTR_SW_ACCU        (0x1 << 12) +#define NAU8540_FLL_FTR_SW_FILTER      (0x0 << 12) + +/* FLL6 (0x9) */ +#define NAU8540_DCO_EN                 (0x1 << 15) +#define NAU8540_SDM_EN                 (0x1 << 14) + +/* PCM_CTRL0 (0x10) */ +#define NAU8540_I2S_BP_SFT             7 +#define NAU8540_I2S_BP_INV             (0x1 << NAU8540_I2S_BP_SFT) +#define NAU8540_I2S_PCMB_SFT           6 +#define NAU8540_I2S_PCMB_EN            (0x1 << NAU8540_I2S_PCMB_SFT) +#define NAU8540_I2S_DL_SFT             2 +#define NAU8540_I2S_DL_MASK            (0x3 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DL_16              (0 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DL_20              (0x1 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DL_24              (0x2 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DL_32              (0x3 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DF_MASK            0x3 +#define NAU8540_I2S_DF_RIGTH           0 +#define NAU8540_I2S_DF_LEFT            0x1 +#define NAU8540_I2S_DF_I2S             0x2 +#define NAU8540_I2S_DF_PCM_AB          0x3 + +/* PCM_CTRL1 (0x11) */ +#define NAU8540_I2S_LRC_DIV_SFT        12 +#define NAU8540_I2S_LRC_DIV_MASK       (0x3 << NAU8540_I2S_LRC_DIV_SFT) +#define NAU8540_I2S_DO12_OE            (0x1 << 4) +#define NAU8540_I2S_MS_SFT             3 +#define NAU8540_I2S_MS_MASK            (0x1 << NAU8540_I2S_MS_SFT) +#define NAU8540_I2S_MS_MASTER          (0x1 << NAU8540_I2S_MS_SFT) +#define NAU8540_I2S_MS_SLAVE           (0x0 << NAU8540_I2S_MS_SFT) +#define NAU8540_I2S_BLK_DIV_MASK       0x7 + +/* PCM_CTRL1 (0x12) */ +#define NAU8540_I2S_DO34_OE            (0x1 << 11) +#define NAU8540_I2S_TSLOT_L_MASK       0x3ff + +/* PCM_CTRL4 (0x14) */ +#define NAU8540_TDM_MODE               (0x1 << 15) +#define NAU8540_TDM_OFFSET_EN          (0x1 << 14) +#define NAU8540_TDM_TX_MASK            0xf + +/* ADC_SAMPLE_RATE (0x3A) */ +#define NAU8540_ADC_OSR_MASK           0x3 +#define NAU8540_ADC_OSR_256            0x3 +#define NAU8540_ADC_OSR_128            0x2 +#define NAU8540_ADC_OSR_64             0x1 +#define NAU8540_ADC_OSR_32             0x0 + +/* VMID_CTRL (0x60) */ +#define NAU8540_VMID_EN                (1 << 6) +#define NAU8540_VMID_SEL_SFT           4 +#define NAU8540_VMID_SEL_MASK          (0x3 << NAU8540_VMID_SEL_SFT) + +/* MIC_BIAS (0x67) */ +#define NAU8540_PU_PRE                 (0x1 << 8) + +/* REFERENCE (0x68) */ +#define NAU8540_PRECHARGE_DIS          (0x1 << 13) +#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12) + + +/* System Clock Source */ +enum { +       NAU8540_CLK_DIS, +       NAU8540_CLK_MCLK, +       NAU8540_CLK_INTERNAL, +       NAU8540_CLK_FLL_MCLK, +       NAU8540_CLK_FLL_BLK, +       NAU8540_CLK_FLL_FS, +}; + +struct nau8540 { +       struct device *dev; +       struct regmap *regmap; +}; + +struct nau8540_fll { +       int mclk_src; +       int ratio; +       int fll_frac; +       int fll_int; +       int clk_ref_div; +}; + +struct nau8540_fll_attr { +       unsigned int param; +       unsigned int val; +}; + +/* over sampling rate */ +struct nau8540_osr_attr { +       unsigned int osr; +       unsigned int clk_src; +}; + + +#endif /* __NAU8540_H__ */ diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 4576f987a4a5..97fbeba9498f 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1231,7 +1231,7 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,  {  	struct snd_soc_codec *codec = dai->codec;  	struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); -	unsigned int val_len = 0, osr; +	unsigned int val_len = 0, osr, ctrl_val, bclk_fs, bclk_div;  	nau8825_sema_acquire(nau8825, 3 * HZ); @@ -1261,6 +1261,24 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,  			osr_adc_sel[osr].clk_src << NAU8825_CLK_ADC_SRC_SFT);  	} +	/* make BCLK and LRC divde configuration if the codec as master. */ +	regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, &ctrl_val); +	if (ctrl_val & NAU8825_I2S_MS_MASTER) { +		/* get the bclk and fs ratio */ +		bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params); +		if (bclk_fs <= 32) +			bclk_div = 2; +		else if (bclk_fs <= 64) +			bclk_div = 1; +		else if (bclk_fs <= 128) +			bclk_div = 0; +		else +			return -EINVAL; +		regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, +			NAU8825_I2S_LRC_DIV_MASK | NAU8825_I2S_BLK_DIV_MASK, +			((bclk_div + 1) << NAU8825_I2S_LRC_DIV_SFT) | bclk_div); +	} +  	switch (params_width(params)) {  	case 16:  		val_len |= NAU8825_I2S_DL_16; diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 39bc02d5bc5d..b9d1207ccef2 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -402,10 +402,8 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,  	u32 val, mask, shift, reg;  	unsigned int rate, fmt, ratio, max_ratio;  	int i, min_frame_size; -	snd_pcm_format_t format;  	rate = params_rate(params); -	format = params_format(params);  	ratio = pcm3168a->sysclk / rate; diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 7150a407ffd9..d9e96e65e1c4 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1163,6 +1163,13 @@ static const struct dmi_system_id force_combo_jack_table[] = {  			DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P")  		}  	}, +	{ +		.ident = "Intel Gemini Lake", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Geminilake") +		} +	},  	{ }  }; diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 0901e25d6db6..7ed62e8c80b4 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -21,7 +21,6 @@  #include <linux/gpio.h>  #include <linux/sched.h>  #include <linux/uaccess.h> -#include <linux/miscdevice.h>  #include <linux/regulator/consumer.h>  #include <linux/pm_qos.h>  #include <linux/sysfs.h> diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index e29a6defefa0..1584ccc3a87b 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -995,7 +995,7 @@ static int rt5640_hp_event(struct snd_soc_dapm_widget *w,  	case SND_SOC_DAPM_PRE_PMD:  		rt5640->hp_mute = 1; -		usleep_range(70000, 75000); +		msleep(70);  		break;  	default: @@ -1059,7 +1059,7 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,  	switch (event) {  	case SND_SOC_DAPM_POST_PMU:  		if (!rt5640->hp_mute) -			usleep_range(80000, 85000); +			msleep(80);  		break; @@ -1227,6 +1227,10 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {  		RT5640_PWR_DAC_L1_BIT, 0, NULL, 0),  	SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5640_PWR_DIG1,  		RT5640_PWR_DAC_R1_BIT, 0, NULL, 0), +	SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5640_PWR_DIG1, +		RT5640_PWR_DAC_L2_BIT, 0, NULL, 0), +	SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5640_PWR_DIG1, +		RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),  	/* SPK/OUT Mixer */  	SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT,  		0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)), @@ -1322,10 +1326,6 @@ static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {  		rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),  	SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,  		RT5640_PWR_MA_BIT, 0, NULL, 0), -	SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5640_PWR_DIG1, -		RT5640_PWR_DAC_L2_BIT, 0, NULL, 0), -	SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5640_PWR_DIG1, -		RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),  	SND_SOC_DAPM_OUTPUT("MONOP"),  	SND_SOC_DAPM_OUTPUT("MONON"), @@ -2313,6 +2313,7 @@ MODULE_DEVICE_TABLE(of, rt5640_of_match);  #ifdef CONFIG_ACPI  static const struct acpi_device_id rt5640_acpi_match[] = {  	{ "INT33CA", 0 }, +	{ "10EC3276", 0 },  	{ "10EC5640", 0 },  	{ "10EC5642", 0 },  	{ "INTCCFFD", 0 }, diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 1ac96ef9ee20..e149f3ce5401 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3109,7 +3109,7 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)  	unsigned int val;  	if (jack_insert) { -		regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0006); +		regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06);  		/* for jack type detect */  		snd_soc_dapm_force_enable_pin(dapm, "LDO2"); @@ -3545,8 +3545,10 @@ MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);  #ifdef CONFIG_ACPI  static const struct acpi_device_id rt5645_acpi_match[] = {  	{ "10EC5645", 0 }, +	{ "10EC5648", 0 },  	{ "10EC5650", 0 },  	{ "10EC5640", 0 }, +	{ "10EC3270", 0 },  	{},  };  MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match); @@ -3658,8 +3660,14 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,  						       GPIOD_IN);  	if (IS_ERR(rt5645->gpiod_hp_det)) { -		dev_err(&i2c->dev, "failed to initialize gpiod\n"); -		return PTR_ERR(rt5645->gpiod_hp_det); +		dev_info(&i2c->dev, "failed to initialize gpiod\n"); +		ret = PTR_ERR(rt5645->gpiod_hp_det); +		/* +		 * Continue if optional gpiod is missing, bail for all other +		 * errors, including -EPROBE_DEFER +		 */ +		if (ret != -ENOENT) +			return ret;  	}  	for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++) diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index db54550aed60..1b7060850340 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -1150,28 +1150,28 @@ static const char * const rt5659_data_select[] = {  	"L/R", "R/L", "L/L", "R/R"  }; -static const SOC_ENUM_SINGLE_DECL(rt5659_if1_01_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5659_if1_01_adc_enum,  	RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT01_SFT, rt5659_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5659_if1_23_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5659_if1_23_adc_enum,  	RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT23_SFT, rt5659_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5659_if1_45_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5659_if1_45_adc_enum,  	RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT45_SFT, rt5659_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5659_if1_67_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5659_if1_67_adc_enum,  	RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT67_SFT, rt5659_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5659_if2_dac_enum, +static SOC_ENUM_SINGLE_DECL(rt5659_if2_dac_enum,  	RT5659_DIG_INF23_DATA, RT5659_IF2_DAC_SEL_SFT, rt5659_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5659_if2_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5659_if2_adc_enum,  	RT5659_DIG_INF23_DATA, RT5659_IF2_ADC_SEL_SFT, rt5659_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5659_if3_dac_enum, +static SOC_ENUM_SINGLE_DECL(rt5659_if3_dac_enum,  	RT5659_DIG_INF23_DATA, RT5659_IF3_DAC_SEL_SFT, rt5659_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5659_if3_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5659_if3_adc_enum,  	RT5659_DIG_INF23_DATA, RT5659_IF3_ADC_SEL_SFT, rt5659_data_select);  static const struct snd_kcontrol_new rt5659_if1_01_adc_swap_mux = @@ -1207,31 +1207,31 @@ static unsigned int rt5659_asrc_clk_map_values[] = {  	0, 1, 2, 3, 5, 6,  }; -static const SOC_VALUE_ENUM_SINGLE_DECL( +static SOC_VALUE_ENUM_SINGLE_DECL(  	rt5659_da_sto_asrc_enum, RT5659_ASRC_2, RT5659_DA_STO_T_SFT, 0x7,  	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values); -static const SOC_VALUE_ENUM_SINGLE_DECL( +static SOC_VALUE_ENUM_SINGLE_DECL(  	rt5659_da_monol_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_L_T_SFT, 0x7,  	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values); -static const SOC_VALUE_ENUM_SINGLE_DECL( +static SOC_VALUE_ENUM_SINGLE_DECL(  	rt5659_da_monor_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_R_T_SFT, 0x7,  	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values); -static const SOC_VALUE_ENUM_SINGLE_DECL( +static SOC_VALUE_ENUM_SINGLE_DECL(  	rt5659_ad_sto1_asrc_enum, RT5659_ASRC_2, RT5659_AD_STO1_T_SFT, 0x7,  	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values); -static const SOC_VALUE_ENUM_SINGLE_DECL( +static SOC_VALUE_ENUM_SINGLE_DECL(  	rt5659_ad_sto2_asrc_enum, RT5659_ASRC_3, RT5659_AD_STO2_T_SFT, 0x7,  	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values); -static const SOC_VALUE_ENUM_SINGLE_DECL( +static SOC_VALUE_ENUM_SINGLE_DECL(  	rt5659_ad_monol_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_L_T_SFT, 0x7,  	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values); -static const SOC_VALUE_ENUM_SINGLE_DECL( +static SOC_VALUE_ENUM_SINGLE_DECL(  	rt5659_ad_monor_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_R_T_SFT, 0x7,  	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values); @@ -1930,14 +1930,14 @@ static const char * const rt5659_dac2_src[] = {  	"IF1 DAC2", "IF2 DAC", "IF3 DAC", "Mono ADC MIX"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_dac_l2_enum, RT5659_DAC_CTRL,  	RT5659_DAC_L2_SEL_SFT, rt5659_dac2_src);  static const struct snd_kcontrol_new rt5659_dac_l2_mux =  	SOC_DAPM_ENUM("DAC L2 Source", rt5659_dac_l2_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_dac_r2_enum, RT5659_DAC_CTRL,  	RT5659_DAC_R2_SEL_SFT, rt5659_dac2_src); @@ -1951,7 +1951,7 @@ static const char * const rt5659_sto1_adc1_src[] = {  	"DAC MIX", "ADC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_sto1_adc1_enum, RT5659_STO1_ADC_MIXER,  	RT5659_STO1_ADC1_SRC_SFT, rt5659_sto1_adc1_src); @@ -1964,7 +1964,7 @@ static const char * const rt5659_sto1_adc_src[] = {  	"ADC1", "ADC2"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_sto1_adc_enum, RT5659_STO1_ADC_MIXER,  	RT5659_STO1_ADC_SRC_SFT, rt5659_sto1_adc_src); @@ -1977,7 +1977,7 @@ static const char * const rt5659_sto1_adc2_src[] = {  	"DAC MIX", "DMIC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_sto1_adc2_enum, RT5659_STO1_ADC_MIXER,  	RT5659_STO1_ADC2_SRC_SFT, rt5659_sto1_adc2_src); @@ -1990,7 +1990,7 @@ static const char * const rt5659_sto1_dmic_src[] = {  	"DMIC1", "DMIC2"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_sto1_dmic_enum, RT5659_STO1_ADC_MIXER,  	RT5659_STO1_DMIC_SRC_SFT, rt5659_sto1_dmic_src); @@ -2004,7 +2004,7 @@ static const char * const rt5659_mono_adc_l2_src[] = {  	"Mono DAC MIXL", "DMIC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_mono_adc_l2_enum, RT5659_MONO_ADC_MIXER,  	RT5659_MONO_ADC_L2_SRC_SFT, rt5659_mono_adc_l2_src); @@ -2018,7 +2018,7 @@ static const char * const rt5659_mono_adc_l1_src[] = {  	"Mono DAC MIXL", "ADC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_mono_adc_l1_enum, RT5659_MONO_ADC_MIXER,  	RT5659_MONO_ADC_L1_SRC_SFT, rt5659_mono_adc_l1_src); @@ -2031,14 +2031,14 @@ static const char * const rt5659_mono_adc_src[] = {  	"ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_mono_adc_l_enum, RT5659_MONO_ADC_MIXER,  	RT5659_MONO_ADC_L_SRC_SFT, rt5659_mono_adc_src);  static const struct snd_kcontrol_new rt5659_mono_adc_l_mux =  	SOC_DAPM_ENUM("Mono ADC L Source", rt5659_mono_adc_l_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_mono_adcr_enum, RT5659_MONO_ADC_MIXER,  	RT5659_MONO_ADC_R_SRC_SFT, rt5659_mono_adc_src); @@ -2051,7 +2051,7 @@ static const char * const rt5659_mono_dmic_l_src[] = {  	"DMIC1 L", "DMIC2 L"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_mono_dmic_l_enum, RT5659_MONO_ADC_MIXER,  	RT5659_MONO_DMIC_L_SRC_SFT, rt5659_mono_dmic_l_src); @@ -2064,7 +2064,7 @@ static const char * const rt5659_mono_adc_r2_src[] = {  	"Mono DAC MIXR", "DMIC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_mono_adc_r2_enum, RT5659_MONO_ADC_MIXER,  	RT5659_MONO_ADC_R2_SRC_SFT, rt5659_mono_adc_r2_src); @@ -2077,7 +2077,7 @@ static const char * const rt5659_mono_adc_r1_src[] = {  	"Mono DAC MIXR", "ADC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_mono_adc_r1_enum, RT5659_MONO_ADC_MIXER,  	RT5659_MONO_ADC_R1_SRC_SFT, rt5659_mono_adc_r1_src); @@ -2090,7 +2090,7 @@ static const char * const rt5659_mono_dmic_r_src[] = {  	"DMIC1 R", "DMIC2 R"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_mono_dmic_r_enum, RT5659_MONO_ADC_MIXER,  	RT5659_MONO_DMIC_R_SRC_SFT, rt5659_mono_dmic_r_src); @@ -2104,14 +2104,14 @@ static const char * const rt5659_dac1_src[] = {  	"IF1 DAC1", "IF2 DAC", "IF3 DAC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_dac_r1_enum, RT5659_AD_DA_MIXER,  	RT5659_DAC1_R_SEL_SFT, rt5659_dac1_src);  static const struct snd_kcontrol_new rt5659_dac_r1_mux =  	SOC_DAPM_ENUM("DAC R1 Source", rt5659_dac_r1_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_dac_l1_enum, RT5659_AD_DA_MIXER,  	RT5659_DAC1_L_SEL_SFT, rt5659_dac1_src); @@ -2124,14 +2124,14 @@ static const char * const rt5659_dig_dac_mix_src[] = {  	"Stereo DAC Mixer", "Mono DAC Mixer"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_dig_dac_mixl_enum, RT5659_DIG_MIXER,  	RT5659_DAC_MIX_L_SFT, rt5659_dig_dac_mix_src);  static const struct snd_kcontrol_new rt5659_dig_dac_mixl_mux =  	SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5659_dig_dac_mixl_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_dig_dac_mixr_enum, RT5659_DIG_MIXER,  	RT5659_DAC_MIX_R_SFT, rt5659_dig_dac_mix_src); @@ -2144,14 +2144,14 @@ static const char * const rt5659_alg_dac1_src[] = {  	"DAC", "Stereo DAC Mixer"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_alg_dac_l1_enum, RT5659_A_DAC_MUX,  	RT5659_A_DACL1_SFT, rt5659_alg_dac1_src);  static const struct snd_kcontrol_new rt5659_alg_dac_l1_mux =  	SOC_DAPM_ENUM("Analog DACL1 Source", rt5659_alg_dac_l1_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_alg_dac_r1_enum, RT5659_A_DAC_MUX,  	RT5659_A_DACR1_SFT, rt5659_alg_dac1_src); @@ -2164,14 +2164,14 @@ static const char * const rt5659_alg_dac2_src[] = {  	"Stereo DAC Mixer", "Mono DAC Mixer"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_alg_dac_l2_enum, RT5659_A_DAC_MUX,  	RT5659_A_DACL2_SFT, rt5659_alg_dac2_src);  static const struct snd_kcontrol_new rt5659_alg_dac_l2_mux =  	SOC_DAPM_ENUM("Analog DAC L2 Source", rt5659_alg_dac_l2_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_alg_dac_r2_enum, RT5659_A_DAC_MUX,  	RT5659_A_DACR2_SFT, rt5659_alg_dac2_src); @@ -2184,7 +2184,7 @@ static const char * const rt5659_if2_adc_in_src[] = {  	"IF_ADC1", "IF_ADC2", "DAC_REF", "IF_ADC3"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_if2_adc_in_enum, RT5659_DIG_INF23_DATA,  	RT5659_IF2_ADC_IN_SFT, rt5659_if2_adc_in_src); @@ -2197,7 +2197,7 @@ static const char * const rt5659_if3_adc_in_src[] = {  	"IF_ADC1", "IF_ADC2", "DAC_REF", "Stereo2_ADC_L/R"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_if3_adc_in_enum, RT5659_DIG_INF23_DATA,  	RT5659_IF3_ADC_IN_SFT, rt5659_if3_adc_in_src); @@ -2210,14 +2210,14 @@ static const char * const rt5659_pdm_src[] = {  	"Mono DAC", "Stereo DAC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_pdm_l_enum, RT5659_PDM_OUT_CTRL,  	RT5659_PDM1_L_SFT, rt5659_pdm_src);  static const struct snd_kcontrol_new rt5659_pdm_l_mux =  	SOC_DAPM_ENUM("PDM L Source", rt5659_pdm_l_enum); -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_pdm_r_enum, RT5659_PDM_OUT_CTRL,  	RT5659_PDM1_R_SFT, rt5659_pdm_src); @@ -2230,7 +2230,7 @@ static const char * const rt5659_spdif_src[] = {  	"IF1_DAC1", "IF1_DAC2", "IF2_DAC", "IF3_DAC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_spdif_enum, RT5659_SPDIF_CTRL,  	RT5659_SPDIF_SEL_SFT, rt5659_spdif_src); @@ -2250,7 +2250,7 @@ static const char * const rt5659_rx_adc_data_src[] = {  	"NUL:AD2:DAC:AD1", "NUL:DAC:DAC:AD2", "NUL:DAC:AD2:DAC"  }; -static const SOC_ENUM_SINGLE_DECL( +static SOC_ENUM_SINGLE_DECL(  	rt5659_rx_adc_data_enum, RT5659_TDM_CTRL_2,  	RT5659_ADCDAT_SRC_SFT, rt5659_rx_adc_data_src); @@ -4018,7 +4018,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,  							GPIOD_OUT_HIGH);  	/* Sleep for 300 ms miniumum */ -	usleep_range(300000, 350000); +	msleep(300);  	rt5659->regmap = devm_regmap_init_i2c(i2c, &rt5659_regmap);  	if (IS_ERR(rt5659->regmap)) { @@ -4230,10 +4230,9 @@ static struct acpi_device_id rt5659_acpi_match[] = {  MODULE_DEVICE_TABLE(acpi, rt5659_acpi_match);  #endif -struct i2c_driver rt5659_i2c_driver = { +static struct i2c_driver rt5659_i2c_driver = {  	.driver = {  		.name = "rt5659", -		.owner = THIS_MODULE,  		.of_match_table = of_match_ptr(rt5659_of_match),  		.acpi_match_table = ACPI_PTR(rt5659_acpi_match),  	}, diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 76cf76a2e9b6..c93490d77f2a 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -526,10 +526,10 @@ static const char * const rt5660_data_select[] = {  	"L/R", "R/L", "L/L", "R/R"  }; -static const SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum, +static SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum,  	RT5660_DIG_INF1_DATA, RT5660_IF1_DAC_IN_SFT, rt5660_data_select); -static const SOC_ENUM_SINGLE_DECL(rt5660_if1_adc_enum, +static SOC_ENUM_SINGLE_DECL(rt5660_if1_adc_enum,  	RT5660_DIG_INF1_DATA, RT5660_IF1_ADC_IN_SFT, rt5660_data_select);  static const struct snd_kcontrol_new rt5660_if1_dac_swap_mux = @@ -1152,7 +1152,7 @@ static int rt5660_resume(struct snd_soc_codec *codec)  	struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);  	if (rt5660->pdata.poweroff_codec_in_suspend) -		usleep_range(350000, 400000); +		msleep(350);  	regcache_cache_only(rt5660->regmap, false);  	regcache_sync(rt5660->regmap); diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 97bafac3bc15..17d20b99f041 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2814,6 +2814,7 @@ MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);  static const struct acpi_device_id rt5670_acpi_match[] = {  	{ "10EC5670", 0},  	{ "10EC5672", 0}, +	{ "10EC5640", 0}, /* quirk */  	{ },  };  MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match); diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index ebd0f7c5ad3b..bd51f3655ee3 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -21,7 +21,6 @@  #include <linux/gpio.h>  #include <linux/sched.h>  #include <linux/uaccess.h> -#include <linux/miscdevice.h>  #include <linux/regulator/consumer.h>  #include <linux/pm_qos.h>  #include <linux/sysfs.h> diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index bb94d50052d7..29bf8c81ae02 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1393,6 +1393,12 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)  			snd_soc_write(codec, AIC3X_PLL_PROGC_REG, pll_c);  			snd_soc_write(codec, AIC3X_PLL_PROGD_REG, pll_d);  		} + +		/* +		 * Delay is needed to reduce pop-noise after syncing back the +		 * registers +		 */ +		mdelay(50);  	} else {  		/*  		 * Do soft reset to this codec instance in order to clear diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 0eb5dcf4c29d..4f5f5710b569 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -21,7 +21,6 @@  #include <linux/firmware.h>  #include <linux/delay.h>  #include <linux/fs.h> -#include <linux/miscdevice.h>  #include <linux/gpio.h>  #include <linux/regulator/consumer.h>  #include <linux/mutex.h> diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index e7ab37d0dd32..1fe358e6be61 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -855,6 +855,8 @@ ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2),  ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2),  ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), +  ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE), @@ -1944,7 +1946,10 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)  	if (ret)  		goto err_adsp2_codec_probe; -	arizona_init_spk(codec); +	ret = arizona_init_spk(codec); +	if (ret < 0) +		return ret; +  	arizona_init_gpio(codec);  	arizona_init_notifiers(codec); diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 585fc706c1b0..1bc942152eff 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -778,6 +778,11 @@ SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),  SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),  SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), +WM_ADSP2_PRELOAD_SWITCH("DSP2", 2), +WM_ADSP2_PRELOAD_SWITCH("DSP3", 3), +WM_ADSP2_PRELOAD_SWITCH("DSP4", 4), +  ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE), @@ -2279,7 +2284,10 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)  	priv->core.arizona->dapm = dapm; -	arizona_init_spk(codec); +	ret = arizona_init_spk(codec); +	if (ret < 0) +		return ret; +  	arizona_init_gpio(codec);  	arizona_init_mono(codec);  	arizona_init_notifiers(codec); diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h index e9c0c76ab73b..c7c6f15b0e42 100644 --- a/sound/soc/codecs/wm8731.h +++ b/sound/soc/codecs/wm8731.h @@ -31,8 +31,8 @@  #define WM8731_CACHEREGNUM 	10 +#define WM8731_SYSCLK_MCLK 0  #define WM8731_SYSCLK_XTAL 1 -#define WM8731_SYSCLK_MCLK 2  #define WM8731_DAI		0 diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 565d477cd790..b8c1940f2243 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -37,8 +37,6 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {  	"DVDD",  }; -#define WM8741_NUM_RATES 6 -  /* codec private data */  struct wm8741_priv {  	struct wm8741_platform_data pdata; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 9bdf5447f6f6..d05d76e79c70 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -280,6 +280,7 @@ static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0);  static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);  static const struct snd_kcontrol_new wm8753_snd_controls[] = { +SOC_SINGLE("Hi-Fi DAC Left/Right channel Swap", WM8753_HIFI, 5, 1, 0),  SOC_DOUBLE_R_TLV("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0, dac_tlv),  SOC_DOUBLE_R_TLV("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0, @@ -1087,7 +1088,7 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec,  {  	u16 ioctl, hifi; -	hifi = snd_soc_read(codec, WM8753_HIFI) & 0x011f; +	hifi = snd_soc_read(codec, WM8753_HIFI) & 0x013f;  	ioctl = snd_soc_read(codec, WM8753_IOCTL) & 0x00ae;  	/* set master/slave audio interface */ diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index ee0c8639c743..49401a8aae64 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -1062,8 +1062,12 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec)  	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);  	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);  	struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); +	int ret; + +	ret = arizona_init_spk(codec); +	if (ret < 0) +		return ret; -	arizona_init_spk(codec);  	arizona_init_notifiers(codec);  	snd_soc_component_disable_pin(component, "HAPTICS"); diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 3694f5958d86..44f447136e22 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -1321,10 +1321,14 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec)  	struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);  	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);  	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); +	int ret;  	priv->core.arizona->dapm = dapm; -	arizona_init_spk(codec); +	ret = arizona_init_spk(codec); +	if (ret < 0) +		return ret; +  	arizona_init_gpio(codec);  	arizona_init_notifiers(codec); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d72ccef9e238..d151224ffcca 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2473,7 +2473,7 @@ static void wm_adsp2_boot_work(struct work_struct *work)  	ret = wm_adsp2_ena(dsp);  	if (ret != 0) -		goto err_mutex; +		goto err_mem;  	ret = wm_adsp_load(dsp);  	if (ret != 0) @@ -2492,14 +2492,14 @@ static void wm_adsp2_boot_work(struct work_struct *work)  	if (ret != 0)  		goto err_ena; -	dsp->booted = true; -  	/* Turn DSP back off until we are ready to run */  	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,  				 ADSP2_SYS_ENA, 0);  	if (ret != 0)  		goto err_ena; +	dsp->booted = true; +  	mutex_unlock(&dsp->pwr_lock);  	return; @@ -2507,6 +2507,9 @@ static void wm_adsp2_boot_work(struct work_struct *work)  err_ena:  	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,  			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); +err_mem: +	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, +			   ADSP2_MEM_ENA, 0);  err_mutex:  	mutex_unlock(&dsp->pwr_lock);  } @@ -2523,6 +2526,43 @@ static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)  		adsp_err(dsp, "Failed to set clock rate: %d\n", ret);  } +int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, +			   struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +	struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); + +	ucontrol->value.integer.value[0] = dsp->preloaded; + +	return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); + +int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, +			   struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +	struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); +	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value; +	char preload[32]; + +	snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", mc->shift); + +	dsp->preloaded = ucontrol->value.integer.value[0]; + +	if (ucontrol->value.integer.value[0]) +		snd_soc_dapm_force_enable_pin(dapm, preload); +	else +		snd_soc_dapm_disable_pin(dapm, preload); + +	snd_soc_dapm_sync(dapm); + +	return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); +  int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,  			 struct snd_kcontrol *kcontrol, int event,  			 unsigned int freq) @@ -2538,6 +2578,8 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,  		queue_work(system_unbound_wq, &dsp->boot_work);  		break;  	case SND_SOC_DAPM_PRE_PMD: +		mutex_lock(&dsp->pwr_lock); +  		wm_adsp_debugfs_clear(dsp);  		dsp->fw_id = 0; @@ -2553,6 +2595,8 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,  		wm_adsp_free_alg_regions(dsp); +		mutex_unlock(&dsp->pwr_lock); +  		adsp_dbg(dsp, "Shutdown complete\n");  		break;  	default: @@ -2575,8 +2619,12 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,  	case SND_SOC_DAPM_POST_PMU:  		flush_work(&dsp->boot_work); -		if (!dsp->booted) -			return -EIO; +		mutex_lock(&dsp->pwr_lock); + +		if (!dsp->booted) { +			ret = -EIO; +			goto err; +		}  		ret = wm_adsp2_ena(dsp);  		if (ret != 0) @@ -2594,18 +2642,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,  		if (ret != 0)  			goto err; -		dsp->running = true; - -		mutex_lock(&dsp->pwr_lock); -  		if (wm_adsp_fw[dsp->fw].num_caps != 0) {  			ret = wm_adsp_buffer_init(dsp); -			if (ret < 0) { -				mutex_unlock(&dsp->pwr_lock); +			if (ret < 0)  				goto err; -			}  		} +		dsp->running = true; +  		mutex_unlock(&dsp->pwr_lock);  		break; @@ -2648,16 +2692,23 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,  err:  	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,  			   ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); +	mutex_unlock(&dsp->pwr_lock);  	return ret;  }  EXPORT_SYMBOL_GPL(wm_adsp2_event);  int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)  { -	dsp->codec = codec; +	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); +	char preload[32]; + +	snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", dsp->num); +	snd_soc_dapm_disable_pin(dapm, preload);  	wm_adsp2_init_debugfs(dsp, codec); +	dsp->codec = codec; +  	return snd_soc_add_codec_controls(codec,  					  &wm_adsp_fw_controls[dsp->num - 1],  					  1); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 411d062c13f2..3706b11053a3 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -62,6 +62,7 @@ struct wm_adsp {  	int fw;  	int fw_ver; +	bool preloaded;  	bool booted;  	bool running; @@ -86,7 +87,12 @@ struct wm_adsp {  	SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \  		wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) +#define WM_ADSP2_PRELOAD_SWITCH(wname, num) \ +	SOC_SINGLE_EXT(wname " Preload Switch", SND_SOC_NOPM, num, 1, 0, \ +		wm_adsp2_preloader_get, wm_adsp2_preloader_put) +  #define WM_ADSP2(wname, num, event_fn) \ +	SND_SOC_DAPM_SPK(wname " Preload", NULL), \  {	.id = snd_soc_dapm_supply, .name = wname " Preloader", \  	.reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \  	.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, \ @@ -110,6 +116,11 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,  int wm_adsp2_event(struct snd_soc_dapm_widget *w,  		   struct snd_kcontrol *kcontrol, int event); +int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, +			   struct snd_ctl_elem_value *ucontrol); +int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, +			   struct snd_ctl_elem_value *ucontrol); +  int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream);  int wm_adsp_compr_free(struct snd_compr_stream *stream);  int wm_adsp_compr_set_params(struct snd_compr_stream *stream, diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 731fb0d86c6a..7a369e0f2093 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -358,13 +358,20 @@ static struct snd_soc_card evm_soc_card = {  static int davinci_evm_probe(struct platform_device *pdev)  {  	struct device_node *np = pdev->dev.of_node; -	const struct of_device_id *match = -		of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev); -	struct snd_soc_dai_link *dai = (struct snd_soc_dai_link *) match->data; +	const struct of_device_id *match; +	struct snd_soc_dai_link *dai;  	struct snd_soc_card_drvdata_davinci *drvdata = NULL;  	struct clk *mclk;  	int ret = 0; +	match = of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev); +	if (!match) { +		dev_err(&pdev->dev, "Error: No device match found\n"); +		return -ENODEV; +	} + +	dai = (struct snd_soc_dai_link *) match->data; +  	evm_soc_card.dai_link = dai;  	dai->codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0); diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index bdf8398cbc81..9c46e4112026 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -121,9 +121,14 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id)  			irq_valid = true;  		} -		/* Data available. Record mode not supported in PIO mode */ -		if (isr[i] & ISR_RXDA) +		/* +		 * Data available. Retrieve samples from FIFO +		 * NOTE: Only two channels supported +		 */ +		if ((isr[i] & ISR_RXDA) && (i == 0) && dev->use_pio) { +			dw_pcm_pop_rx(dev);  			irq_valid = true; +		}  		/* Error Handling: TX */  		if (isr[i] & ISR_TXFO) { diff --git a/sound/soc/dwc/designware_pcm.c b/sound/soc/dwc/designware_pcm.c index 4a83a22fa3cb..459ec861e6b6 100644 --- a/sound/soc/dwc/designware_pcm.c +++ b/sound/soc/dwc/designware_pcm.c @@ -41,10 +41,33 @@ static unsigned int dw_pcm_tx_##sample_bits(struct dw_i2s_dev *dev, \  	return tx_ptr; \  } +#define dw_pcm_rx_fn(sample_bits) \ +static unsigned int dw_pcm_rx_##sample_bits(struct dw_i2s_dev *dev, \ +		struct snd_pcm_runtime *runtime, unsigned int rx_ptr, \ +		bool *period_elapsed) \ +{ \ +	u##sample_bits (*p)[2] = (void *)runtime->dma_area; \ +	unsigned int period_pos = rx_ptr % runtime->period_size; \ +	int i; \ +\ +	for (i = 0; i < dev->fifo_th; i++) { \ +		p[rx_ptr][0] = ioread32(dev->i2s_base + LRBR_LTHR(0)); \ +		p[rx_ptr][1] = ioread32(dev->i2s_base + RRBR_RTHR(0)); \ +		period_pos++; \ +		if (++rx_ptr >= runtime->buffer_size) \ +			rx_ptr = 0; \ +	} \ +	*period_elapsed = period_pos >= runtime->period_size; \ +	return rx_ptr; \ +} +  dw_pcm_tx_fn(16);  dw_pcm_tx_fn(32); +dw_pcm_rx_fn(16); +dw_pcm_rx_fn(32);  #undef dw_pcm_tx_fn +#undef dw_pcm_rx_fn  static const struct snd_pcm_hardware dw_pcm_hardware = {  	.info = SNDRV_PCM_INFO_INTERLEAVED | @@ -57,6 +80,7 @@ static const struct snd_pcm_hardware dw_pcm_hardware = {  	.rate_min = 32000,  	.rate_max = 48000,  	.formats = SNDRV_PCM_FMTBIT_S16_LE | +		SNDRV_PCM_FMTBIT_S24_LE |  		SNDRV_PCM_FMTBIT_S32_LE,  	.channels_min = 2,  	.channels_max = 2, @@ -68,27 +92,51 @@ static const struct snd_pcm_hardware dw_pcm_hardware = {  	.fifo_size = 16,  }; -void dw_pcm_push_tx(struct dw_i2s_dev *dev) +static void dw_pcm_transfer(struct dw_i2s_dev *dev, bool push)  { -	struct snd_pcm_substream *tx_substream; -	bool tx_active, period_elapsed; +	struct snd_pcm_substream *substream; +	bool active, period_elapsed;  	rcu_read_lock(); -	tx_substream = rcu_dereference(dev->tx_substream); -	tx_active = tx_substream && snd_pcm_running(tx_substream); -	if (tx_active) { -		unsigned int tx_ptr = READ_ONCE(dev->tx_ptr); -		unsigned int new_tx_ptr = dev->tx_fn(dev, tx_substream->runtime, -				tx_ptr, &period_elapsed); -		cmpxchg(&dev->tx_ptr, tx_ptr, new_tx_ptr); +	if (push) +		substream = rcu_dereference(dev->tx_substream); +	else +		substream = rcu_dereference(dev->rx_substream); +	active = substream && snd_pcm_running(substream); +	if (active) { +		unsigned int ptr; +		unsigned int new_ptr; + +		if (push) { +			ptr = READ_ONCE(dev->tx_ptr); +			new_ptr = dev->tx_fn(dev, substream->runtime, ptr, +					&period_elapsed); +			cmpxchg(&dev->tx_ptr, ptr, new_ptr); +		} else { +			ptr = READ_ONCE(dev->rx_ptr); +			new_ptr = dev->rx_fn(dev, substream->runtime, ptr, +					&period_elapsed); +			cmpxchg(&dev->rx_ptr, ptr, new_ptr); +		}  		if (period_elapsed) -			snd_pcm_period_elapsed(tx_substream); +			snd_pcm_period_elapsed(substream);  	}  	rcu_read_unlock();  } + +void dw_pcm_push_tx(struct dw_i2s_dev *dev) +{ +	dw_pcm_transfer(dev, true); +}  EXPORT_SYMBOL_GPL(dw_pcm_push_tx); +void dw_pcm_pop_rx(struct dw_i2s_dev *dev) +{ +	dw_pcm_transfer(dev, false); +} +EXPORT_SYMBOL_GPL(dw_pcm_pop_rx); +  static int dw_pcm_open(struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime = substream->runtime; @@ -126,20 +174,18 @@ static int dw_pcm_hw_params(struct snd_pcm_substream *substream,  	switch (params_format(hw_params)) {  	case SNDRV_PCM_FORMAT_S16_LE:  		dev->tx_fn = dw_pcm_tx_16; +		dev->rx_fn = dw_pcm_rx_16;  		break; +	case SNDRV_PCM_FORMAT_S24_LE:  	case SNDRV_PCM_FORMAT_S32_LE:  		dev->tx_fn = dw_pcm_tx_32; +		dev->rx_fn = dw_pcm_rx_32;  		break;  	default:  		dev_err(dev->dev, "invalid format\n");  		return -EINVAL;  	} -	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { -		dev_err(dev->dev, "only playback is available\n"); -		return -EINVAL; -	} -  	ret = snd_pcm_lib_malloc_pages(substream,  			params_buffer_bytes(hw_params));  	if (ret < 0) @@ -163,13 +209,21 @@ static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)  	case SNDRV_PCM_TRIGGER_START:  	case SNDRV_PCM_TRIGGER_RESUME:  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -		WRITE_ONCE(dev->tx_ptr, 0); -		rcu_assign_pointer(dev->tx_substream, substream); +		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +			WRITE_ONCE(dev->tx_ptr, 0); +			rcu_assign_pointer(dev->tx_substream, substream); +		} else { +			WRITE_ONCE(dev->rx_ptr, 0); +			rcu_assign_pointer(dev->rx_substream, substream); +		}  		break;  	case SNDRV_PCM_TRIGGER_STOP:  	case SNDRV_PCM_TRIGGER_SUSPEND:  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -		rcu_assign_pointer(dev->tx_substream, NULL); +		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +			rcu_assign_pointer(dev->tx_substream, NULL); +		else +			rcu_assign_pointer(dev->rx_substream, NULL);  		break;  	default:  		ret = -EINVAL; @@ -183,7 +237,12 @@ static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime = substream->runtime;  	struct dw_i2s_dev *dev = runtime->private_data; -	snd_pcm_uframes_t pos = READ_ONCE(dev->tx_ptr); +	snd_pcm_uframes_t pos; + +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		pos = READ_ONCE(dev->tx_ptr); +	else +		pos = READ_ONCE(dev->rx_ptr);  	return pos < runtime->buffer_size ? pos : 0;  } diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h index 68afd7577343..91dc70a826f8 100644 --- a/sound/soc/dwc/local.h +++ b/sound/soc/dwc/local.h @@ -105,20 +105,27 @@ struct dw_i2s_dev {  	struct i2s_clk_config_data config;  	int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); -	/* data related to PIO transfers (TX) */ +	/* data related to PIO transfers */  	bool use_pio;  	struct snd_pcm_substream __rcu *tx_substream; +	struct snd_pcm_substream __rcu *rx_substream;  	unsigned int (*tx_fn)(struct dw_i2s_dev *dev,  			struct snd_pcm_runtime *runtime, unsigned int tx_ptr,  			bool *period_elapsed); +	unsigned int (*rx_fn)(struct dw_i2s_dev *dev, +			struct snd_pcm_runtime *runtime, unsigned int rx_ptr, +			bool *period_elapsed);  	unsigned int tx_ptr; +	unsigned int rx_ptr;  };  #if IS_ENABLED(CONFIG_SND_DESIGNWARE_PCM)  void dw_pcm_push_tx(struct dw_i2s_dev *dev); +void dw_pcm_pop_rx(struct dw_i2s_dev *dev);  int dw_pcm_register(struct platform_device *pdev);  #else  void dw_pcm_push_tx(struct dw_i2s_dev *dev) { } +void dw_pcm_pop_rx(struct dw_i2s_dev *dev) { }  int dw_pcm_register(struct platform_device *pdev)  {  	return -EINVAL; diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c index f200d1cfc4bd..667f4215dfc0 100644 --- a/sound/soc/fsl/efika-audio-fabric.c +++ b/sound/soc/fsl/efika-audio-fabric.c @@ -26,7 +26,6 @@  #include <sound/soc.h>  #include "mpc5200_dma.h" -#include "mpc5200_psc_ac97.h"  #define DRV_NAME "efika-audio-fabric" diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 9fadf7e31c5f..18e5ce81527d 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -668,7 +668,7 @@ static struct snd_soc_dai_driver fsl_sai_dai = {  	.playback = {  		.stream_name = "CPU-Playback",  		.channels_min = 1, -		.channels_max = 2, +		.channels_max = 32,  		.rate_min = 8000,  		.rate_max = 192000,  		.rates = SNDRV_PCM_RATE_KNOT, @@ -677,7 +677,7 @@ static struct snd_soc_dai_driver fsl_sai_dai = {  	.capture = {  		.stream_name = "CPU-Capture",  		.channels_min = 1, -		.channels_max = 2, +		.channels_max = 32,  		.rate_min = 8000,  		.rate_max = 192000,  		.rates = SNDRV_PCM_RATE_KNOT, diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 243700cc29e6..07ee355ee385 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -25,7 +25,6 @@  #include <asm/mpc52xx_psc.h>  #include "mpc5200_dma.h" -#include "mpc5200_psc_ac97.h"  #define DRV_NAME "mpc5200-psc-ac97" diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h deleted file mode 100644 index e881e784b270..000000000000 --- a/sound/soc/fsl/mpc5200_psc_ac97.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Freescale MPC5200 PSC in AC97 mode - * ALSA SoC Digital Audio Interface (DAI) driver - * - */ - -#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ -#define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ - -#define MPC5200_AC97_NORMAL 0 -#define MPC5200_AC97_SPDIF 1 - -#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ */ diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index cf026252cd4a..4924575d2e95 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -98,7 +98,8 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,  }  EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); -int asoc_simple_card_parse_clk(struct device_node *node, +int asoc_simple_card_parse_clk(struct device *dev, +			       struct device_node *node,  			       struct device_node *dai_of_node,  			       struct asoc_simple_dai *simple_dai)  { @@ -111,14 +112,13 @@ int asoc_simple_card_parse_clk(struct device_node *node,  	 *  or "system-clock-frequency = <xxx>"  	 *  or device's module clock.  	 */ -	clk = of_clk_get(node, 0); +	clk = devm_get_clk_from_child(dev, node, NULL);  	if (!IS_ERR(clk)) {  		simple_dai->sysclk = clk_get_rate(clk); -		simple_dai->clk = clk;  	} else if (!of_property_read_u32(node, "system-clock-frequency", &val)) {  		simple_dai->sysclk = val;  	} else { -		clk = of_clk_get(dai_of_node, 0); +		clk = devm_get_clk_from_child(dev, dai_of_node, NULL);  		if (!IS_ERR(clk))  			simple_dai->sysclk = clk_get_rate(clk);  	} diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index a385ff6bfa4b..85b4f1806514 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -278,11 +278,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,  	if (ret < 0)  		goto dai_link_of_err; -	ret = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai); +	ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);  	if (ret < 0)  		goto dai_link_of_err; -	ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai); +	ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);  	if (ret < 0)  		goto dai_link_of_err; diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index bb86ee042490..308ff4c11a8d 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -128,7 +128,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,  		if (ret)  			return ret; -		ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props); +		ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai_props);  		if (ret < 0)  			return ret; @@ -153,7 +153,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,  		if (ret < 0)  			return ret; -		ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props); +		ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai_props);  		if (ret < 0)  			return ret; diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index c1610a054d65..33ceb207ee70 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -123,10 +123,8 @@ static int img_prl_out_hw_params(struct snd_pcm_substream *substream,  	struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);  	unsigned int rate, channels;  	u32 reg, control_set = 0; -	snd_pcm_format_t format;  	rate = params_rate(params); -	format = params_format(params);  	channels = params_channels(params);  	switch (params_format(params)) { diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index fd5d1e091038..526855ad479e 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -2,7 +2,7 @@ config SND_MFLD_MACHINE  	tristate "SOC Machine Audio driver for Intel Medfield MID platform"  	depends on INTEL_SCU_IPC  	select SND_SOC_SN95031 -	select SND_SST_MFLD_PLATFORM +	select SND_SST_ATOM_HIFI2_PLATFORM  	select SND_SST_IPC_PCI  	help            This adds support for ASoC machine driver for Intel(R) MID Medfield platform @@ -10,7 +10,7 @@ config SND_MFLD_MACHINE            Say Y if you have such a device.            If unsure select "N". -config SND_SST_MFLD_PLATFORM +config SND_SST_ATOM_HIFI2_PLATFORM  	tristate  	select SND_SOC_COMPRESS @@ -31,13 +31,10 @@ config SND_SOC_INTEL_SST  	tristate  	select SND_SOC_INTEL_SST_ACPI if ACPI  	select SND_SOC_INTEL_SST_MATCH if ACPI -	depends on (X86 || COMPILE_TEST) -# firmware stuff depends DW_DMAC_CORE; since there is no depends-on from -# the reverse selection, each machine driver needs to select -# SND_SOC_INTEL_SST_FIRMWARE carefully depending on DW_DMAC_CORE  config SND_SOC_INTEL_SST_FIRMWARE  	tristate +	select DW_DMAC_CORE  config SND_SOC_INTEL_SST_ACPI  	tristate @@ -47,16 +44,18 @@ config SND_SOC_INTEL_SST_MATCH  config SND_SOC_INTEL_HASWELL  	tristate +	select SND_SOC_INTEL_SST  	select SND_SOC_INTEL_SST_FIRMWARE  config SND_SOC_INTEL_BAYTRAIL  	tristate +	select SND_SOC_INTEL_SST +	select SND_SOC_INTEL_SST_FIRMWARE  config SND_SOC_INTEL_HASWELL_MACH  	tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"  	depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM -	depends on DW_DMAC_CORE -	select SND_SOC_INTEL_SST +	depends on DMADEVICES  	select SND_SOC_INTEL_HASWELL  	select SND_SOC_RT5640  	help @@ -68,7 +67,6 @@ config SND_SOC_INTEL_HASWELL_MACH  config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH  	tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode"  	depends on X86 && ACPI && I2C -	select SND_SOC_INTEL_SST  	select SND_SOC_INTEL_SKYLAKE  	select SND_SOC_DA7219  	select SND_SOC_MAX98357A @@ -84,7 +82,6 @@ config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH  config SND_SOC_INTEL_BXT_RT298_MACH  	tristate "ASoC Audio driver for Broxton with RT298 I2S mode"  	depends on X86 && ACPI && I2C -	select SND_SOC_INTEL_SST  	select SND_SOC_INTEL_SKYLAKE  	select SND_SOC_RT298  	select SND_SOC_DMIC @@ -99,9 +96,8 @@ config SND_SOC_INTEL_BXT_RT298_MACH  config SND_SOC_INTEL_BYT_RT5640_MACH  	tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"  	depends on X86_INTEL_LPSS && I2C -	depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n) -	select SND_SOC_INTEL_SST -	select SND_SOC_INTEL_SST_FIRMWARE +	depends on DMADEVICES +	depends on SND_SST_IPC_ACPI = n  	select SND_SOC_INTEL_BAYTRAIL  	select SND_SOC_RT5640  	help @@ -112,9 +108,8 @@ config SND_SOC_INTEL_BYT_RT5640_MACH  config SND_SOC_INTEL_BYT_MAX98090_MACH  	tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"  	depends on X86_INTEL_LPSS && I2C -	depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n) -	select SND_SOC_INTEL_SST -	select SND_SOC_INTEL_SST_FIRMWARE +	depends on DMADEVICES +	depends on SND_SST_IPC_ACPI = n  	select SND_SOC_INTEL_BAYTRAIL  	select SND_SOC_MAX98090  	help @@ -123,9 +118,8 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH  config SND_SOC_INTEL_BDW_RT5677_MACH  	tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec" -	depends on X86_INTEL_LPSS && GPIOLIB && I2C && DW_DMAC -	depends on DW_DMAC_CORE=y -	select SND_SOC_INTEL_SST +	depends on X86_INTEL_LPSS && GPIOLIB && I2C +	depends on DMADEVICES  	select SND_SOC_INTEL_HASWELL  	select SND_SOC_RT5677  	help @@ -134,10 +128,8 @@ config SND_SOC_INTEL_BDW_RT5677_MACH  config SND_SOC_INTEL_BROADWELL_MACH  	tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" -	depends on X86_INTEL_LPSS && I2C && DW_DMAC && \ -		   I2C_DESIGNWARE_PLATFORM -	depends on DW_DMAC_CORE -	select SND_SOC_INTEL_SST +	depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM +	depends on DMADEVICES  	select SND_SOC_INTEL_HASWELL  	select SND_SOC_RT286  	help @@ -150,7 +142,7 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH          tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec"  	depends on X86 && I2C && ACPI  	select SND_SOC_RT5640 -	select SND_SST_MFLD_PLATFORM +	select SND_SST_ATOM_HIFI2_PLATFORM  	select SND_SST_IPC_ACPI  	select SND_SOC_INTEL_SST_MATCH if ACPI  	help @@ -163,7 +155,7 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH          tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec"  	depends on X86 && I2C && ACPI  	select SND_SOC_RT5651 -	select SND_SST_MFLD_PLATFORM +	select SND_SST_ATOM_HIFI2_PLATFORM  	select SND_SST_IPC_ACPI  	select SND_SOC_INTEL_SST_MATCH if ACPI  	help @@ -176,7 +168,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH          tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"          depends on X86_INTEL_LPSS && I2C && ACPI          select SND_SOC_RT5670 -        select SND_SST_MFLD_PLATFORM +        select SND_SST_ATOM_HIFI2_PLATFORM          select SND_SST_IPC_ACPI  	select SND_SOC_INTEL_SST_MATCH if ACPI          help @@ -189,7 +181,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH  	tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec"  	depends on X86_INTEL_LPSS && I2C && ACPI  	select SND_SOC_RT5645 -	select SND_SST_MFLD_PLATFORM +	select SND_SST_ATOM_HIFI2_PLATFORM  	select SND_SST_IPC_ACPI  	select SND_SOC_INTEL_SST_MATCH if ACPI  	help @@ -202,7 +194,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH  	depends on X86_INTEL_LPSS && I2C && ACPI  	select SND_SOC_MAX98090  	select SND_SOC_TS3A227E -	select SND_SST_MFLD_PLATFORM +	select SND_SST_ATOM_HIFI2_PLATFORM  	select SND_SST_IPC_ACPI  	select SND_SOC_INTEL_SST_MATCH if ACPI  	help @@ -220,7 +212,6 @@ config SND_SOC_INTEL_SKYLAKE  config SND_SOC_INTEL_SKL_RT286_MACH  	tristate "ASoC Audio driver for SKL with RT286 I2S mode"  	depends on X86 && ACPI && I2C -	select SND_SOC_INTEL_SST  	select SND_SOC_INTEL_SKYLAKE  	select SND_SOC_RT286  	select SND_SOC_DMIC @@ -234,7 +225,6 @@ config SND_SOC_INTEL_SKL_RT286_MACH  config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH  	tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode"  	depends on X86_INTEL_LPSS && I2C -	select SND_SOC_INTEL_SST  	select SND_SOC_INTEL_SKYLAKE  	select SND_SOC_NAU8825  	select SND_SOC_SSM4567 @@ -249,7 +239,6 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH  config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH  	tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode"  	depends on X86_INTEL_LPSS && I2C -	select SND_SOC_INTEL_SST  	select SND_SOC_INTEL_SKYLAKE  	select SND_SOC_NAU8825  	select SND_SOC_MAX98357A diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 2b45435e6245..cdd495f7ee2c 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SST) += common/  # Platform Support  obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/  obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/ -obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += atom/ +obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += atom/  obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/  # Machine support diff --git a/sound/soc/intel/atom/Makefile b/sound/soc/intel/atom/Makefile index ce8074fa6d66..aa6548c6feab 100644 --- a/sound/soc/intel/atom/Makefile +++ b/sound/soc/intel/atom/Makefile @@ -1,7 +1,8 @@ -snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \ -	        sst-mfld-platform-compress.o sst-atom-controls.o +snd-soc-sst-atom-hifi2-platform-objs :=	sst-mfld-platform-pcm.o \ +					sst-mfld-platform-compress.o \ +					sst-atom-controls.o -obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o +obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += snd-soc-sst-atom-hifi2-platform.o  # DSP driver  obj-$(CONFIG_SND_SST_IPC) += sst/ diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index c7b3cbf92faf..0f3604b55942 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -801,13 +801,11 @@ static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai,  	switch (format) {  	case SND_SOC_DAIFMT_NB_NF: -		return SSP_FS_ACTIVE_LOW; -	case SND_SOC_DAIFMT_NB_IF: +	case SND_SOC_DAIFMT_IB_NF:  		return SSP_FS_ACTIVE_HIGH; +	case SND_SOC_DAIFMT_NB_IF:  	case SND_SOC_DAIFMT_IB_IF:  		return SSP_FS_ACTIVE_LOW; -	case SND_SOC_DAIFMT_IB_NF: -		return SSP_FS_ACTIVE_HIGH;  	default:  		dev_err(dai->dev, "Invalid frame sync polarity %d\n", format);  	} @@ -1087,8 +1085,8 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {  	SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),  	SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),  	SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL), -	SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop), -	SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop), +	SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_STEREO, sst_set_media_loop), +	SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_STEREO, sst_set_media_loop),  	SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),  	/* Media Mixers */ diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index f5a8050351b5..21cac1c8dd4c 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -357,14 +357,14 @@ static void sst_media_close(struct snd_pcm_substream *substream,  		struct snd_soc_dai *dai)  {  	struct sst_runtime_stream *stream; -	int ret_val = 0, str_id; +	int str_id;  	stream = substream->runtime->private_data;  	power_down_sst(stream);  	str_id = stream->stream_info.str_id;  	if (str_id) -		ret_val = stream->ops->close(sst->dev, str_id); +		stream->ops->close(sst->dev, str_id);  	module_put(sst->dev->driver->owner);  	kfree(stream);  } @@ -839,4 +839,5 @@ MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");  MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");  MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");  MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sst-atom-hifi2-platform");  MODULE_ALIAS("platform:sst-mfld-platform"); diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index f4d92bbc5373..747c0f393d2d 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -400,6 +400,7 @@ static int sst_acpi_remove(struct platform_device *pdev)  static unsigned long cht_machine_id;  #define CHT_SURFACE_MACH 1 +#define BYT_THINKPAD_10  2  static int cht_surface_quirk_cb(const struct dmi_system_id *id)  { @@ -407,6 +408,23 @@ static int cht_surface_quirk_cb(const struct dmi_system_id *id)  	return 1;  } +static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id) +{ +	cht_machine_id = BYT_THINKPAD_10; +	return 1; +} + + +static const struct dmi_system_id byt_table[] = { +	{ +		.callback = byt_thinkpad10_quirk_cb, +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "20C3001VHH"), +		}, +	}, +	{ } +};  static const struct dmi_system_id cht_table[] = {  	{ @@ -424,6 +442,10 @@ static struct sst_acpi_mach cht_surface_mach = {  	"10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,  								&chv_platform_data }; +static struct sst_acpi_mach byt_thinkpad_10 = { +	"10EC5640", "cht-bsw-rt5672", "intel/fw_sst_0f28.bin", "cht-bsw", NULL, +	                                                        &byt_rvp_platform_data }; +  static struct sst_acpi_mach *cht_quirk(void *arg)  {  	struct sst_acpi_mach *mach = arg; @@ -436,8 +458,21 @@ static struct sst_acpi_mach *cht_quirk(void *arg)  		return mach;  } +static struct sst_acpi_mach *byt_quirk(void *arg) +{ +	struct sst_acpi_mach *mach = arg; + +	dmi_check_system(byt_table); + +	if (cht_machine_id == BYT_THINKPAD_10) +		return &byt_thinkpad_10; +	else +		return mach; +} + +  static struct sst_acpi_mach sst_acpi_bytcr[] = { -	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL, +	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", byt_quirk,  						&byt_rvp_platform_data },  	{"10EC5642", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,  						&byt_rvp_platform_data }, @@ -445,6 +480,12 @@ static struct sst_acpi_mach sst_acpi_bytcr[] = {  						&byt_rvp_platform_data },  	{"10EC5651", "bytcr_rt5651", "intel/fw_sst_0f28.bin", "bytcr_rt5651", NULL,  						&byt_rvp_platform_data }, +	/* some Baytrail platforms rely on RT5645, use CHT machine driver */ +	{"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL, +						&byt_rvp_platform_data }, +	{"10EC5648", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL, +						&byt_rvp_platform_data }, +  	{},  }; @@ -458,12 +499,19 @@ static struct sst_acpi_mach sst_acpi_chv[] = {  						&chv_platform_data },  	{"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,  						&chv_platform_data }, +	{"10EC3270", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, +						&chv_platform_data }, +  	{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,  						&chv_platform_data },  	/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */  	{"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk,  						&chv_platform_data }, - +	{"10EC3276", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL, +						&chv_platform_data }, +	/* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ +	{"10EC5651", "bytcr_rt5651", "intel/fw_sst_22a8.bin", "bytcr_rt5651", NULL, +						&chv_platform_data },  	{},  }; diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 374bb61c596d..14c2d9d18180 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -260,10 +260,8 @@ static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,  	u32 data_size, i;  	void *data_offset;  	struct stream_info *stream; -	union ipc_header_high msg_high;  	u32 msg_low, pipe_id; -	msg_high = msg->mrfld_header.p.header_high;  	msg_low = msg->mrfld_header.p.header_low_payload;  	msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;  	data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr)); diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index 51bdeeecb7c8..83d8dda15233 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -394,7 +394,6 @@ int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)  {  	int retval = 0;  	struct stream_info *str_info; -	struct intel_sst_ops *ops;  	dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id); @@ -407,7 +406,6 @@ int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)  	str_info = get_stream_info(sst_drv_ctx, str_id);  	if (!str_info)  		return -EINVAL; -	ops = sst_drv_ctx->ops;  	mutex_lock(&str_info->lock);  	if (str_info->status != STREAM_UN_INIT) { diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 4d7e9decfa92..faf865bb1765 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -270,6 +270,8 @@ static int broadwell_audio_probe(struct platform_device *pdev)  {  	broadwell_rt286.dev = &pdev->dev; +	snd_soc_set_dmi_name(&broadwell_rt286, NULL); +  	return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);  } diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 1b4330cd2739..2cda06cde4d1 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -33,6 +33,17 @@  #define QUAD_CHANNEL		4  static struct snd_soc_jack broxton_headset; +static struct snd_soc_jack broxton_hdmi[3]; + +struct bxt_hdmi_pcm { +	struct list_head head; +	struct snd_soc_dai *codec_dai; +	int device; +}; + +struct bxt_card_private { +	struct list_head hdmi_pcm_list; +};  enum {  	BXT_DPCM_AUDIO_PB = 0, @@ -84,9 +95,9 @@ static const struct snd_soc_dapm_route broxton_map[] = {  	{"codec0_in", NULL, "ssp1 Rx"},  	{"ssp1 Rx", NULL, "Capture"}, -	{"HDMI1", NULL, "hif5 Output"}, -	{"HDMI2", NULL, "hif6 Output"}, -	{"HDMI3", NULL, "hif7 Output"}, +	{"HDMI1", NULL, "hif5-0 Output"}, +	{"HDMI2", NULL, "hif6-0 Output"}, +	{"HDMI2", NULL, "hif7-0 Output"},  	{"hifi3", NULL, "iDisp3 Tx"},  	{"iDisp3 Tx", NULL, "iDisp3_out"}, @@ -147,9 +158,20 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)  static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)  { +	struct bxt_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);  	struct snd_soc_dai *dai = rtd->codec_dai; +	struct bxt_hdmi_pcm *pcm; + +	pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); +	if (!pcm) +		return -ENOMEM; -	return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id); +	pcm->device = BXT_DPCM_AUDIO_HDMI1_PB + dai->id; +	pcm->codec_dai = dai; + +	list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + +	return 0;  }  static int broxton_da7219_fe_init(struct snd_soc_pcm_runtime *rtd) @@ -357,7 +379,6 @@ static struct snd_soc_dai_link broxton_dais[] = {  		.platform_name = "0000:00:0e.0",  		.init = NULL,  		.dpcm_capture = 1, -		.ignore_suspend = 1,  		.nonatomic = 1,  		.dynamic = 1,  		.ops = &broxton_refcap_ops, @@ -497,6 +518,40 @@ static struct snd_soc_dai_link broxton_dais[] = {  	},  }; +#define NAME_SIZE	32 +static int bxt_card_late_probe(struct snd_soc_card *card) +{ +	struct bxt_card_private *ctx = snd_soc_card_get_drvdata(card); +	struct bxt_hdmi_pcm *pcm; +	struct snd_soc_codec *codec = NULL; +	int err, i = 0; +	char jack_name[NAME_SIZE]; + +	list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { +		codec = pcm->codec_dai->codec; +		snprintf(jack_name, sizeof(jack_name), +			"HDMI/DP, pcm=%d Jack", pcm->device); +		err = snd_soc_card_jack_new(card, jack_name, +					SND_JACK_AVOUT, &broxton_hdmi[i], +					NULL, 0); + +		if (err) +			return err; + +		err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, +						&broxton_hdmi[i]); +		if (err < 0) +			return err; + +		i++; +	} + +	if (!codec) +		return -EINVAL; + +	return hdac_hdmi_jack_port_init(codec, &card->dapm); +} +  /* broxton audio machine driver for SPT + da7219 */  static struct snd_soc_card broxton_audio_card = {  	.name = "bxtda7219max", @@ -510,11 +565,22 @@ static struct snd_soc_card broxton_audio_card = {  	.dapm_routes = broxton_map,  	.num_dapm_routes = ARRAY_SIZE(broxton_map),  	.fully_routed = true, +	.late_probe = bxt_card_late_probe,  };  static int broxton_audio_probe(struct platform_device *pdev)  { +	struct bxt_card_private *ctx; + +	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); +	if (!ctx) +		return -ENOMEM; + +	INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +  	broxton_audio_card.dev = &pdev->dev; +	snd_soc_card_set_drvdata(&broxton_audio_card, ctx); +  	return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card);  } diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 1309405b3808..176c080a9818 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -26,8 +26,19 @@  #include "../../codecs/hdac_hdmi.h"  #include "../../codecs/rt298.h" -static struct snd_soc_jack broxton_headset;  /* Headset jack detection DAPM pins */ +static struct snd_soc_jack broxton_headset; +static struct snd_soc_jack broxton_hdmi[3]; + +struct bxt_hdmi_pcm { +	struct list_head head; +	struct snd_soc_dai *codec_dai; +	int device; +}; + +struct bxt_rt286_private { +	struct list_head hdmi_pcm_list; +};  enum {  	BXT_DPCM_AUDIO_PB = 0, @@ -82,9 +93,9 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {  	{"DMIC1 Pin", NULL, "DMIC2"},  	{"DMic", NULL, "SoC DMIC"}, -	{"HDMI1", NULL, "hif5 Output"}, -	{"HDMI2", NULL, "hif6 Output"}, -	{"HDMI3", NULL, "hif7 Output"}, +	{"HDMI1", NULL, "hif5-0 Output"}, +	{"HDMI2", NULL, "hif6-0 Output"}, +	{"HDMI2", NULL, "hif7-0 Output"},  	/* CODEC BE connections */  	{ "AIF1 Playback", NULL, "ssp5 Tx"}, @@ -139,9 +150,20 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)  static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)  { +	struct bxt_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card);  	struct snd_soc_dai *dai = rtd->codec_dai; +	struct bxt_hdmi_pcm *pcm; -	return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id); +	pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); +	if (!pcm) +		return -ENOMEM; + +	pcm->device = BXT_DPCM_AUDIO_HDMI1_PB + dai->id; +	pcm->codec_dai = dai; + +	list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + +	return 0;  }  static int broxton_ssp5_fixup(struct snd_soc_pcm_runtime *rtd, @@ -432,6 +454,41 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {  	},  }; +#define NAME_SIZE	32 +static int bxt_card_late_probe(struct snd_soc_card *card) +{ +	struct bxt_rt286_private *ctx = snd_soc_card_get_drvdata(card); +	struct bxt_hdmi_pcm *pcm; +	struct snd_soc_codec *codec = NULL; +	int err, i = 0; +	char jack_name[NAME_SIZE]; + +	list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { +		codec = pcm->codec_dai->codec; +		snprintf(jack_name, sizeof(jack_name), +			"HDMI/DP, pcm=%d Jack", pcm->device); +		err = snd_soc_card_jack_new(card, jack_name, +					SND_JACK_AVOUT, &broxton_hdmi[i], +					NULL, 0); + +		if (err) +			return err; + +		err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, +						&broxton_hdmi[i]); +		if (err < 0) +			return err; + +		i++; +	} + +	if (!codec) +		return -EINVAL; + +	return hdac_hdmi_jack_port_init(codec, &card->dapm); +} + +  /* broxton audio machine driver for SPT + RT298S */  static struct snd_soc_card broxton_rt298 = {  	.name = "broxton-rt298", @@ -445,11 +502,22 @@ static struct snd_soc_card broxton_rt298 = {  	.dapm_routes = broxton_rt298_map,  	.num_dapm_routes = ARRAY_SIZE(broxton_rt298_map),  	.fully_routed = true, +	.late_probe = bxt_card_late_probe, +  };  static int broxton_audio_probe(struct platform_device *pdev)  { +	struct bxt_rt286_private *ctx; + +	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); +	if (!ctx) +		return -ENOMEM; + +	INIT_LIST_HEAD(&ctx->hdmi_pcm_list); +  	broxton_rt298.dev = &pdev->dev; +	snd_soc_card_set_drvdata(&broxton_rt298, ctx);  	return devm_snd_soc_register_card(&pdev->dev, &broxton_rt298);  } diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 8d2fb2d6f532..5c7219fb3aa8 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -387,6 +387,16 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {  						 BYT_RT5640_SSP0_AIF1),  	}, +	{ +		.callback = byt_rt5640_quirk_cb, +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), +		}, +		.driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP | +						 BYT_RT5640_MCLK_EN | +						 BYT_RT5640_SSP0_AIF1), + +	},  	{}  }; @@ -546,7 +556,7 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,  		 */  		ret = snd_soc_dai_set_fmt(rtd->cpu_dai,  					SND_SOC_DAIFMT_I2S     | -					SND_SOC_DAIFMT_NB_IF   | +					SND_SOC_DAIFMT_NB_NF   |  					SND_SOC_DAIFMT_CBS_CFS  			);  		if (ret < 0) { @@ -572,7 +582,7 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,  		 */  		ret = snd_soc_dai_set_fmt(rtd->cpu_dai,  					SND_SOC_DAIFMT_I2S     | -					SND_SOC_DAIFMT_NB_IF   | +					SND_SOC_DAIFMT_NB_NF   |  					SND_SOC_DAIFMT_CBS_CFS  			);  		if (ret < 0) { @@ -856,7 +866,6 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)  static struct platform_driver snd_byt_rt5640_mc_driver = {  	.driver = {  		.name = "bytcr_rt5640", -		.pm = &snd_soc_pm_ops,  	},  	.probe = snd_byt_rt5640_mc_probe,  }; diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 2d24dc04b597..3186f015939f 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -185,7 +185,7 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd,  	 */  	ret = snd_soc_dai_set_fmt(rtd->cpu_dai,  				  SND_SOC_DAIFMT_I2S     | -				  SND_SOC_DAIFMT_NB_IF   | +				  SND_SOC_DAIFMT_NB_NF   |  				  SND_SOC_DAIFMT_CBS_CFS  				  ); @@ -319,7 +319,6 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)  static struct platform_driver snd_byt_rt5651_mc_driver = {  	.driver = {  		.name = "bytcr_rt5651", -		.pm = &snd_soc_pm_ops,  	},  	.probe = snd_byt_rt5651_mc_probe,  }; diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index f504a0e18f91..5bcde01d15e6 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -23,7 +23,11 @@  #include <linux/module.h>  #include <linux/acpi.h>  #include <linux/platform_device.h> +#include <linux/dmi.h>  #include <linux/slab.h> +#include <asm/cpu_device_id.h> +#include <asm/platform_sst_audio.h> +#include <linux/clk.h>  #include <sound/pcm.h>  #include <sound/pcm_params.h>  #include <sound/soc.h> @@ -33,7 +37,8 @@  #include "../common/sst-acpi.h"  #define CHT_PLAT_CLK_3_HZ	19200000 -#define CHT_CODEC_DAI	"rt5645-aif1" +#define CHT_CODEC_DAI1	"rt5645-aif1" +#define CHT_CODEC_DAI2	"rt5645-aif2"  struct cht_acpi_card {  	char *codec_id; @@ -45,15 +50,36 @@ struct cht_mc_private {  	struct snd_soc_jack jack;  	struct cht_acpi_card *acpi_card;  	char codec_name[16]; +	struct clk *mclk;  }; +#define CHT_RT5645_MAP(quirk)	((quirk) & 0xff) +#define CHT_RT5645_SSP2_AIF2     BIT(16) /* default is using AIF1  */ +#define CHT_RT5645_SSP0_AIF1     BIT(17) +#define CHT_RT5645_SSP0_AIF2     BIT(18) + +static unsigned long cht_rt5645_quirk = 0; + +static void log_quirks(struct device *dev) +{ +	if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) +		dev_info(dev, "quirk SSP2_AIF2 enabled"); +	if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) +		dev_info(dev, "quirk SSP0_AIF1 enabled"); +	if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) +		dev_info(dev, "quirk SSP0_AIF2 enabled"); +} +  static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)  {  	struct snd_soc_pcm_runtime *rtd;  	list_for_each_entry(rtd, &card->rtd_list, list) { -		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, -			     strlen(CHT_CODEC_DAI))) +		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI1, +			     strlen(CHT_CODEC_DAI1))) +			return rtd->codec_dai; +		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI2, +			     strlen(CHT_CODEC_DAI2)))  			return rtd->codec_dai;  	}  	return NULL; @@ -65,6 +91,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,  	struct snd_soc_dapm_context *dapm = w->dapm;  	struct snd_soc_card *card = dapm->card;  	struct snd_soc_dai *codec_dai; +	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);  	int ret;  	codec_dai = cht_get_codec_dai(card); @@ -73,19 +100,30 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,  		return -EIO;  	} -	if (!SND_SOC_DAPM_EVENT_OFF(event)) -		return 0; +	if (SND_SOC_DAPM_EVENT_ON(event)) { +		if (ctx->mclk) { +			ret = clk_prepare_enable(ctx->mclk); +			if (ret < 0) { +				dev_err(card->dev, +					"could not configure MCLK state"); +				return ret; +			} +		} +	} else { +		/* Set codec sysclk source to its internal clock because codec PLL will +		 * be off when idle and MCLK will also be off when codec is +		 * runtime suspended. Codec needs clock for jack detection and button +		 * press. MCLK is turned off with clock framework or ACPI. +		 */ +		ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK, +					48000 * 512, SND_SOC_CLOCK_IN); +		if (ret < 0) { +			dev_err(card->dev, "can't set codec sysclk: %d\n", ret); +			return ret; +		} -	/* Set codec sysclk source to its internal clock because codec PLL will -	 * be off when idle and MCLK will also be off by ACPI when codec is -	 * runtime suspended. Codec needs clock for jack detection and button -	 * press. -	 */ -	ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK, -			0, SND_SOC_CLOCK_IN); -	if (ret < 0) { -		dev_err(card->dev, "can't set codec sysclk: %d\n", ret); -		return ret; +		if (ctx->mclk) +			clk_disable_unprepare(ctx->mclk);  	}  	return 0; @@ -97,7 +135,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {  	SND_SOC_DAPM_MIC("Int Mic", NULL),  	SND_SOC_DAPM_SPK("Ext Spk", NULL),  	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, -			platform_clock_control, SND_SOC_DAPM_POST_PMD), +			platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),  };  static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = { @@ -109,12 +147,6 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {  	{"Headphone", NULL, "HPOR"},  	{"Ext Spk", NULL, "SPOL"},  	{"Ext Spk", NULL, "SPOR"}, -	{"AIF1 Playback", NULL, "ssp2 Tx"}, -	{"ssp2 Tx", NULL, "codec_out0"}, -	{"ssp2 Tx", NULL, "codec_out1"}, -	{"codec_in0", NULL, "ssp2 Rx" }, -	{"codec_in1", NULL, "ssp2 Rx" }, -	{"ssp2 Rx", NULL, "AIF1 Capture"},  	{"Headphone", NULL, "Platform Clock"},  	{"Headset Mic", NULL, "Platform Clock"},  	{"Int Mic", NULL, "Platform Clock"}, @@ -130,16 +162,42 @@ static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = {  	{"Headphone", NULL, "HPOR"},  	{"Ext Spk", NULL, "SPOL"},  	{"Ext Spk", NULL, "SPOR"}, +	{"Headphone", NULL, "Platform Clock"}, +	{"Headset Mic", NULL, "Platform Clock"}, +	{"Int Mic", NULL, "Platform Clock"}, +	{"Ext Spk", NULL, "Platform Clock"}, +}; + +static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_map[] = {  	{"AIF1 Playback", NULL, "ssp2 Tx"},  	{"ssp2 Tx", NULL, "codec_out0"},  	{"ssp2 Tx", NULL, "codec_out1"},  	{"codec_in0", NULL, "ssp2 Rx" },  	{"codec_in1", NULL, "ssp2 Rx" },  	{"ssp2 Rx", NULL, "AIF1 Capture"}, -	{"Headphone", NULL, "Platform Clock"}, -	{"Headset Mic", NULL, "Platform Clock"}, -	{"Int Mic", NULL, "Platform Clock"}, -	{"Ext Spk", NULL, "Platform Clock"}, +}; + +static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = { +	{"AIF2 Playback", NULL, "ssp2 Tx"}, +	{"ssp2 Tx", NULL, "codec_out0"}, +	{"ssp2 Tx", NULL, "codec_out1"}, +	{"codec_in0", NULL, "ssp2 Rx" }, +	{"codec_in1", NULL, "ssp2 Rx" }, +	{"ssp2 Rx", NULL, "AIF2 Capture"}, +}; + +static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = { +	{"AIF1 Playback", NULL, "ssp0 Tx"}, +	{"ssp0 Tx", NULL, "modem_out"}, +	{"modem_in", NULL, "ssp0 Rx" }, +	{"ssp0 Rx", NULL, "AIF1 Capture"}, +}; + +static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = { +	{"AIF2 Playback", NULL, "ssp0 Tx"}, +	{"ssp0 Tx", NULL, "modem_out"}, +	{"modem_in", NULL, "ssp0 Rx" }, +	{"ssp0 Rx", NULL, "AIF2 Capture"},  };  static const struct snd_kcontrol_new cht_mc_controls[] = { @@ -185,28 +243,65 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,  	return 0;  } +/* uncomment when we have a real quirk +static int cht_rt5645_quirk_cb(const struct dmi_system_id *id) +{ +	cht_rt5645_quirk = (unsigned long)id->driver_data; +	return 1; +} +*/ + +static const struct dmi_system_id cht_rt5645_quirk_table[] = { +	{ +	}, +}; +  static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)  {  	int ret;  	int jack_type;  	struct snd_soc_codec *codec = runtime->codec; -	struct snd_soc_dai *codec_dai = runtime->codec_dai; +	struct snd_soc_card *card = runtime->card;  	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); -	/* Select clk_i2s1_asrc as ASRC clock source */ -	rt5645_sel_asrc_clk_src(codec, -				RT5645_DA_STEREO_FILTER | -				RT5645_DA_MONO_L_FILTER | -				RT5645_DA_MONO_R_FILTER | -				RT5645_AD_STEREO_FILTER, -				RT5645_CLK_SEL_I2S1_ASRC); +	if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) || +	    (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { +		/* Select clk_i2s2_asrc as ASRC clock source */ +		rt5645_sel_asrc_clk_src(codec, +					RT5645_DA_STEREO_FILTER | +					RT5645_DA_MONO_L_FILTER | +					RT5645_DA_MONO_R_FILTER | +					RT5645_AD_STEREO_FILTER, +					RT5645_CLK_SEL_I2S2_ASRC); +	} else { +		/* Select clk_i2s1_asrc as ASRC clock source */ +		rt5645_sel_asrc_clk_src(codec, +					RT5645_DA_STEREO_FILTER | +					RT5645_DA_MONO_L_FILTER | +					RT5645_DA_MONO_R_FILTER | +					RT5645_AD_STEREO_FILTER, +					RT5645_CLK_SEL_I2S1_ASRC); +	} -	/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ -	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); -	if (ret < 0) { -		dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); -		return ret; +	if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) { +		ret = snd_soc_dapm_add_routes(&card->dapm, +					cht_rt5645_ssp2_aif2_map, +					ARRAY_SIZE(cht_rt5645_ssp2_aif2_map)); +	} else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) { +		ret = snd_soc_dapm_add_routes(&card->dapm, +					cht_rt5645_ssp0_aif1_map, +					ARRAY_SIZE(cht_rt5645_ssp0_aif1_map)); +	} else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) { +		ret = snd_soc_dapm_add_routes(&card->dapm, +					cht_rt5645_ssp0_aif2_map, +					ARRAY_SIZE(cht_rt5645_ssp0_aif2_map)); +	} else { +		ret = snd_soc_dapm_add_routes(&card->dapm, +					cht_rt5645_ssp2_aif1_map, +					ARRAY_SIZE(cht_rt5645_ssp2_aif1_map));  	} +	if (ret) +		return ret;  	if (ctx->acpi_card->codec_type == CODEC_TYPE_RT5650)  		jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | @@ -225,12 +320,33 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)  	rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack); +	if (ctx->mclk) { +		/* +		 * The firmware might enable the clock at +		 * boot (this information may or may not +		 * be reflected in the enable clock register). +		 * To change the rate we must disable the clock +		 * first to cover these cases. Due to common +		 * clock framework restrictions that do not allow +		 * to disable a clock that has not been enabled, +		 * we need to enable the clock first. +		 */ +		ret = clk_prepare_enable(ctx->mclk); +		if (!ret) +			clk_disable_unprepare(ctx->mclk); + +		ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ); + +		if (ret) +			dev_err(runtime->dev, "unable to set MCLK rate\n"); +	}  	return ret;  }  static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,  			    struct snd_pcm_hw_params *params)  { +	int ret;  	struct snd_interval *rate = hw_param_interval(params,  			SNDRV_PCM_HW_PARAM_RATE);  	struct snd_interval *channels = hw_param_interval(params, @@ -240,8 +356,67 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,  	rate->min = rate->max = 48000;  	channels->min = channels->max = 2; -	/* set SSP2 to 24-bit */ -	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); +	if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) || +		(cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { + +		/* set SSP0 to 16-bit */ +		params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + +		/* +		 * Default mode for SSP configuration is TDM 4 slot, override config +		 * with explicit setting to I2S 2ch 16-bit. The word length is set with +		 * dai_set_tdm_slot() since there is no other API exposed +		 */ +		ret = snd_soc_dai_set_fmt(rtd->cpu_dai, +					SND_SOC_DAIFMT_I2S     | +					SND_SOC_DAIFMT_NB_NF   | +					SND_SOC_DAIFMT_CBS_CFS +			); +		if (ret < 0) { +			dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); +			return ret; +		} + +		ret = snd_soc_dai_set_fmt(rtd->codec_dai, +					SND_SOC_DAIFMT_I2S     | +					SND_SOC_DAIFMT_NB_NF   | +					SND_SOC_DAIFMT_CBS_CFS +			); +		if (ret < 0) { +			dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); +			return ret; +		} + +		ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16); +		if (ret < 0) { +			dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); +			return ret; +		} + +	} else { + +		/* set SSP2 to 24-bit */ +		params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + +		/* +		 * Default mode for SSP configuration is TDM 4 slot +		 */ +		ret = snd_soc_dai_set_fmt(rtd->codec_dai, +					SND_SOC_DAIFMT_DSP_B | +					SND_SOC_DAIFMT_IB_NF | +					SND_SOC_DAIFMT_CBS_CFS); +		if (ret < 0) { +			dev_err(rtd->dev, "can't set format to TDM %d\n", ret); +			return ret; +		} + +		/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ +		ret = snd_soc_dai_set_tdm_slot(rtd->codec_dai, 0xF, 0xF, 4, 24); +		if (ret < 0) { +			dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret); +			return ret; +		} +	}  	return 0;  } @@ -303,8 +478,6 @@ static struct snd_soc_dai_link cht_dailink[] = {  		.no_pcm = 1,  		.codec_dai_name = "rt5645-aif1",  		.codec_name = "i2c-10EC5645:00", -		.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF -					| SND_SOC_DAIFMT_CBS_CFS,  		.init = cht_codec_init,  		.be_hw_params_fixup = cht_codec_fixup,  		.nonatomic = true, @@ -344,10 +517,31 @@ static struct snd_soc_card snd_soc_card_chtrt5650 = {  static struct cht_acpi_card snd_soc_cards[] = {  	{"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},  	{"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645}, +	{"10EC5648", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645}, +	{"10EC3270", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},  	{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},  }; -static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ +static char cht_rt5645_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ +static char cht_rt5645_codec_aif_name[12]; /*  = "rt5645-aif[1|2]" */ +static char cht_rt5645_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */ + +static bool is_valleyview(void) +{ +	static const struct x86_cpu_id cpu_ids[] = { +		{ X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ +		{} +	}; + +	if (!x86_match_cpu(cpu_ids)) +		return false; +	return true; +} + +struct acpi_chan_package {   /* ACPICA seems to require 64 bit integers */ +	u64 aif_value;       /* 1: AIF1, 2: AIF2 */ +	u64 mclock_value;    /* usually 25MHz (0x17d7940), ignored */ +};  static int snd_cht_mc_probe(struct platform_device *pdev)  { @@ -358,22 +552,33 @@ static int snd_cht_mc_probe(struct platform_device *pdev)  	struct sst_acpi_mach *mach;  	const char *i2c_name = NULL;  	int dai_index = 0; +	bool found = false; +	bool is_bytcr = false;  	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);  	if (!drv)  		return -ENOMEM; +	mach = (&pdev->dev)->platform_data; +  	for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) { -		if (acpi_dev_found(snd_soc_cards[i].codec_id)) { +		if (acpi_dev_found(snd_soc_cards[i].codec_id) && +			(!strncmp(snd_soc_cards[i].codec_id, mach->id, 8))) {  			dev_dbg(&pdev->dev,  				"found codec %s\n", snd_soc_cards[i].codec_id);  			card = snd_soc_cards[i].soc_card;  			drv->acpi_card = &snd_soc_cards[i]; +			found = true;  			break;  		}  	} + +	if (!found) { +		dev_err(&pdev->dev, "No matching HID found in supported list\n"); +		return -ENODEV; +	} +  	card->dev = &pdev->dev; -	mach = card->dev->platform_data;  	sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id);  	/* set correct codec name */ @@ -386,9 +591,105 @@ static int snd_cht_mc_probe(struct platform_device *pdev)  	/* fixup codec name based on HID */  	i2c_name = sst_acpi_find_name_from_hid(mach->id);  	if (i2c_name != NULL) { -		snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name), +		snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name),  			"%s%s", "i2c-", i2c_name); -		cht_dailink[dai_index].codec_name = cht_rt5640_codec_name; +		cht_dailink[dai_index].codec_name = cht_rt5645_codec_name; +	} + +	/* +	 * swap SSP0 if bytcr is detected +	 * (will be overridden if DMI quirk is detected) +	 */ +	if (is_valleyview()) { +		struct sst_platform_info *p_info = mach->pdata; +		const struct sst_res_info *res_info = p_info->res_info; + +		if (res_info->acpi_ipc_irq_index == 0) +			is_bytcr = true; +	} + +	if (is_bytcr) { +		/* +		 * Baytrail CR platforms may have CHAN package in BIOS, try +		 * to find relevant routing quirk based as done on Windows +		 * platforms. We have to read the information directly from the +		 * BIOS, at this stage the card is not created and the links +		 * with the codec driver/pdata are non-existent +		 */ + +		struct acpi_chan_package chan_package; + +		/* format specified: 2 64-bit integers */ +		struct acpi_buffer format = {sizeof("NN"), "NN"}; +		struct acpi_buffer state = {0, NULL}; +		struct sst_acpi_package_context pkg_ctx; +		bool pkg_found = false; + +		state.length = sizeof(chan_package); +		state.pointer = &chan_package; + +		pkg_ctx.name = "CHAN"; +		pkg_ctx.length = 2; +		pkg_ctx.format = &format; +		pkg_ctx.state = &state; +		pkg_ctx.data_valid = false; + +		pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx); +		if (pkg_found) { +			if (chan_package.aif_value == 1) { +				dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); +				cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF1; +			} else  if (chan_package.aif_value == 2) { +				dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n"); +				cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2; +			} else { +				dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n"); +				pkg_found = false; +			} +		} + +		if (!pkg_found) { +			/* no BIOS indications, assume SSP0-AIF2 connection */ +			cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2; +		} +	} + +	/* check quirks before creating card */ +	dmi_check_system(cht_rt5645_quirk_table); +	log_quirks(&pdev->dev); + +	if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) || +		(cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { + +		/* fixup codec aif name */ +		snprintf(cht_rt5645_codec_aif_name, +			sizeof(cht_rt5645_codec_aif_name), +			"%s", "rt5645-aif2"); + +		cht_dailink[dai_index].codec_dai_name = +			cht_rt5645_codec_aif_name; +	} + +	if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) || +		(cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { + +		/* fixup cpu dai name name */ +		snprintf(cht_rt5645_cpu_dai_name, +			sizeof(cht_rt5645_cpu_dai_name), +			"%s", "ssp0-port"); + +		cht_dailink[dai_index].cpu_dai_name = +			cht_rt5645_cpu_dai_name; +	} + +	if (is_valleyview()) { +		drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); +		if (IS_ERR(drv->mclk)) { +			dev_err(&pdev->dev, +				"Failed to get MCLK from pmc_plt_clk_3: %ld\n", +				PTR_ERR(drv->mclk)); +			return PTR_ERR(drv->mclk); +		}  	}  	snd_soc_card_set_drvdata(card, drv); diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index fddd1cd12f13..3b12bc1fa518 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -32,6 +32,7 @@  static struct snd_soc_jack skylake_headset;  static struct snd_soc_card skylake_audio_card;  static const struct snd_pcm_hw_constraint_list *dmic_constraints; +static struct snd_soc_jack skylake_hdmi[3];  struct skl_hdmi_pcm {  	struct list_head head; @@ -111,8 +112,8 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {  	SND_SOC_DAPM_MIC("Headset Mic", NULL),  	SND_SOC_DAPM_SPK("Spk", NULL),  	SND_SOC_DAPM_MIC("SoC DMIC", NULL), -	SND_SOC_DAPM_SPK("DP", NULL), -	SND_SOC_DAPM_SPK("HDMI", NULL), +	SND_SOC_DAPM_SPK("DP1", NULL), +	SND_SOC_DAPM_SPK("DP2", NULL),  	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,  			platform_clock_control, SND_SOC_DAPM_PRE_PMU |  			SND_SOC_DAPM_POST_PMD), @@ -130,9 +131,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {  	{ "MIC", NULL, "Headset Mic" },  	{ "DMic", NULL, "SoC DMIC" }, -	{"HDMI", NULL, "hif5 Output"}, -	{"DP", NULL, "hif6 Output"}, -  	/* CODEC BE connections */  	{ "HiFi Playback", NULL, "ssp0 Tx" },  	{ "ssp0 Tx", NULL, "codec0_out" }, @@ -603,19 +601,39 @@ static struct snd_soc_dai_link skylake_dais[] = {  	},  }; +#define NAME_SIZE	32  static int skylake_card_late_probe(struct snd_soc_card *card)  {  	struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(card);  	struct skl_hdmi_pcm *pcm; -	int err; +	struct snd_soc_codec *codec = NULL; +	int err, i = 0; +	char jack_name[NAME_SIZE];  	list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { -		err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device); +		codec = pcm->codec_dai->codec; +		snprintf(jack_name, sizeof(jack_name), +			"HDMI/DP, pcm=%d Jack", pcm->device); +		err = snd_soc_card_jack_new(card, jack_name, +					SND_JACK_AVOUT, +					&skylake_hdmi[i], +					NULL, 0); + +		if (err) +			return err; + +		err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, +						&skylake_hdmi[i]);  		if (err < 0)  			return err; + +		i++;  	} -	return 0; +	if (!codec) +		return -EINVAL; + +	return hdac_hdmi_jack_port_init(codec, &card->dapm);  }  /* skylake audio machine driver for SPT + NAU88L25 */ diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 8ab865ee0cad..eb7751b0599b 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -36,6 +36,7 @@  static struct snd_soc_jack skylake_headset;  static struct snd_soc_card skylake_audio_card;  static const struct snd_pcm_hw_constraint_list *dmic_constraints; +static struct snd_soc_jack skylake_hdmi[3];  struct skl_hdmi_pcm {  	struct list_head head; @@ -115,8 +116,8 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {  	SND_SOC_DAPM_SPK("Left Speaker", NULL),  	SND_SOC_DAPM_SPK("Right Speaker", NULL),  	SND_SOC_DAPM_MIC("SoC DMIC", NULL), -	SND_SOC_DAPM_SPK("DP", NULL), -	SND_SOC_DAPM_SPK("HDMI", NULL), +	SND_SOC_DAPM_SPK("DP1", NULL), +	SND_SOC_DAPM_SPK("DP2", NULL),  	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,  			platform_clock_control, SND_SOC_DAPM_PRE_PMU |  			SND_SOC_DAPM_POST_PMD), @@ -135,8 +136,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {  	{"MIC", NULL, "Headset Mic"},  	{"DMic", NULL, "SoC DMIC"}, -	{"HDMI", NULL, "hif5 Output"}, -	{"DP", NULL, "hif6 Output"},  	/* CODEC BE connections */  	{ "Left Playback", NULL, "ssp0 Tx"},  	{ "Right Playback", NULL, "ssp0 Tx"}, @@ -653,19 +652,39 @@ static struct snd_soc_dai_link skylake_dais[] = {  	},  }; +#define NAME_SIZE	32  static int skylake_card_late_probe(struct snd_soc_card *card)  {  	struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(card);  	struct skl_hdmi_pcm *pcm; -	int err; +	struct snd_soc_codec *codec = NULL; +	int err, i = 0; +	char jack_name[NAME_SIZE];  	list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { -		err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device); +		codec = pcm->codec_dai->codec; +		snprintf(jack_name, sizeof(jack_name), +			"HDMI/DP, pcm=%d Jack", pcm->device); +		err = snd_soc_card_jack_new(card, jack_name, +					SND_JACK_AVOUT, +					&skylake_hdmi[i], +					NULL, 0); + +		if (err) +			return err; + +		err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, +						&skylake_hdmi[i]);  		if (err < 0)  			return err; + +		i++;  	} -	return 0; +	if (!codec) +		return -EINVAL; + +	return hdac_hdmi_jack_port_init(codec, &card->dapm);  }  /* skylake audio machine driver for SPT + NAU88L25 */ diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index dc5c3611a6ff..f5ab7b8d51d1 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -29,6 +29,7 @@  #include "../../codecs/hdac_hdmi.h"  static struct snd_soc_jack skylake_headset; +static struct snd_soc_jack skylake_hdmi[3];  struct skl_hdmi_pcm {  	struct list_head head; @@ -94,10 +95,6 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {  	{"DMIC1 Pin", NULL, "DMIC2"},  	{"DMic", NULL, "SoC DMIC"}, -	{"HDMI1", NULL, "hif5 Output"}, -	{"HDMI2", NULL, "hif6 Output"}, -	{"HDMI3", NULL, "hif7 Output"}, -  	/* CODEC BE connections */  	{ "AIF1 Playback", NULL, "ssp0 Tx"},  	{ "ssp0 Tx", NULL, "codec0_out"}, @@ -458,19 +455,38 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {  	},  }; +#define NAME_SIZE	32  static int skylake_card_late_probe(struct snd_soc_card *card)  {  	struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(card);  	struct skl_hdmi_pcm *pcm; -	int err; +	struct snd_soc_codec *codec = NULL; +	int err, i = 0; +	char jack_name[NAME_SIZE];  	list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { -		err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device); +		codec = pcm->codec_dai->codec; +		snprintf(jack_name, sizeof(jack_name), +			"HDMI/DP, pcm=%d Jack", pcm->device); +		err = snd_soc_card_jack_new(card, jack_name, +					SND_JACK_AVOUT, &skylake_hdmi[i], +					NULL, 0); + +		if (err) +			return err; + +		err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, +						&skylake_hdmi[i]);  		if (err < 0)  			return err; + +		i++;  	} -	return 0; +	if (!codec) +		return -EINVAL; + +	return hdac_hdmi_jack_port_init(codec, &card->dapm);  }  /* skylake audio machine driver for SPT + RT286S */ diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index c00ede4ea4d7..11c0805393ff 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -252,44 +252,44 @@ void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset,  EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced);  int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask, -			 u32 target, u32 timeout, char *operation) +			 u32 target, u32 time, char *operation)  { -	int time, ret;  	u32 reg; -	bool done = false; +	unsigned long timeout; +	int k = 0, s = 500;  	/* -	 * we will poll for couple of ms using mdelay, if not successful -	 * then go to longer sleep using usleep_range +	 * split the loop into sleeps of varying resolution. more accurately, +	 * the range of wakeups are: +	 * Phase 1(first 5ms): min sleep 0.5ms; max sleep 1ms. +	 * Phase 2:( 5ms to 10ms) : min sleep 0.5ms; max sleep 10ms +	 * (usleep_range (500, 1000) and usleep_range(5000, 10000) are +	 * both possible in this phase depending on whether k > 10 or not). +	 * Phase 3: (beyond 10 ms) min sleep 5ms; max sleep 10ms.  	 */ -	/* check if set state successful */ -	for (time = 0; time < 5; time++) { -		if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) { -			done = true; -			break; -		} -		mdelay(1); +	timeout = jiffies + msecs_to_jiffies(time); +	while (((sst_dsp_shim_read_unlocked(ctx, offset) & mask) != target) +		&& time_before(jiffies, timeout)) { +		k++; +		if (k > 10) +			s = 5000; + +		usleep_range(s, 2*s);  	} -	if (done ==  false) { -		/* sleeping in 10ms steps so adjust timeout value */ -		timeout /= 10; +	reg = sst_dsp_shim_read_unlocked(ctx, offset); -		for (time = 0; time < timeout; time++) { -			if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) -				break; +	if ((reg & mask) == target) { +		dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s successful\n", +					reg, operation); -			usleep_range(5000, 10000); -		} +		return 0;  	} -	reg = sst_dsp_shim_read_unlocked(ctx, offset); -	dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation, -			(time < timeout) ? "successful" : "timedout"); -	ret = time < timeout ? 0 : -ETIME; - -	return ret; +	dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s timedout\n", +					reg, operation); +	return -ETIME;  }  EXPORT_SYMBOL_GPL(sst_dsp_register_poll); diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 1f9f33d34000..15a063a403cc 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -23,7 +23,6 @@  #include "../common/sst-dsp.h"  #include "../common/sst-dsp-priv.h"  #include "skl-sst-ipc.h" -#include "skl-tplg-interface.h"  #define BXT_BASEFW_TIMEOUT	3000  #define BXT_INIT_TIMEOUT	500 @@ -52,7 +51,7 @@ static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)  }  static int -bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo) +bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count)  {  	struct snd_dma_buffer dmab;  	struct skl_sst *skl = ctx->thread_context; @@ -61,11 +60,11 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo)  	int ret = 0, i, dma_id, stream_tag;  	/* library indices start from 1 to N. 0 represents base FW */ -	for (i = 1; i < minfo->lib_count; i++) { -		ret = request_firmware(&fw, minfo->lib[i].name, ctx->dev); +	for (i = 1; i < lib_count; i++) { +		ret = request_firmware(&fw, linfo[i].name, ctx->dev);  		if (ret < 0) {  			dev_err(ctx->dev, "Request lib %s failed:%d\n", -					minfo->lib[i].name, ret); +					linfo[i].name, ret);  			return ret;  		} @@ -96,7 +95,7 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo)  		ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i);  		if (ret < 0)  			dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n", -					minfo->lib[i].name, ret); +					linfo[i].name, ret);  		ctx->dsp_ops.trigger(ctx->dev, false, stream_tag);  		ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag); @@ -119,8 +118,7 @@ load_library_failed:  static int sst_bxt_prepare_fw(struct sst_dsp *ctx,  			const void *fwdata, u32 fwsize)  { -	int stream_tag, ret, i; -	u32 reg; +	int stream_tag, ret;  	stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);  	if (stream_tag <= 0) { @@ -153,23 +151,13 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,  	}  	/* Step 4: Wait for DONE Bit */ -	for (i = BXT_INIT_TIMEOUT; i > 0; --i) { -		reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE); - -		if (reg & SKL_ADSP_REG_HIPCIE_DONE) { -			sst_dsp_shim_update_bits_forced(ctx, -					SKL_ADSP_REG_HIPCIE, +	ret = sst_dsp_register_poll(ctx, SKL_ADSP_REG_HIPCIE,  					SKL_ADSP_REG_HIPCIE_DONE, -					SKL_ADSP_REG_HIPCIE_DONE); -			break; -		} -		mdelay(1); -	} -	if (!i) { -		dev_info(ctx->dev, "Waiting for HIPCIE done, reg: 0x%x\n", reg); -		sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCIE, -				SKL_ADSP_REG_HIPCIE_DONE, -				SKL_ADSP_REG_HIPCIE_DONE); +					SKL_ADSP_REG_HIPCIE_DONE, +					BXT_INIT_TIMEOUT, "HIPCIE Done"); +	if (ret < 0) { +		dev_err(ctx->dev, "Timout for Purge Request%d\n", ret); +		goto base_fw_load_failed;  	}  	/* Step 5: power down core1 */ @@ -184,19 +172,10 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,  	skl_ipc_op_int_enable(ctx);  	/* Step 7: Wait for ROM init */ -	for (i = BXT_INIT_TIMEOUT; i > 0; --i) { -		if (SKL_FW_INIT == -				(sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) & -				SKL_FW_STS_MASK)) { - -			dev_info(ctx->dev, "ROM loaded, continue FW loading\n"); -			break; -		} -		mdelay(1); -	} -	if (!i) { -		dev_err(ctx->dev, "Timeout for ROM init, HIPCIE: 0x%x\n", reg); -		ret = -EIO; +	ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK, +			SKL_FW_INIT, BXT_INIT_TIMEOUT, "ROM Load"); +	if (ret < 0) { +		dev_err(ctx->dev, "Timeout for ROM init, ret:%d\n", ret);  		goto base_fw_load_failed;  	} @@ -432,7 +411,6 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)  	int ret;  	struct skl_ipc_dxstate_info dx;  	unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); -	struct skl_dfw_manifest *minfo = &skl->manifest;  	if (skl->fw_loaded == false) {  		skl->boot_complete = false; @@ -442,8 +420,9 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)  			return ret;  		} -		if (minfo->lib_count > 1) { -			ret = bxt_load_library(ctx, minfo); +		if (skl->lib_count > 1) { +			ret = bxt_load_library(ctx, skl->lib_info, +						skl->lib_count);  			if (ret < 0) {  				dev_err(ctx->dev, "reload libs failed: %d\n", ret);  				return ret; @@ -640,8 +619,9 @@ int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx)  	skl_dsp_init_core_state(sst); -	if (ctx->manifest.lib_count > 1) { -		ret = sst->fw_ops.load_library(sst, &ctx->manifest); +	if (ctx->lib_count > 1) { +		ret = sst->fw_ops.load_library(sst, ctx->lib_info, +						ctx->lib_count);  		if (ret < 0) {  			dev_err(dev, "Load Library failed : %x\n", ret);  			return ret; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index e79cbcf6e462..e66870474f10 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -220,6 +220,13 @@ static const struct skl_dsp_ops dsp_ops[] = {  		.init_fw = bxt_sst_init_fw,  		.cleanup = bxt_sst_dsp_cleanup  	}, +	{ +		.id = 0x3198, +		.loader_ops = bxt_get_loader_ops, +		.init = bxt_sst_dsp_init, +		.init_fw = bxt_sst_init_fw, +		.cleanup = bxt_sst_dsp_cleanup +	},  };  const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 3f8e6f0b7eb5..7eb9c419dc7f 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -102,14 +102,16 @@ static void dump_config(struct device *dev, u32 instance_id, u8 linktype,  }  static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, -				u32 instance_id, u8 link_type, u8 dirn) +		u32 instance_id, u8 link_type, u8 dirn, u8 dev_type)  { -	dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d\n", -		epnt->virtual_bus_id, epnt->linktype, epnt->direction); +	dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n", +			epnt->virtual_bus_id, epnt->linktype, +			epnt->direction, epnt->device_type);  	if ((epnt->virtual_bus_id == instance_id) &&  			(epnt->linktype == link_type) && -			(epnt->direction == dirn)) +			(epnt->direction == dirn) && +			(epnt->device_type == dev_type))  		return true;  	else  		return false; @@ -117,7 +119,8 @@ static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,  struct nhlt_specific_cfg  *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, -			u8 s_fmt, u8 num_ch, u32 s_rate, u8 dirn) +			u8 s_fmt, u8 num_ch, u32 s_rate, +			u8 dirn, u8 dev_type)  {  	struct nhlt_fmt *fmt;  	struct nhlt_endpoint *epnt; @@ -135,7 +138,8 @@ struct nhlt_specific_cfg  	dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count);  	for (j = 0; j < nhlt->endpoint_count; j++) { -		if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) { +		if (skl_check_ep_match(dev, epnt, instance, link_type, +						dirn, dev_type)) {  			fmt = (struct nhlt_fmt *)(epnt->config.caps +  						 epnt->config.size);  			sp_config = skl_get_specific_cfg(dev, fmt, num_ch, @@ -189,9 +193,9 @@ int skl_get_dmic_geo(struct skl *skl)  	return dmic_geo;  } -static void skl_nhlt_trim_space(struct skl *skl) +static void skl_nhlt_trim_space(char *trim)  { -	char *s = skl->tplg_name; +	char *s = trim;  	int cnt;  	int i; @@ -218,7 +222,43 @@ int skl_nhlt_update_topology_bin(struct skl *skl)  		skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id,  		nhlt->header.oem_revision, "-tplg.bin"); -	skl_nhlt_trim_space(skl); +	skl_nhlt_trim_space(skl->tplg_name);  	return 0;  } + +static ssize_t skl_nhlt_platform_id_show(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	struct pci_dev *pci = to_pci_dev(dev); +	struct hdac_ext_bus *ebus = pci_get_drvdata(pci); +	struct skl *skl = ebus_to_skl(ebus); +	struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; +	char platform_id[32]; + +	sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id, +			nhlt->header.oem_id, nhlt->header.oem_table_id, +			nhlt->header.oem_revision); + +	skl_nhlt_trim_space(platform_id); +	return sprintf(buf, "%s\n", platform_id); +} + +static DEVICE_ATTR(platform_id, 0444, skl_nhlt_platform_id_show, NULL); + +int skl_nhlt_create_sysfs(struct skl *skl) +{ +	struct device *dev = &skl->pci->dev; + +	if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr)) +		dev_warn(dev, "Error creating sysfs entry\n"); + +	return 0; +} + +void skl_nhlt_remove_sysfs(struct skl *skl) +{ +	struct device *dev = &skl->pci->dev; + +	sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr); +} diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 6c6b63a6b338..e12520e142ff 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -137,6 +137,80 @@ static void skl_set_suspend_active(struct snd_pcm_substream *substream,  		skl->supend_active--;  } +int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) +{ +	struct hdac_ext_bus *ebus = dev_get_drvdata(dev); +	struct hdac_bus *bus = ebus_to_hbus(ebus); +	unsigned int format_val; +	struct hdac_stream *hstream; +	struct hdac_ext_stream *stream; +	int err; + +	hstream = snd_hdac_get_stream(bus, params->stream, +					params->host_dma_id + 1); +	if (!hstream) +		return -EINVAL; + +	stream = stream_to_hdac_ext_stream(hstream); +	snd_hdac_ext_stream_decouple(ebus, stream, true); + +	format_val = snd_hdac_calc_stream_format(params->s_freq, +				params->ch, params->format, 32, 0); + +	dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", +		format_val, params->s_freq, params->ch, params->format); + +	snd_hdac_stream_reset(hdac_stream(stream)); +	err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); +	if (err < 0) +		return err; + +	err = snd_hdac_stream_setup(hdac_stream(stream)); +	if (err < 0) +		return err; + +	hdac_stream(stream)->prepared = 1; + +	return 0; +} + +int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params) +{ +	struct hdac_ext_bus *ebus = dev_get_drvdata(dev); +	struct hdac_bus *bus = ebus_to_hbus(ebus); +	unsigned int format_val; +	struct hdac_stream *hstream; +	struct hdac_ext_stream *stream; +	struct hdac_ext_link *link; + +	hstream = snd_hdac_get_stream(bus, params->stream, +					params->link_dma_id + 1); +	if (!hstream) +		return -EINVAL; + +	stream = stream_to_hdac_ext_stream(hstream); +	snd_hdac_ext_stream_decouple(ebus, stream, true); +	format_val = snd_hdac_calc_stream_format(params->s_freq, +				params->ch, params->format, 24, 0); + +	dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", +		format_val, params->s_freq, params->ch, params->format); + +	snd_hdac_ext_link_stream_reset(stream); + +	snd_hdac_ext_link_stream_setup(stream, format_val); + +	list_for_each_entry(link, &ebus->hlink_list, list) { +		if (link->index == params->link_index) +			snd_hdac_ext_link_set_stream_id(link, +					hstream->stream_tag); +	} + +	stream->link_prepared = 1; + +	return 0; +} +  static int skl_pcm_open(struct snd_pcm_substream *substream,  		struct snd_soc_dai *dai)  { @@ -188,32 +262,6 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,  	return 0;  } -static int skl_get_format(struct snd_pcm_substream *substream, -		struct snd_soc_dai *dai) -{ -	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); -	struct skl_dma_params *dma_params; -	struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); -	int format_val = 0; - -	if ((ebus_to_hbus(ebus))->ppcap) { -		struct snd_pcm_runtime *runtime = substream->runtime; - -		format_val = snd_hdac_calc_stream_format(runtime->rate, -						runtime->channels, -						runtime->format, -						32, 0); -	} else { -		struct snd_soc_dai *codec_dai = rtd->codec_dai; - -		dma_params = snd_soc_dai_get_dma_data(codec_dai, substream); -		if (dma_params) -			format_val = dma_params->format; -	} - -	return format_val; -} -  static int skl_be_prepare(struct snd_pcm_substream *substream,  		struct snd_soc_dai *dai)  { @@ -234,37 +282,19 @@ static int skl_be_prepare(struct snd_pcm_substream *substream,  static int skl_pcm_prepare(struct snd_pcm_substream *substream,  		struct snd_soc_dai *dai)  { -	struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);  	struct skl *skl = get_skl_ctx(dai->dev); -	unsigned int format_val; -	int err;  	struct skl_module_cfg *mconfig;  	dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);  	mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); -	format_val = skl_get_format(substream, dai); -	dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n", -				hdac_stream(stream)->stream_tag, format_val); -	snd_hdac_stream_reset(hdac_stream(stream)); -  	/* In case of XRUN recovery, reset the FW pipe to clean state */  	if (mconfig && (substream->runtime->status->state ==  					SNDRV_PCM_STATE_XRUN))  		skl_reset_pipe(skl->skl_sst, mconfig->pipe); -	err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); -	if (err < 0) -		return err; - -	err = snd_hdac_stream_setup(hdac_stream(stream)); -	if (err < 0) -		return err; - -	hdac_stream(stream)->prepared = 1; - -	return err; +	return 0;  }  static int skl_pcm_hw_params(struct snd_pcm_substream *substream, @@ -295,6 +325,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,  	p_params.s_freq = params_rate(params);  	p_params.host_dma_id = dma_id;  	p_params.stream = substream->stream; +	p_params.format = params_format(params);  	m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);  	if (m_cfg) @@ -438,7 +469,6 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,  	switch (cmd) {  	case SNDRV_PCM_TRIGGER_RESUME:  		if (!w->ignore_suspend) { -			skl_pcm_prepare(substream, dai);  			/*  			 * enable DMA Resume enable bit for the stream, set the  			 * dpib & lpib position to resume before starting the @@ -447,7 +477,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,  			snd_hdac_ext_stream_drsm_enable(ebus, true,  						hdac_stream(stream)->index);  			snd_hdac_ext_stream_set_dpibr(ebus, stream, -							stream->dpib); +							stream->lpib);  			snd_hdac_ext_stream_set_lpib(stream, stream->lpib);  		} @@ -459,7 +489,6 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,  		 * pipeline is started but there is a delay in starting the  		 * DMA channel on the host.  		 */ -		snd_hdac_ext_stream_decouple(ebus, stream, true);  		ret = skl_decoupled_trigger(substream, cmd);  		if (ret < 0)  			return ret; @@ -506,9 +535,10 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,  	struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);  	struct hdac_ext_stream *link_dev;  	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); -	struct hdac_ext_dma_params *dma_params;  	struct snd_soc_dai *codec_dai = rtd->codec_dai;  	struct skl_pipe_params p_params = {0}; +	struct hdac_ext_link *link; +	int stream_tag;  	link_dev = snd_hdac_ext_stream_assign(ebus, substream,  					HDAC_EXT_STREAM_TYPE_LINK); @@ -517,16 +547,22 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,  	snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); +	link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name); +	if (!link) +		return -EINVAL; + +	stream_tag = hdac_stream(link_dev)->stream_tag; +  	/* set the stream tag in the codec dai dma params  */ -	dma_params = snd_soc_dai_get_dma_data(codec_dai, substream); -	if (dma_params) -		dma_params->stream_tag =  hdac_stream(link_dev)->stream_tag; +	snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0);  	p_params.s_fmt = snd_pcm_format_width(params_format(params));  	p_params.ch = params_channels(params);  	p_params.s_freq = params_rate(params);  	p_params.stream = substream->stream; -	p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1; +	p_params.link_dma_id = stream_tag - 1; +	p_params.link_index = link->index; +	p_params.format = params_format(params);  	return skl_tplg_be_update_params(dai, &p_params);  } @@ -534,41 +570,15 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,  static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,  		struct snd_soc_dai *dai)  { -	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); -	struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); -	struct hdac_ext_stream *link_dev = -			snd_soc_dai_get_dma_data(dai, substream); -	unsigned int format_val = 0; -	struct skl_dma_params *dma_params; -	struct snd_soc_dai *codec_dai = rtd->codec_dai; -	struct hdac_ext_link *link;  	struct skl *skl = get_skl_ctx(dai->dev);  	struct skl_module_cfg *mconfig = NULL; -	dma_params  = (struct skl_dma_params *) -			snd_soc_dai_get_dma_data(codec_dai, substream); -	if (dma_params) -		format_val = dma_params->format; -	dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d codec_dai_name=%s\n", -			hdac_stream(link_dev)->stream_tag, format_val, codec_dai->name); - -	link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name); -	if (!link) -		return -EINVAL; - -	snd_hdac_ext_link_stream_reset(link_dev); -  	/* In case of XRUN recovery, reset the FW pipe to clean state */  	mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream); -	if (mconfig && (substream->runtime->status->state == -					SNDRV_PCM_STATE_XRUN)) +	if (mconfig && !mconfig->pipe->passthru && +		(substream->runtime->status->state == SNDRV_PCM_STATE_XRUN))  		skl_reset_pipe(skl->skl_sst, mconfig->pipe); -	snd_hdac_ext_link_stream_setup(link_dev, format_val); - -	snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag); -	link_dev->link_prepared = 1; -  	return 0;  } @@ -583,10 +593,8 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream,  	dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);  	switch (cmd) {  	case SNDRV_PCM_TRIGGER_RESUME: -		skl_link_pcm_prepare(substream, dai);  	case SNDRV_PCM_TRIGGER_START:  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -		snd_hdac_ext_stream_decouple(ebus, stream, true);  		snd_hdac_ext_link_stream_start(link_dev);  		break; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 7c272ba0f4b5..849410d0823e 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -19,7 +19,6 @@  #include <linux/interrupt.h>  #include <sound/memalloc.h>  #include "skl-sst-cldma.h" -#include "skl-tplg-interface.h"  #include "skl-topology.h"  struct sst_dsp; @@ -145,7 +144,7 @@ struct skl_dsp_fw_ops {  	int (*load_fw)(struct sst_dsp  *ctx);  	/* FW module parser/loader */  	int (*load_library)(struct sst_dsp *ctx, -		struct skl_dfw_manifest *minfo); +		struct skl_lib_info *linfo, int count);  	int (*parse_fw)(struct sst_dsp *ctx);  	int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id);  	int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id); @@ -236,5 +235,4 @@ int skl_get_pvt_instance_id_map(struct skl_sst *ctx,  void skl_freeup_uuid_list(struct skl_sst *ctx);  int skl_dsp_strip_extended_manifest(struct firmware *fw); -  #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index cc40341233fa..9660ace379ab 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -97,8 +97,9 @@ struct skl_sst {  	/* multi-core */  	struct skl_dsp_cores cores; -	/* tplg manifest */ -	struct skl_dfw_manifest manifest; +	/* library info */ +	struct skl_lib_info  lib_info[SKL_MAX_LIB]; +	int lib_count;  	/* Callback to update D0i3C register */  	void (*update_d0i3c)(struct device *dev, bool enable); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index bd313c907b20..ed58b5b3555a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -330,6 +330,31 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,  			multiplier;  } +static u8 skl_tplg_be_dev_type(int dev_type) +{ +	int ret; + +	switch (dev_type) { +	case SKL_DEVICE_BT: +		ret = NHLT_DEVICE_BT; +		break; + +	case SKL_DEVICE_DMIC: +		ret = NHLT_DEVICE_DMIC; +		break; + +	case SKL_DEVICE_I2S: +		ret = NHLT_DEVICE_I2S; +		break; + +	default: +		ret = NHLT_DEVICE_INVALID; +		break; +	} + +	return ret; +} +  static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,  						struct skl_sst *ctx)  { @@ -338,6 +363,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,  	u32 ch, s_freq, s_fmt;  	struct nhlt_specific_cfg *cfg;  	struct skl *skl = get_skl_ctx(ctx->dev); +	u8 dev_type = skl_tplg_be_dev_type(m_cfg->dev_type);  	/* check if we already have blob */  	if (m_cfg->formats_config.caps_size > 0) @@ -374,7 +400,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,  	/* update the blob based on virtual bus_id and default params */  	cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type, -					s_fmt, ch, s_freq, dir); +					s_fmt, ch, s_freq, dir, dev_type);  	if (cfg) {  		m_cfg->formats_config.caps_size = cfg->size;  		m_cfg->formats_config.caps = (u32 *) &cfg->caps; @@ -496,6 +522,20 @@ static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w)  	return 0;  } +static int skl_tplg_module_prepare(struct skl_sst *ctx, struct skl_pipe *pipe, +		struct snd_soc_dapm_widget *w, struct skl_module_cfg *mcfg) +{ +	switch (mcfg->dev_type) { +	case SKL_DEVICE_HDAHOST: +		return skl_pcm_host_dma_prepare(ctx->dev, pipe->p_params); + +	case SKL_DEVICE_HDALINK: +		return skl_pcm_link_dma_prepare(ctx->dev, pipe->p_params); +	} + +	return 0; +} +  /*   * Inside a pipe instance, we can have various modules. These modules need   * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by @@ -535,6 +575,11 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)  			mconfig->m_state = SKL_MODULE_LOADED;  		} +		/* prepare the DMA if the module is gateway cpr */ +		ret = skl_tplg_module_prepare(ctx, pipe, w, mconfig); +		if (ret < 0) +			return ret; +  		/* update blob if blob is null for be with default value */  		skl_tplg_update_be_blob(w, ctx); @@ -974,7 +1019,6 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,  	struct skl_module_cfg *src_module = NULL, *dst_module;  	struct skl_sst *ctx = skl->skl_sst;  	struct skl_pipe *s_pipe = mconfig->pipe; -	int ret = 0;  	if (s_pipe->state == SKL_PIPE_INVALID)  		return -EINVAL; @@ -996,7 +1040,7 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,  		src_module = dst_module;  	} -	ret = skl_delete_pipe(ctx, mconfig->pipe); +	skl_delete_pipe(ctx, mconfig->pipe);  	return skl_tplg_unload_pipe_modules(ctx, s_pipe);  } @@ -1207,6 +1251,7 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,  		switch (mcfg->dev_type) {  		case SKL_DEVICE_HDALINK:  			pipe->p_params->link_dma_id = params->link_dma_id; +			pipe->p_params->link_index = params->link_index;  			break;  		case SKL_DEVICE_HDAHOST: @@ -1220,6 +1265,7 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,  		pipe->p_params->ch = params->ch;  		pipe->p_params->s_freq = params->s_freq;  		pipe->p_params->stream = params->stream; +		pipe->p_params->format = params->format;  	} else {  		memcpy(pipe->p_params, params, sizeof(*params)); @@ -1428,6 +1474,7 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,  	struct nhlt_specific_cfg *cfg;  	struct skl *skl = get_skl_ctx(dai->dev);  	int link_type = skl_tplg_be_link_type(mconfig->dev_type); +	u8 dev_type = skl_tplg_be_dev_type(mconfig->dev_type);  	skl_tplg_fill_dma_id(mconfig, params); @@ -1437,7 +1484,8 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,  	/* update the blob based on virtual bus_id*/  	cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,  					params->s_fmt, params->ch, -					params->s_freq, params->stream); +					params->s_freq, params->stream, +					dev_type);  	if (cfg) {  		mconfig->formats_config.caps_size = cfg->size;  		mconfig->formats_config.caps = (u32 *) &cfg->caps; @@ -2280,20 +2328,21 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt,  static int skl_tplg_fill_str_mfest_tkn(struct device *dev,  		struct snd_soc_tplg_vendor_string_elem *str_elem, -		struct skl_dfw_manifest *minfo) +		struct skl *skl)  {  	int tkn_count = 0;  	static int ref_count;  	switch (str_elem->token) {  	case SKL_TKN_STR_LIB_NAME: -		if (ref_count > minfo->lib_count - 1) { +		if (ref_count > skl->skl_sst->lib_count - 1) {  			ref_count = 0;  			return -EINVAL;  		} -		strncpy(minfo->lib[ref_count].name, str_elem->string, -				ARRAY_SIZE(minfo->lib[ref_count].name)); +		strncpy(skl->skl_sst->lib_info[ref_count].name, +			str_elem->string, +			ARRAY_SIZE(skl->skl_sst->lib_info[ref_count].name));  		ref_count++;  		tkn_count++;  		break; @@ -2308,14 +2357,14 @@ static int skl_tplg_fill_str_mfest_tkn(struct device *dev,  static int skl_tplg_get_str_tkn(struct device *dev,  		struct snd_soc_tplg_vendor_array *array, -		struct skl_dfw_manifest *minfo) +		struct skl *skl)  {  	int tkn_count = 0, ret;  	struct snd_soc_tplg_vendor_string_elem *str_elem;  	str_elem = (struct snd_soc_tplg_vendor_string_elem *)array->value;  	while (tkn_count < array->num_elems) { -		ret = skl_tplg_fill_str_mfest_tkn(dev, str_elem, minfo); +		ret = skl_tplg_fill_str_mfest_tkn(dev, str_elem, skl);  		str_elem++;  		if (ret < 0) @@ -2329,13 +2378,13 @@ static int skl_tplg_get_str_tkn(struct device *dev,  static int skl_tplg_get_int_tkn(struct device *dev,  		struct snd_soc_tplg_vendor_value_elem *tkn_elem, -		struct skl_dfw_manifest *minfo) +		struct skl *skl)  {  	int tkn_count = 0;  	switch (tkn_elem->token) {  	case SKL_TKN_U32_LIB_COUNT: -		minfo->lib_count = tkn_elem->value; +		skl->skl_sst->lib_count = tkn_elem->value;  		tkn_count++;  		break; @@ -2352,7 +2401,7 @@ static int skl_tplg_get_int_tkn(struct device *dev,   * type.   */  static int skl_tplg_get_manifest_tkn(struct device *dev, -		char *pvt_data, struct skl_dfw_manifest *minfo, +		char *pvt_data, struct skl *skl,  		int block_size)  {  	int tkn_count = 0, ret; @@ -2368,7 +2417,7 @@ static int skl_tplg_get_manifest_tkn(struct device *dev,  		off += array->size;  		switch (array->type) {  		case SND_SOC_TPLG_TUPLE_TYPE_STRING: -			ret = skl_tplg_get_str_tkn(dev, array, minfo); +			ret = skl_tplg_get_str_tkn(dev, array, skl);  			if (ret < 0)  				return ret; @@ -2390,7 +2439,7 @@ static int skl_tplg_get_manifest_tkn(struct device *dev,  		while (tkn_count <= array->num_elems - 1) {  			ret = skl_tplg_get_int_tkn(dev, -					tkn_elem, minfo); +					tkn_elem, skl);  			if (ret < 0)  				return ret; @@ -2411,7 +2460,7 @@ static int skl_tplg_get_manifest_tkn(struct device *dev,   * preceded by descriptors for type and size of data block.   */  static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, -			struct device *dev, struct skl_dfw_manifest *minfo) +			struct device *dev, struct skl *skl)  {  	struct snd_soc_tplg_vendor_array *array;  	int num_blocks, block_size = 0, block_type, off = 0; @@ -2454,7 +2503,7 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest,  		data = (manifest->priv.data + off);  		if (block_type == SKL_TYPE_TUPLE) { -			ret = skl_tplg_get_manifest_tkn(dev, data, minfo, +			ret = skl_tplg_get_manifest_tkn(dev, data, skl,  					block_size);  			if (ret < 0) @@ -2472,27 +2521,23 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest,  static int skl_manifest_load(struct snd_soc_component *cmpnt,  				struct snd_soc_tplg_manifest *manifest)  { -	struct skl_dfw_manifest *minfo;  	struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);  	struct hdac_bus *bus = ebus_to_hbus(ebus);  	struct skl *skl = ebus_to_skl(ebus); -	int ret = 0;  	/* proceed only if we have private data defined */  	if (manifest->priv.size == 0)  		return 0; -	minfo = &skl->skl_sst->manifest; - -	skl_tplg_get_manifest_data(manifest, bus->dev, minfo); +	skl_tplg_get_manifest_data(manifest, bus->dev, skl); -	if (minfo->lib_count > HDA_MAX_LIB) { +	if (skl->skl_sst->lib_count > SKL_MAX_LIB) {  		dev_err(bus->dev, "Exceeding max Library count. Got:%d\n", -					minfo->lib_count); -		ret = -EINVAL; +					skl->skl_sst->lib_count); +		return  -EINVAL;  	} -	return ret; +	return 0;  }  static struct snd_soc_tplg_ops skl_tplg_ops  = { diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 08d39280b07b..fefab0e99a3b 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -254,6 +254,8 @@ struct skl_pipe_params {  	u32 s_freq;  	u32 s_fmt;  	u8 linktype; +	snd_pcm_format_t format; +	int link_index;  	int stream;  }; @@ -332,6 +334,19 @@ struct skl_pipeline {  	struct list_head node;  }; +#define SKL_LIB_NAME_LENGTH 128 +#define SKL_MAX_LIB 16 + +struct skl_lib_info { +	char name[SKL_LIB_NAME_LENGTH]; +	const struct firmware *fw; +}; + +struct skl_manifest { +	u32 lib_count; +	struct skl_lib_info lib[SKL_MAX_LIB]; +}; +  static inline struct skl *get_skl_ctx(struct device *dev)  {  	struct hdac_ext_bus *ebus = dev_get_drvdata(dev); @@ -383,4 +398,8 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,  struct skl_module_cfg *skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai,  								int stream);  enum skl_bitdepth skl_get_bit_depth(int params); +int skl_pcm_host_dma_prepare(struct device *dev, +			struct skl_pipe_params *params); +int skl_pcm_link_dma_prepare(struct device *dev, +			struct skl_pipe_params *params);  #endif diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 2f6281e056d6..7a2febf99019 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -157,18 +157,6 @@ struct skl_dfw_algo_data {  	char params[0];  } __packed; -#define LIB_NAME_LENGTH	128 -#define HDA_MAX_LIB	16 - -struct lib_info { -	char name[LIB_NAME_LENGTH]; -} __packed; - -struct skl_dfw_manifest { -	u32 lib_count; -	struct lib_info lib[HDA_MAX_LIB]; -} __packed; -  enum skl_tkn_dir {  	SKL_DIR_IN,  	SKL_DIR_OUT diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index da5db5098274..0c57d4eaae3a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -732,6 +732,10 @@ static int skl_probe(struct pci_dev *pci,  		goto out_display_power_off;  	} +	err = skl_nhlt_create_sysfs(skl); +	if (err < 0) +		goto out_nhlt_free; +  	skl_nhlt_update_topology_bin(skl);  	pci_set_drvdata(skl->pci, ebus); @@ -852,6 +856,7 @@ static void skl_remove(struct pci_dev *pci)  	skl_free_dsp(skl);  	skl_machine_device_unregister(skl);  	skl_dmic_device_unregister(skl); +	skl_nhlt_remove_sysfs(skl);  	skl_nhlt_free(skl->nhlt);  	skl_free(ebus);  	dev_set_drvdata(&pci->dev, NULL); @@ -878,6 +883,10 @@ static struct sst_acpi_mach sst_kbl_devdata[] = {  	{}  }; +static struct sst_acpi_mach sst_glk_devdata[] = { +	{ "INT343A", "glk_alc298s_i2s", "intel/dsp_fw_glk.bin", NULL, NULL, NULL }, +}; +  /* PCI IDs */  static const struct pci_device_id skl_ids[] = {  	/* Sunrise Point-LP */ @@ -889,6 +898,9 @@ static const struct pci_device_id skl_ids[] = {  	/* KBL */  	{ PCI_DEVICE(0x8086, 0x9D71),  		.driver_data = (unsigned long)&sst_kbl_devdata}, +	/* GLK */ +	{ PCI_DEVICE(0x8086, 0x3198), +		.driver_data = (unsigned long)&sst_glk_devdata},  	{ 0, }  };  MODULE_DEVICE_TABLE(pci, skl_ids); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4986e3929dd3..bbef77d2b917 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -118,7 +118,8 @@ int skl_platform_register(struct device *dev);  struct nhlt_acpi_table *skl_nhlt_init(struct device *dev);  void skl_nhlt_free(struct nhlt_acpi_table *addr);  struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, -			u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); +					u8 link_type, u8 s_fmt, u8 no_ch, +					u32 s_rate, u8 dirn, u8 dev_type);  int skl_get_dmic_geo(struct skl *skl);  int skl_nhlt_update_topology_bin(struct skl *skl); @@ -130,5 +131,7 @@ int skl_resume_dsp(struct skl *skl);  void skl_cleanup_resources(struct skl *skl);  const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id);  void skl_update_d0i3c(struct device *dev, bool enable); +int skl_nhlt_create_sysfs(struct skl *skl); +void skl_nhlt_remove_sysfs(struct skl *skl);  #endif /* __SOUND_SOC_SKL_H */ diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index 34a6123480d3..c7fa3e663463 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -1578,6 +1578,7 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)  	pm_runtime_enable(&pdev->dev);  	if (!pm_runtime_enabled(&pdev->dev))  		goto err_pm_disable; +	pm_runtime_get_sync(&pdev->dev);  	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);  	if (ret) { @@ -1617,6 +1618,7 @@ static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev)  	pm_runtime_disable(&pdev->dev);  	if (!pm_runtime_status_suspended(&pdev->dev))  		mt2701_afe_runtime_suspend(&pdev->dev); +	pm_runtime_put_sync(&pdev->dev);  	snd_soc_unregister_component(&pdev->dev);  	snd_soc_unregister_platform(&pdev->dev); diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index 5524a2c727ec..46c8e6ae00b4 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -79,17 +79,11 @@ static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime)  	/* enable jack detection */  	ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, -				    &mt8173_max98090_jack, NULL, 0); +				    &mt8173_max98090_jack, +				    mt8173_max98090_jack_pins, +				    ARRAY_SIZE(mt8173_max98090_jack_pins));  	if (ret) { -		dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret); -		return ret; -	} - -	ret = snd_soc_jack_add_pins(&mt8173_max98090_jack, -				    ARRAY_SIZE(mt8173_max98090_jack_pins), -				    mt8173_max98090_jack_pins); -	if (ret) { -		dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret); +		dev_err(card->dev, "Can't create a new Jack %d\n", ret);  		return ret;  	} diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index a002ab892772..b42f301c6b96 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -119,23 +119,33 @@ static int mxs_saif_set_clk(struct mxs_saif *saif,  	 * Set SAIF clock  	 *  	 * The SAIF clock should be either 384*fs or 512*fs. -	 * If MCLK is used, the SAIF clk ratio need to match mclk ratio. -	 *  For 32x mclk, set saif clk as 512*fs. -	 *  For 48x mclk, set saif clk as 384*fs. +	 * If MCLK is used, the SAIF clk ratio needs to match mclk ratio. +	 *  For 256x, 128x, 64x, and 32x sub-rates, set saif clk as 512*fs. +	 *  For 192x, 96x, and 48x sub-rates, set saif clk as 384*fs.  	 *  	 * If MCLK is not used, we just set saif clk to 512*fs.  	 */  	clk_prepare_enable(master_saif->clk);  	if (master_saif->mclk_in_use) { -		if (mclk % 32 == 0) { +		switch (mclk / rate) { +		case 32: +		case 64: +		case 128: +		case 256: +		case 512:  			scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;  			ret = clk_set_rate(master_saif->clk, 512 * rate); -		} else if (mclk % 48 == 0) { +			break; +		case 48: +		case 96: +		case 192: +		case 384:  			scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;  			ret = clk_set_rate(master_saif->clk, 384 * rate); -		} else { -			/* SAIF MCLK should be either 32x or 48x */ +			break; +		default: +			/* SAIF MCLK should be a sub-rate of 512x or 384x */  			clk_disable_unprepare(master_saif->clk);  			return -EINVAL;  		} @@ -299,6 +309,16 @@ static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)  		return -EBUSY;  	} +	/* If SAIF1 is configured as slave, the clk gate needs to be cleared +	 * before the register can be written. +	 */ +	if (saif->id != saif->master_id) { +		__raw_writel(BM_SAIF_CTRL_SFTRST, +			saif->base + SAIF_CTRL + MXS_CLR_ADDR); +		__raw_writel(BM_SAIF_CTRL_CLKGATE, +			saif->base + SAIF_CTRL + MXS_CLR_ADDR); +	} +  	scr0 = __raw_readl(saif->base + SAIF_CTRL);  	scr0 = scr0 & ~BM_SAIF_CTRL_BITCLK_EDGE & ~BM_SAIF_CTRL_LRCLK_POLARITY \  		& ~BM_SAIF_CTRL_JUSTIFY & ~BM_SAIF_CTRL_DELAY; diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/mcbsp.h index 61e93b1c185d..46ae1269a698 100644 --- a/sound/soc/omap/mcbsp.h +++ b/sound/soc/omap/mcbsp.h @@ -323,8 +323,11 @@ struct omap_mcbsp {  	unsigned int fmt;  	unsigned int in_freq; +	unsigned int latency[2];  	int clk_div;  	int wlen; + +	struct pm_qos_request pm_qos_req;  };  void omap_mcbsp_config(struct omap_mcbsp *mcbsp, diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index d018e966e533..6b40bdbef336 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -157,6 +157,17 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,  				    struct snd_soc_dai *cpu_dai)  {  	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); +	int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); +	int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; +	int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + +	if (mcbsp->latency[stream2]) +		pm_qos_update_request(&mcbsp->pm_qos_req, +				      mcbsp->latency[stream2]); +	else if (mcbsp->latency[stream1]) +		pm_qos_remove_request(&mcbsp->pm_qos_req); + +	mcbsp->latency[stream1] = 0;  	if (!cpu_dai->active) {  		omap_mcbsp_free(mcbsp); @@ -164,6 +175,28 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,  	}  } +static int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream, +				  struct snd_soc_dai *cpu_dai) +{ +	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); +	struct pm_qos_request *pm_qos_req = &mcbsp->pm_qos_req; +	int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); +	int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; +	int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; +	int latency = mcbsp->latency[stream2]; + +	/* Prevent omap hardware from hitting off between FIFO fills */ +	if (!latency || mcbsp->latency[stream1] < latency) +		latency = mcbsp->latency[stream1]; + +	if (pm_qos_request_active(pm_qos_req)) +		pm_qos_update_request(pm_qos_req, latency); +	else if (latency) +		pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency); + +	return 0; +} +  static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,  				  struct snd_soc_dai *cpu_dai)  { @@ -226,6 +259,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,  	int wlen, channels, wpf;  	int pkt_size = 0;  	unsigned int format, div, framesize, master; +	unsigned int buffer_size = mcbsp->pdata->buffer_size;  	dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);  	channels = params_channels(params); @@ -240,7 +274,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,  	default:  		return -EINVAL;  	} -	if (mcbsp->pdata->buffer_size) { +	if (buffer_size) { +		int latency; +  		if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {  			int period_words, max_thrsh;  			int divider = 0; @@ -271,6 +307,12 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,  			/* Use packet mode for non mono streams */  			pkt_size = channels;  		} + +		latency = ((((buffer_size - pkt_size) / channels) * 1000) +				 / (params->rate_num / params->rate_den)); + +		mcbsp->latency[substream->stream] = latency; +  		omap_mcbsp_set_threshold(substream, pkt_size);  	} @@ -554,6 +596,7 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,  static const struct snd_soc_dai_ops mcbsp_dai_ops = {  	.startup	= omap_mcbsp_dai_startup,  	.shutdown	= omap_mcbsp_dai_shutdown, +	.prepare	= omap_mcbsp_dai_prepare,  	.trigger	= omap_mcbsp_dai_trigger,  	.delay		= omap_mcbsp_dai_delay,  	.hw_params	= omap_mcbsp_dai_hw_params, @@ -835,6 +878,9 @@ static int asoc_mcbsp_remove(struct platform_device *pdev)  	if (mcbsp->pdata->ops && mcbsp->pdata->ops->free)  		mcbsp->pdata->ops->free(mcbsp->id); +	if (pm_qos_request_active(&mcbsp->pm_qos_req)) +		pm_qos_remove_request(&mcbsp->pm_qos_req); +  	omap_mcbsp_cleanup(mcbsp);  	clk_put(mcbsp->fclk); diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index 086c37a85630..8ab7032631b7 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -22,9 +22,6 @@  #include <asm/mach-types.h> -#include "pxa2xx-ac97.h" - -  #define E740_AUDIO_OUT 1  #define E740_AUDIO_IN  2 diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index 7823278012a6..fdcd94adee7c 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -22,8 +22,6 @@  #include <asm/mach-types.h> -#include "pxa2xx-ac97.h" -  static int e750_spk_amp_event(struct snd_soc_dapm_widget *w,  				struct snd_kcontrol *kcontrol, int event)  { diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index 07b9c6e17df9..2df714f70ec0 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -21,8 +21,6 @@  #include <mach/audio.h>  #include <mach/eseries-gpio.h> -#include "pxa2xx-ac97.h" -  static int e800_spk_amp_event(struct snd_soc_dapm_widget *w,  				struct snd_kcontrol *kcontrol, int event)  { diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c index 966163d1c813..6f2020f6c8d3 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c @@ -30,8 +30,6 @@  #include <asm/mach-types.h>  #include <mach/audio.h> -#include "pxa2xx-ac97.h" -  static struct snd_soc_dai_link em_x270_dai[] = {  	{  		.name = "AC97", diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 0fe0abec8fc4..8760a6687885 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -53,7 +53,6 @@  #include <sound/initval.h>  #include <sound/ac97_codec.h> -#include "pxa2xx-ac97.h"  #include "../codecs/wm9713.h"  #define AC97_GPIO_PULL		0x58 diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 387492d46b6c..97167048572d 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -27,8 +27,6 @@  #include <mach/audio.h>  #include <linux/platform_data/asoc-palm27x.h> -#include "pxa2xx-ac97.h" -  static struct snd_soc_jack hs_jack;  /* Headphones jack detection DAPM pins */ diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 9615e6de1306..2e2fb1838ec2 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -27,8 +27,6 @@  #include <mach/regs-ac97.h>  #include <mach/audio.h> -#include "pxa2xx-ac97.h" -  static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)  {  	pxa2xx_ac97_try_warm_reset(ac97); diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h deleted file mode 100644 index a49c21ba3842..000000000000 --- a/sound/soc/pxa/pxa2xx-ac97.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * linux/sound/soc/pxa/pxa2xx-ac97.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _PXA2XX_AC97_H -#define _PXA2XX_AC97_H - -/* pxa2xx DAI ID's */ -#define PXA2XX_DAI_AC97_HIFI	0 -#define PXA2XX_DAI_AC97_AUX		1 -#define PXA2XX_DAI_AC97_MIC		2 - -#endif diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 2e312c62e3c7..e022b2a777f6 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -31,8 +31,6 @@  #include <mach/tosa.h>  #include <mach/audio.h> -#include "pxa2xx-ac97.h" -  #define TOSA_HP        0  #define TOSA_MIC_INT   1  #define TOSA_HEADSET   2 diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index 8f301c72ee5e..6fbcdf02c88d 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -22,7 +22,6 @@  #include <sound/soc.h>  #include "../codecs/wm9713.h" -#include "pxa2xx-ac97.h"  #include "pxa-ssp.h"  /* diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index 3eef0c37ba50..8aed72be3224 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -175,29 +175,28 @@ static int apq8016_lpass_init(struct platform_device *pdev)  	drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk");  	if (IS_ERR(drvdata->pcnoc_mport_clk)) { -		dev_err(&pdev->dev, "%s() error getting pcnoc-mport-clk: %ld\n", -				__func__, PTR_ERR(drvdata->pcnoc_mport_clk)); +		dev_err(&pdev->dev, "error getting pcnoc-mport-clk: %ld\n", +			PTR_ERR(drvdata->pcnoc_mport_clk));  		return PTR_ERR(drvdata->pcnoc_mport_clk);  	}  	ret = clk_prepare_enable(drvdata->pcnoc_mport_clk);  	if (ret) { -		dev_err(&pdev->dev, "%s() Error enabling pcnoc-mport-clk: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "Error enabling pcnoc-mport-clk: %d\n", +			ret);  		return ret;  	}  	drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk");  	if (IS_ERR(drvdata->pcnoc_sway_clk)) { -		dev_err(&pdev->dev, "%s() error getting pcnoc-sway-clk: %ld\n", -				__func__, PTR_ERR(drvdata->pcnoc_sway_clk)); +		dev_err(&pdev->dev, "error getting pcnoc-sway-clk: %ld\n", +			PTR_ERR(drvdata->pcnoc_sway_clk));  		return PTR_ERR(drvdata->pcnoc_sway_clk);  	}  	ret = clk_prepare_enable(drvdata->pcnoc_sway_clk);  	if (ret) { -		dev_err(&pdev->dev, "%s() Error enabling pcnoc_sway_clk: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "Error enabling pcnoc_sway_clk: %d\n", ret);  		return ret;  	} diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index eff3f9a8b685..5202a584e0c6 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -33,13 +33,10 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,  	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);  	int ret; -	if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) -		return 0; -  	ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);  	if (ret) -		dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n", -				__func__, freq, ret); +		dev_err(dai->dev, "error setting mi2s osrclk to %u: %d\n", +			freq, ret);  	return ret;  } @@ -50,23 +47,16 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,  	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);  	int ret; -	if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) { -		ret = clk_prepare_enable( -				drvdata->mi2s_osr_clk[dai->driver->id]); -		if (ret) { -			dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n", -					__func__, ret); -			return ret; -		} +	ret = clk_prepare_enable(drvdata->mi2s_osr_clk[dai->driver->id]); +	if (ret) { +		dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret); +		return ret;  	}  	ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);  	if (ret) { -		dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n", -				__func__, ret); -		if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) -			clk_disable_unprepare( -				drvdata->mi2s_osr_clk[dai->driver->id]); +		dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); +		clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);  		return ret;  	} @@ -80,8 +70,7 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,  	clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); -	if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) -		clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); +	clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);  }  static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, @@ -96,8 +85,7 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,  	bitwidth = snd_pcm_format_width(format);  	if (bitwidth < 0) { -		dev_err(dai->dev, "%s() invalid bit width given: %d\n", -				__func__, bitwidth); +		dev_err(dai->dev, "invalid bit width given: %d\n", bitwidth);  		return bitwidth;  	} @@ -115,8 +103,7 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,  		regval |= LPAIF_I2SCTL_BITWIDTH_32;  		break;  	default: -		dev_err(dai->dev, "%s() invalid bitwidth given: %d\n", -				__func__, bitwidth); +		dev_err(dai->dev, "invalid bitwidth given: %d\n", bitwidth);  		return -EINVAL;  	} @@ -143,8 +130,8 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,  			regval |= LPAIF_I2SCTL_SPKMONO_STEREO;  			break;  		default: -			dev_err(dai->dev, "%s() invalid channels given: %u\n", -					__func__, channels); +			dev_err(dai->dev, "invalid channels given: %u\n", +				channels);  			return -EINVAL;  		}  	} else { @@ -170,8 +157,8 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,  			regval |= LPAIF_I2SCTL_MICMONO_STEREO;  			break;  		default: -			dev_err(dai->dev, "%s() invalid channels given: %u\n", -					__func__, channels); +			dev_err(dai->dev, "invalid channels given: %u\n", +				channels);  			return -EINVAL;  		}  	} @@ -180,16 +167,15 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,  			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),  			   regval);  	if (ret) { -		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", -				__func__, ret); +		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);  		return ret;  	}  	ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],  			   rate * bitwidth * 2);  	if (ret) { -		dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n", -				__func__, rate * bitwidth * 2, ret); +		dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n", +			rate * bitwidth * 2, ret);  		return ret;  	} @@ -206,8 +192,7 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,  			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),  			   0);  	if (ret) -		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", -				__func__, ret); +		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);  	return ret;  } @@ -231,8 +216,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,  			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),  			mask, val);  	if (ret) -		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", -				__func__, ret); +		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);  	return ret;  } @@ -261,8 +245,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,  						dai->driver->id),  				mask, val);  		if (ret) -			dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", -					__func__, ret); +			dev_err(dai->dev, "error writing to i2sctl reg: %d\n", +				ret);  		break;  	case SNDRV_PCM_TRIGGER_STOP:  	case SNDRV_PCM_TRIGGER_SUSPEND: @@ -280,8 +264,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,  						dai->driver->id),  				mask, val);  		if (ret) -			dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", -					__func__, ret); +			dev_err(dai->dev, "error writing to i2sctl reg: %d\n", +				ret);  		break;  	} @@ -308,8 +292,7 @@ int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)  	ret = regmap_write(drvdata->lpaif_map,  			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);  	if (ret) -		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", -				__func__, ret); +		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret);  	return ret;  } @@ -451,8 +434,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)  	dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);  	if (dsp_of_node) { -		dev_err(&pdev->dev, "%s() DSP exists and holds audio resources\n", -				__func__); +		dev_err(&pdev->dev, "DSP exists and holds audio resources\n");  		return -EBUSY;  	} @@ -473,8 +455,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)  	drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);  	if (IS_ERR((void const __force *)drvdata->lpaif)) { -		dev_err(&pdev->dev, "%s() error mapping reg resource: %ld\n", -				__func__, +		dev_err(&pdev->dev, "error mapping reg resource: %ld\n",  				PTR_ERR((void const __force *)drvdata->lpaif));  		return PTR_ERR((void const __force *)drvdata->lpaif);  	} @@ -486,8 +467,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)  	drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,  			&lpass_cpu_regmap_config);  	if (IS_ERR(drvdata->lpaif_map)) { -		dev_err(&pdev->dev, "%s() error initializing regmap: %ld\n", -				__func__, PTR_ERR(drvdata->lpaif_map)); +		dev_err(&pdev->dev, "error initializing regmap: %ld\n", +			PTR_ERR(drvdata->lpaif_map));  		return PTR_ERR(drvdata->lpaif_map);  	} @@ -505,9 +486,10 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)  								clk_name);  		if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {  			dev_warn(&pdev->dev, -				"%s() error getting mi2s-osr-clk: %ld\n", -				__func__, +				"error getting optional mi2s-osr-clk: %ld\n",  				PTR_ERR(drvdata->mi2s_osr_clk[dai_id])); + +			drvdata->mi2s_osr_clk[dai_id] = NULL;  		}  		if (variant->num_dai > 1) @@ -519,8 +501,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)  							    clk_name);  		if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {  			dev_err(&pdev->dev, -				"%s() error getting mi2s-bit-clk: %ld\n", -				__func__, +				"error getting mi2s-bit-clk: %ld\n",  				PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));  			return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);  		} @@ -528,24 +509,23 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)  	drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");  	if (IS_ERR(drvdata->ahbix_clk)) { -		dev_err(&pdev->dev, "%s() error getting ahbix-clk: %ld\n", -				__func__, PTR_ERR(drvdata->ahbix_clk)); +		dev_err(&pdev->dev, "error getting ahbix-clk: %ld\n", +			PTR_ERR(drvdata->ahbix_clk));  		return PTR_ERR(drvdata->ahbix_clk);  	}  	ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);  	if (ret) { -		dev_err(&pdev->dev, "%s() error setting rate on ahbix_clk: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "error setting rate on ahbix_clk: %d\n", +			ret);  		return ret;  	} -	dev_dbg(&pdev->dev, "%s() set ahbix_clk rate to %lu\n", __func__, -			clk_get_rate(drvdata->ahbix_clk)); +	dev_dbg(&pdev->dev, "set ahbix_clk rate to %lu\n", +		clk_get_rate(drvdata->ahbix_clk));  	ret = clk_prepare_enable(drvdata->ahbix_clk);  	if (ret) { -		dev_err(&pdev->dev, "%s() error enabling ahbix_clk: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "error enabling ahbix_clk: %d\n", ret);  		return ret;  	} @@ -554,15 +534,14 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)  					      variant->dai_driver,  					      variant->num_dai);  	if (ret) { -		dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "error registering cpu driver: %d\n", ret);  		goto err_clk;  	}  	ret = asoc_qcom_lpass_platform_register(pdev);  	if (ret) { -		dev_err(&pdev->dev, "%s() error registering platform driver: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "error registering platform driver: %d\n", +			ret);  		goto err_clk;  	} diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index dd5bdd0da730..7aabf08de3d4 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -89,8 +89,7 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)  			LPAIF_DMACTL_REG(v, dma_ch, dir), 0);  	if (ret) {  		dev_err(soc_runtime->dev, -			"%s() error writing to rdmactl reg: %d\n", -			__func__, ret); +			"error writing to rdmactl reg: %d\n", ret);  			return ret;  	} @@ -103,8 +102,8 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)  	ret = snd_pcm_hw_constraint_integer(runtime,  			SNDRV_PCM_HW_PARAM_PERIODS);  	if (ret < 0) { -		dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n", -				__func__, ret); +		dev_err(soc_runtime->dev, "setting constraints failed: %d\n", +			ret);  		return -EINVAL;  	} @@ -151,8 +150,8 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,  	bitwidth = snd_pcm_format_width(format);  	if (bitwidth < 0) { -		dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n", -				__func__, bitwidth); +		dev_err(soc_runtime->dev, "invalid bit width given: %d\n", +				bitwidth);  		return bitwidth;  	} @@ -177,8 +176,9 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,  			regval |= LPAIF_DMACTL_WPSCNT_FOUR;  			break;  		default: -			dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", -					__func__, bitwidth, channels); +			dev_err(soc_runtime->dev, +				"invalid PCM config given: bw=%d, ch=%u\n", +				bitwidth, channels);  			return -EINVAL;  		}  		break; @@ -201,22 +201,23 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,  			regval |= LPAIF_DMACTL_WPSCNT_EIGHT;  			break;  		default: -			dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", -					__func__, bitwidth, channels); +			dev_err(soc_runtime->dev, +				"invalid PCM config given: bw=%d, ch=%u\n", +				bitwidth, channels);  			return -EINVAL;  		}  		break;  	default: -		dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", -				__func__, bitwidth, channels); +		dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n", +			bitwidth, channels);  		return -EINVAL;  	}  	ret = regmap_write(drvdata->lpaif_map,  			LPAIF_DMACTL_REG(v, ch, dir), regval);  	if (ret) { -		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", -				__func__, ret); +		dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", +			ret);  		return ret;  	} @@ -237,8 +238,8 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)  	reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream);  	ret = regmap_write(drvdata->lpaif_map, reg, 0);  	if (ret) -		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", -				__func__, ret); +		dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", +			ret);  	return ret;  } @@ -260,8 +261,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)  			LPAIF_DMABASE_REG(v, ch, dir),  			runtime->dma_addr);  	if (ret) { -		dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", -				__func__, ret); +		dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n", +			ret);  		return ret;  	} @@ -269,8 +270,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)  			LPAIF_DMABUFF_REG(v, ch, dir),  			(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);  	if (ret) { -		dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", -				__func__, ret); +		dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n", +			ret);  		return ret;  	} @@ -278,8 +279,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)  			LPAIF_DMAPER_REG(v, ch, dir),  			(snd_pcm_lib_period_bytes(substream) >> 2) - 1);  	if (ret) { -		dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", -				__func__, ret); +		dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n", +			ret);  		return ret;  	} @@ -287,8 +288,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)  			LPAIF_DMACTL_REG(v, ch, dir),  			LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);  	if (ret) { -		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", -				__func__, ret); +		dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", +			ret);  		return ret;  	} @@ -317,8 +318,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,  				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),  				LPAIF_IRQ_ALL(ch));  		if (ret) { -			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", -					__func__, ret); +			dev_err(soc_runtime->dev, +				"error writing to irqclear reg: %d\n", ret);  			return ret;  		} @@ -327,8 +328,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,  				LPAIF_IRQ_ALL(ch),  				LPAIF_IRQ_ALL(ch));  		if (ret) { -			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", -					__func__, ret); +			dev_err(soc_runtime->dev, +				"error writing to irqen reg: %d\n", ret);  			return ret;  		} @@ -337,8 +338,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,  				LPAIF_DMACTL_ENABLE_MASK,  				LPAIF_DMACTL_ENABLE_ON);  		if (ret) { -			dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", -					__func__, ret); +			dev_err(soc_runtime->dev, +				"error writing to rdmactl reg: %d\n", ret);  			return ret;  		}  		break; @@ -350,8 +351,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,  				LPAIF_DMACTL_ENABLE_MASK,  				LPAIF_DMACTL_ENABLE_OFF);  		if (ret) { -			dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", -					__func__, ret); +			dev_err(soc_runtime->dev, +				"error writing to rdmactl reg: %d\n", ret);  			return ret;  		} @@ -359,8 +360,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,  				LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),  				LPAIF_IRQ_ALL(ch), 0);  		if (ret) { -			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", -					__func__, ret); +			dev_err(soc_runtime->dev, +				"error writing to irqen reg: %d\n", ret);  			return ret;  		}  		break; @@ -386,16 +387,16 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(  	ret = regmap_read(drvdata->lpaif_map,  			LPAIF_DMABASE_REG(v, ch, dir), &base_addr);  	if (ret) { -		dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", -				__func__, ret); +		dev_err(soc_runtime->dev, +			"error reading from rdmabase reg: %d\n", ret);  		return ret;  	}  	ret = regmap_read(drvdata->lpaif_map,  			LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);  	if (ret) { -		dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", -				__func__, ret); +		dev_err(soc_runtime->dev, +			"error reading from rdmacurr reg: %d\n", ret);  		return ret;  	} @@ -439,8 +440,8 @@ static irqreturn_t lpass_dma_interrupt_handler(  				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),  				LPAIF_IRQ_PER(chan));  		if (rv) { -			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", -					__func__, rv); +			dev_err(soc_runtime->dev, +				"error writing to irqclear reg: %d\n", rv);  			return IRQ_NONE;  		}  		snd_pcm_period_elapsed(substream); @@ -452,11 +453,11 @@ static irqreturn_t lpass_dma_interrupt_handler(  				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),  				LPAIF_IRQ_XRUN(chan));  		if (rv) { -			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", -					__func__, rv); +			dev_err(soc_runtime->dev, +				"error writing to irqclear reg: %d\n", rv);  			return IRQ_NONE;  		} -		dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__); +		dev_warn(soc_runtime->dev, "xrun warning\n");  		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);  		ret = IRQ_HANDLED;  	} @@ -466,11 +467,11 @@ static irqreturn_t lpass_dma_interrupt_handler(  				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),  				LPAIF_IRQ_ERR(chan));  		if (rv) { -			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", -					__func__, rv); +			dev_err(soc_runtime->dev, +				"error writing to irqclear reg: %d\n", rv);  			return IRQ_NONE;  		} -		dev_err(soc_runtime->dev, "%s() bus access error\n", __func__); +		dev_err(soc_runtime->dev, "bus access error\n");  		snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);  		ret = IRQ_HANDLED;  	} @@ -488,8 +489,7 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)  	rv = regmap_read(drvdata->lpaif_map,  			LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);  	if (rv) { -		pr_err("%s() error reading from irqstat reg: %d\n", -				__func__, rv); +		pr_err("error reading from irqstat reg: %d\n", rv);  		return IRQ_NONE;  	} @@ -571,8 +571,8 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)  	drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");  	if (drvdata->lpaif_irq < 0) { -		dev_err(&pdev->dev, "%s() error getting irq handle: %d\n", -				__func__, drvdata->lpaif_irq); +		dev_err(&pdev->dev, "error getting irq handle: %d\n", +			drvdata->lpaif_irq);  		return -ENODEV;  	} @@ -580,8 +580,7 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)  	ret = regmap_write(drvdata->lpaif_map,  			LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);  	if (ret) { -		dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);  		return ret;  	} @@ -589,8 +588,7 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)  			lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,  			"lpass-irq-lpaif", drvdata);  	if (ret) { -		dev_err(&pdev->dev, "%s() irq request failed: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "irq request failed: %d\n", ret);  		return ret;  	} diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c index 8fcac2ac3aa6..c5207af14104 100644 --- a/sound/soc/qcom/storm.c +++ b/sound/soc/qcom/storm.c @@ -36,8 +36,7 @@ static int storm_ops_hw_params(struct snd_pcm_substream *substream,  	bitwidth = snd_pcm_format_width(format);  	if (bitwidth < 0) { -		dev_err(card->dev, "%s() invalid bit width given: %d\n", -				__func__, bitwidth); +		dev_err(card->dev, "invalid bit width given: %d\n", bitwidth);  		return bitwidth;  	} @@ -50,8 +49,8 @@ static int storm_ops_hw_params(struct snd_pcm_substream *substream,  	ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0);  	if (ret) { -		dev_err(card->dev, "%s() error setting sysclk to %u: %d\n", -				__func__, sysclk_freq, ret); +		dev_err(card->dev, "error setting sysclk to %u: %d\n", +			sysclk_freq, ret);  		return ret;  	} @@ -76,16 +75,14 @@ static int storm_parse_of(struct snd_soc_card *card)  	dai_link->cpu_of_node = of_parse_phandle(np, "cpu", 0);  	if (!dai_link->cpu_of_node) { -		dev_err(card->dev, "%s() error getting cpu phandle\n", -				__func__); +		dev_err(card->dev, "error getting cpu phandle\n");  		return -EINVAL;  	}  	dai_link->platform_of_node = dai_link->cpu_of_node;  	dai_link->codec_of_node = of_parse_phandle(np, "codec", 0);  	if (!dai_link->codec_of_node) { -		dev_err(card->dev, "%s() error getting codec phandle\n", -				__func__); +		dev_err(card->dev, "error getting codec phandle\n");  		return -EINVAL;  	} @@ -106,8 +103,7 @@ static int storm_platform_probe(struct platform_device *pdev)  	ret = snd_soc_of_parse_card_name(card, "qcom,model");  	if (ret) { -		dev_err(&pdev->dev, "%s() error parsing card name: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "error parsing card name: %d\n", ret);  		return ret;  	} @@ -116,15 +112,13 @@ static int storm_platform_probe(struct platform_device *pdev)  	ret = storm_parse_of(card);  	if (ret) { -		dev_err(&pdev->dev, "%s() error resolving dai links: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "error resolving dai links: %d\n", ret);  		return ret;  	}  	ret = devm_snd_soc_register_card(&pdev->dev, card);  	if (ret) -		dev_err(&pdev->dev, "%s() error registering soundcard: %d\n", -				__func__, ret); +		dev_err(&pdev->dev, "error registering soundcard: %d\n", ret);  	return ret; diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index c783f9a22595..e3ca1e973de5 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -42,6 +42,15 @@ config SND_SOC_ROCKCHIP_RT5645  	  Say Y or M here if you want to add support for SoC audio on Rockchip  	  boards using the RT5645/RT5650 codec, such as Veyron. +config SND_SOC_RK3288_HDMI_ANALOG +	tristate "ASoC support multiple codecs for Rockchip RK3288 boards" +	depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP +	select SND_SOC_ROCKCHIP_I2S +	select SND_SOC_HDMI_CODEC +	help +	  Say Y or M here if you want to add support for SoC audio on Rockchip +	  RK3288 boards using an analog output and the built-in HDMI audio. +  config SND_SOC_RK3399_GRU_SOUND  	tristate "ASoC support multiple codecs for Rockchip RK3399 GRU boards"  	depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP && SPI diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 84e5c7c700e7..991f91bea9f9 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -7,8 +7,10 @@ obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o  snd-soc-rockchip-max98090-objs := rockchip_max98090.o  snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o +snd-soc-rk3288-hdmi-analog-objs := rk3288_hdmi_analog.o  snd-soc-rk3399-gru-sound-objs := rk3399_gru_sound.o  obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o  obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o +obj-$(CONFIG_SND_SOC_RK3288_HDMI_ANALOG) += snd-soc-rk3288-hdmi-analog.o  obj-$(CONFIG_SND_SOC_RK3399_GRU_SOUND) += snd-soc-rk3399-gru-sound.o diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c new file mode 100644 index 000000000000..b60abf322ce1 --- /dev/null +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -0,0 +1,299 @@ +/* + * Rockchip machine ASoC driver for RK3288 boards that have an HDMI and analog + * audio output + * + * Copyright (c) 2016, Collabora Ltd. + * + * Authors: Sjoerd Simons <sjoerd.simons@collabora.com>, + *	    Romain Perier <romain.perier@collabora.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include "rockchip_i2s.h" + +#define DRV_NAME "rk3288-snd-hdmi-analog" + +struct rk_drvdata { +	int gpio_hp_en; +	int gpio_hp_det; +}; + +static int rk_hp_power(struct snd_soc_dapm_widget *w, +		       struct snd_kcontrol *k, int event) +{ +	struct rk_drvdata *machine = snd_soc_card_get_drvdata(w->dapm->card); + +	if (!gpio_is_valid(machine->gpio_hp_en)) +		return 0; + +	gpio_set_value_cansleep(machine->gpio_hp_en, +				SND_SOC_DAPM_EVENT_ON(event)); + +	return 0; +} + +static struct snd_soc_jack headphone_jack; +static struct snd_soc_jack_pin headphone_jack_pins[] = { +	{ +		.pin = "Analog", +		.mask = SND_JACK_HEADPHONE +	}, +}; + +static const struct snd_soc_dapm_widget rk_dapm_widgets[] = { +	SND_SOC_DAPM_HP("Analog", rk_hp_power), +	SND_SOC_DAPM_LINE("HDMI", NULL), +}; + +static const struct snd_kcontrol_new rk_mc_controls[] = { +	SOC_DAPM_PIN_SWITCH("Analog"), +	SOC_DAPM_PIN_SWITCH("HDMI"), +}; + +static int rk_hw_params(struct snd_pcm_substream *substream, +			struct snd_pcm_hw_params *params) +{ +	int ret = 0; +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai *cpu_dai = rtd->cpu_dai; +	struct snd_soc_dai *codec_dai = rtd->codec_dai; +	int mclk; + +	switch (params_rate(params)) { +	case 8000: +	case 16000: +	case 24000: +	case 32000: +	case 48000: +	case 64000: +	case 96000: +		mclk = 12288000; +		break; +	case 11025: +	case 22050: +	case 44100: +	case 88200: +		mclk = 11289600; +		break; +	default: +		return -EINVAL; +	} + +	ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, +				     SND_SOC_CLOCK_OUT); + +	if (ret && ret != -ENOTSUPP) { +		dev_err(codec_dai->dev, "Can't set cpu clock %d\n", ret); +		return ret; +	} + +	ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, +				     SND_SOC_CLOCK_IN); +	if (ret && ret != -ENOTSUPP) { +		dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret); +		return ret; +	} + +	return 0; +} + +static struct snd_soc_jack_gpio rk_hp_jack_gpio = { +	.name = "Headphone detection", +	.report = SND_JACK_HEADPHONE, +	.debounce_time = 150 +}; + +static int rk_init(struct snd_soc_pcm_runtime *runtime) +{ +	struct rk_drvdata *machine = snd_soc_card_get_drvdata(runtime->card); + +	/* Enable Headset Jack detection */ +	if (gpio_is_valid(machine->gpio_hp_det)) { +		snd_soc_card_jack_new(runtime->card, "Headphone Jack", +				      SND_JACK_HEADPHONE, &headphone_jack, +				      headphone_jack_pins, +				      ARRAY_SIZE(headphone_jack_pins)); +		rk_hp_jack_gpio.gpio = machine->gpio_hp_det; +		snd_soc_jack_add_gpios(&headphone_jack, 1, &rk_hp_jack_gpio); +	} + +	return 0; +} + +static struct snd_soc_ops rk_ops = { +	.hw_params = rk_hw_params, +}; + +static struct snd_soc_dai_link_component rk_codecs[] = { +	{ }, +	{ +		.name = "hdmi-audio-codec.2.auto", +		.dai_name = "hdmi-hifi.0", +	}, +}; + +static struct snd_soc_dai_link rk_dailink = { +	.name = "Codecs", +	.stream_name = "Audio", +	.init = rk_init, +	.ops = &rk_ops, +	.codecs = rk_codecs, +	.num_codecs = ARRAY_SIZE(rk_codecs), +	/* Set codecs as slave */ +	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | +		SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_card_rk = { +	.name = "ROCKCHIP-I2S", +	.dai_link = &rk_dailink, +	.num_links = 1, +	.num_aux_devs = 0, +	.dapm_widgets = rk_dapm_widgets, +	.num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets), +	.controls = rk_mc_controls, +	.num_controls = ARRAY_SIZE(rk_mc_controls), +}; + +static int snd_rk_mc_probe(struct platform_device *pdev) +{ +	int ret = 0; +	struct snd_soc_card *card = &snd_soc_card_rk; +	struct device_node *np = pdev->dev.of_node; +	struct rk_drvdata *machine; +	struct of_phandle_args args; + +	machine = devm_kzalloc(&pdev->dev, sizeof(struct rk_drvdata), +			       GFP_KERNEL); +	if (!machine) +		return -ENOMEM; + +	card->dev = &pdev->dev; + +	machine->gpio_hp_det = of_get_named_gpio(np, +		"rockchip,hp-det-gpios", 0); +	if (!gpio_is_valid(machine->gpio_hp_det) && machine->gpio_hp_det != -ENODEV) +		return machine->gpio_hp_det; + +	machine->gpio_hp_en = of_get_named_gpio(np, +		"rockchip,hp-en-gpios", 0); +	if (!gpio_is_valid(machine->gpio_hp_en) && machine->gpio_hp_en != -ENODEV) +		return machine->gpio_hp_en; + +	if (gpio_is_valid(machine->gpio_hp_en)) { +		ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en, +					    GPIOF_OUT_INIT_LOW, "hp_en"); +		if (ret) { +			dev_err(card->dev, "cannot get hp_en gpio\n"); +			return ret; +		} +	} + +	ret = snd_soc_of_parse_card_name(card, "rockchip,model"); +	if (ret) { +		dev_err(card->dev, "SoC parse card name failed %d\n", ret); +		return ret; +	} + +	rk_dailink.codecs[0].of_node = of_parse_phandle(np, +							"rockchip,audio-codec", +							0); +	if (!rk_dailink.codecs[0].of_node) { +		dev_err(&pdev->dev, +			"Property 'rockchip,audio-codec' missing or invalid\n"); +		return -EINVAL; +	} +	ret = of_parse_phandle_with_fixed_args(np, "rockchip,audio-codec", +					       0, 0, &args); +	if (ret) { +		dev_err(&pdev->dev, +			"Unable to parse property 'rockchip,audio-codec'\n"); +		return ret; +	} + +	ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name); +	if (ret) { +		dev_err(&pdev->dev, "Unable to get codec_dai_name\n"); +		return ret; +	} + +	rk_dailink.cpu_of_node = of_parse_phandle(np, "rockchip,i2s-controller", +						  0); +	if (!rk_dailink.cpu_of_node) { +		dev_err(&pdev->dev, +			"Property 'rockchip,i2s-controller' missing or invalid\n"); +		return -EINVAL; +	} + +	rk_dailink.platform_of_node = rk_dailink.cpu_of_node; + +	ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing"); +	if (ret) { +		dev_err(&pdev->dev, +			"Unable to parse 'rockchip,routing' property\n"); +		return ret; +	} + +	snd_soc_card_set_drvdata(card, machine); + +	ret = devm_snd_soc_register_card(&pdev->dev, card); +	if (ret == -EPROBE_DEFER) +		return -EPROBE_DEFER; +	if (ret) { +		dev_err(&pdev->dev, +			"Soc register card failed %d\n", ret); +		return ret; +	} + +	platform_set_drvdata(pdev, card); + +	return ret; +} + +static const struct of_device_id rockchip_sound_of_match[] = { +	{ .compatible = "rockchip,rk3288-hdmi-analog", }, +	{}, +}; + +MODULE_DEVICE_TABLE(of, rockchip_sound_of_match); + +static struct platform_driver rockchip_sound_driver = { +	.probe = snd_rk_mc_probe, +	.driver = { +		.name = DRV_NAME, +		.owner = THIS_MODULE, +		.pm = &snd_soc_pm_ops, +		.of_match_table = rockchip_sound_of_match, +	}, +}; + +module_platform_driver(rockchip_sound_driver); + +MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.com>"); +MODULE_DESCRIPTION("Rockchip RK3288 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 7c423151ef7d..f1f1d7959a1b 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -111,6 +111,7 @@ config SND_SOC_SAMSUNG_RX1950_UDA1380  config SND_SOC_SMARTQ  	tristate "SoC I2S Audio support for SmartQ board"  	depends on MACH_SMARTQ || COMPILE_TEST +	depends on GPIOLIB || COMPILE_TEST  	depends on I2C  	select SND_SAMSUNG_I2S  	select SND_SOC_WM8750 @@ -193,6 +194,7 @@ config SND_SOC_ARNDALE_RT5631_ALC5631  config SND_SOC_SAMSUNG_TM2_WM5110  	tristate "SoC I2S Audio support for WM5110 on TM2 board"  	depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER +	depends on GPIOLIB || COMPILE_TEST  	select SND_SOC_MAX98504  	select SND_SOC_WM5110  	select SND_SAMSUNG_I2S diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c index cda656e4afc6..9104c98deeb7 100644 --- a/sound/soc/samsung/dmaengine.c +++ b/sound/soc/samsung/dmaengine.c @@ -37,8 +37,12 @@ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,  	pcm_conf->prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;  	pcm_conf->compat_filter_fn = filter; -	pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx; -	pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx; +	if (dev->of_node) { +		pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx; +		pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx; +	} else { +		flags |= SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME; +	}  	return devm_snd_dmaengine_pcm_register(dev, pcm_conf, flags);  } diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index e00974bc5616..52a47ed292a4 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -34,11 +34,6 @@  #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) -enum samsung_dai_type { -	TYPE_PRI, -	TYPE_SEC, -}; -  struct samsung_i2s_variant_regs {  	unsigned int	bfs_off;  	unsigned int	rfs_off; @@ -54,7 +49,6 @@ struct samsung_i2s_variant_regs {  };  struct samsung_i2s_dai_data { -	int dai_type;  	u32 quirks;  	const struct samsung_i2s_variant_regs *i2s_variant_regs;  }; @@ -483,6 +477,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,  	unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;  	u32 mod, mask, val = 0;  	unsigned long flags; +	int ret = 0; + +	pm_runtime_get_sync(dai->dev);  	spin_lock_irqsave(i2s->lock, flags);  	mod = readl(i2s->addr + I2SMOD); @@ -507,7 +504,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,  					&& (mod & cdcon_mask))))) {  			dev_err(&i2s->pdev->dev,  				"%s:%d Other DAI busy\n", __func__, __LINE__); -			return -EAGAIN; +			ret = -EAGAIN; +			goto err;  		}  		if (dir == SND_SOC_CLOCK_IN) @@ -535,7 +533,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,  				} else {  					i2s->rclk_srcrate =  						clk_get_rate(i2s->op_clk); -					return 0; +					goto done;  				}  			} @@ -546,8 +544,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,  				i2s->op_clk = clk_get(&i2s->pdev->dev,  						"i2s_opclk0"); -			if (WARN_ON(IS_ERR(i2s->op_clk))) -				return PTR_ERR(i2s->op_clk); +			if (WARN_ON(IS_ERR(i2s->op_clk))) { +				ret = PTR_ERR(i2s->op_clk); +				i2s->op_clk = NULL; +				goto err; +			}  			clk_prepare_enable(i2s->op_clk);  			i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); @@ -561,12 +562,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,  				|| (clk_id && !(mod & rsrc_mask))) {  			dev_err(&i2s->pdev->dev,  				"%s:%d Other DAI busy\n", __func__, __LINE__); -			return -EAGAIN; +			ret = -EAGAIN; +			goto err;  		} else {  			/* Call can't be on the active DAI */  			i2s->op_clk = other->op_clk;  			i2s->rclk_srcrate = other->rclk_srcrate; -			return 0; +			goto done;  		}  		if (clk_id == 1) @@ -574,7 +576,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,  		break;  	default:  		dev_err(&i2s->pdev->dev, "We don't serve that!\n"); -		return -EINVAL; +		ret = -EINVAL; +		goto err;  	}  	spin_lock_irqsave(i2s->lock, flags); @@ -582,8 +585,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,  	mod = (mod & ~mask) | val;  	writel(mod, i2s->addr + I2SMOD);  	spin_unlock_irqrestore(i2s->lock, flags); +done: +	pm_runtime_put(dai->dev);  	return 0; +err: +	pm_runtime_put(dai->dev); +	return ret;  }  static int i2s_set_fmt(struct snd_soc_dai *dai, @@ -652,6 +660,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,  		return -EINVAL;  	} +	pm_runtime_get_sync(dai->dev);  	spin_lock_irqsave(i2s->lock, flags);  	mod = readl(i2s->addr + I2SMOD);  	/* @@ -661,6 +670,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,  	if (any_active(i2s) &&  		((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {  		spin_unlock_irqrestore(i2s->lock, flags); +		pm_runtime_put(dai->dev);  		dev_err(&i2s->pdev->dev,  				"%s:%d Other DAI busy\n", __func__, __LINE__);  		return -EAGAIN; @@ -670,6 +680,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,  	mod |= tmp;  	writel(mod, i2s->addr + I2SMOD);  	spin_unlock_irqrestore(i2s->lock, flags); +	pm_runtime_put(dai->dev);  	return 0;  } @@ -681,6 +692,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,  	u32 mod, mask = 0, val = 0;  	unsigned long flags; +	WARN_ON(!pm_runtime_active(dai->dev)); +  	if (!is_secondary(i2s))  		mask |= (MOD_DC2_EN | MOD_DC1_EN); @@ -769,6 +782,8 @@ static int i2s_startup(struct snd_pcm_substream *substream,  	struct i2s_dai *other = get_other_dai(i2s);  	unsigned long flags; +	pm_runtime_get_sync(dai->dev); +  	spin_lock_irqsave(&lock, flags);  	i2s->mode |= DAI_OPENED; @@ -806,6 +821,8 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,  	i2s->bfs = 0;  	spin_unlock_irqrestore(&lock, flags); + +	pm_runtime_put(dai->dev);  }  static int config_setup(struct i2s_dai *i2s) @@ -880,6 +897,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream,  	case SNDRV_PCM_TRIGGER_START:  	case SNDRV_PCM_TRIGGER_RESUME:  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +		pm_runtime_get_sync(dai->dev);  		spin_lock_irqsave(i2s->lock, flags);  		if (config_setup(i2s)) { @@ -908,6 +926,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream,  		}  		spin_unlock_irqrestore(i2s->lock, flags); +		pm_runtime_put(dai->dev);  		break;  	} @@ -922,13 +941,16 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai,  	switch (div_id) {  	case SAMSUNG_I2S_DIV_BCLK: +		pm_runtime_get_sync(dai->dev);  		if ((any_active(i2s) && div && (get_bfs(i2s) != div))  			|| (other && other->bfs && (other->bfs != div))) { +			pm_runtime_put(dai->dev);  			dev_err(&i2s->pdev->dev,  				"%s:%d Other DAI busy\n", __func__, __LINE__);  			return -EAGAIN;  		}  		i2s->bfs = div; +		pm_runtime_put(dai->dev);  		break;  	default:  		dev_err(&i2s->pdev->dev, @@ -947,6 +969,8 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)  	snd_pcm_sframes_t delay;  	const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; +	WARN_ON(!pm_runtime_active(dai->dev)); +  	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)  		delay = FIC_RXCOUNT(reg);  	else if (is_secondary(i2s)) @@ -960,24 +984,12 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)  #ifdef CONFIG_PM  static int i2s_suspend(struct snd_soc_dai *dai)  { -	struct i2s_dai *i2s = to_info(dai); - -	i2s->suspend_i2smod = readl(i2s->addr + I2SMOD); -	i2s->suspend_i2scon = readl(i2s->addr + I2SCON); -	i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR); - -	return 0; +	return pm_runtime_force_suspend(dai->dev);  }  static int i2s_resume(struct snd_soc_dai *dai)  { -	struct i2s_dai *i2s = to_info(dai); - -	writel(i2s->suspend_i2scon, i2s->addr + I2SCON); -	writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); -	writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR); - -	return 0; +	return pm_runtime_force_resume(dai->dev);  }  #else  #define i2s_suspend NULL @@ -990,6 +1002,8 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)  	struct i2s_dai *other = get_other_dai(i2s);  	unsigned long flags; +	pm_runtime_get_sync(dai->dev); +  	if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */  		snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback,  					   NULL); @@ -1022,6 +1036,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)  	if (!is_opened(other))  		i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,  				0, SND_SOC_CLOCK_IN); +	pm_runtime_put(dai->dev);  	return 0;  } @@ -1031,6 +1046,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)  	struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);  	unsigned long flags; +	pm_runtime_get_sync(dai->dev); +  	if (!is_secondary(i2s)) {  		if (i2s->quirks & QUIRK_NEED_RSTCLR) {  			spin_lock_irqsave(i2s->lock, flags); @@ -1039,6 +1056,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)  		}  	} +	pm_runtime_put(dai->dev); +  	return 0;  } @@ -1066,7 +1085,6 @@ static const struct snd_soc_component_driver samsung_i2s_component = {  static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)  {  	struct i2s_dai *i2s; -	int ret;  	i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL);  	if (i2s == NULL) @@ -1091,33 +1109,21 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)  		i2s->i2s_dai_drv.capture.channels_max = 2;  		i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES;  		i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; -		dev_set_drvdata(&i2s->pdev->dev, i2s); -	} else {	/* Create a new platform_device for Secondary */ -		i2s->pdev = platform_device_alloc("samsung-i2s-sec", -1); -		if (!i2s->pdev) -			return NULL; - -		i2s->pdev->dev.parent = &pdev->dev; - -		platform_set_drvdata(i2s->pdev, i2s); -		ret = platform_device_add(i2s->pdev); -		if (ret < 0) -			return NULL;  	} -  	return i2s;  } -static void i2s_free_sec_dai(struct i2s_dai *i2s) -{ -	platform_device_del(i2s->pdev); -} -  #ifdef CONFIG_PM  static int i2s_runtime_suspend(struct device *dev)  {  	struct i2s_dai *i2s = dev_get_drvdata(dev); +	i2s->suspend_i2smod = readl(i2s->addr + I2SMOD); +	i2s->suspend_i2scon = readl(i2s->addr + I2SCON); +	i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR); + +	if (i2s->op_clk) +		clk_disable_unprepare(i2s->op_clk);  	clk_disable_unprepare(i2s->clk);  	return 0; @@ -1128,6 +1134,12 @@ static int i2s_runtime_resume(struct device *dev)  	struct i2s_dai *i2s = dev_get_drvdata(dev);  	clk_prepare_enable(i2s->clk); +	if (i2s->op_clk) +		clk_prepare_enable(i2s->op_clk); + +	writel(i2s->suspend_i2scon, i2s->addr + I2SCON); +	writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); +	writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);  	return 0;  } @@ -1179,13 +1191,13 @@ static int i2s_register_clock_provider(struct platform_device *pdev)  		u32 val = readl(i2s->addr + I2SPSR);  		writel(val | PSR_PSREN, i2s->addr + I2SPSR); -		i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(NULL, +		i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev,  				"i2s_rclksrc", p_names, ARRAY_SIZE(p_names),  				CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,  				i2s->addr + I2SMOD, reg_info->rclksrc_off,  				1, 0, i2s->lock); -		i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(NULL, +		i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev,  				"i2s_presc", "i2s_rclksrc",  				CLK_SET_RATE_PARENT,  				i2s->addr + I2SPSR, 8, 6, 0, i2s->lock); @@ -1196,7 +1208,7 @@ static int i2s_register_clock_provider(struct platform_device *pdev)  	of_property_read_string_index(dev->of_node,  				"clock-output-names", 0, &clk_name[0]); -	i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(NULL, clk_name[0], +	i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, clk_name[0],  				p_names[0], CLK_SET_RATE_PARENT,  				i2s->addr + I2SMOD, reg_info->cdclkcon_off,  				CLK_GATE_SET_TO_DISABLE, i2s->lock); @@ -1218,7 +1230,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)  {  	struct i2s_dai *pri_dai, *sec_dai = NULL;  	struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; -	struct samsung_i2s *i2s_cfg = NULL;  	struct resource *res;  	u32 regs_base, quirks = 0, idma_addr = 0;  	struct device_node *np = pdev->dev.of_node; @@ -1231,22 +1242,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)  		i2s_dai_data = (struct samsung_i2s_dai_data *)  				platform_get_device_id(pdev)->driver_data; -	/* Call during the secondary interface registration */ -	if (i2s_dai_data->dai_type == TYPE_SEC) { -		sec_dai = dev_get_drvdata(&pdev->dev); -		if (!sec_dai) { -			dev_err(&pdev->dev, "Unable to get drvdata\n"); -			return -EFAULT; -		} -		ret = samsung_asoc_dma_platform_register(&pdev->dev, -					sec_dai->filter, "tx-sec", NULL); -		if (ret != 0) -			return ret; - -		return devm_snd_soc_register_component(&sec_dai->pdev->dev, -						&samsung_i2s_component, -						&sec_dai->i2s_dai_drv, 1); -	}  	pri_dai = i2s_alloc_dai(pdev, false);  	if (!pri_dai) { @@ -1267,13 +1262,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)  		pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture;  		pri_dai->filter = i2s_pdata->dma_filter; -		if (&i2s_pdata->type) -			i2s_cfg = &i2s_pdata->type.i2s; - -		if (i2s_cfg) { -			quirks = i2s_cfg->quirks; -			idma_addr = i2s_cfg->idma_addr; -		} +		quirks = i2s_pdata->type.quirks; +		idma_addr = i2s_pdata->type.idma_addr;  	} else {  		quirks = i2s_dai_data->quirks;  		if (of_property_read_u32(np, "samsung,idma-addr", @@ -1305,6 +1295,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)  	}  	pri_dai->dma_playback.addr = regs_base + I2STXD;  	pri_dai->dma_capture.addr = regs_base + I2SRXD; +	pri_dai->dma_playback.chan_name = "tx"; +	pri_dai->dma_capture.chan_name = "rx";  	pri_dai->dma_playback.addr_width = 4;  	pri_dai->dma_capture.addr_width = 4;  	pri_dai->quirks = quirks; @@ -1318,6 +1310,12 @@ static int samsung_i2s_probe(struct platform_device *pdev)  	if (ret < 0)  		goto err_disable_clk; +	ret = devm_snd_soc_register_component(&pdev->dev, +					&samsung_i2s_component, +					&pri_dai->i2s_dai_drv, 1); +	if (ret < 0) +		goto err_disable_clk; +  	if (quirks & QUIRK_SEC_DAI) {  		sec_dai = i2s_alloc_dai(pdev, true);  		if (!sec_dai) { @@ -1329,6 +1327,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)  		sec_dai->lock = &pri_dai->spinlock;  		sec_dai->variant_regs = pri_dai->variant_regs;  		sec_dai->dma_playback.addr = regs_base + I2STXDS; +		sec_dai->dma_playback.chan_name = "tx-sec";  		if (!np) {  			sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec; @@ -1342,6 +1341,17 @@ static int samsung_i2s_probe(struct platform_device *pdev)  		sec_dai->idma_playback.addr = idma_addr;  		sec_dai->pri_dai = pri_dai;  		pri_dai->sec_dai = sec_dai; + +		ret = samsung_asoc_dma_platform_register(&pdev->dev, +					sec_dai->filter, "tx-sec", NULL); +		if (ret < 0) +			goto err_disable_clk; + +		ret = devm_snd_soc_register_component(&pdev->dev, +						&samsung_i2s_component, +						&sec_dai->i2s_dai_drv, 1); +		if (ret < 0) +			goto err_disable_clk;  	}  	if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { @@ -1350,13 +1360,9 @@ static int samsung_i2s_probe(struct platform_device *pdev)  		goto err_disable_clk;  	} -	ret = devm_snd_soc_register_component(&pri_dai->pdev->dev, -					&samsung_i2s_component, -					&pri_dai->i2s_dai_drv, 1); -	if (ret < 0) -		goto err_free_dai; - +	dev_set_drvdata(&pdev->dev, pri_dai); +	pm_runtime_set_active(&pdev->dev);  	pm_runtime_enable(&pdev->dev);  	ret = i2s_register_clock_provider(pdev); @@ -1364,9 +1370,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)  		return 0;  	pm_runtime_disable(&pdev->dev); -err_free_dai: -	if (sec_dai) -		i2s_free_sec_dai(sec_dai);  err_disable_clk:  	clk_disable_unprepare(pri_dai->clk);  	return ret; @@ -1374,25 +1377,20 @@ err_disable_clk:  static int samsung_i2s_remove(struct platform_device *pdev)  { -	struct i2s_dai *i2s, *other; +	struct i2s_dai *pri_dai, *sec_dai; -	i2s = dev_get_drvdata(&pdev->dev); -	other = get_other_dai(i2s); +	pri_dai = dev_get_drvdata(&pdev->dev); +	sec_dai = pri_dai->sec_dai; -	if (other) { -		other->pri_dai = NULL; -		other->sec_dai = NULL; -	} else { -		pm_runtime_disable(&pdev->dev); -	} +	pri_dai->sec_dai = NULL; +	sec_dai->pri_dai = NULL; -	if (!is_secondary(i2s)) { -		i2s_unregister_clock_provider(pdev); -		clk_disable_unprepare(i2s->clk); -	} +	pm_runtime_get_sync(&pdev->dev); +	pm_runtime_disable(&pdev->dev); -	i2s->pri_dai = NULL; -	i2s->sec_dai = NULL; +	i2s_unregister_clock_provider(pdev); +	clk_disable_unprepare(pri_dai->clk); +	pm_runtime_put_noidle(&pdev->dev);  	return 0;  } @@ -1454,49 +1452,37 @@ static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {  };  static const struct samsung_i2s_dai_data i2sv3_dai_type = { -	.dai_type = TYPE_PRI,  	.quirks = QUIRK_NO_MUXPSR,  	.i2s_variant_regs = &i2sv3_regs,  };  static const struct samsung_i2s_dai_data i2sv5_dai_type = { -	.dai_type = TYPE_PRI,  	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |  			QUIRK_SUPPORTS_IDMA,  	.i2s_variant_regs = &i2sv3_regs,  };  static const struct samsung_i2s_dai_data i2sv6_dai_type = { -	.dai_type = TYPE_PRI,  	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |  			QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,  	.i2s_variant_regs = &i2sv6_regs,  };  static const struct samsung_i2s_dai_data i2sv7_dai_type = { -	.dai_type = TYPE_PRI,  	.quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |  			QUIRK_SUPPORTS_TDM,  	.i2s_variant_regs = &i2sv7_regs,  };  static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = { -	.dai_type = TYPE_PRI,  	.quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,  	.i2s_variant_regs = &i2sv5_i2s1_regs,  }; -static const struct samsung_i2s_dai_data samsung_dai_type_sec = { -	.dai_type = TYPE_SEC, -}; -  static const struct platform_device_id samsung_i2s_driver_ids[] = {  	{  		.name           = "samsung-i2s",  		.driver_data	= (kernel_ulong_t)&i2sv3_dai_type, -	}, { -		.name           = "samsung-i2s-sec", -		.driver_data    = (kernel_ulong_t)&samsung_dai_type_sec,  	},  	{},  }; @@ -1528,6 +1514,8 @@ MODULE_DEVICE_TABLE(of, exynos_i2s_match);  static const struct dev_pm_ops samsung_i2s_pm = {  	SET_RUNTIME_PM_OPS(i2s_runtime_suspend,  				i2s_runtime_resume, NULL) +	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, +				     pm_runtime_force_resume)  };  static struct platform_driver samsung_i2s_driver = { diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index 6d0b8897fa6c..0a4718207e6e 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -35,10 +35,12 @@  #include <linux/platform_data/asoc-s3c.h>  static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_out = { +	.chan_name	= "tx",  	.addr_width	= 4,  };  static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_in = { +	.chan_name	= "rx",  	.addr_width	= 4,  }; diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 07f5091b33e8..91e6871e5413 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -31,10 +31,12 @@  #include "s3c24xx-i2s.h"  static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = { +	.chan_name	= "tx",  	.addr_width	= 2,  };  static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = { +	.chan_name	= "rx",  	.addr_width	= 2,  }; diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index de724ce7b955..6e4dfa7e2c89 100644 --- a/sound/soc/samsung/smdk_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -32,14 +32,11 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,  	struct snd_soc_pcm_runtime *rtd = substream->private_data;  	struct snd_soc_dai *codec_dai = rtd->codec_dai;  	unsigned int pll_out; -	int bfs, rfs, ret; +	int rfs, ret;  	switch (params_width(params)) {  	case 8: -		bfs = 16; -		break;  	case 16: -		bfs = 32;  		break;  	default:  		return -EINVAL; diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index 5cdf7d19b87f..24cc9d63ce87 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -12,6 +12,7 @@  #include <linux/clk.h>  #include <linux/gpio.h> +#include <linux/gpio/consumer.h>  #include <linux/module.h>  #include <linux/of.h>  #include <sound/pcm_params.h> diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 99b5b0835c1e..47b370cb2d3b 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -363,8 +363,6 @@ struct rsnd_mod *rsnd_mod_next(int *iterator,  		if (!mod)  			continue; -		(*iterator)++; -  		return mod;  	} diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index b90df77662df..7410ec0174db 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -374,10 +374,10 @@ struct rsnd_mod *rsnd_mod_next(int *iterator,  			       int array_size);  #define for_each_rsnd_mod(iterator, pos, io)				\  	for (iterator = 0;						\ -	     (pos = rsnd_mod_next(&iterator, io, NULL, 0));) +	     (pos = rsnd_mod_next(&iterator, io, NULL, 0)); iterator++)  #define for_each_rsnd_mod_arrays(iterator, pos, io, array, size)	\  	for (iterator = 0;						\ -	     (pos = rsnd_mod_next(&iterator, io, array, size));) +	     (pos = rsnd_mod_next(&iterator, io, array, size)); iterator++)  #define for_each_rsnd_mod_array(iterator, pos, io, array)		\  	for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array)) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 3a8f65bd1bf9..42db48db09ba 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -390,6 +390,9 @@ static int rsnd_src_init(struct rsnd_mod *mod,  {  	struct rsnd_src *src = rsnd_mod_to_src(mod); +	/* reset sync convert_rate */ +	src->sync.val = 0; +  	rsnd_mod_power_on(mod);  	rsnd_src_activation(mod); @@ -398,9 +401,6 @@ static int rsnd_src_init(struct rsnd_mod *mod,  	rsnd_src_status_clear(mod); -	/* reset sync convert_rate */ -	src->sync.val = 0; -  	return 0;  } diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c index 6c8b0b0c56ec..36dae41f65fc 100644 --- a/sound/soc/soc-ac97.c +++ b/sound/soc/soc-ac97.c @@ -251,7 +251,7 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);  /**   * snd_soc_free_ac97_codec - free AC97 codec device - * @codec: audio codec + * @ac97: snd_ac97 device to be freed   *   * Frees AC97 codec device resources.   */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index baa1afa41e3d..a110d3987d4a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -34,6 +34,7 @@  #include <linux/ctype.h>  #include <linux/slab.h>  #include <linux/of.h> +#include <linux/dmi.h>  #include <sound/core.h>  #include <sound/jack.h>  #include <sound/pcm.h> @@ -979,7 +980,7 @@ EXPORT_SYMBOL_GPL(snd_soc_find_dai);   * @card: soc card   * @id: DAI link ID to match   * @name: DAI link name to match, optional - * @stream name: DAI link stream name to match, optional + * @stream_name: DAI link stream name to match, optional   *   * This function will search all existing DAI links of the soc card to   * find the link of the same ID. Since DAI links may not have their @@ -1593,6 +1594,27 @@ static int soc_probe_dai(struct snd_soc_dai *dai, int order)  	return 0;  } +static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, +				struct snd_soc_pcm_runtime *rtd) +{ +	int i, ret = 0; + +	for (i = 0; i < num_dais; ++i) { +		struct snd_soc_dai_driver *drv = dais[i]->driver; + +		if (!rtd->dai_link->no_pcm && drv->pcm_new) +			ret = drv->pcm_new(rtd, dais[i]); +		if (ret < 0) { +			dev_err(dais[i]->dev, +				"ASoC: Failed to bind %s with pcm device\n", +				dais[i]->name); +			return ret; +		} +	} + +	return 0; +} +  static int soc_link_dai_widgets(struct snd_soc_card *card,  				struct snd_soc_dai_link *dai_link,  				struct snd_soc_pcm_runtime *rtd) @@ -1704,6 +1726,13 @@ static int soc_probe_link_dais(struct snd_soc_card *card,  				       dai_link->stream_name, ret);  				return ret;  			} +			ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); +			if (ret < 0) +				return ret; +			ret = soc_link_dai_pcm_new(rtd->codec_dais, +						   rtd->num_codecs, rtd); +			if (ret < 0) +				return ret;  		} else {  			INIT_DELAYED_WORK(&rtd->delayed_work,  						codec2codec_close_delayed_work); @@ -1888,6 +1917,139 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,  }  EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt); + +/* Trim special characters, and replace '-' with '_' since '-' is used to + * separate different DMI fields in the card long name. Only number and + * alphabet characters and a few separator characters are kept. + */ +static void cleanup_dmi_name(char *name) +{ +	int i, j = 0; + +	for (i = 0; name[i]; i++) { +		if (isalnum(name[i]) || (name[i] == '.') +		    || (name[i] == '_')) +			name[j++] = name[i]; +		else if (name[i] == '-') +			name[j++] = '_'; +	} + +	name[j] = '\0'; +} + +/** + * snd_soc_set_dmi_name() - Register DMI names to card + * @card: The card to register DMI names + * @flavour: The flavour "differentiator" for the card amongst its peers. + * + * An Intel machine driver may be used by many different devices but are + * difficult for userspace to differentiate, since machine drivers ususally + * use their own name as the card short name and leave the card long name + * blank. To differentiate such devices and fix bugs due to lack of + * device-specific configurations, this function allows DMI info to be used + * as the sound card long name, in the format of + * "vendor-product-version-board" + * (Character '-' is used to separate different DMI fields here). + * This will help the user space to load the device-specific Use Case Manager + * (UCM) configurations for the card. + * + * Possible card long names may be: + * DellInc.-XPS139343-01-0310JH + * ASUSTeKCOMPUTERINC.-T100TA-1.0-T100TA + * Circuitco-MinnowboardMaxD0PLATFORM-D0-MinnowBoardMAX + * + * This function also supports flavoring the card longname to provide + * the extra differentiation, like "vendor-product-version-board-flavor". + * + * We only keep number and alphabet characters and a few separator characters + * in the card long name since UCM in the user space uses the card long names + * as card configuration directory names and AudoConf cannot support special + * charactors like SPACE. + * + * Returns 0 on success, otherwise a negative error code. + */ +int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) +{ +	const char *vendor, *product, *product_version, *board; +	size_t longname_buf_size = sizeof(card->snd_card->longname); +	size_t len; + +	if (card->long_name) +		return 0; /* long name already set by driver or from DMI */ + +	/* make up dmi long name as: vendor.product.version.board */ +	vendor = dmi_get_system_info(DMI_BOARD_VENDOR); +	if (!vendor) { +		dev_warn(card->dev, "ASoC: no DMI vendor name!\n"); +		return 0; +	} + +	snprintf(card->dmi_longname, sizeof(card->snd_card->longname), +			 "%s", vendor); +	cleanup_dmi_name(card->dmi_longname); + +	product = dmi_get_system_info(DMI_PRODUCT_NAME); +	if (product) { +		len = strlen(card->dmi_longname); +		snprintf(card->dmi_longname + len, +			 longname_buf_size - len, +			 "-%s", product); + +		len++;	/* skip the separator "-" */ +		if (len < longname_buf_size) +			cleanup_dmi_name(card->dmi_longname + len); + +		/* some vendors like Lenovo may only put a self-explanatory +		 * name in the product version field +		 */ +		product_version = dmi_get_system_info(DMI_PRODUCT_VERSION); +		if (product_version) { +			len = strlen(card->dmi_longname); +			snprintf(card->dmi_longname + len, +				 longname_buf_size - len, +				 "-%s", product_version); + +			len++; +			if (len < longname_buf_size) +				cleanup_dmi_name(card->dmi_longname + len); +		} +	} + +	board = dmi_get_system_info(DMI_BOARD_NAME); +	if (board) { +		len = strlen(card->dmi_longname); +		snprintf(card->dmi_longname + len, +			 longname_buf_size - len, +			 "-%s", board); + +		len++; +		if (len < longname_buf_size) +			cleanup_dmi_name(card->dmi_longname + len); +	} else if (!product) { +		/* fall back to using legacy name */ +		dev_warn(card->dev, "ASoC: no DMI board/product name!\n"); +		return 0; +	} + +	/* Add flavour to dmi long name */ +	if (flavour) { +		len = strlen(card->dmi_longname); +		snprintf(card->dmi_longname + len, +			 longname_buf_size - len, +			 "-%s", flavour); + +		len++; +		if (len < longname_buf_size) +			cleanup_dmi_name(card->dmi_longname + len); +	} + +	/* set the card long name */ +	card->long_name = card->dmi_longname; + +	return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); +  static int snd_soc_instantiate_card(struct snd_soc_card *card)  {  	struct snd_soc_codec *codec; @@ -2976,6 +3138,8 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,  	component->remove = component->driver->remove;  	component->suspend = component->driver->suspend;  	component->resume = component->driver->resume; +	component->pcm_new = component->driver->pcm_new; +	component->pcm_free= component->driver->pcm_free;  	dapm = &component->dapm;  	dapm->dev = dev; @@ -3158,6 +3322,21 @@ static void snd_soc_platform_drv_remove(struct snd_soc_component *component)  	platform->driver->remove(platform);  } +static int snd_soc_platform_drv_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ +	struct snd_soc_platform *platform = rtd->platform; + +	return platform->driver->pcm_new(rtd); +} + +static void snd_soc_platform_drv_pcm_free(struct snd_pcm *pcm) +{ +	struct snd_soc_pcm_runtime *rtd = pcm->private_data; +	struct snd_soc_platform *platform = rtd->platform; + +	platform->driver->pcm_free(pcm); +} +  /**   * snd_soc_add_platform - Add a platform to the ASoC core   * @dev: The parent device for the platform @@ -3181,6 +3360,10 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,  		platform->component.probe = snd_soc_platform_drv_probe;  	if (platform_drv->remove)  		platform->component.remove = snd_soc_platform_drv_remove; +	if (platform_drv->pcm_new) +		platform->component.pcm_new = snd_soc_platform_drv_pcm_new; +	if (platform_drv->pcm_free) +		platform->component.pcm_free = snd_soc_platform_drv_pcm_free;  #ifdef CONFIG_DEBUG_FS  	platform->component.debugfs_prefix = "platform"; @@ -3492,10 +3675,10 @@ found:  EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);  /* Retrieve a card's name from device tree */ -int snd_soc_of_parse_card_name_from_node(struct snd_soc_card *card, -					 struct device_node *np, -					 const char *propname) +int snd_soc_of_parse_card_name(struct snd_soc_card *card, +			       const char *propname)  { +	struct device_node *np;  	int ret;  	if (!card->dev) { @@ -3503,8 +3686,7 @@ int snd_soc_of_parse_card_name_from_node(struct snd_soc_card *card,  		return -EINVAL;  	} -	if (!np) -		np = card->dev->of_node; +	np = card->dev->of_node;  	ret = of_property_read_string_index(np, propname, 0, &card->name);  	/* @@ -3521,7 +3703,7 @@ int snd_soc_of_parse_card_name_from_node(struct snd_soc_card *card,  	return 0;  } -EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name_from_node); +EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name);  static const struct snd_soc_dapm_widget simple_widgets[] = {  	SND_SOC_DAPM_MIC("Microphone", NULL), @@ -3530,17 +3712,14 @@ static const struct snd_soc_dapm_widget simple_widgets[] = {  	SND_SOC_DAPM_SPK("Speaker", NULL),  }; -int snd_soc_of_parse_audio_simple_widgets_from_node(struct snd_soc_card *card, -					  struct device_node *np, +int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,  					  const char *propname)  { +	struct device_node *np = card->dev->of_node;  	struct snd_soc_dapm_widget *widgets;  	const char *template, *wname;  	int i, j, num_widgets, ret; -	if (!np) -		np = card->dev->of_node; -  	num_widgets = of_property_count_strings(np, propname);  	if (num_widgets < 0) {  		dev_err(card->dev, @@ -3611,7 +3790,7 @@ int snd_soc_of_parse_audio_simple_widgets_from_node(struct snd_soc_card *card,  	return 0;  } -EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets_from_node); +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);  static int snd_soc_of_get_slot_mask(struct device_node *np,  				    const char *prop_name, @@ -3667,18 +3846,15 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np,  }  EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); -void snd_soc_of_parse_audio_prefix_from_node(struct snd_soc_card *card, -				   struct device_node *np, +void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card,  				   struct snd_soc_codec_conf *codec_conf,  				   struct device_node *of_node,  				   const char *propname)  { +	struct device_node *np = card->dev->of_node;  	const char *str;  	int ret; -	if (!np) -		np = card->dev->of_node; -  	ret = of_property_read_string(np, propname, &str);  	if (ret < 0) {  		/* no prefix is not error */ @@ -3688,19 +3864,16 @@ void snd_soc_of_parse_audio_prefix_from_node(struct snd_soc_card *card,  	codec_conf->of_node	= of_node;  	codec_conf->name_prefix	= str;  } -EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix_from_node); +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix); -int snd_soc_of_parse_audio_routing_from_node(struct snd_soc_card *card, -				   struct device_node *np, +int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,  				   const char *propname)  { +	struct device_node *np = card->dev->of_node;  	int num_routes;  	struct snd_soc_dapm_route *routes;  	int i, ret; -	if (!np) -		np = card->dev->of_node; -  	num_routes = of_property_count_strings(np, propname);  	if (num_routes < 0 || num_routes & 1) {  		dev_err(card->dev, @@ -3747,7 +3920,7 @@ int snd_soc_of_parse_audio_routing_from_node(struct snd_soc_card *card,  	return 0;  } -EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing_from_node); +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing);  unsigned int snd_soc_of_parse_daifmt(struct device_node *np,  				     const char *prefix, @@ -4020,8 +4193,6 @@ static void __exit snd_soc_exit(void)  	snd_soc_util_exit();  	snd_soc_debugfs_exit(); -#ifdef CONFIG_DEBUG_FS -#endif  	platform_driver_unregister(&soc_driver);  }  module_exit(snd_soc_exit); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 27dd02e57b31..dcef67a9bd48 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -363,6 +363,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,  				snd_soc_dapm_new_control_unlocked(widget->dapm,  				&template);  			kfree(name); +			if (IS_ERR(data->widget)) { +				ret = PTR_ERR(data->widget); +				goto err_data; +			}  			if (!data->widget) {  				ret = -ENOMEM;  				goto err_data; @@ -397,6 +401,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,  			data->widget = snd_soc_dapm_new_control_unlocked(  						widget->dapm, &template);  			kfree(name); +			if (IS_ERR(data->widget)) { +				ret = PTR_ERR(data->widget); +				goto err_data; +			}  			if (!data->widget) {  				ret = -ENOMEM;  				goto err_data; @@ -3403,11 +3411,22 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,  	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);  	w = snd_soc_dapm_new_control_unlocked(dapm, widget); +	/* Do not nag about probe deferrals */ +	if (IS_ERR(w)) { +		int ret = PTR_ERR(w); + +		if (ret != -EPROBE_DEFER) +			dev_err(dapm->dev, +				"ASoC: Failed to create DAPM control %s (%d)\n", +				widget->name, ret); +		goto out_unlock; +	}  	if (!w)  		dev_err(dapm->dev,  			"ASoC: Failed to create DAPM control %s\n",  			widget->name); +out_unlock:  	mutex_unlock(&dapm->card->dapm_mutex);  	return w;  } @@ -3430,6 +3449,8 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,  		w->regulator = devm_regulator_get(dapm->dev, w->name);  		if (IS_ERR(w->regulator)) {  			ret = PTR_ERR(w->regulator); +			if (ret == -EPROBE_DEFER) +				return ERR_PTR(ret);  			dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",  				w->name, ret);  			return NULL; @@ -3448,6 +3469,8 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,  		w->clk = devm_clk_get(dapm->dev, w->name);  		if (IS_ERR(w->clk)) {  			ret = PTR_ERR(w->clk); +			if (ret == -EPROBE_DEFER) +				return ERR_PTR(ret);  			dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",  				w->name, ret);  			return NULL; @@ -3566,6 +3589,16 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,  	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);  	for (i = 0; i < num; i++) {  		w = snd_soc_dapm_new_control_unlocked(dapm, widget); +		if (IS_ERR(w)) { +			ret = PTR_ERR(w); +			/* Do not nag about probe deferrals */ +			if (ret == -EPROBE_DEFER) +				break; +			dev_err(dapm->dev, +				"ASoC: Failed to create DAPM control %s (%d)\n", +				widget->name, ret); +			break; +		}  		if (!w) {  			dev_err(dapm->dev,  				"ASoC: Failed to create DAPM control %s\n", @@ -3842,6 +3875,15 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,  	dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name);  	w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template); +	if (IS_ERR(w)) { +		ret = PTR_ERR(w); +		/* Do not nag about probe deferrals */ +		if (ret != -EPROBE_DEFER) +			dev_err(card->dev, +				"ASoC: Failed to create %s widget (%d)\n", +				link_name, ret); +		goto outfree_kcontrol_news; +	}  	if (!w) {  		dev_err(card->dev, "ASoC: Failed to create %s widget\n",  			link_name); @@ -3893,6 +3935,16 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,  			template.name);  		w = snd_soc_dapm_new_control_unlocked(dapm, &template); +		if (IS_ERR(w)) { +			int ret = PTR_ERR(w); + +			/* Do not nag about probe deferrals */ +			if (ret != -EPROBE_DEFER) +				dev_err(dapm->dev, +				"ASoC: Failed to create %s widget (%d)\n", +				dai->driver->playback.stream_name, ret); +			return ret; +		}  		if (!w) {  			dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",  				dai->driver->playback.stream_name); @@ -3912,6 +3964,16 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,  			template.name);  		w = snd_soc_dapm_new_control_unlocked(dapm, &template); +		if (IS_ERR(w)) { +			int ret = PTR_ERR(w); + +			/* Do not nag about probe deferrals */ +			if (ret != -EPROBE_DEFER) +				dev_err(dapm->dev, +				"ASoC: Failed to create %s widget (%d)\n", +				dai->driver->playback.stream_name, ret); +			return ret; +		}  		if (!w) {  			dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",  				dai->driver->capture.stream_name); diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 17eb14935577..d53786498b61 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -263,6 +263,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)  	struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);  	const struct snd_dmaengine_pcm_config *config = pcm->config;  	struct device *dev = rtd->platform->dev; +	struct snd_dmaengine_dai_dma_data *dma_data;  	struct snd_pcm_substream *substream;  	size_t prealloc_buffer_size;  	size_t max_buffer_size; @@ -282,6 +283,13 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)  		if (!substream)  			continue; +		dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + +		if (!pcm->chan[i] && +		    (pcm->flags & SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) +			pcm->chan[i] = dma_request_slave_channel(dev, +				dma_data->chan_name); +  		if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {  			pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,  				substream); @@ -350,7 +358,9 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,  	const char *name;  	struct dma_chan *chan; -	if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !dev->of_node) +	if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | +			   SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || +	    !dev->of_node)  		return 0;  	if (config && config->dma_dev) { diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 9fc1a7bb8b95..500f98c730b9 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -120,7 +120,7 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,  EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);  /** - * snd_soc_read_signed - Read a codec register and interprete as signed value + * snd_soc_read_signed - Read a codec register and interpret as signed value   * @component: component   * @reg: Register to read   * @mask: Mask to use after shifting the register value diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 6aba14009c92..efc5831f205d 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1055,7 +1055,6 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,  				   int cmd)  {  	struct snd_soc_pcm_runtime *rtd = substream->private_data; -	struct snd_soc_platform *platform = rtd->platform;  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;  	struct snd_soc_dai *codec_dai;  	int i, ret; @@ -1071,12 +1070,6 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,  		}  	} -	if (platform->driver->bespoke_trigger) { -		ret = platform->driver->bespoke_trigger(substream, cmd); -		if (ret < 0) -			return ret; -	} -  	if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) {  		ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai);  		if (ret < 0) @@ -1116,13 +1109,6 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)  	}  	delay += codec_delay; -	/* -	 * None of the existing platform drivers implement delay(), so -	 * for now the codec_dai of first multicodec entry is used -	 */ -	if (platform->driver->delay) -		delay += platform->driver->delay(substream, rtd->codec_dais[0]); -  	runtime->delay = delay;  	return offset; @@ -2642,12 +2628,25 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)  	return ret;  } +static void soc_pcm_free(struct snd_pcm *pcm) +{ +	struct snd_soc_pcm_runtime *rtd = pcm->private_data; +	struct snd_soc_component *component; + +	list_for_each_entry(component, &rtd->card->component_dev_list, +			    card_list) { +		if (component->pcm_free) +			component->pcm_free(pcm); +	} +} +  /* create a new pcm */  int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)  {  	struct snd_soc_platform *platform = rtd->platform;  	struct snd_soc_dai *codec_dai;  	struct snd_soc_dai *cpu_dai = rtd->cpu_dai; +	struct snd_soc_component *component;  	struct snd_pcm *pcm;  	char new_name[64];  	int ret = 0, playback = 0, capture = 0; @@ -2756,17 +2755,18 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)  	if (capture)  		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); -	if (platform->driver->pcm_new) { -		ret = platform->driver->pcm_new(rtd); -		if (ret < 0) { -			dev_err(platform->dev, -				"ASoC: pcm constructor failed: %d\n", -				ret); -			return ret; +	list_for_each_entry(component, &rtd->card->component_dev_list, card_list) { +		if (component->pcm_new) { +			ret = component->pcm_new(rtd); +			if (ret < 0) { +				dev_err(component->dev, +					"ASoC: pcm constructor failed: %d\n", +					ret); +				return ret; +			}  		}  	} - -	pcm->private_free = platform->driver->pcm_free; +	pcm->private_free = soc_pcm_free;  out:  	dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",  		 (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, @@ -2874,15 +2874,6 @@ int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,  }  EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params); -int snd_soc_platform_trigger(struct snd_pcm_substream *substream, -		int cmd, struct snd_soc_platform *platform) -{ -	if (platform->driver->ops && platform->driver->ops->trigger) -		return platform->driver->ops->trigger(substream, cmd); -	return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_platform_trigger); -  #ifdef CONFIG_DEBUG_FS  static const char *dpcm_state_string(enum snd_soc_dpcm_state state)  { diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index fbfb1fab88d5..aff3d8129ac9 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1555,6 +1555,15 @@ widget:  		widget = snd_soc_dapm_new_control(dapm, &template);  	else  		widget = snd_soc_dapm_new_control_unlocked(dapm, &template); +	if (IS_ERR(widget)) { +		ret = PTR_ERR(widget); +		/* Do not nag about probe deferrals */ +		if (ret != -EPROBE_DEFER) +			dev_err(tplg->dev, +				"ASoC: failed to create widget %s controls (%d)\n", +				w->name, ret); +		goto hdr_err; +	}  	if (widget == NULL) {  		dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",  			w->name); @@ -1862,7 +1871,7 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,  {  	struct snd_soc_tplg_pcm *pcm, *_pcm;  	int count = hdr->count; -	int i, err; +	int i;  	bool abi_match;  	if (tplg->pass != SOC_TPLG_PASS_PCM_DAI) @@ -1896,7 +1905,7 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,  			_pcm = pcm;  		} else {  			abi_match = false; -			err = pcm_new_ver(tplg, pcm, &_pcm); +			pcm_new_ver(tplg, pcm, &_pcm);  		}  		/* create the FE DAIs and DAI links */ @@ -1918,7 +1927,7 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,  /**   * set_link_hw_format - Set the HW audio format of the physical DAI link. - * @tplg: topology context + * @link: &snd_soc_dai_link which should be updated   * @cfg: physical link configs.   *   * Topology context contains a list of supported HW formats (configs) and @@ -1969,7 +1978,7 @@ static void set_link_hw_format(struct snd_soc_dai_link *link,  /**   * link_new_ver - Create a new physical link config from the old   * version of source. - * @toplogy: topology context + * @tplg: topology context   * @src: old version of phyical link config as a source   * @link: latest version of physical link config created from the source   * @@ -2211,7 +2220,7 @@ static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,  /**   * manifest_new_ver - Create a new version of manifest from the old version   * of source. - * @toplogy: topology context + * @tplg: topology context   * @src: old version of manifest as a source   * @manifest: latest version of manifest created from the source   * diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig index 6c344e16aca4..22408bc2d6ec 100644 --- a/sound/soc/sunxi/Kconfig +++ b/sound/soc/sunxi/Kconfig @@ -9,9 +9,20 @@ config SND_SUN4I_CODEC  	  Select Y or M to add support for the Codec embedded in the Allwinner  	  A10 and affiliated SoCs. +config SND_SUN8I_CODEC +	tristate "Allwinner SUN8I audio codec" +	depends on OF +	depends on MACH_SUN8I || COMPILE_TEST +	select REGMAP_MMIO +	help +	  This option enables the digital part of the internal audio codec for +	  Allwinner sun8i SoC (and particularly A33). + +	  Say Y or M if you want to add sun8i digital audio codec support. +  config SND_SUN8I_CODEC_ANALOG  	tristate "Allwinner sun8i Codec Analog Controls Support" -	depends on MACH_SUN8I || COMPILE_TEST +	depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST  	select REGMAP  	help  	  Say Y or M if you want to add support for the analog controls for diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile index 241c0df9ca0c..1f1af6271731 100644 --- a/sound/soc/sunxi/Makefile +++ b/sound/soc/sunxi/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o  obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o  obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o  obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o +obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 848af01692a0..c3aab10fa085 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1058,6 +1058,7 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {  	{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },  	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },  	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, +	{ "Line Out Source Playback Route", "Mono Differential", "Right Mixer" },  	{ "LINEOUT", NULL, "Line Out Source Playback Route" },  	/* ADC Routes */ diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index f24d19526603..3635bbc72cbc 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -14,9 +14,11 @@  #include <linux/clk.h>  #include <linux/dmaengine.h>  #include <linux/module.h> +#include <linux/of_device.h>  #include <linux/platform_device.h>  #include <linux/pm_runtime.h>  #include <linux/regmap.h> +#include <linux/reset.h>  #include <sound/dmaengine_pcm.h>  #include <sound/pcm_params.h> @@ -92,6 +94,7 @@ struct sun4i_i2s {  	struct clk	*bus_clk;  	struct clk	*mod_clk;  	struct regmap	*regmap; +	struct reset_control *rst;  	unsigned int	mclk_freq; @@ -651,9 +654,22 @@ static int sun4i_i2s_runtime_suspend(struct device *dev)  	return 0;  } +struct sun4i_i2s_quirks { +	bool has_reset; +}; + +static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = { +	.has_reset	= false, +}; + +static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { +	.has_reset	= true, +}; +  static int sun4i_i2s_probe(struct platform_device *pdev)  {  	struct sun4i_i2s *i2s; +	const struct sun4i_i2s_quirks *quirks;  	struct resource *res;  	void __iomem *regs;  	int irq, ret; @@ -674,6 +690,12 @@ static int sun4i_i2s_probe(struct platform_device *pdev)  		return irq;  	} +	quirks = of_device_get_match_data(&pdev->dev); +	if (!quirks) { +		dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); +		return -ENODEV; +	} +  	i2s->bus_clk = devm_clk_get(&pdev->dev, "apb");  	if (IS_ERR(i2s->bus_clk)) {  		dev_err(&pdev->dev, "Can't get our bus clock\n"); @@ -692,12 +714,29 @@ static int sun4i_i2s_probe(struct platform_device *pdev)  		dev_err(&pdev->dev, "Can't get our mod clock\n");  		return PTR_ERR(i2s->mod_clk);  	} -	 + +	if (quirks->has_reset) { +		i2s->rst = devm_reset_control_get(&pdev->dev, NULL); +		if (IS_ERR(i2s->rst)) { +			dev_err(&pdev->dev, "Failed to get reset control\n"); +			return PTR_ERR(i2s->rst); +		} +	} + +	if (!IS_ERR(i2s->rst)) { +		ret = reset_control_deassert(i2s->rst); +		if (ret) { +			dev_err(&pdev->dev, +				"Failed to deassert the reset control\n"); +			return -EINVAL; +		} +	} +  	i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG; -	i2s->playback_dma_data.maxburst = 4; +	i2s->playback_dma_data.maxburst = 8;  	i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; -	i2s->capture_dma_data.maxburst = 4; +	i2s->capture_dma_data.maxburst = 8;  	pm_runtime_enable(&pdev->dev);  	if (!pm_runtime_enabled(&pdev->dev)) { @@ -727,23 +766,37 @@ err_suspend:  		sun4i_i2s_runtime_suspend(&pdev->dev);  err_pm_disable:  	pm_runtime_disable(&pdev->dev); +	if (!IS_ERR(i2s->rst)) +		reset_control_assert(i2s->rst);  	return ret;  }  static int sun4i_i2s_remove(struct platform_device *pdev)  { +	struct sun4i_i2s *i2s = dev_get_drvdata(&pdev->dev); +  	snd_dmaengine_pcm_unregister(&pdev->dev);  	pm_runtime_disable(&pdev->dev);  	if (!pm_runtime_status_suspended(&pdev->dev))  		sun4i_i2s_runtime_suspend(&pdev->dev); +	if (!IS_ERR(i2s->rst)) +		reset_control_assert(i2s->rst); +  	return 0;  }  static const struct of_device_id sun4i_i2s_match[] = { -	{ .compatible = "allwinner,sun4i-a10-i2s", }, +	{ +		.compatible = "allwinner,sun4i-a10-i2s", +		.data = &sun4i_a10_i2s_quirks, +	}, +	{ +		.compatible = "allwinner,sun6i-a31-i2s", +		.data = &sun6i_a31_i2s_quirks, +	},  	{}  };  MODULE_DEVICE_TABLE(of, sun4i_i2s_match); diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index 88fbb3a1e660..eaefd07a5ed0 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -103,6 +103,8 @@  	#define SUN4I_SPDIF_ISTA_RXOSTA			BIT(1)  	#define SUN4I_SPDIF_ISTA_RXASTA			BIT(0) +#define SUN8I_SPDIF_TXFIFO	(0x20) +  #define SUN4I_SPDIF_TXCNT	(0x24)  #define SUN4I_SPDIF_RXCNT	(0x28) @@ -403,17 +405,38 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = {  	.name = "spdif",  }; -static const struct snd_soc_dapm_widget dit_widgets[] = { -	SND_SOC_DAPM_OUTPUT("spdif-out"), +struct sun4i_spdif_quirks { +	unsigned int reg_dac_txdata;	/* TX FIFO offset for DMA config */ +	bool has_reset; +}; + +static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { +	.reg_dac_txdata	= SUN4I_SPDIF_TXFIFO,  }; -static const struct snd_soc_dapm_route dit_routes[] = { -	{ "spdif-out", NULL, "Playback" }, +static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { +	.reg_dac_txdata	= SUN4I_SPDIF_TXFIFO, +	.has_reset	= true, +}; + +static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { +	.reg_dac_txdata	= SUN8I_SPDIF_TXFIFO, +	.has_reset	= true,  };  static const struct of_device_id sun4i_spdif_of_match[] = { -	{ .compatible = "allwinner,sun4i-a10-spdif", }, -	{ .compatible = "allwinner,sun6i-a31-spdif", }, +	{ +		.compatible = "allwinner,sun4i-a10-spdif", +		.data = &sun4i_a10_spdif_quirks, +	}, +	{ +		.compatible = "allwinner,sun6i-a31-spdif", +		.data = &sun6i_a31_spdif_quirks, +	}, +	{ +		.compatible = "allwinner,sun8i-h3-spdif", +		.data = &sun8i_h3_spdif_quirks, +	},  	{ /* sentinel */ }  };  MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match); @@ -446,6 +469,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev)  {  	struct sun4i_spdif_dev *host;  	struct resource *res; +	const struct sun4i_spdif_quirks *quirks;  	int ret;  	void __iomem *base; @@ -467,6 +491,12 @@ static int sun4i_spdif_probe(struct platform_device *pdev)  	if (IS_ERR(base))  		return PTR_ERR(base); +	quirks = of_device_get_match_data(&pdev->dev); +	if (quirks == NULL) { +		dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); +		return -ENODEV; +	} +  	host->regmap = devm_regmap_init_mmio(&pdev->dev, base,  						&sun4i_spdif_regmap_config); @@ -480,23 +510,21 @@ static int sun4i_spdif_probe(struct platform_device *pdev)  	host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");  	if (IS_ERR(host->spdif_clk)) {  		dev_err(&pdev->dev, "failed to get a spdif clock.\n"); -		ret = PTR_ERR(host->spdif_clk); -		goto err_disable_apb_clk; +		return PTR_ERR(host->spdif_clk);  	} -	host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO; +	host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata;  	host->dma_params_tx.maxburst = 8;  	host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;  	platform_set_drvdata(pdev, host); -	if (of_device_is_compatible(pdev->dev.of_node, -				    "allwinner,sun6i-a31-spdif")) { +	if (quirks->has_reset) {  		host->rst = devm_reset_control_get_optional(&pdev->dev, NULL);  		if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) {  			ret = -EPROBE_DEFER;  			dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); -			goto err_disable_apb_clk; +			return ret;  		}  		if (!IS_ERR(host->rst))  			reset_control_deassert(host->rst); @@ -505,7 +533,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev)  	ret = devm_snd_soc_register_component(&pdev->dev,  				&sun4i_spdif_component, &sun4i_spdif_dai, 1);  	if (ret) -		goto err_disable_apb_clk; +		return ret;  	pm_runtime_enable(&pdev->dev);  	if (!pm_runtime_enabled(&pdev->dev)) { @@ -523,9 +551,6 @@ err_suspend:  		sun4i_spdif_runtime_suspend(&pdev->dev);  err_unregister:  	pm_runtime_disable(&pdev->dev); -	snd_soc_unregister_component(&pdev->dev); -err_disable_apb_clk: -	clk_disable_unprepare(host->apb_clk);  	return ret;  } @@ -535,9 +560,6 @@ static int sun4i_spdif_remove(struct platform_device *pdev)  	if (!pm_runtime_status_suspended(&pdev->dev))  		sun4i_spdif_runtime_suspend(&pdev->dev); -	snd_soc_unregister_platform(&pdev->dev); -	snd_soc_unregister_component(&pdev->dev); -  	return 0;  } diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c index af02290ebe49..72331332b72e 100644 --- a/sound/soc/sunxi/sun8i-codec-analog.c +++ b/sound/soc/sunxi/sun8i-codec-analog.c @@ -398,11 +398,37 @@ static const struct snd_kcontrol_new sun8i_codec_hp_src[] = {  		      sun8i_codec_hp_src_enum),  }; +static int sun8i_headphone_amp_event(struct snd_soc_dapm_widget *w, +				     struct snd_kcontrol *k, int event) +{ +	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + +	if (SND_SOC_DAPM_EVENT_ON(event)) { +		snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL, +					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN), +					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN)); +		/* +		 * Need a delay to have the amplifier up. 700ms seems the best +		 * compromise between the time to let the amplifier up and the +		 * time not to feel this delay while playing a sound. +		 */ +		msleep(700); +	} else if (SND_SOC_DAPM_EVENT_OFF(event)) { +		snd_soc_component_update_bits(component, SUN8I_ADDA_PAEN_HP_CTRL, +					      BIT(SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN), +					      0x0); +	} + +	return 0; +} +  static const struct snd_soc_dapm_widget sun8i_codec_headphone_widgets[] = {  	SND_SOC_DAPM_MUX("Headphone Source Playback Route",  			 SND_SOC_NOPM, 0, 0, sun8i_codec_hp_src), -	SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL, -			     SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0), +	SND_SOC_DAPM_OUT_DRV_E("Headphone Amp", SUN8I_ADDA_PAEN_HP_CTRL, +			       SUN8I_ADDA_PAEN_HP_CTRL_HPPAEN, 0, NULL, 0, +			       sun8i_headphone_amp_event, +			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),  	SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUN8I_ADDA_PAEN_HP_CTRL,  			    SUN8I_ADDA_PAEN_HP_CTRL_COMPTEN, 0, NULL, 0),  	SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUN8I_ADDA_PAEN_HP_CTRL, diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c new file mode 100644 index 000000000000..b92bdc8361af --- /dev/null +++ b/sound/soc/sunxi/sun8i-codec.c @@ -0,0 +1,498 @@ +/* + * This driver supports the digital controls for the internal codec + * found in Allwinner's A33 SoCs. + * + * (C) Copyright 2010-2016 + * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com> + * huangxin <huangxin@Reuuimllatech.com> + * Mylène Josserand <mylene.josserand@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#define SUN8I_SYSCLK_CTL				0x00c +#define SUN8I_SYSCLK_CTL_AIF1CLK_ENA			11 +#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL		9 +#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC			8 +#define SUN8I_SYSCLK_CTL_SYSCLK_ENA			3 +#define SUN8I_SYSCLK_CTL_SYSCLK_SRC			0 +#define SUN8I_MOD_CLK_ENA				0x010 +#define SUN8I_MOD_CLK_ENA_AIF1				15 +#define SUN8I_MOD_CLK_ENA_DAC				2 +#define SUN8I_MOD_RST_CTL				0x014 +#define SUN8I_MOD_RST_CTL_AIF1				15 +#define SUN8I_MOD_RST_CTL_DAC				2 +#define SUN8I_SYS_SR_CTRL				0x018 +#define SUN8I_SYS_SR_CTRL_AIF1_FS			12 +#define SUN8I_SYS_SR_CTRL_AIF2_FS			8 +#define SUN8I_AIF1CLK_CTRL				0x040 +#define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD		15 +#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV		14 +#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV		13 +#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV		9 +#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV		6 +#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16		(1 << 6) +#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ		4 +#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16		(1 << 4) +#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT		2 +#define SUN8I_AIF1_DACDAT_CTRL				0x048 +#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA		15 +#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA		14 +#define SUN8I_DAC_DIG_CTRL				0x120 +#define SUN8I_DAC_DIG_CTRL_ENDA			15 +#define SUN8I_DAC_MXR_SRC				0x130 +#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L	15 +#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L	14 +#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL	13 +#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL		12 +#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R	11 +#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R	10 +#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR	9 +#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR		8 + +#define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK		GENMASK(15, 12) +#define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK		GENMASK(11, 8) +#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK	GENMASK(5, 4) +#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK	GENMASK(8, 6) + +struct sun8i_codec { +	struct device	*dev; +	struct regmap	*regmap; +	struct clk	*clk_module; +	struct clk	*clk_bus; +}; + +static int sun8i_codec_runtime_resume(struct device *dev) +{ +	struct sun8i_codec *scodec = dev_get_drvdata(dev); +	int ret; + +	ret = clk_prepare_enable(scodec->clk_module); +	if (ret) { +		dev_err(dev, "Failed to enable the module clock\n"); +		return ret; +	} + +	ret = clk_prepare_enable(scodec->clk_bus); +	if (ret) { +		dev_err(dev, "Failed to enable the bus clock\n"); +		goto err_disable_modclk; +	} + +	regcache_cache_only(scodec->regmap, false); + +	ret = regcache_sync(scodec->regmap); +	if (ret) { +		dev_err(dev, "Failed to sync regmap cache\n"); +		goto err_disable_clk; +	} + +	return 0; + +err_disable_clk: +	clk_disable_unprepare(scodec->clk_bus); + +err_disable_modclk: +	clk_disable_unprepare(scodec->clk_module); + +	return ret; +} + +static int sun8i_codec_runtime_suspend(struct device *dev) +{ +	struct sun8i_codec *scodec = dev_get_drvdata(dev); + +	regcache_cache_only(scodec->regmap, true); +	regcache_mark_dirty(scodec->regmap); + +	clk_disable_unprepare(scodec->clk_module); +	clk_disable_unprepare(scodec->clk_bus); + +	return 0; +} + +static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params) +{ +	unsigned int rate = params_rate(params); + +	switch (rate) { +	case 8000: +	case 7350: +		return 0x0; +	case 11025: +		return 0x1; +	case 12000: +		return 0x2; +	case 16000: +		return 0x3; +	case 22050: +		return 0x4; +	case 24000: +		return 0x5; +	case 32000: +		return 0x6; +	case 44100: +		return 0x7; +	case 48000: +		return 0x8; +	case 96000: +		return 0x9; +	case 192000: +		return 0xa; +	default: +		return -EINVAL; +	} +} + +static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ +	struct sun8i_codec *scodec = snd_soc_codec_get_drvdata(dai->codec); +	u32 value; + +	/* clock masters */ +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBS_CFS: /* DAI Slave */ +		value = 0x0; /* Codec Master */ +		break; +	case SND_SOC_DAIFMT_CBM_CFM: /* DAI Master */ +		value = 0x1; /* Codec Slave */ +		break; +	default: +		return -EINVAL; +	} +	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, +			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD), +			   value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD); + +	/* clock inversion */ +	switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +	case SND_SOC_DAIFMT_NB_NF: /* Normal */ +		value = 0x0; +		break; +	case SND_SOC_DAIFMT_IB_IF: /* Inversion */ +		value = 0x1; +		break; +	default: +		return -EINVAL; +	} +	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, +			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV), +			   value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV); +	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, +			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV), +			   value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV); + +	/* DAI format */ +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_I2S: +		value = 0x0; +		break; +	case SND_SOC_DAIFMT_LEFT_J: +		value = 0x1; +		break; +	case SND_SOC_DAIFMT_RIGHT_J: +		value = 0x2; +		break; +	case SND_SOC_DAIFMT_DSP_A: +	case SND_SOC_DAIFMT_DSP_B: +		value = 0x3; +		break; +	default: +		return -EINVAL; +	} +	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, +			   BIT(SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT), +			   value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT); + +	return 0; +} + +static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, +				 struct snd_pcm_hw_params *params, +				 struct snd_soc_dai *dai) +{ +	struct sun8i_codec *scodec = snd_soc_codec_get_drvdata(dai->codec); +	int sample_rate; + +	/* +	 * The CPU DAI handles only a sample of 16 bits. Configure the +	 * codec to handle this type of sample resolution. +	 */ +	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, +			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK, +			   SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16); + +	regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, +			   SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK, +			   SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16); + +	sample_rate = sun8i_codec_get_hw_rate(params); +	if (sample_rate < 0) +		return sample_rate; + +	regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL, +			   SUN8I_SYS_SR_CTRL_AIF1_FS_MASK, +			   sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS); +	regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL, +			   SUN8I_SYS_SR_CTRL_AIF2_FS_MASK, +			   sample_rate << SUN8I_SYS_SR_CTRL_AIF2_FS); + +	return 0; +} + +static const struct snd_kcontrol_new sun8i_output_left_mixer_controls[] = { +	SOC_DAPM_SINGLE("LSlot 0", SUN8I_DAC_MXR_SRC, +			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L, 1, 0), +	SOC_DAPM_SINGLE("LSlot 1", SUN8I_DAC_MXR_SRC, +			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L, 1, 0), +	SOC_DAPM_SINGLE("DACL", SUN8I_DAC_MXR_SRC, +			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL, 1, 0), +	SOC_DAPM_SINGLE("ADCL", SUN8I_DAC_MXR_SRC, +			SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL, 1, 0), +}; + +static const struct snd_kcontrol_new sun8i_output_right_mixer_controls[] = { +	SOC_DAPM_SINGLE("RSlot 0", SUN8I_DAC_MXR_SRC, +			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0), +	SOC_DAPM_SINGLE("RSlot 1", SUN8I_DAC_MXR_SRC, +			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0), +	SOC_DAPM_SINGLE("DACR", SUN8I_DAC_MXR_SRC, +			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0), +	SOC_DAPM_SINGLE("ADCR", SUN8I_DAC_MXR_SRC, +			SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0), +}; + +static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { +	/* Digital parts of the DACs */ +	SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA, +			    0, NULL, 0), + +	/* Analog DAC */ +	SND_SOC_DAPM_DAC("Digital Left DAC", "Playback", SUN8I_AIF1_DACDAT_CTRL, +			 SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0), +	SND_SOC_DAPM_DAC("Digital Right DAC", "Playback", SUN8I_AIF1_DACDAT_CTRL, +			 SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), + +	/* DAC Mixers */ +	SND_SOC_DAPM_MIXER("Left DAC Mixer", SND_SOC_NOPM, 0, 0, +			   sun8i_output_left_mixer_controls, +			   ARRAY_SIZE(sun8i_output_left_mixer_controls)), +	SND_SOC_DAPM_MIXER("Right DAC Mixer", SND_SOC_NOPM, 0, 0, +			   sun8i_output_right_mixer_controls, +			   ARRAY_SIZE(sun8i_output_right_mixer_controls)), + +	/* Clocks */ +	SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA, +			    SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0), +	SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA, +			    SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0), +	SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL, +			    SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0), +	SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL, +			    SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0), + +	SND_SOC_DAPM_SUPPLY("AIF1 PLL", SUN8I_SYSCLK_CTL, +			    SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL, 0, NULL, 0), +	/* Inversion as 0=AIF1, 1=AIF2 */ +	SND_SOC_DAPM_SUPPLY("SYSCLK AIF1", SUN8I_SYSCLK_CTL, +			    SUN8I_SYSCLK_CTL_SYSCLK_SRC, 1, NULL, 0), + +	/* Module reset */ +	SND_SOC_DAPM_SUPPLY("RST AIF1", SUN8I_MOD_RST_CTL, +			    SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0), +	SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL, +			    SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0), + +	SND_SOC_DAPM_OUTPUT("HP"), +}; + +static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { +	/* Clock Routes */ +	{ "AIF1", NULL, "SYSCLK AIF1" }, +	{ "AIF1 PLL", NULL, "AIF1" }, +	{ "RST AIF1", NULL, "AIF1 PLL" }, +	{ "MODCLK AFI1", NULL, "RST AIF1" }, +	{ "DAC", NULL, "MODCLK AFI1" }, + +	{ "RST DAC", NULL, "SYSCLK" }, +	{ "MODCLK DAC", NULL, "RST DAC" }, +	{ "DAC", NULL, "MODCLK DAC" }, + +	/* DAC Routes */ +	{ "Digital Left DAC", NULL, "DAC" }, +	{ "Digital Right DAC", NULL, "DAC" }, + +	/* DAC Mixer Routes */ +	{ "Left DAC Mixer", "LSlot 0", "Digital Left DAC"}, +	{ "Right DAC Mixer", "RSlot 0", "Digital Right DAC"}, + +	/* End of route : HP out */ +	{ "HP", NULL, "Left DAC Mixer" }, +	{ "HP", NULL, "Right DAC Mixer" }, +}; + +static struct snd_soc_dai_ops sun8i_codec_dai_ops = { +	.hw_params = sun8i_codec_hw_params, +	.set_fmt = sun8i_set_fmt, +}; + +static struct snd_soc_dai_driver sun8i_codec_dai = { +	.name = "sun8i", +	/* playback capabilities */ +	.playback = { +		.stream_name = "Playback", +		.channels_min = 1, +		.channels_max = 2, +		.rates = SNDRV_PCM_RATE_8000_192000, +		.formats = SNDRV_PCM_FMTBIT_S16_LE, +	}, +	/* pcm operations */ +	.ops = &sun8i_codec_dai_ops, +}; + +static struct snd_soc_codec_driver sun8i_soc_codec = { +	.component_driver = { +		.dapm_widgets		= sun8i_codec_dapm_widgets, +		.num_dapm_widgets	= ARRAY_SIZE(sun8i_codec_dapm_widgets), +		.dapm_routes		= sun8i_codec_dapm_routes, +		.num_dapm_routes	= ARRAY_SIZE(sun8i_codec_dapm_routes), +	}, +}; + +static const struct regmap_config sun8i_codec_regmap_config = { +	.reg_bits	= 32, +	.reg_stride	= 4, +	.val_bits	= 32, +	.max_register	= SUN8I_DAC_MXR_SRC, + +	.cache_type	= REGCACHE_FLAT, +}; + +static int sun8i_codec_probe(struct platform_device *pdev) +{ +	struct resource *res_base; +	struct sun8i_codec *scodec; +	void __iomem *base; +	int ret; + +	scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL); +	if (!scodec) +		return -ENOMEM; + +	scodec->dev = &pdev->dev; + +	scodec->clk_module = devm_clk_get(&pdev->dev, "mod"); +	if (IS_ERR(scodec->clk_module)) { +		dev_err(&pdev->dev, "Failed to get the module clock\n"); +		return PTR_ERR(scodec->clk_module); +	} + +	scodec->clk_bus = devm_clk_get(&pdev->dev, "bus"); +	if (IS_ERR(scodec->clk_bus)) { +		dev_err(&pdev->dev, "Failed to get the bus clock\n"); +		return PTR_ERR(scodec->clk_bus); +	} + +	res_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	base = devm_ioremap_resource(&pdev->dev, res_base); +	if (IS_ERR(base)) { +		dev_err(&pdev->dev, "Failed to map the registers\n"); +		return PTR_ERR(base); +	} + +	scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, +					       &sun8i_codec_regmap_config); +	if (IS_ERR(scodec->regmap)) { +		dev_err(&pdev->dev, "Failed to create our regmap\n"); +		return PTR_ERR(scodec->regmap); +	} + +	platform_set_drvdata(pdev, scodec); + +	pm_runtime_enable(&pdev->dev); +	if (!pm_runtime_enabled(&pdev->dev)) { +		ret = sun8i_codec_runtime_resume(&pdev->dev); +		if (ret) +			goto err_pm_disable; +	} + +	ret = snd_soc_register_codec(&pdev->dev, &sun8i_soc_codec, +				     &sun8i_codec_dai, 1); +	if (ret) { +		dev_err(&pdev->dev, "Failed to register codec\n"); +		goto err_suspend; +	} + +	return ret; + +err_suspend: +	if (!pm_runtime_status_suspended(&pdev->dev)) +		sun8i_codec_runtime_suspend(&pdev->dev); + +err_pm_disable: +	pm_runtime_disable(&pdev->dev); + +	return ret; +} + +static int sun8i_codec_remove(struct platform_device *pdev) +{ +	struct snd_soc_card *card = platform_get_drvdata(pdev); +	struct sun8i_codec *scodec = snd_soc_card_get_drvdata(card); + +	pm_runtime_disable(&pdev->dev); +	if (!pm_runtime_status_suspended(&pdev->dev)) +		sun8i_codec_runtime_suspend(&pdev->dev); + +	snd_soc_unregister_codec(&pdev->dev); +	clk_disable_unprepare(scodec->clk_module); +	clk_disable_unprepare(scodec->clk_bus); + +	return 0; +} + +static const struct of_device_id sun8i_codec_of_match[] = { +	{ .compatible = "allwinner,sun8i-a33-codec" }, +	{} +}; +MODULE_DEVICE_TABLE(of, sun8i_codec_of_match); + +static const struct dev_pm_ops sun8i_codec_pm_ops = { +	SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend, +			   sun8i_codec_runtime_resume, NULL) +}; + +static struct platform_driver sun8i_codec_driver = { +	.driver = { +		.name = "sun8i-codec", +		.of_match_table = sun8i_codec_of_match, +		.pm = &sun8i_codec_pm_ops, +	}, +	.probe = sun8i_codec_probe, +	.remove = sun8i_codec_remove, +}; +module_platform_driver(sun8i_codec_driver); + +MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver"); +MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sun8i-codec"); diff --git a/sound/soc/zte/zx-i2s.c b/sound/soc/zte/zx-i2s.c index 1cad93dc1fcf..a865f37c2a56 100644 --- a/sound/soc/zte/zx-i2s.c +++ b/sound/soc/zte/zx-i2s.c @@ -95,7 +95,8 @@  struct zx_i2s_info {  	struct snd_dmaengine_dai_dma_data	dma_playback;  	struct snd_dmaengine_dai_dma_data	dma_capture; -	struct clk				*dai_clk; +	struct clk				*dai_wclk; +	struct clk				*dai_pclk;  	void __iomem				*reg_base;  	int					master;  	resource_size_t				mapbase; @@ -225,7 +226,7 @@ static int zx_i2s_hw_params(struct snd_pcm_substream *substream,  	struct zx_i2s_info *i2s = snd_soc_dai_get_drvdata(socdai);  	struct snd_dmaengine_dai_dma_data *dma_data;  	unsigned int lane, ch_num, len, ret = 0; -	unsigned long val, format; +	unsigned long val;  	unsigned long chn_cfg;  	dma_data = snd_soc_dai_get_dma_data(socdai, substream); @@ -238,15 +239,12 @@ static int zx_i2s_hw_params(struct snd_pcm_substream *substream,  	switch (params_format(params)) {  	case SNDRV_PCM_FORMAT_S16_LE: -		format = 0;  		len = 16;  		break;  	case SNDRV_PCM_FORMAT_S24_LE: -		format = 1;  		len = 24;  		break;  	case SNDRV_PCM_FORMAT_S32_LE: -		format = 2;  		len = 32;  		break;  	default: @@ -278,8 +276,9 @@ static int zx_i2s_hw_params(struct snd_pcm_substream *substream,  	writel_relaxed(val, i2s->reg_base + ZX_I2S_TIMING_CTRL);  	if (i2s->master) -		ret = clk_set_rate(i2s->dai_clk, -				   params_rate(params) * ch_num * CLK_RAT); +		ret = clk_set_rate(i2s->dai_wclk, +				params_rate(params) * ch_num * CLK_RAT); +  	return ret;  } @@ -331,8 +330,19 @@ static int zx_i2s_startup(struct snd_pcm_substream *substream,  			  struct snd_soc_dai *dai)  {  	struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev); +	int ret; -	return clk_prepare_enable(zx_i2s->dai_clk); +	ret = clk_prepare_enable(zx_i2s->dai_wclk); +	if (ret) +		return ret; + +	ret = clk_prepare_enable(zx_i2s->dai_pclk); +	if (ret) { +		clk_disable_unprepare(zx_i2s->dai_wclk); +		return ret; +	} + +	return ret;  }  static void zx_i2s_shutdown(struct snd_pcm_substream *substream, @@ -340,7 +350,8 @@ static void zx_i2s_shutdown(struct snd_pcm_substream *substream,  {  	struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev); -	clk_disable_unprepare(zx_i2s->dai_clk); +	clk_disable_unprepare(zx_i2s->dai_wclk); +	clk_disable_unprepare(zx_i2s->dai_pclk);  }  static struct snd_soc_dai_ops zx_i2s_dai_ops = { @@ -384,10 +395,16 @@ static int zx_i2s_probe(struct platform_device *pdev)  	if (!zx_i2s)  		return -ENOMEM; -	zx_i2s->dai_clk = devm_clk_get(&pdev->dev, "tx"); -	if (IS_ERR(zx_i2s->dai_clk)) { -		dev_err(&pdev->dev, "Fail to get clk\n"); -		return PTR_ERR(zx_i2s->dai_clk); +	zx_i2s->dai_wclk = devm_clk_get(&pdev->dev, "wclk"); +	if (IS_ERR(zx_i2s->dai_wclk)) { +		dev_err(&pdev->dev, "Fail to get wclk\n"); +		return PTR_ERR(zx_i2s->dai_wclk); +	} + +	zx_i2s->dai_pclk = devm_clk_get(&pdev->dev, "pclk"); +	if (IS_ERR(zx_i2s->dai_pclk)) { +		dev_err(&pdev->dev, "Fail to get pclk\n"); +		return PTR_ERR(zx_i2s->dai_pclk);  	}  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
