diff options
| author | Knut Petersen <Knut_Petersen@t-online.de> | 2013-08-13 21:18:12 +0200 | 
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2013-08-14 17:02:36 +0200 | 
| commit | b892ca1c9fe71e829e7b9ed79b8398649de259d7 (patch) | |
| tree | ecdb5c181dbcbff9df362b64d3b9874e783ed5e1 /sound | |
| parent | e80c60f3cbe76fa95029abc53b1a29172b51b96a (diff) | |
| download | linux-b892ca1c9fe71e829e7b9ed79b8398649de259d7.tar.bz2 | |
ALSA: rme96: Add pcm stream synchronization
The hardware does support synchronized start/pause/stop of pcm streams,
so there is no reason not to add that feature after more than ten years.
Some minor coding style / white space fixes in the surroundings of the
changes.
Signed-off-by: Knut Petersen <Knut_Petersen@t-online.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
| -rw-r--r-- | sound/pci/rme96.c | 185 | 
1 files changed, 110 insertions, 75 deletions
| diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 2a8ad9d1a2ae..4e9a5563eeca 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -198,6 +198,31 @@ MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard.");  #define RME96_AD1852_VOL_BITS 14  #define RME96_AD1855_VOL_BITS 10 +/* Defines for snd_rme96_trigger */ +#define RME96_TB_START_PLAYBACK 1 +#define RME96_TB_START_CAPTURE 2 +#define RME96_TB_STOP_PLAYBACK 4 +#define RME96_TB_STOP_CAPTURE 8 +#define RME96_TB_RESET_PLAYPOS 16 +#define RME96_TB_RESET_CAPTUREPOS 32 +#define RME96_TB_CLEAR_PLAYBACK_IRQ 64 +#define RME96_TB_CLEAR_CAPTURE_IRQ 128 +#define RME96_RESUME_PLAYBACK	(RME96_TB_START_PLAYBACK) +#define RME96_RESUME_CAPTURE	(RME96_TB_START_CAPTURE) +#define RME96_RESUME_BOTH	(RME96_RESUME_PLAYBACK \ +				| RME96_RESUME_CAPTURE) +#define RME96_START_PLAYBACK	(RME96_TB_START_PLAYBACK \ +				| RME96_TB_RESET_PLAYPOS) +#define RME96_START_CAPTURE	(RME96_TB_START_CAPTURE \ +				| RME96_TB_RESET_CAPTUREPOS) +#define RME96_START_BOTH	(RME96_START_PLAYBACK \ +				| RME96_START_CAPTURE) +#define RME96_STOP_PLAYBACK	(RME96_TB_STOP_PLAYBACK \ +				| RME96_TB_CLEAR_PLAYBACK_IRQ) +#define RME96_STOP_CAPTURE	(RME96_TB_STOP_CAPTURE \ +				| RME96_TB_CLEAR_CAPTURE_IRQ) +#define RME96_STOP_BOTH		(RME96_STOP_PLAYBACK \ +				| RME96_STOP_CAPTURE)  struct rme96 {  	spinlock_t    lock; @@ -344,6 +369,7 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info =  {  	.info =		     (SNDRV_PCM_INFO_MMAP_IOMEM |  			      SNDRV_PCM_INFO_MMAP_VALID | +			      SNDRV_PCM_INFO_SYNC_START |  			      SNDRV_PCM_INFO_INTERLEAVED |  			      SNDRV_PCM_INFO_PAUSE),  	.formats =	     (SNDRV_PCM_FMTBIT_S16_LE | @@ -373,6 +399,7 @@ static struct snd_pcm_hardware snd_rme96_capture_spdif_info =  {  	.info =		     (SNDRV_PCM_INFO_MMAP_IOMEM |  			      SNDRV_PCM_INFO_MMAP_VALID | +			      SNDRV_PCM_INFO_SYNC_START |  			      SNDRV_PCM_INFO_INTERLEAVED |  			      SNDRV_PCM_INFO_PAUSE),  	.formats =	     (SNDRV_PCM_FMTBIT_S16_LE | @@ -402,6 +429,7 @@ static struct snd_pcm_hardware snd_rme96_playback_adat_info =  {  	.info =		     (SNDRV_PCM_INFO_MMAP_IOMEM |  			      SNDRV_PCM_INFO_MMAP_VALID | +			      SNDRV_PCM_INFO_SYNC_START |  			      SNDRV_PCM_INFO_INTERLEAVED |  			      SNDRV_PCM_INFO_PAUSE),  	.formats =	     (SNDRV_PCM_FMTBIT_S16_LE | @@ -427,6 +455,7 @@ static struct snd_pcm_hardware snd_rme96_capture_adat_info =  {  	.info =		     (SNDRV_PCM_INFO_MMAP_IOMEM |  			      SNDRV_PCM_INFO_MMAP_VALID | +			      SNDRV_PCM_INFO_SYNC_START |  			      SNDRV_PCM_INFO_INTERLEAVED |  			      SNDRV_PCM_INFO_PAUSE),  	.formats =	     (SNDRV_PCM_FMTBIT_S16_LE | @@ -1045,54 +1074,35 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream,  }  static void -snd_rme96_playback_start(struct rme96 *rme96, -			 int from_pause) +snd_rme96_trigger(struct rme96 *rme96, +		  int op)  { -	if (!from_pause) { +	if (op & RME96_TB_RESET_PLAYPOS)  		writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); -	} - -	rme96->wcreg |= RME96_WCR_START; -	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); -} - -static void -snd_rme96_capture_start(struct rme96 *rme96, -			int from_pause) -{ -	if (!from_pause) { +	if (op & RME96_TB_RESET_CAPTUREPOS)  		writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); -	} - -	rme96->wcreg |= RME96_WCR_START_2; +	if (op & RME96_TB_CLEAR_PLAYBACK_IRQ) { +		rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); +		if (rme96->rcreg & RME96_RCR_IRQ) +			writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); +	} +	if (op & RME96_TB_CLEAR_CAPTURE_IRQ) { +		rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); +		if (rme96->rcreg & RME96_RCR_IRQ_2) +			writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); +	} +	if (op & RME96_TB_START_PLAYBACK) +		rme96->wcreg |= RME96_WCR_START; +	if (op & RME96_TB_STOP_PLAYBACK) +		rme96->wcreg &= ~RME96_WCR_START; +	if (op & RME96_TB_START_CAPTURE) +		rme96->wcreg |= RME96_WCR_START_2; +	if (op & RME96_TB_STOP_CAPTURE) +		rme96->wcreg &= ~RME96_WCR_START_2;  	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);  } -static void -snd_rme96_playback_stop(struct rme96 *rme96) -{ -	/* -	 * Check if there is an unconfirmed IRQ, if so confirm it, or else -	 * the hardware will not stop generating interrupts -	 */ -	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); -	if (rme96->rcreg & RME96_RCR_IRQ) { -		writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); -	}	 -	rme96->wcreg &= ~RME96_WCR_START; -	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); -} -static void -snd_rme96_capture_stop(struct rme96 *rme96) -{ -	rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); -	if (rme96->rcreg & RME96_RCR_IRQ_2) { -		writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); -	}	 -	rme96->wcreg &= ~RME96_WCR_START_2; -	writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); -}  static irqreturn_t  snd_rme96_interrupt(int irq, @@ -1155,6 +1165,7 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream)  	struct rme96 *rme96 = snd_pcm_substream_chip(substream);  	struct snd_pcm_runtime *runtime = substream->runtime; +	snd_pcm_set_sync(substream);  	spin_lock_irq(&rme96->lock);	          if (rme96->playback_substream != NULL) {  		spin_unlock_irq(&rme96->lock); @@ -1191,6 +1202,7 @@ snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream)  	struct rme96 *rme96 = snd_pcm_substream_chip(substream);  	struct snd_pcm_runtime *runtime = substream->runtime; +	snd_pcm_set_sync(substream);  	runtime->hw = snd_rme96_capture_spdif_info;          if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&              (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) @@ -1222,6 +1234,7 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream)  	struct rme96 *rme96 = snd_pcm_substream_chip(substream);  	struct snd_pcm_runtime *runtime = substream->runtime;         +	snd_pcm_set_sync(substream);  	spin_lock_irq(&rme96->lock);	          if (rme96->playback_substream != NULL) {  		spin_unlock_irq(&rme96->lock); @@ -1253,6 +1266,7 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream)  	struct rme96 *rme96 = snd_pcm_substream_chip(substream);  	struct snd_pcm_runtime *runtime = substream->runtime; +	snd_pcm_set_sync(substream);  	runtime->hw = snd_rme96_capture_adat_info;          if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {                  /* makes no sense to use analog input. Note that analog @@ -1288,7 +1302,7 @@ snd_rme96_playback_close(struct snd_pcm_substream *substream)  	spin_lock_irq(&rme96->lock);	  	if (RME96_ISPLAYING(rme96)) { -		snd_rme96_playback_stop(rme96); +		snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);  	}  	rme96->playback_substream = NULL;  	rme96->playback_periodsize = 0; @@ -1309,7 +1323,7 @@ snd_rme96_capture_close(struct snd_pcm_substream *substream)  	spin_lock_irq(&rme96->lock);	  	if (RME96_ISRECORDING(rme96)) { -		snd_rme96_capture_stop(rme96); +		snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);  	}  	rme96->capture_substream = NULL;  	rme96->capture_periodsize = 0; @@ -1324,7 +1338,7 @@ snd_rme96_playback_prepare(struct snd_pcm_substream *substream)  	spin_lock_irq(&rme96->lock);	  	if (RME96_ISPLAYING(rme96)) { -		snd_rme96_playback_stop(rme96); +		snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);  	}  	writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);  	spin_unlock_irq(&rme96->lock); @@ -1338,7 +1352,7 @@ snd_rme96_capture_prepare(struct snd_pcm_substream *substream)  	spin_lock_irq(&rme96->lock);	  	if (RME96_ISRECORDING(rme96)) { -		snd_rme96_capture_stop(rme96); +		snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);  	}  	writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);  	spin_unlock_irq(&rme96->lock); @@ -1350,41 +1364,53 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream,  			   int cmd)  {  	struct rme96 *rme96 = snd_pcm_substream_chip(substream); +	struct snd_pcm_substream *s; +	bool sync; + +	snd_pcm_group_for_each_entry(s, substream) { +		if (snd_pcm_substream_chip(s) == rme96) +			snd_pcm_trigger_done(s, substream); +	} + +	sync = (rme96->playback_substream && rme96->capture_substream) && +	       (rme96->playback_substream->group == +		rme96->capture_substream->group);  	switch (cmd) {  	case SNDRV_PCM_TRIGGER_START:  		if (!RME96_ISPLAYING(rme96)) { -			if (substream != rme96->playback_substream) { +			if (substream != rme96->playback_substream)  				return -EBUSY; -			} -			snd_rme96_playback_start(rme96, 0); +			snd_rme96_trigger(rme96, sync ? RME96_START_BOTH +						 : RME96_START_PLAYBACK);  		}  		break;  	case SNDRV_PCM_TRIGGER_STOP:  		if (RME96_ISPLAYING(rme96)) { -			if (substream != rme96->playback_substream) { +			if (substream != rme96->playback_substream)  				return -EBUSY; -			} -			snd_rme96_playback_stop(rme96); +			snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH +						 :  RME96_STOP_PLAYBACK);  		}  		break;  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -		if (RME96_ISPLAYING(rme96)) { -			snd_rme96_playback_stop(rme96); -		} +		if (RME96_ISPLAYING(rme96)) +			snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH +						 : RME96_STOP_PLAYBACK);  		break;  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -		if (!RME96_ISPLAYING(rme96)) { -			snd_rme96_playback_start(rme96, 1); -		} +		if (!RME96_ISPLAYING(rme96)) +			snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH +						 : RME96_RESUME_PLAYBACK);  		break; -		 +  	default:  		return -EINVAL;  	} +  	return 0;  } @@ -1393,38 +1419,49 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream,  			  int cmd)  {  	struct rme96 *rme96 = snd_pcm_substream_chip(substream); +	struct snd_pcm_substream *s; +	bool sync; + +	snd_pcm_group_for_each_entry(s, substream) { +		if (snd_pcm_substream_chip(s) == rme96) +			snd_pcm_trigger_done(s, substream); +	} + +	sync = (rme96->playback_substream && rme96->capture_substream) && +	       (rme96->playback_substream->group == +		rme96->capture_substream->group);  	switch (cmd) {  	case SNDRV_PCM_TRIGGER_START:  		if (!RME96_ISRECORDING(rme96)) { -			if (substream != rme96->capture_substream) { +			if (substream != rme96->capture_substream)  				return -EBUSY; -			} -			snd_rme96_capture_start(rme96, 0); +			snd_rme96_trigger(rme96, sync ? RME96_START_BOTH +						 : RME96_START_CAPTURE);  		}  		break;  	case SNDRV_PCM_TRIGGER_STOP:  		if (RME96_ISRECORDING(rme96)) { -			if (substream != rme96->capture_substream) { +			if (substream != rme96->capture_substream)  				return -EBUSY; -			} -			snd_rme96_capture_stop(rme96); +			snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH +						 : RME96_STOP_CAPTURE);  		}  		break;  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -		if (RME96_ISRECORDING(rme96)) { -			snd_rme96_capture_stop(rme96); -		} +		if (RME96_ISRECORDING(rme96)) +			snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH +						 : RME96_STOP_CAPTURE);  		break;  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -		if (!RME96_ISRECORDING(rme96)) { -			snd_rme96_capture_start(rme96, 1); -		} +		if (!RME96_ISRECORDING(rme96)) +			snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH +						 : RME96_RESUME_CAPTURE);  		break; -		 +  	default:  		return -EINVAL;  	} @@ -1505,8 +1542,7 @@ snd_rme96_free(void *private_data)  	        return;  	}  	if (rme96->irq >= 0) { -		snd_rme96_playback_stop(rme96); -		snd_rme96_capture_stop(rme96); +		snd_rme96_trigger(rme96, RME96_STOP_BOTH);  		rme96->areg &= ~RME96_AR_DAC_EN;  		writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);  		free_irq(rme96->irq, (void *)rme96); @@ -1606,8 +1642,7 @@ snd_rme96_create(struct rme96 *rme96)  	rme96->capture_periodsize = 0;  	/* make sure playback/capture is stopped, if by some reason active */ -	snd_rme96_playback_stop(rme96); -	snd_rme96_capture_stop(rme96); +	snd_rme96_trigger(rme96, RME96_STOP_BOTH);  	/* set default values in registers */  	rme96->wcreg = |