diff options
Diffstat (limited to 'sound/pci/hda/hda_intel.c')
| -rw-r--r-- | sound/pci/hda/hda_intel.c | 132 | 
1 files changed, 109 insertions, 23 deletions
| diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4cea6bb6fad..418bfc0eb0a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -415,6 +415,8 @@ struct azx_dev {  	unsigned int opened :1;  	unsigned int running :1;  	unsigned int irq_pending :1; +	unsigned int prepared:1; +	unsigned int locked:1;  	/*  	 * For VIA:  	 *  A flag to ensure DMA position is 0 @@ -426,8 +428,25 @@ struct azx_dev {  	struct timecounter  azx_tc;  	struct cyclecounter azx_cc; + +#ifdef CONFIG_SND_HDA_DSP_LOADER +	struct mutex dsp_mutex; +#endif  }; +/* DSP lock helpers */ +#ifdef CONFIG_SND_HDA_DSP_LOADER +#define dsp_lock_init(dev)	mutex_init(&(dev)->dsp_mutex) +#define dsp_lock(dev)		mutex_lock(&(dev)->dsp_mutex) +#define dsp_unlock(dev)		mutex_unlock(&(dev)->dsp_mutex) +#define dsp_is_locked(dev)	((dev)->locked) +#else +#define dsp_lock_init(dev)	do {} while (0) +#define dsp_lock(dev)		do {} while (0) +#define dsp_unlock(dev)		do {} while (0) +#define dsp_is_locked(dev)	0 +#endif +  /* CORB/RIRB */  struct azx_rb {  	u32 *buf;		/* CORB/RIRB buffer @@ -527,6 +546,10 @@ struct azx {  	/* card list (for power_save trigger) */  	struct list_head list; + +#ifdef CONFIG_SND_HDA_DSP_LOADER +	struct azx_dev saved_azx_dev; +#endif  };  #define CREATE_TRACE_POINTS @@ -1793,15 +1816,25 @@ azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)  		dev = chip->capture_index_offset;  		nums = chip->capture_streams;  	} -	for (i = 0; i < nums; i++, dev++) -		if (!chip->azx_dev[dev].opened) { -			res = &chip->azx_dev[dev]; -			if (res->assigned_key == key) -				break; +	for (i = 0; i < nums; i++, dev++) { +		struct azx_dev *azx_dev = &chip->azx_dev[dev]; +		dsp_lock(azx_dev); +		if (!azx_dev->opened && !dsp_is_locked(azx_dev)) { +			res = azx_dev; +			if (res->assigned_key == key) { +				res->opened = 1; +				res->assigned_key = key; +				dsp_unlock(azx_dev); +				return azx_dev; +			}  		} +		dsp_unlock(azx_dev); +	}  	if (res) { +		dsp_lock(res);  		res->opened = 1;  		res->assigned_key = key; +		dsp_unlock(res);  	}  	return res;  } @@ -2009,6 +2042,12 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,  	struct azx_dev *azx_dev = get_azx_dev(substream);  	int ret; +	dsp_lock(azx_dev); +	if (dsp_is_locked(azx_dev)) { +		ret = -EBUSY; +		goto unlock; +	} +  	mark_runtime_wc(chip, azx_dev, substream, false);  	azx_dev->bufsize = 0;  	azx_dev->period_bytes = 0; @@ -2016,8 +2055,10 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,  	ret = snd_pcm_lib_malloc_pages(substream,  					params_buffer_bytes(hw_params));  	if (ret < 0) -		return ret; +		goto unlock;  	mark_runtime_wc(chip, azx_dev, substream, true); + unlock: +	dsp_unlock(azx_dev);  	return ret;  } @@ -2029,16 +2070,21 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)  	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];  	/* reset BDL address */ -	azx_sd_writel(azx_dev, SD_BDLPL, 0); -	azx_sd_writel(azx_dev, SD_BDLPU, 0); -	azx_sd_writel(azx_dev, SD_CTL, 0); -	azx_dev->bufsize = 0; -	azx_dev->period_bytes = 0; -	azx_dev->format_val = 0; +	dsp_lock(azx_dev); +	if (!dsp_is_locked(azx_dev)) { +		azx_sd_writel(azx_dev, SD_BDLPL, 0); +		azx_sd_writel(azx_dev, SD_BDLPU, 0); +		azx_sd_writel(azx_dev, SD_CTL, 0); +		azx_dev->bufsize = 0; +		azx_dev->period_bytes = 0; +		azx_dev->format_val = 0; +	}  	snd_hda_codec_cleanup(apcm->codec, hinfo, substream);  	mark_runtime_wc(chip, azx_dev, substream, false); +	azx_dev->prepared = 0; +	dsp_unlock(azx_dev);  	return snd_pcm_lib_free_pages(substream);  } @@ -2055,6 +2101,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)  		snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);  	unsigned short ctls = spdif ? spdif->ctls : 0; +	dsp_lock(azx_dev); +	if (dsp_is_locked(azx_dev)) { +		err = -EBUSY; +		goto unlock; +	} +  	azx_stream_reset(chip, azx_dev);  	format_val = snd_hda_calc_stream_format(runtime->rate,  						runtime->channels, @@ -2065,7 +2117,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)  		snd_printk(KERN_ERR SFX  			   "%s: invalid format_val, rate=%d, ch=%d, format=%d\n",  			   pci_name(chip->pci), runtime->rate, runtime->channels, runtime->format); -		return -EINVAL; +		err = -EINVAL; +		goto unlock;  	}  	bufsize = snd_pcm_lib_buffer_bytes(substream); @@ -2084,7 +2137,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)  		azx_dev->no_period_wakeup = runtime->no_period_wakeup;  		err = azx_setup_periods(chip, substream, azx_dev);  		if (err < 0) -			return err; +			goto unlock;  	}  	/* wallclk has 24Mhz clock source */ @@ -2101,8 +2154,14 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)  	if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) &&  	    stream_tag > chip->capture_streams)  		stream_tag -= chip->capture_streams; -	return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag, +	err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,  				     azx_dev->format_val, substream); + + unlock: +	if (!err) +		azx_dev->prepared = 1; +	dsp_unlock(azx_dev); +	return err;  }  static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) @@ -2117,6 +2176,9 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)  	azx_dev = get_azx_dev(substream);  	trace_azx_pcm_trigger(chip, azx_dev, cmd); +	if (dsp_is_locked(azx_dev) || !azx_dev->prepared) +		return -EPIPE; +  	switch (cmd) {  	case SNDRV_PCM_TRIGGER_START:  		rstart = 1; @@ -2621,17 +2683,27 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,  	struct azx_dev *azx_dev;  	int err; -	if (snd_hda_lock_devices(bus)) -		return -EBUSY; +	azx_dev = azx_get_dsp_loader_dev(chip); + +	dsp_lock(azx_dev); +	spin_lock_irq(&chip->reg_lock); +	if (azx_dev->running || azx_dev->locked) { +		spin_unlock_irq(&chip->reg_lock); +		err = -EBUSY; +		goto unlock; +	} +	azx_dev->prepared = 0; +	chip->saved_azx_dev = *azx_dev; +	azx_dev->locked = 1; +	spin_unlock_irq(&chip->reg_lock);  	err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG,  				  snd_dma_pci_data(chip->pci),  				  byte_size, bufp);  	if (err < 0) -		goto unlock; +		goto err_alloc;  	mark_pages_wc(chip, bufp, true); -	azx_dev = azx_get_dsp_loader_dev(chip);  	azx_dev->bufsize = byte_size;  	azx_dev->period_bytes = byte_size;  	azx_dev->format_val = format; @@ -2649,13 +2721,20 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,  		goto error;  	azx_setup_controller(chip, azx_dev); +	dsp_unlock(azx_dev);  	return azx_dev->stream_tag;   error:  	mark_pages_wc(chip, bufp, false);  	snd_dma_free_pages(bufp); -unlock: -	snd_hda_unlock_devices(bus); + err_alloc: +	spin_lock_irq(&chip->reg_lock); +	if (azx_dev->opened) +		*azx_dev = chip->saved_azx_dev; +	azx_dev->locked = 0; +	spin_unlock_irq(&chip->reg_lock); + unlock: +	dsp_unlock(azx_dev);  	return err;  } @@ -2677,9 +2756,10 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus,  	struct azx *chip = bus->private_data;  	struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); -	if (!dmab->area) +	if (!dmab->area || !azx_dev->locked)  		return; +	dsp_lock(azx_dev);  	/* reset BDL address */  	azx_sd_writel(azx_dev, SD_BDLPL, 0);  	azx_sd_writel(azx_dev, SD_BDLPU, 0); @@ -2692,7 +2772,12 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus,  	snd_dma_free_pages(dmab);  	dmab->area = NULL; -	snd_hda_unlock_devices(bus); +	spin_lock_irq(&chip->reg_lock); +	if (azx_dev->opened) +		*azx_dev = chip->saved_azx_dev; +	azx_dev->locked = 0; +	spin_unlock_irq(&chip->reg_lock); +	dsp_unlock(azx_dev);  }  #endif /* CONFIG_SND_HDA_DSP_LOADER */ @@ -3481,6 +3566,7 @@ static int azx_first_init(struct azx *chip)  	}  	for (i = 0; i < chip->num_streams; i++) { +		dsp_lock_init(&chip->azx_dev[i]);  		/* allocate memory for the BDL for each stream */  		err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,  					  snd_dma_pci_data(chip->pci), |