コード例 #1
0
ファイル: snd_ps3.c プロジェクト: CSCLOG/beaglebone
/*
 * wait for all dma is done.
 * NOTE: caller should reset card->running before call.
 *       If not, the interrupt handler will re-start DMA,
 *       then DMA is never stopped.
 */
static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card)
{
	int stop_forced;
	/*
	 * wait for the last dma is done
	 */

	/*
	 * expected maximum DMA done time is 5.7ms + something (DMA itself).
	 * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next
	 * DMA kick event would occur.
	 */
	stop_forced = snd_ps3_verify_dma_stop(card, 700, 1);

	/*
	 * clear outstanding interrupts.
	 */
	update_reg(PS3_AUDIO_INTR_0, 0);
	update_reg(PS3_AUDIO_AX_IS, 0);

	/*
	 *revert CLEAR bit since it will not reset automatically after DMA stop
	 */
	if (stop_forced)
		update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0);
	/* ensure the hardware sees changes */
	wmb();
}
コード例 #2
0
ファイル: snd_ps3.c プロジェクト: CSCLOG/beaglebone
/*
 * av setting
 * NOTE: calling this function may generate audio interrupt.
 */
static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
{
	int ret, retries, i;
	pr_debug("%s: start\n", __func__);

	ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
				  card->avs.avs_audio_rate,
				  card->avs.avs_audio_width,
				  card->avs.avs_audio_format,
				  card->avs.avs_audio_source);
	/*
	 * Reset the following unwanted settings:
	 */

	/* disable all 3wire buffers */
	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
			~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
			  PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
			  PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
			  PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
			0);
	wmb();	/* ensure the hardware sees the change */
	/* wait for actually stopped */
	retries = 1000;
	while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
		(PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
		 PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
		 PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
		 PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
	       --retries) {
		udelay(1);
	}

	/* reset buffer pointer */
	for (i = 0; i < 4; i++) {
		update_reg(PS3_AUDIO_AO_3WCTRL(i),
			   PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
		udelay(10);
	}
	wmb(); /* ensure the hardware actually start resetting */

	/* enable 3wire#0 buffer */
	update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0));


	/* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */
	update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
			~PS3_AUDIO_AO_3WCTRL_ASODF,
			PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
	update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
			~PS3_AUDIO_AO_SPDCTRL_SPODF,
			PS3_AUDIO_AO_SPDCTRL_SPODF_LSB);
	/* ensure all the setting above is written back to register */
	wmb();
	/* avsetting driver altered AX_IE, caller must reset it if you want */
	pr_debug("%s: end\n", __func__);
	return ret;
}
コード例 #3
0
static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
{
	int ret, retries, i;
	pr_debug("%s: start\n", __func__);

	ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
				  card->avs.avs_audio_rate,
				  card->avs.avs_audio_width,
				  card->avs.avs_audio_format,
				  card->avs.avs_audio_source);

	
	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
			~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
			  PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
			  PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
			  PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
			0);
	wmb();	
	
	retries = 1000;
	while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
		(PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
		 PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
		 PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
		 PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
	       --retries) {
		udelay(1);
	}

	
	for (i = 0; i < 4; i++) {
		update_reg(PS3_AUDIO_AO_3WCTRL(i),
			   PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
		udelay(10);
	}
	wmb(); 

	
	update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0));


	
	update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
			~PS3_AUDIO_AO_3WCTRL_ASODF,
			PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
	update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
			~PS3_AUDIO_AO_SPDCTRL_SPODF,
			PS3_AUDIO_AO_SPDCTRL_SPODF_LSB);
	/* ensure all the setting above is written back to register */
	wmb();
	
	pr_debug("%s: end\n", __func__);
	return ret;
}
コード例 #4
0
static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card)
{
	int stop_forced;

	stop_forced = snd_ps3_verify_dma_stop(card, 700, 1);

	update_reg(PS3_AUDIO_INTR_0, 0);
	update_reg(PS3_AUDIO_AX_IS, 0);

	if (stop_forced)
		update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0);
	
	wmb();
}
コード例 #5
0
static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
{

	uint32_t port_intr;
	int underflow_occured = 0;
	struct snd_ps3_card_info *card = dev_id;

	if (!card->running) {
		update_reg(PS3_AUDIO_AX_IS, 0);
		update_reg(PS3_AUDIO_INTR_0, 0);
		return IRQ_HANDLED;
	}

	port_intr = read_reg(PS3_AUDIO_AX_IS);
	if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0));
		if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
			write_reg(PS3_AUDIO_AX_IS, port_intr);
			underflow_occured = 1;
		}
		if (card->silent) {
			
			snd_ps3_program_dma(card,
				(underflow_occured) ?
				SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
				SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
			snd_ps3_kick_dma(card);
			card->silent--;
		} else {
			snd_ps3_program_dma(card,
				(underflow_occured) ?
				SND_PS3_DMA_FILLTYPE_FIRSTFILL :
				SND_PS3_DMA_FILLTYPE_RUNNING);
			snd_ps3_kick_dma(card);
			snd_pcm_period_elapsed(card->substream);
		}
	} else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0));
		snd_ps3_program_dma(card,
				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
		snd_ps3_kick_dma(card);
		snd_ps3_program_dma(card,
				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
		snd_ps3_kick_dma(card);
	}
	
	return IRQ_HANDLED;
};
コード例 #6
0
ファイル: snd_ps3.c プロジェクト: CSCLOG/beaglebone
static void snd_ps3_kick_dma(struct snd_ps3_card_info *card)
{

	update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST);
	/* ensure the hardware sees the change */
	wmb();
}
コード例 #7
0
ファイル: snd_ps3.c プロジェクト: CSCLOG/beaglebone
static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card,
				   int count, int force_stop)
{
	int dma_ch, done, retries, stop_forced = 0;
	uint32_t status;

	for (dma_ch = 0; dma_ch < 8; dma_ch++) {
		retries = count;
		do {
			status = read_reg(PS3_AUDIO_KICK(dma_ch)) &
				PS3_AUDIO_KICK_STATUS_MASK;
			switch (status) {
			case PS3_AUDIO_KICK_STATUS_DONE:
			case PS3_AUDIO_KICK_STATUS_NOTIFY:
			case PS3_AUDIO_KICK_STATUS_CLEAR:
			case PS3_AUDIO_KICK_STATUS_ERROR:
				done = 1;
				break;
			default:
				done = 0;
				udelay(10);
			}
		} while (!done && --retries);
		if (!retries && force_stop) {
			pr_info("%s: DMA ch %d is not stopped.",
				__func__, dma_ch);
			/* last resort. force to stop dma.
			 *  NOTE: this cause DMA done interrupts
			 */
			update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR);
			stop_forced = 1;
		}
	}
	return stop_forced;
}
コード例 #8
0
static void snd_ps3_kick_dma(struct snd_ps3_card_info *card)
{

	update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST);
	
	wmb();
}
コード例 #9
0
ファイル: snd_ps3.c プロジェクト: CSCLOG/beaglebone
static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
			       int cmd)
{
	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
	int ret = 0;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		/* clear outstanding interrupts  */
		update_reg(PS3_AUDIO_AX_IS, 0);

		spin_lock(&card->dma_lock);
		{
			card->running = 1;
		}
		spin_unlock(&card->dma_lock);

		snd_ps3_program_dma(card,
				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
		snd_ps3_kick_dma(card);
		while (read_reg(PS3_AUDIO_KICK(7)) &
		       PS3_AUDIO_KICK_STATUS_MASK) {
			udelay(1);
		}
		snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
		snd_ps3_kick_dma(card);
		break;

	case SNDRV_PCM_TRIGGER_STOP:
		spin_lock(&card->dma_lock);
		{
			card->running = 0;
		}
		spin_unlock(&card->dma_lock);
		snd_ps3_wait_for_dma_stop(card);
		break;
	default:
		break;

	}

	return ret;
};
コード例 #10
0
static void __devinit snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
{

	
	write_reg(PS3_AUDIO_INTR_EN_0, 0);

	
	update_mask_reg(PS3_AUDIO_AX_IC,
			PS3_AUDIO_AX_IC_AASOIMD_MASK,
			PS3_AUDIO_AX_IC_AASOIMD_EVERY4);

	
	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
			~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED |
			  PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED),
			0);
	update_reg(PS3_AUDIO_AO_3WMCTRL,
		   PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
}
コード例 #11
0
ファイル: snd_ps3.c プロジェクト: CSCLOG/beaglebone
static void __devinit snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
{
	/*
	 * avsetting driver seems to never change the followings
	 * so, init them here once
	 */

	/* no dma interrupt needed */
	write_reg(PS3_AUDIO_INTR_EN_0, 0);

	/* use every 4 buffer empty interrupt */
	update_mask_reg(PS3_AUDIO_AX_IC,
			PS3_AUDIO_AX_IC_AASOIMD_MASK,
			PS3_AUDIO_AX_IC_AASOIMD_EVERY4);

	/* enable 3wire clocks */
	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
			~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED |
			  PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED),
			0);
	update_reg(PS3_AUDIO_AO_3WMCTRL,
		   PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
}
コード例 #12
0
ファイル: snd_ps3.c プロジェクト: CSCLOG/beaglebone
/*
 * Interrupt handler
 */
static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
{

	uint32_t port_intr;
	int underflow_occured = 0;
	struct snd_ps3_card_info *card = dev_id;

	if (!card->running) {
		update_reg(PS3_AUDIO_AX_IS, 0);
		update_reg(PS3_AUDIO_INTR_0, 0);
		return IRQ_HANDLED;
	}

	port_intr = read_reg(PS3_AUDIO_AX_IS);
	/*
	 *serial buffer empty detected (every 4 times),
	 *program next dma and kick it
	 */
	if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0));
		if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
			write_reg(PS3_AUDIO_AX_IS, port_intr);
			underflow_occured = 1;
		}
		if (card->silent) {
			/* we are still in silent time */
			snd_ps3_program_dma(card,
				(underflow_occured) ?
				SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
				SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
			snd_ps3_kick_dma(card);
			card->silent--;
		} else {
			snd_ps3_program_dma(card,
				(underflow_occured) ?
				SND_PS3_DMA_FILLTYPE_FIRSTFILL :
				SND_PS3_DMA_FILLTYPE_RUNNING);
			snd_ps3_kick_dma(card);
			snd_pcm_period_elapsed(card->substream);
		}
	} else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0));
		/*
		 * serial out underflow, but buffer empty not detected.
		 * in this case, fill fifo with 0 to recover.  After
		 * filling dummy data, serial automatically start to
		 * consume them and then will generate normal buffer
		 * empty interrupts.
		 * If both buffer underflow and buffer empty are occurred,
		 * it is better to do nomal data transfer than empty one
		 */
		snd_ps3_program_dma(card,
				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
		snd_ps3_kick_dma(card);
		snd_ps3_program_dma(card,
				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
		snd_ps3_kick_dma(card);
	}
	/* clear interrupt cause */
	return IRQ_HANDLED;
};