/**
 * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation
 * @substream: PCM substream
 * @cmd: Trigger command
 *
 * Returns 0 on success, a negative error code otherwise.
 *
 * This function can be used as the PCM trigger callback for dmaengine based PCM
 * driver implementations.
 */
int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
	int ret;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		ret = dmaengine_pcm_prepare_and_submit(substream);
		if (ret)
			return ret;
		dma_async_issue_pending(prtd->dma_chan);
		break;
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		dmaengine_resume(prtd->dma_chan);
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		dmaengine_pause(prtd->dma_chan);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
		dmaengine_terminate_all(prtd->dma_chan);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}
Exemplo n.º 2
0
static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport)
{
	struct uart_port *port = &sirfport->port;
	struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
	struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
	struct circ_buf *xmit = &port->state->xmit;
	unsigned long tran_size;
	unsigned long tran_start;
	unsigned long pio_tx_size;

	tran_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
	tran_start = (unsigned long)(xmit->buf + xmit->tail);
	if (uart_circ_empty(xmit) || uart_tx_stopped(port) ||
			!tran_size)
		return;
	if (sirfport->tx_dma_state == TX_DMA_PAUSE) {
		dmaengine_resume(sirfport->tx_dma_chan);
		return;
	}
	if (sirfport->tx_dma_state == TX_DMA_RUNNING)
		return;
	if (!sirfport->is_atlas7)
		wr_regl(port, ureg->sirfsoc_int_en_reg,
				rd_regl(port, ureg->sirfsoc_int_en_reg)&
				~(uint_en->sirfsoc_txfifo_empty_en));
	else
		wr_regl(port, SIRFUART_INT_EN_CLR,
				uint_en->sirfsoc_txfifo_empty_en);
	/*
	 * DMA requires buffer address and buffer length are both aligned with
	 * 4 bytes, so we use PIO for
	 * 1. if address is not aligned with 4bytes, use PIO for the first 1~3
	 * bytes, and move to DMA for the left part aligned with 4bytes
	 * 2. if buffer length is not aligned with 4bytes, use DMA for aligned
	 * part first, move to PIO for the left 1~3 bytes
	 */
	if (tran_size < 4 || BYTES_TO_ALIGN(tran_start)) {
		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP);
		wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl,
			rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)|
			SIRFUART_IO_MODE);
		if (BYTES_TO_ALIGN(tran_start)) {
			pio_tx_size = sirfsoc_uart_pio_tx_chars(sirfport,
				BYTES_TO_ALIGN(tran_start));
			tran_size -= pio_tx_size;
		}
		if (tran_size < 4)
			sirfsoc_uart_pio_tx_chars(sirfport, tran_size);
		if (!sirfport->is_atlas7)
			wr_regl(port, ureg->sirfsoc_int_en_reg,
				rd_regl(port, ureg->sirfsoc_int_en_reg)|
				uint_en->sirfsoc_txfifo_empty_en);
		else
			wr_regl(port, ureg->sirfsoc_int_en_reg,
				uint_en->sirfsoc_txfifo_empty_en);
		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
	} else {
		/* tx transfer mode switch into dma mode */
		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP);
		wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl,
			rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)&
			~SIRFUART_IO_MODE);
		wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
		tran_size &= ~(0x3);

		sirfport->tx_dma_addr = dma_map_single(port->dev,
			xmit->buf + xmit->tail,
			tran_size, DMA_TO_DEVICE);
		sirfport->tx_dma_desc = dmaengine_prep_slave_single(
			sirfport->tx_dma_chan, sirfport->tx_dma_addr,
			tran_size, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
		if (!sirfport->tx_dma_desc) {
			dev_err(port->dev, "DMA prep slave single fail\n");
			return;
		}
		sirfport->tx_dma_desc->callback =
			sirfsoc_uart_tx_dma_complete_callback;
		sirfport->tx_dma_desc->callback_param = (void *)sirfport;
		sirfport->transfer_size = tran_size;

		dmaengine_submit(sirfport->tx_dma_desc);
		dma_async_issue_pending(sirfport->tx_dma_chan);
		sirfport->tx_dma_state = TX_DMA_RUNNING;
	}
}
Exemplo n.º 3
0
static enum hrtimer_restart
	sirfsoc_uart_rx_dma_hrtimer_callback(struct hrtimer *hrt)
{
	struct sirfsoc_uart_port *sirfport;
	struct uart_port *port;
	int count, inserted;
	struct dma_tx_state tx_state;
	struct tty_struct *tty;
	struct sirfsoc_register *ureg;
	struct circ_buf *xmit;
	struct sirfsoc_fifo_status *ufifo_st;
	int max_pio_cnt;

	sirfport = container_of(hrt, struct sirfsoc_uart_port, hrt);
	port = &sirfport->port;
	inserted = 0;
	tty = port->state->port.tty;
	ureg = &sirfport->uart_reg->uart_reg;
	xmit = &sirfport->rx_dma_items.xmit;
	ufifo_st = &sirfport->uart_reg->fifo_status;

	dmaengine_tx_status(sirfport->rx_dma_chan,
			sirfport->rx_dma_items.cookie, &tx_state);
	if (SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue !=
		sirfport->rx_last_pos) {
		xmit->head = SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
		sirfport->rx_last_pos = xmit->head;
		sirfport->pio_fetch_cnt = 0;
	}
	count = CIRC_CNT_TO_END(xmit->head, xmit->tail,
			SIRFSOC_RX_DMA_BUF_SIZE);
	while (count > 0) {
		inserted = tty_insert_flip_string(tty->port,
			(const unsigned char *)&xmit->buf[xmit->tail], count);
		if (!inserted)
			goto next_hrt;
		port->icount.rx += inserted;
		xmit->tail = (xmit->tail + inserted) &
				(SIRFSOC_RX_DMA_BUF_SIZE - 1);
		count = CIRC_CNT_TO_END(xmit->head, xmit->tail,
				SIRFSOC_RX_DMA_BUF_SIZE);
		tty_flip_buffer_push(tty->port);
	}
	/*
	 * if RX DMA buffer data have all push into tty buffer, and there is
	 * only little data(less than a dma transfer unit) left in rxfifo,
	 * fetch it out in pio mode and switch back to dma immediately
	 */
	if (!inserted && !count &&
		((rd_regl(port, ureg->sirfsoc_rx_fifo_status) &
		SIRFUART_RX_FIFO_MASK) > sirfport->pio_fetch_cnt)) {
		dmaengine_pause(sirfport->rx_dma_chan);
		/* switch to pio mode */
		wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
			rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) |
			SIRFUART_IO_MODE);
		/*
		 * UART controller SWH_DMA_IO register have CLEAR_RX_ADDR_EN
		 * When found changing I/O to DMA mode, it clears
		 * two low bits of read point;
		 * USP have similar FRADDR_CLR_EN bit in USP_RX_DMA_IO_CTRL.
		 * Fetch data out from rxfifo into DMA buffer in PIO mode,
		 * while switch back to DMA mode, the data fetched will override
		 * by DMA, as hardware have a strange behaviour:
		 * after switch back to DMA mode, check rxfifo status it will
		 * be the number PIO fetched, so record the fetched data count
		 * to avoid the repeated fetch
		 */
		max_pio_cnt = 3;
		while (!(rd_regl(port, ureg->sirfsoc_rx_fifo_status) &
			ufifo_st->ff_empty(port)) && max_pio_cnt--) {
			xmit->buf[xmit->head] =
				rd_regl(port, ureg->sirfsoc_rx_fifo_data);
			xmit->head = (xmit->head + 1) &
					(SIRFSOC_RX_DMA_BUF_SIZE - 1);
			sirfport->pio_fetch_cnt++;
		}
		/* switch back to dma mode */
		wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
			rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) &
			~SIRFUART_IO_MODE);
		dmaengine_resume(sirfport->rx_dma_chan);
	}
next_hrt:
	hrtimer_forward_now(hrt, ns_to_ktime(sirfport->rx_period_time));
	return HRTIMER_RESTART;
}