示例#1
0
/*******************************************************************************
* mvAudioPlaybackStatusGet - Get Playback status parameters
*
* DESCRIPTION:
*
* INPUT:
*       ctrl: pointer to MV_AUDIO_PLAYBACK_STATUS structure
* OUTPUT:
*		ctrl: pointer to MV_AUDIO_PLAYBACK_STATUS structure
* RETURN:
*       None
*
*******************************************************************************/
MV_VOID mvAudioPlaybackStatusGet(int unit, MV_AUDIO_PLAYBACK_STATUS *status)
{
	status->muteI2S = ((MV_REG_READ(MV_AUDIO_PLAYBACK_CTRL_REG(unit))&APCR_PLAY_I2S_MUTE_MASK)?
						MV_TRUE:MV_FALSE);
	status->enableI2S = ((MV_REG_READ(MV_AUDIO_PLAYBACK_CTRL_REG(unit))&APCR_PLAY_I2S_ENABLE_MASK)?
						MV_TRUE:MV_FALSE);
	status->muteSPDIF = ((MV_REG_READ(MV_AUDIO_PLAYBACK_CTRL_REG(unit))&APCR_PLAY_SPDIF_MUTE_MASK)?
						MV_TRUE:MV_FALSE);
	status->enableSPDIF = ((MV_REG_READ(MV_AUDIO_PLAYBACK_CTRL_REG(unit))&APCR_PLAY_SPDIF_ENABLE_MASK)?
						MV_TRUE:MV_FALSE);
	status->pause = ((MV_REG_READ(MV_AUDIO_PLAYBACK_CTRL_REG(unit))&APCR_PLAY_PAUSE_MASK)?
						MV_TRUE:MV_FALSE);

}
示例#2
0
static int mv88fx_i2s_resume(struct snd_soc_dai *cpu_dai)
{
	struct mv88fx_snd_chip *chip = mv88fx_pcm_snd_chip[cpu_dai->id];

	mv88fx_snd_debug("");

	if (!cpu_dai->active)
		return 0;

	mv88fx_i2_hw_init(chip->port);

	spin_lock(&chip->reg_lock);

	mv88fx_snd_writel(chip->base,
		MV_AUDIO_INT_CAUSE_REG(chip->port), 0xffffffff);
	/* restore registers */
	mv88fx_snd_writel(chip->base,
		MV_AUDIO_RECORD_CTRL_REG(chip->port),
		mv88fx_i2s_info[chip->port].capture_cntrl_reg);
	mv88fx_snd_writel(chip->base,
		MV_AUDIO_PLAYBACK_CTRL_REG(chip->port),
		mv88fx_i2s_info[chip->port].playback_cntrl_reg);
	/* enable interrupts */
	mv88fx_snd_writel(chip->base,
		MV_AUDIO_INT_MASK_REG(chip->port),
		mv88fx_i2s_info[chip->port].irq_mask);

	spin_unlock(&chip->reg_lock);

	return 0;
}
示例#3
0
static int mv88fx_i2_hw_init(int port)
{
	mv88fx_snd_debug("");

	mv88fx_snd_writel(mv88fx_i2s_info[port].base,
		MV_AUDIO_INT_CAUSE_REG(port), 0xffffffff);
	mv88fx_snd_writel(mv88fx_i2s_info[port].base,
		MV_AUDIO_INT_MASK_REG(port), 0);
	mv88fx_snd_writel(mv88fx_i2s_info[port].base,
		MV_AUDIO_SPDIF_REC_INT_CAUSE_MASK_REG(port), 0);

//	if (MV_OK != mvAudioInit(mv88fx_i2s_info.port))
//		return -EIO;
	mvSysAudioInit(port);

	/* Disable all playback/recording */
	mv88fx_snd_bitreset(mv88fx_i2s_info[port].base,
		MV_AUDIO_PLAYBACK_CTRL_REG(port),
		(APCR_PLAY_I2S_ENABLE_MASK | APCR_PLAY_SPDIF_ENABLE_MASK));

	mv88fx_snd_bitreset(mv88fx_i2s_info[port].base,
		MV_AUDIO_RECORD_CTRL_REG(port),
		(ARCR_RECORD_SPDIF_EN_MASK | ARCR_RECORD_I2S_EN_MASK));

	if (MV_OK != mvSPDIFRecordTclockSet(port)) {
		mv88fx_snd_error("mvSPDIFRecordTclockSet failed");
		return -ENOMEM;
	}
	return 0;
}
示例#4
0
/*******************************************************************************
* mvAudioPlaybackControlGet - Get Playback general parameters
*
* DESCRIPTION:
*
* INPUT:
*       ctrl: pointer to MV_AUDIO_PLAYBACK_CTRL structure
* OUTPUT:
*		ctrl: pointer to MV_AUDIO_PLAYBACK_CTRL structure
* RETURN:
*       None
*
*******************************************************************************/
MV_VOID mvAudioPlaybackControlGet(int unit, MV_AUDIO_PLAYBACK_CTRL *ctrl)
{
	MV_U32 reg = MV_REG_READ(MV_AUDIO_PLAYBACK_CTRL_REG(unit));

	ctrl->burst = (reg & APCR_PLAY_BURST_SIZE_MASK) >> APCR_PLAY_BURST_SIZE_OFFS;
	ctrl->loopBack = (reg & APCR_LOOPBACK_MASK) >> APCR_LOOPBACK_OFFS;
	ctrl->monoMode = (reg & APCR_PLAY_MONO_MASK) >> APCR_PLAY_MONO_OFFS;

	ctrl->bufferPhyBase = MV_REG_READ(MV_AUDIO_PLAYBACK_BUFF_START_REG(unit));
	reg = MV_REG_READ(MV_AUDIO_PLAYBACK_BUFF_SIZE_REG(unit));
	ctrl->bufferSize = AUDIO_REG_TO_SIZE(reg);

   	ctrl->intByteCount = MV_REG_READ(MV_AUDIO_PLAYBACK_BYTE_CNTR_INT_REG(unit));
}
示例#5
0
static int mv88fx_i2s_suspend(struct snd_soc_dai *cpu_dai)
{
	struct mv88fx_snd_chip *chip = mv88fx_pcm_snd_chip[cpu_dai->id];

	mv88fx_snd_debug("");

	if (!cpu_dai->active)
		return 0;


	spin_lock(&chip->reg_lock);

	/* save registers */
	mv88fx_i2s_info[chip->port].irq_mask = mv88fx_snd_readl(chip->base,
			MV_AUDIO_INT_MASK_REG(chip->port));
	mv88fx_i2s_info[chip->port].capture_cntrl_reg = mv88fx_snd_readl(chip->base,
			MV_AUDIO_RECORD_CTRL_REG(chip->port));
	mv88fx_i2s_info[chip->port].playback_cntrl_reg = mv88fx_snd_readl(chip->base,
			MV_AUDIO_PLAYBACK_CTRL_REG(chip->port));

	/* clear all interrupts */
	mv88fx_snd_writel(chip->base,
		MV_AUDIO_INT_CAUSE_REG(chip->port), 0xffffffff);
	/* disable all interrupts */
	mv88fx_snd_writel(chip->base,
		MV_AUDIO_INT_MASK_REG(chip->port), 0);
	/* pause dma */
	mv88fx_snd_bitset(chip->base,
		MV_AUDIO_RECORD_CTRL_REG(chip->port), ARCR_RECORD_PAUSE_MASK);
	mv88fx_snd_bitset(chip->base,
		MV_AUDIO_PLAYBACK_CTRL_REG(chip->port), APCR_PLAY_PAUSE_MASK);


	spin_unlock(&chip->reg_lock);

	return 0;
}
示例#6
0
/*******************************************************************************
* mvAudioPlaybackControlSet - Set Playback general parameters
*
* DESCRIPTION:
*
* INPUT:
*       ctrl: pointer to MV_AUDIO_PLAYBACK_CTRL structure
* OUTPUT:
*		None
* RETURN:
*       MV_OK on success , MV_FAIL on fail
*
*******************************************************************************/
MV_STATUS mvAudioPlaybackControlSet(int unit, MV_AUDIO_PLAYBACK_CTRL *ctrl)
{
	MV_AUDIO_DEC_WIN audioWin;
	MV_CPU_DEC_WIN  cpuWin;
	MV_ADDR_WIN   bufAddrWin;
	MV_U32 target;
	MV_U32 reg;

	if (ctrl->monoMode >= AUDIO_PLAY_OTHER_MONO)
	{
		mvOsPrintf("mvAudioPlaybackControlSet: Error ,illegal monoMode %x\n",
				   ctrl->monoMode );

		return MV_FAIL;

	}

	if ((ctrl->burst != AUDIO_32BYTE_BURST) &&
		(ctrl->burst != AUDIO_128BYTE_BURST))
	{
		mvOsPrintf("mvAudioPlaybackControlSet: Error ,illegal burst %x\n",
				   ctrl->burst );

		return MV_FAIL;

	}

	if (ctrl->bufferPhyBase & (MV_AUDIO_BUFFER_MIN_ALIGN - 1))
	{
		mvOsPrintf("mvAudioPlaybackControlSet: Error ,bufferPhyBase is not"\
				   "\n aligned to 0x%x bytes\n",MV_AUDIO_BUFFER_MIN_ALIGN );

		return MV_FAIL;
	}

	if  ((ctrl->bufferSize <= audioBurstBytesNumGet(ctrl->burst))||
		 (ctrl->bufferSize & (audioBurstBytesNumGet(ctrl->burst) - 1))||
		 (ctrl->bufferSize > AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX))
		 )
	{
		mvOsPrintf("mvAudioPlaybackControlSet: Error, bufferSize smaller"\
				   "\nthan or not multiple of 0x%x bytes or larger than"\
				   "\n 0x%x",
				   audioBurstBytesNumGet(ctrl->burst),
				   AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX));

		return MV_FAIL;
	}


	reg = MV_REG_READ(MV_AUDIO_PLAYBACK_CTRL_REG(unit));
	reg &= ~(APCR_PLAY_BURST_SIZE_MASK|APCR_LOOPBACK_MASK|APCR_PLAY_MONO_MASK |
             APCR_PLAY_SAMPLE_SIZE_MASK);
	reg |= ctrl->burst << APCR_PLAY_BURST_SIZE_OFFS;
	reg |= ctrl->loopBack << APCR_LOOPBACK_OFFS;
	reg |= ctrl->monoMode << APCR_PLAY_MONO_OFFS;
    	reg |= ctrl->sampleSize << APCR_PLAY_SAMPLE_SIZE_OFFS;
	MV_REG_WRITE(MV_AUDIO_PLAYBACK_CTRL_REG(unit), reg);

	/* Get the details of the Playback address window*/
	if( mvAudioWinGet( MV_AUDIO_PLAYBACK_WIN_NUM, &audioWin ) != MV_OK )
	{
		mvOsPrintf("mvAudioPlaybackControlSet: Error calling mvAudioWinGet on win %d\n",
				   MV_AUDIO_PLAYBACK_WIN_NUM);
		return MV_FAIL;
	}

	bufAddrWin.baseHigh = 0;
	bufAddrWin.baseLow = ctrl->bufferPhyBase;
	bufAddrWin.size = ctrl->bufferSize;

	/* If Playback window is not enabled or buffer address is not within window boundries
	   then try to set a new value to the Playback window by
	   Geting the target of where the buffer exist, if the buffer is within the window
	   of the new target then set the Playback window to that target
	   else return Fail
    */

	if((audioWin.enable != MV_TRUE) ||
	  (MV_TRUE != ctrlWinWithinWinTest(&bufAddrWin, &audioWin.addrWin)))
	{
		/* Get the target of the buffer that user require*/
		target = mvCpuIfTargetOfBaseAddressGet(ctrl->bufferPhyBase);
		if (MAX_TARGETS == target)
		{
			mvOsPrintf("mvCpuIfTargetOfBaseAddressGet: Error calling mvAudioWinGet on address 0x%x\n",
					   ctrl->bufferPhyBase);
			return MV_FAIL;
		}

		/* Get the window details of this target*/
		if (MV_OK != mvCpuIfTargetWinGet(target, &cpuWin))
		{
			mvOsPrintf("mvAudioPlaybackControlSet: Error calling mvCpuIfTargetWinGet on target %d\n",
					   target);
			return MV_FAIL;

		}

		/* if the address window of the target is enabled and te user buffer is within
		   that target address window then set the palyback\recording window to the
		   target window

		*/
		if((cpuWin.enable == MV_TRUE) &&
		  (MV_TRUE == ctrlWinWithinWinTest(&bufAddrWin, &cpuWin.addrWin)))
		{
			audioWin.addrWin.baseHigh = cpuWin.addrWin.baseHigh;
			audioWin.addrWin.baseLow = cpuWin.addrWin.baseLow;
			audioWin.addrWin.size = cpuWin.addrWin.size;
			audioWin.enable = cpuWin.enable;
			audioWin.target = target;


			if( mvAudioWinSet( MV_AUDIO_PLAYBACK_WIN_NUM, &audioWin ) != MV_OK )
			{
				mvOsPrintf("mvAudioPlaybackControlSet: Error calling mvAudioWinGet on win %d\n",
						   MV_AUDIO_PLAYBACK_WIN_NUM);
				return MV_FAIL;
			}

		}
		else
		{
			mvOsPrintf("mvAudioPlaybackControlSet: Error buffer is not within a valid target\n");
			return MV_FAIL;

		}
	}
    	/* Set the interrupt byte count.                            */
    	reg = ctrl->intByteCount & APBCI_BYTE_COUNT_MASK;
    	MV_REG_WRITE(MV_AUDIO_PLAYBACK_BYTE_CNTR_INT_REG(unit), reg);

	MV_REG_WRITE(MV_AUDIO_PLAYBACK_BUFF_START_REG(unit), ctrl->bufferPhyBase);
	MV_REG_WRITE(MV_AUDIO_PLAYBACK_BUFF_SIZE_REG(unit),
				 AUDIO_SIZE_TO_REG(ctrl->bufferSize));

	return MV_OK;
}
示例#7
0
/*******************************************************************************
* mvAudioPlaybackControlSet - Set Playback general parameters
*
* DESCRIPTION:
*
* INPUT:
*       ctrl: pointer to MV_AUDIO_PLAYBACK_CTRL structure
* OUTPUT:
*		None
* RETURN:
*       MV_OK on success , MV_FAIL on fail
*
*******************************************************************************/
MV_STATUS mvAudioPlaybackControlSet(int unit, MV_AUDIO_PLAYBACK_CTRL *ctrl)
{
	MV_U32 reg;

	if (ctrl->monoMode >= AUDIO_PLAY_OTHER_MONO)
	{
		mvOsPrintf("mvAudioPlaybackControlSet: Error ,illegal monoMode %x\n",
				   ctrl->monoMode );

		return MV_FAIL;

	}

	if ((ctrl->burst != AUDIO_32BYTE_BURST) &&
		(ctrl->burst != AUDIO_128BYTE_BURST))
	{
		mvOsPrintf("mvAudioPlaybackControlSet: Error ,illegal burst %x\n",
				   ctrl->burst );

		return MV_FAIL;

	}

	if (ctrl->bufferPhyBase & (MV_AUDIO_BUFFER_MIN_ALIGN - 1))
	{
		mvOsPrintf("mvAudioPlaybackControlSet: Error ,bufferPhyBase is not"\
				   "\n aligned to 0x%x bytes\n",MV_AUDIO_BUFFER_MIN_ALIGN );

		return MV_FAIL;
	}

	if  ((ctrl->bufferSize <= audioBurstBytesNumGet(ctrl->burst))||
		 (ctrl->bufferSize & (audioBurstBytesNumGet(ctrl->burst) - 1))||
		 (ctrl->bufferSize > AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX))
		 )
	{
		mvOsPrintf("mvAudioPlaybackControlSet: Error, bufferSize smaller"\
				   "\nthan or not multiple of 0x%x bytes or larger than"\
				   "\n 0x%x",
				   audioBurstBytesNumGet(ctrl->burst),
				   AUDIO_REG_TO_SIZE(APBBCR_SIZE_MAX));

		return MV_FAIL;
	}


	reg = MV_REG_READ(MV_AUDIO_PLAYBACK_CTRL_REG(unit));
	reg &= ~(APCR_PLAY_BURST_SIZE_MASK|APCR_LOOPBACK_MASK|APCR_PLAY_MONO_MASK |
             APCR_PLAY_SAMPLE_SIZE_MASK | APCR_PLAY_PAUSE_MASK |
		APCR_PLAY_SPDIF_MUTE_MASK | APCR_PLAY_I2S_MUTE_MASK);
	reg |= ctrl->burst << APCR_PLAY_BURST_SIZE_OFFS;
	reg |= ctrl->loopBack << APCR_LOOPBACK_OFFS;
	reg |= ctrl->monoMode << APCR_PLAY_MONO_OFFS;
    	reg |= ctrl->sampleSize << APCR_PLAY_SAMPLE_SIZE_OFFS;
	MV_REG_WRITE(MV_AUDIO_PLAYBACK_CTRL_REG(unit), reg);

#ifndef MV_AUDIO_SKIP_WIN_DECODING
	if(mvAudioReplaceAddrWin(unit, MV_AUDIO_PLAYBACK_WIN_NUM, ctrl->bufferPhyBase,
				ctrl->bufferSize) != MV_OK)
	{
		mvOsPrintf("mvAudioRecordControlSet: Failed to replace address decoding window.\n");
		return MV_FAIL;
	}
#endif

    	/* Set the interrupt byte count.                            */
    	reg = ctrl->intByteCount & APBCI_BYTE_COUNT_MASK;
    	MV_REG_WRITE(MV_AUDIO_PLAYBACK_BYTE_CNTR_INT_REG(unit), reg);

	MV_REG_WRITE(MV_AUDIO_PLAYBACK_BUFF_START_REG(unit), ctrl->bufferPhyBase);
	MV_REG_WRITE(MV_AUDIO_PLAYBACK_BUFF_SIZE_REG(unit),
				 AUDIO_SIZE_TO_REG(ctrl->bufferSize));

	return MV_OK;
}
示例#8
0
static int mv88fx_i2s_snd_hw_playback_set(struct mv88fx_snd_chip *chip,
					  struct snd_pcm_substream *substream)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct mv88fx_snd_stream *audio_stream = runtime->private_data;
	MV_AUDIO_PLAYBACK_CTRL pcm_play_ctrl;
	MV_I2S_PLAYBACK_CTRL i2s_play_ctrl;
	MV_SPDIF_PLAYBACK_CTRL spdif_play_ctrl;
	MV_AUDIO_FREQ_DATA dco_ctrl;

	mv88fx_snd_debug("chip=%p chip->base=%p", chip, chip->base);

	memset(&pcm_play_ctrl, 0, sizeof(pcm_play_ctrl));
	memset(&dco_ctrl, 0, sizeof(dco_ctrl));
	memset(&i2s_play_ctrl, 0, sizeof(i2s_play_ctrl));
	memset(&spdif_play_ctrl, 0, sizeof(spdif_play_ctrl));

	dco_ctrl.offset = chip->dco_ctrl_offst;

	mv88fx_snd_debug("rate: %u	", runtime->rate);
	switch (runtime->rate) {
	case 44100:
		dco_ctrl.baseFreq = AUDIO_FREQ_44_1KH;
		break;
	case 48000:
		dco_ctrl.baseFreq = AUDIO_FREQ_48KH;
		break;
	case 96000:
		dco_ctrl.baseFreq = AUDIO_FREQ_96KH;
		break;
	default:
		mv88fx_snd_error("Requested rate %d is not supported",
				 runtime->rate);
		return -1;
	}

	pcm_play_ctrl.burst = (chip->burst == 128) ? AUDIO_128BYTE_BURST :
	    AUDIO_32BYTE_BURST;

	pcm_play_ctrl.loopBack = chip->loopback;

	if (mv88fx_pcm_is_stereo(runtime)) {
		pcm_play_ctrl.monoMode = AUDIO_PLAY_MONO_OFF;
	} else {

		switch (audio_stream->mono_mode) {
		case MONO_LEFT:
			pcm_play_ctrl.monoMode = AUDIO_PLAY_LEFT_MONO;
			break;
		case MONO_RIGHT:
			pcm_play_ctrl.monoMode = AUDIO_PLAY_RIGHT_MONO;
			break;
		case MONO_BOTH:
		default:
			pcm_play_ctrl.monoMode = AUDIO_PLAY_BOTH_MONO;
			break;
		}
	}

	if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
		pcm_play_ctrl.sampleSize = SAMPLE_16BIT;
		i2s_play_ctrl.sampleSize = SAMPLE_16BIT;
	} else if (runtime->format == SNDRV_PCM_FORMAT_S24_LE) {
		pcm_play_ctrl.sampleSize = SAMPLE_24BIT;
		i2s_play_ctrl.sampleSize = SAMPLE_24BIT;

	} else if (runtime->format == SNDRV_PCM_FORMAT_S32_LE) {
		pcm_play_ctrl.sampleSize = SAMPLE_32BIT;
		i2s_play_ctrl.sampleSize = SAMPLE_32BIT;
	} else {
		mv88fx_snd_error("Requested format %d is not supported",
				 runtime->format);
		return -1;
	}

	/* buffer and period sizes in frame */
	pcm_play_ctrl.bufferPhyBase = runtime->dma_addr;
	pcm_play_ctrl.bufferSize =
	    frames_to_bytes(runtime, runtime->buffer_size);
	pcm_play_ctrl.intByteCount =
	    frames_to_bytes(runtime, runtime->period_size);

	/* I2S playback streem stuff */
	/*i2s_play_ctrl.sampleSize = pcm_play_ctrl.sampleSize; */
	i2s_play_ctrl.justification = I2S_JUSTIFIED;
	i2s_play_ctrl.sendLastFrame = 0;

	spdif_play_ctrl.nonPcm = MV_FALSE;

	spdif_play_ctrl.validity = chip->ch_stat_valid;

	if (audio_stream->stat_mem) {
		spdif_play_ctrl.userBitsFromMemory = MV_TRUE;
		spdif_play_ctrl.validityFromMemory = MV_TRUE;
		spdif_play_ctrl.blockStartInternally = MV_FALSE;
	} else {
		spdif_play_ctrl.userBitsFromMemory = MV_FALSE;
		spdif_play_ctrl.validityFromMemory = MV_FALSE;
		spdif_play_ctrl.blockStartInternally = MV_TRUE;
	}

	mv88fx_snd_debug("");
	/* If this is non-PCM sound, mute I2S channel */
	spin_lock_irq(&chip->reg_lock);

	mv88fx_snd_debug("chip=%p chip->base=%p port=%d", chip, chip->base,
			 chip->port);

	if (!(mv88fx_snd_readl(chip->base,
		MV_AUDIO_PLAYBACK_CTRL_REG(chip->port)) &
		(APCR_PLAY_I2S_ENABLE_MASK | APCR_PLAY_SPDIF_ENABLE_MASK))) {

		mv88fx_snd_debug("");

		if (MV_OK != mvAudioDCOCtrlSet(chip->port, &dco_ctrl)) {
			mv88fx_snd_error
			    ("Failed to initialize DCO clock control.");
			return -1;
			mv88fx_snd_debug("");

		}
	}

	mv88fx_snd_debug("");

	if (audio_stream->clock_src == DCO_CLOCK)
		while ((mv88fx_snd_readl(chip->base,
			 MV_AUDIO_SPCR_DCO_STATUS_REG(chip->port)) &
			ASDSR_DCO_LOCK_MASK) == 0)
			;
	else if (audio_stream->clock_src == SPCR_CLOCK)
		while ((mv88fx_snd_readl(chip->base,
			 MV_AUDIO_SPCR_DCO_STATUS_REG(chip->port)) &
			ASDSR_SPCR_LOCK_MASK) == 0)
			;

	mv88fx_snd_debug("");

	if (MV_OK != mvAudioPlaybackControlSet(chip->port, &pcm_play_ctrl)) {
		mv88fx_snd_error("Failed to initialize PCM playback control.");
		return -1;
	}
	mv88fx_snd_debug("");

	if (MV_OK != mvI2SPlaybackCtrlSet(chip->port, &i2s_play_ctrl)) {
		mv88fx_snd_error("Failed to initialize I2S playback control.");
		return -1;
	}
	mv88fx_snd_debug("");

	mvSPDIFPlaybackCtrlSet(chip->port, &spdif_play_ctrl);

	mv88fx_snd_debug("");

	spin_unlock_irq(&chip->reg_lock);

	return 0;
}
示例#9
0
static int mv88fx_i2s_playback_trigger(struct snd_pcm_substream *substream,
				       int cmd)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct mv88fx_snd_stream *audio_stream = runtime->private_data;
	struct mv88fx_snd_chip *chip = mv88fx_pcm_get_chip(substream);
	int result = 0;

	mv88fx_snd_debug("substream=%p cmd=%d audio_stream=%p", substream, cmd,
			 audio_stream);

	spin_lock(&chip->reg_lock);
	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:

		mv88fx_snd_debug("");
		/* enable interrupt */
		mv88fx_snd_bitset(chip->base, MV_AUDIO_INT_MASK_REG(chip->port),
				  AICR_PLAY_BYTES_INT);

		/* make sure the dma in pause state */
		mv88fx_snd_bitset(chip->base,
			MV_AUDIO_PLAYBACK_CTRL_REG(chip->port),
			APCR_PLAY_PAUSE_MASK);

		/* enable dma */
		if ((audio_stream->dig_mode & I2S) && (chip->pcm_mode == PCM))
			mv88fx_snd_bitset(chip->base,
				MV_AUDIO_PLAYBACK_CTRL_REG(chip->port),
				APCR_PLAY_I2S_ENABLE_MASK);

		if (audio_stream->dig_mode & SPDIF)
			mv88fx_snd_bitset(chip->base,
				MV_AUDIO_PLAYBACK_CTRL_REG(chip->port),
				APCR_PLAY_SPDIF_ENABLE_MASK);

		/* start dma */
		mv88fx_snd_bitreset(chip->base,
				    MV_AUDIO_PLAYBACK_CTRL_REG(chip->port),
				    APCR_PLAY_PAUSE_MASK);

		break;
	case SNDRV_PCM_TRIGGER_STOP:

		mv88fx_snd_debug("");

		/* disable interrupt */
		mv88fx_snd_bitreset(chip->base,
			MV_AUDIO_INT_MASK_REG(chip->port),
			AICR_PLAY_BYTES_INT);

		/* make sure the dma in pause state */
		mv88fx_snd_bitset(chip->base,
			MV_AUDIO_PLAYBACK_CTRL_REG(chip->port),
			APCR_PLAY_PAUSE_MASK);

		/* always stop both I2S and SPDIF */
		mv88fx_snd_bitreset(chip->base,
				    MV_AUDIO_PLAYBACK_CTRL_REG(chip->port),
				    (APCR_PLAY_I2S_ENABLE_MASK |
				     APCR_PLAY_SPDIF_ENABLE_MASK));

		/* check if busy twice */
		while (mv88fx_snd_readl(chip->base,
			MV_AUDIO_PLAYBACK_CTRL_REG(chip->port)) &
		       APCR_PLAY_BUSY_MASK)
			;

		while (mv88fx_snd_readl(chip->base,
			MV_AUDIO_PLAYBACK_CTRL_REG(chip->port)) &
		       APCR_PLAY_BUSY_MASK)
			;

		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_SUSPEND:
		mv88fx_snd_debug("");

		mv88fx_snd_bitset(chip->base,
			MV_AUDIO_PLAYBACK_CTRL_REG(chip->port),
			APCR_PLAY_PAUSE_MASK);
		break;
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		mv88fx_snd_debug("");

		mv88fx_snd_bitreset(chip->base,
				    MV_AUDIO_PLAYBACK_CTRL_REG(chip->port),
				    APCR_PLAY_PAUSE_MASK);

		break;
	default:
		result = -EINVAL;
		break;
	}
	spin_unlock(&chip->reg_lock);
	mv88fx_snd_debug("result=%d", result);
	return result;
}