diff options
Diffstat (limited to 'sound/soc/intel/skylake/skl-pcm.c')
| -rw-r--r-- | sound/soc/intel/skylake/skl-pcm.c | 118 | 
1 files changed, 86 insertions, 32 deletions
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index e12520e142ff..e91bbcffc856 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -21,6 +21,7 @@  #include <linux/pci.h>  #include <linux/pm_runtime.h> +#include <linux/delay.h>  #include <sound/pcm_params.h>  #include <sound/soc.h>  #include "skl.h" @@ -155,7 +156,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params)  	snd_hdac_ext_stream_decouple(ebus, stream, true);  	format_val = snd_hdac_calc_stream_format(params->s_freq, -				params->ch, params->format, 32, 0); +			params->ch, params->format, params->host_bps, 0);  	dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",  		format_val, params->s_freq, params->ch, params->format); @@ -190,8 +191,8 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params)  	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); +	format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, +					params->format, params->link_bps, 0);  	dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",  		format_val, params->s_freq, params->ch, params->format); @@ -262,23 +263,6 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,  	return 0;  } -static int skl_be_prepare(struct snd_pcm_substream *substream, -		struct snd_soc_dai *dai) -{ -	struct skl *skl = get_skl_ctx(dai->dev); -	struct skl_sst *ctx = skl->skl_sst; -	struct skl_module_cfg *mconfig; - -	if (dai->playback_widget->power || dai->capture_widget->power) -		return 0; - -	mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream); -	if (mconfig == NULL) -		return -EINVAL; - -	return skl_dsp_set_dma_control(ctx, mconfig); -} -  static int skl_pcm_prepare(struct snd_pcm_substream *substream,  		struct snd_soc_dai *dai)  { @@ -326,6 +310,11 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,  	p_params.host_dma_id = dma_id;  	p_params.stream = substream->stream;  	p_params.format = params_format(params); +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		p_params.host_bps = dai->driver->playback.sig_bits; +	else +		p_params.host_bps = dai->driver->capture.sig_bits; +  	m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);  	if (m_cfg) @@ -564,6 +553,11 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,  	p_params.link_index = link->index;  	p_params.format = params_format(params); +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		p_params.link_bps = codec_dai->driver->playback.sig_bits; +	else +		p_params.link_bps = codec_dai->driver->capture.sig_bits; +  	return skl_tplg_be_update_params(dai, &p_params);  } @@ -649,7 +643,6 @@ static struct snd_soc_dai_ops skl_dmic_dai_ops = {  static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {  	.hw_params = skl_be_hw_params, -	.prepare = skl_be_prepare,  };  static struct snd_soc_dai_ops skl_link_dai_ops = { @@ -670,6 +663,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000,  		.formats = SNDRV_PCM_FMTBIT_S16_LE |  			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, +		.sig_bits = 32,  	},  	.capture = {  		.stream_name = "System Capture", @@ -677,6 +671,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  		.channels_max = HDA_STEREO,  		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,  		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, +		.sig_bits = 32,  	},  },  { @@ -688,6 +683,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  		.channels_max = HDA_QUAD,  		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,  		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, +		.sig_bits = 32,  	},  },  { @@ -699,6 +695,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  		.channels_max = HDA_STEREO,  		.rates = SNDRV_PCM_RATE_48000,  		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, +		.sig_bits = 32,  	},  },  { @@ -710,6 +707,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  		.channels_max = HDA_STEREO,  		.rates = SNDRV_PCM_RATE_48000,  		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, +		.sig_bits = 32,  	},  },  { @@ -721,6 +719,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  		.channels_max = HDA_QUAD,  		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,  		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, +		.sig_bits = 32,  	},  },  { @@ -736,6 +735,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  			SNDRV_PCM_RATE_192000,  		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |  			SNDRV_PCM_FMTBIT_S32_LE, +		.sig_bits = 32,  	},  },  { @@ -751,6 +751,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  			SNDRV_PCM_RATE_192000,  		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |  			SNDRV_PCM_FMTBIT_S32_LE, +		.sig_bits = 32,  	},  },  { @@ -766,6 +767,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  			SNDRV_PCM_RATE_192000,  		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |  			SNDRV_PCM_FMTBIT_S32_LE, +		.sig_bits = 32,  	},  }, @@ -949,14 +951,12 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {  static int skl_platform_open(struct snd_pcm_substream *substream)  { -	struct snd_pcm_runtime *runtime;  	struct snd_soc_pcm_runtime *rtd = substream->private_data;  	struct snd_soc_dai_link *dai_link = rtd->dai_link;  	dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__,  					dai_link->cpu_dai_name); -	runtime = substream->runtime;  	snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw);  	return 0; @@ -1062,13 +1062,31 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer  	 * HAD space reflects the actual data that is transferred.  	 * Use the position buffer for capture, as DPIB write gets  	 * completed earlier than the actual data written to the DDR. +	 * +	 * For capture stream following workaround is required to fix the +	 * incorrect position reporting. +	 * +	 * 1. Wait for 20us before reading the DMA position in buffer once +	 * the interrupt is generated for stream completion as update happens +	 * on the HDA frame boundary i.e. 20.833uSec. +	 * 2. Read DPIB register to flush the DMA position value. This dummy +	 * read is required to flush DMA position value. +	 * 3. Read the DMA Position-in-Buffer. This value now will be equal to +	 * or greater than period boundary.  	 */ -	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {  		pos = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE +  				(AZX_REG_VS_SDXDPIB_XINTERVAL *  				hdac_stream(hstream)->index)); -	else +	} else { +		udelay(20); +		readl(ebus->bus.remap_addr + +				AZX_REG_VS_SDXDPIB_XBASE + +				(AZX_REG_VS_SDXDPIB_XINTERVAL * +				 hdac_stream(hstream)->index));  		pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream)); +	}  	if (pos >= hdac_stream(hstream)->bufsize)  		pos = 0; @@ -1165,7 +1183,7 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)  						snd_dma_pci_data(skl->pci),  						size, MAX_PREALLOC_SIZE);  		if (retval) { -			dev_err(dai->dev, "dma buffer allocationf fail\n"); +			dev_err(dai->dev, "dma buffer allocation fail\n");  			return retval;  		}  	} @@ -1173,29 +1191,52 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)  	return retval;  } +static int skl_get_module_info(struct skl *skl, struct skl_module_cfg *mconfig) +{ +	struct skl_sst *ctx = skl->skl_sst; +	struct uuid_module *module; +	uuid_le *uuid_mod; + +	uuid_mod = (uuid_le *)mconfig->guid; + +	if (list_empty(&ctx->uuid_list)) { +		dev_err(ctx->dev, "Module list is empty\n"); +		return -EIO; +	} + +	list_for_each_entry(module, &ctx->uuid_list, list) { +		if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) { +			mconfig->id.module_id = module->id; +			mconfig->is_loadable = module->is_loadable; +			return 0; +		} +	} + +	return -EIO; +} +  static int skl_populate_modules(struct skl *skl)  {  	struct skl_pipeline *p;  	struct skl_pipe_module *m;  	struct snd_soc_dapm_widget *w;  	struct skl_module_cfg *mconfig; -	int ret; +	int ret = 0;  	list_for_each_entry(p, &skl->ppl_list, node) {  		list_for_each_entry(m, &p->pipe->w_list, node) { -  			w = m->w;  			mconfig = w->priv; -			ret = snd_skl_get_module_info(skl->skl_sst, mconfig); +			ret = skl_get_module_info(skl, mconfig);  			if (ret < 0) {  				dev_err(skl->skl_sst->dev, -					"query module info failed:%d\n", ret); -				goto err; +					"query module info failed\n"); +				return ret;  			}  		}  	} -err: +  	return ret;  } @@ -1232,6 +1273,7 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform)  		}  		skl_populate_modules(skl);  		skl->skl_sst->update_d0i3c = skl_update_d0i3c; +		skl_dsp_enable_notification(skl->skl_sst, false);  	}  	pm_runtime_mark_last_busy(platform->dev);  	pm_runtime_put_autosuspend(platform->dev); @@ -1256,6 +1298,7 @@ int skl_platform_register(struct device *dev)  	struct skl *skl = ebus_to_skl(ebus);  	INIT_LIST_HEAD(&skl->ppl_list); +	INIT_LIST_HEAD(&skl->bind_list);  	ret = snd_soc_register_platform(dev, &skl_platform_drv);  	if (ret) { @@ -1276,6 +1319,17 @@ int skl_platform_register(struct device *dev)  int skl_platform_unregister(struct device *dev)  { +	struct hdac_ext_bus *ebus = dev_get_drvdata(dev); +	struct skl *skl = ebus_to_skl(ebus); +	struct skl_module_deferred_bind *modules, *tmp; + +	if (!list_empty(&skl->bind_list)) { +		list_for_each_entry_safe(modules, tmp, &skl->bind_list, node) { +			list_del(&modules->node); +			kfree(modules); +		} +	} +  	snd_soc_unregister_component(dev);  	snd_soc_unregister_platform(dev);  	return 0;  | 
