139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/*
239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * drivers/media/video/omap24xxcam-dma.c
339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Copyright (C) 2004 MontaVista Software, Inc.
539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Copyright (C) 2004 Texas Instruments.
639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Copyright (C) 2007 Nokia Corporation.
739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Contact: Sakari Ailus <sakari.ailus@nokia.com>
939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
1039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Based on code from Andy Lowe <source@mvista.com> and
1139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *                    David Cohen <david.cohen@indt.org.br>.
1239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
1339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * This program is free software; you can redistribute it and/or
1439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * modify it under the terms of the GNU General Public License
1539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * version 2 as published by the Free Software Foundation.
1639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
1739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * This program is distributed in the hope that it will be useful, but
1839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * WITHOUT ANY WARRANTY; without even the implied warranty of
1939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
2039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * General Public License for more details.
2139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
2239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * You should have received a copy of the GNU General Public License
2339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * along with this program; if not, write to the Free Software
2439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
2539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * 02110-1301 USA
2639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
2739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
2839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus#include <linux/kernel.h>
2939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus#include <linux/io.h>
3039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus#include <linux/scatterlist.h>
3139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
3239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus#include "omap24xxcam.h"
3339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
3439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/*
3539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
3639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * DMA hardware.
3739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
3839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
3939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
4039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* Ack all interrupt on CSR and IRQSTATUS_L0 */
4139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_dmahw_ack_all(unsigned long base)
4239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
4339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	u32 csr;
4439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int i;
4539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
4639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
4739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i));
4839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		/* ack interrupt in CSR */
4939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr);
5039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
5139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf);
5239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
5339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
5439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* Ack dmach on CSR and IRQSTATUS_L0 */
5539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach)
5639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
5739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	u32 csr;
5839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
5939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach));
6039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* ack interrupt in CSR */
6139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr);
6239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* ack interrupt in IRQSTATUS */
6339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach));
6439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
6539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	return csr;
6639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
6739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
6839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic int omap24xxcam_dmahw_running(unsigned long base, int dmach)
6939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
7039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE;
7139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
7239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
7339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach,
7439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus					     dma_addr_t start, u32 len)
7539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
7639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
7739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    CAMDMA_CCR_SEL_SRC_DST_SYNC
7839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_BS
7939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_DST_AMODE_POST_INC
8039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_SRC_AMODE_POST_INC
8139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_FS
8239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_WR_ACTIVE
8339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_RD_ACTIVE
8439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_SYNCHRO_CAMERA);
8539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0);
8639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len);
8739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1);
8839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach),
8939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    CAMDMA_CSDP_WRITE_MODE_POSTED
9039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CSDP_DST_BURST_EN_32
9139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CSDP_DST_PACKED
9239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CSDP_SRC_BURST_EN_32
9339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CSDP_SRC_PACKED
9439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CSDP_DATA_TYPE_8BITS);
9539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0);
9639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start);
9739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0);
9839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
9939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0);
10039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0);
10139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CSR(dmach),
10239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    CAMDMA_CSR_MISALIGNED_ERR
10339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CSR_SECURE_ERR
10439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CSR_TRANS_ERR
10539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CSR_BLOCK
10639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CSR_DROP);
10739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CICR(dmach),
10839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    CAMDMA_CICR_MISALIGNED_ERR_IE
10939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CICR_SECURE_ERR_IE
11039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CICR_TRANS_ERR_IE
11139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CICR_BLOCK_IE
11239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CICR_DROP_IE);
11339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
11439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
11539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach)
11639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
11739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
11839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    CAMDMA_CCR_SEL_SRC_DST_SYNC
11939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_BS
12039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_DST_AMODE_POST_INC
12139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_SRC_AMODE_POST_INC
12239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_ENABLE
12339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_FS
12439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_CCR_SYNCHRO_CAMERA);
12539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
12639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
12739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach,
12839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus					     int free_dmach)
12939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
13039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int prev_dmach, ch;
13139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
13239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	if (dmach == 0)
13339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		prev_dmach = NUM_CAMDMA_CHANNELS - 1;
13439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	else
13539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		prev_dmach = dmach - 1;
13639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach),
13739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
13839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* Did we chain the DMA transfer before the previous one
13939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	 * finished?
14039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	 */
14139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS;
14239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch))
14339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		 & CAMDMA_CCR_ENABLE)) {
14439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		if (ch == dmach) {
14539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			/* The previous transfer has ended and this one
14639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			 * hasn't started, so we must not have chained
14739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			 * to the previous one in time.  We'll have to
14839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			 * start it now.
14939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			 */
15039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			omap24xxcam_dmahw_transfer_start(base, dmach);
15139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			break;
15239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		} else
15339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
15439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
15539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
15639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
15739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* Abort all chained DMA transfers. After all transfers have been
15839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * aborted and the DMA controller is idle, the completion routines for
15939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * any aborted transfers will be called in sequence. The DMA
16039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * controller may not be idle after this routine completes, because
16139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * the completion routines might start new transfers.
16239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
16339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach)
16439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
16539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* mask all interrupts from this channel */
16639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
16739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* unlink this channel */
16839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0,
16939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			      CAMDMA_CLNK_CTRL_ENABLE_LNK);
17039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* disable this channel */
17139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
17239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
17339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
17439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_dmahw_init(unsigned long base)
17539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
17639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
17739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
17839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
17939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
18039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
18139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10,
18239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			      CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
18339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
18439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf);
18539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
18639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
18739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/*
18839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
18939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Individual DMA channel handling.
19039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
19139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
19239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
19339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* Start a DMA transfer from the camera to memory.
19439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Returns zero if the transfer was successfully started, or non-zero if all
19539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * DMA channels are already in use or starting is currently inhibited.
19639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
19739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start,
19839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				 u32 len, dma_callback_t callback, void *arg)
19939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
20039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	unsigned long flags;
20139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int dmach;
20239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
20339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock_irqsave(&dma->lock, flags);
20439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
20539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	if (!dma->free_dmach || atomic_read(&dma->dma_stop)) {
20639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		spin_unlock_irqrestore(&dma->lock, flags);
20739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		return -EBUSY;
20839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
20939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
21039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dmach = dma->next_dmach;
21139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
21239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dma->ch_state[dmach].callback = callback;
21339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dma->ch_state[dmach].arg = arg;
21439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
21539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len);
21639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
21739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* We're ready to start the DMA transfer. */
21839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
21939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	if (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
22039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		/* A transfer is already in progress, so try to chain to it. */
22139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		omap24xxcam_dmahw_transfer_chain(dma->base, dmach,
22239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus						 dma->free_dmach);
22339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	} else {
22439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		/* No transfer is in progress, so we'll just start this one
22539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		 * now.
22639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		 */
22739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		omap24xxcam_dmahw_transfer_start(dma->base, dmach);
22839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
22939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
23039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
23139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dma->free_dmach--;
23239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
23339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_unlock_irqrestore(&dma->lock, flags);
23439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
23539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	return 0;
23639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
23739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
23839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* Abort all chained DMA transfers. After all transfers have been
23939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * aborted and the DMA controller is idle, the completion routines for
24039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * any aborted transfers will be called in sequence. The DMA
24139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * controller may not be idle after this routine completes, because
24239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * the completion routines might start new transfers.
24339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
24439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr)
24539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
24639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	unsigned long flags;
24739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int dmach, i, free_dmach;
24839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dma_callback_t callback;
24939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	void *arg;
25039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
25139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock_irqsave(&dma->lock, flags);
25239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
25339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* stop any DMA transfers in progress */
25439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS;
25539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
25639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		omap24xxcam_dmahw_abort_ch(dma->base, dmach);
25739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS;
25839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
25939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
26039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* We have to be careful here because the callback routine
26139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	 * might start a new DMA transfer, and we only want to abort
26239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	 * transfers that were started before this routine was called.
26339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	 */
26439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	free_dmach = dma->free_dmach;
26539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) &&
26639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	       (free_dmach < NUM_CAMDMA_CHANNELS)) {
26739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		dmach = (dma->next_dmach + dma->free_dmach)
26839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			% NUM_CAMDMA_CHANNELS;
26939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		callback = dma->ch_state[dmach].callback;
27039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		arg = dma->ch_state[dmach].arg;
27139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		dma->free_dmach++;
27239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		free_dmach++;
27339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		if (callback) {
27439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			/* leave interrupts disabled during callback */
27539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			spin_unlock(&dma->lock);
27639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			(*callback) (dma, csr, arg);
27739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			spin_lock(&dma->lock);
27839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		}
27939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
28039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
28139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_unlock_irqrestore(&dma->lock, flags);
28239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
28339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
28439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* Abort all chained DMA transfers. After all transfers have been
28539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * aborted and the DMA controller is idle, the completion routines for
28639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * any aborted transfers will be called in sequence. If the completion
28739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * routines attempt to start a new DMA transfer it will fail, so the
28839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * DMA controller will be idle after this routine completes.
28939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
29039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr)
29139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
29239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	atomic_inc(&dma->dma_stop);
29339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_dma_abort(dma, csr);
29439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	atomic_dec(&dma->dma_stop);
29539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
29639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
29739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* Camera DMA interrupt service routine. */
29839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusvoid omap24xxcam_dma_isr(struct omap24xxcam_dma *dma)
29939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
30039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int dmach;
30139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dma_callback_t callback;
30239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	void *arg;
30339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	u32 csr;
30439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
30539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		| CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
30639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		| CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
30739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
30839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock(&dma->lock);
30939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
31039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	if (dma->free_dmach == NUM_CAMDMA_CHANNELS) {
31139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		/* A camera DMA interrupt occurred while all channels
31239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		 * are idle, so we'll acknowledge the interrupt in the
31339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		 * IRQSTATUS register and exit.
31439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		 */
31539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		omap24xxcam_dmahw_ack_all(dma->base);
31639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		spin_unlock(&dma->lock);
31739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		return;
31839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
31939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
32039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	while (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
32139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		dmach = (dma->next_dmach + dma->free_dmach)
32239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			% NUM_CAMDMA_CHANNELS;
32339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		if (omap24xxcam_dmahw_running(dma->base, dmach)) {
32439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			/* This buffer hasn't finished yet, so we're done. */
32539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			break;
32639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		}
32739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach);
32839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		if (csr & csr_error) {
32939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			/* A DMA error occurred, so stop all DMA
33039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			 * transfers in progress.
33139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			 */
33239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			spin_unlock(&dma->lock);
33339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			omap24xxcam_dma_stop(dma, csr);
33439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			return;
33539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		} else {
33639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			callback = dma->ch_state[dmach].callback;
33739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			arg = dma->ch_state[dmach].arg;
33839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			dma->free_dmach++;
33939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			if (callback) {
34039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				spin_unlock(&dma->lock);
34139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				(*callback) (dma, csr, arg);
34239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				spin_lock(&dma->lock);
34339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			}
34439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		}
34539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
34639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
34739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_unlock(&dma->lock);
34839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
34939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_sgdma_process(
35039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		container_of(dma, struct omap24xxcam_sgdma, dma));
35139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
35239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
35339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusvoid omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
35439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
35539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	unsigned long flags;
35639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
35739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock_irqsave(&dma->lock, flags);
35839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
35939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_dmahw_init(dma->base);
36039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
36139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_unlock_irqrestore(&dma->lock, flags);
36239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
36339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
36439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
36539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				 unsigned long base)
36639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
36739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int ch;
36839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
36939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* group all channels on DMA IRQ0 and unmask irq */
37039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock_init(&dma->lock);
37139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dma->base = base;
37239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dma->free_dmach = NUM_CAMDMA_CHANNELS;
37339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	dma->next_dmach = 0;
37439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
37539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		dma->ch_state[ch].callback = NULL;
37639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		dma->ch_state[ch].arg = NULL;
37739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
37839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
37939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
38039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/*
38139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
38239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Scatter-gather DMA.
38339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
38439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * High-level DMA construct for transferring whole picture frames to
38539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * memory that is discontinuous.
38639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus *
38739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
38839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
38939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* DMA completion routine for the scatter-gather DMA fragments. */
39039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusstatic void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr,
39139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				       void *arg)
39239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
39339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	struct omap24xxcam_sgdma *sgdma =
39439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		container_of(dma, struct omap24xxcam_sgdma, dma);
39539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int sgslot = (int)arg;
39639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	struct sgdma_state *sg_state;
39739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
39839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		| CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
39939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		| CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
40039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
40139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock(&sgdma->lock);
40239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
40339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* We got an interrupt, we can remove the timer */
40439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	del_timer(&sgdma->reset_timer);
40539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
40639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state = sgdma->sg_state + sgslot;
40739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	if (!sg_state->queued_sglist) {
40839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		spin_unlock(&sgdma->lock);
40939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		printk(KERN_ERR "%s: sgdma completed when none queued!\n",
41039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		       __func__);
41139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		return;
41239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
41339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
41439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->csr |= csr;
41539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	if (!--sg_state->queued_sglist) {
41639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		/* Queue for this sglist is empty, so check to see if we're
41739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		 * done.
41839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		 */
41939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		if ((sg_state->next_sglist == sg_state->sglen)
42039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		    || (sg_state->csr & csr_error)) {
42139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			sgdma_callback_t callback = sg_state->callback;
42239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			void *arg = sg_state->arg;
42339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			u32 sg_csr = sg_state->csr;
42439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			/* All done with this sglist */
42539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			sgdma->free_sgdma++;
42639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			if (callback) {
42739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				spin_unlock(&sgdma->lock);
42839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				(*callback) (sgdma, sg_csr, arg);
42939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				return;
43039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			}
43139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		}
43239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
43339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
43439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_unlock(&sgdma->lock);
43539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
43639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
43739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* Start queued scatter-gather DMA transfers. */
43839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusvoid omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma)
43939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
44039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	unsigned long flags;
44139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int queued_sgdma, sgslot;
44239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	struct sgdma_state *sg_state;
44339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
44439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		| CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
44539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		| CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
44639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
44739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock_irqsave(&sgdma->lock, flags);
44839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
44939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma;
45039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
45139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	while (queued_sgdma > 0) {
45239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sg_state = sgdma->sg_state + sgslot;
45339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		while ((sg_state->next_sglist < sg_state->sglen) &&
45439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		       !(sg_state->csr & csr_error)) {
45539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			const struct scatterlist *sglist;
45639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			unsigned int len;
45739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
45839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			sglist = sg_state->sglist + sg_state->next_sglist;
45939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			/* try to start the next DMA transfer */
46039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			if (sg_state->next_sglist + 1 == sg_state->sglen) {
46139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				/*
46239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				 *  On the last sg, we handle the case where
46339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				 *  cam->img.pix.sizeimage % PAGE_ALIGN != 0
46439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				 */
46539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				len = sg_state->len - sg_state->bytes_read;
46639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			} else {
46739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				len = sg_dma_len(sglist);
46839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			}
46939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
47039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			if (omap24xxcam_dma_start(&sgdma->dma,
47139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus						  sg_dma_address(sglist),
47239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus						  len,
47339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus						  omap24xxcam_sgdma_callback,
47439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus						  (void *)sgslot)) {
47539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				/* DMA start failed */
47639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				spin_unlock_irqrestore(&sgdma->lock, flags);
47739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				return;
47839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			} else {
47939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				unsigned long expires;
48039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				/* DMA start was successful */
48139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				sg_state->next_sglist++;
48239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				sg_state->bytes_read += len;
48339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				sg_state->queued_sglist++;
48439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
48539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				/* We start the reset timer */
48639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				expires = jiffies + HZ;
48739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				mod_timer(&sgdma->reset_timer, expires);
48839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			}
48939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		}
49039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		queued_sgdma--;
49139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sgslot = (sgslot + 1) % NUM_SG_DMA;
49239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
49339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
49439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_unlock_irqrestore(&sgdma->lock, flags);
49539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
49639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
49739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/*
49839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Queue a scatter-gather DMA transfer from the camera to memory.
49939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Returns zero if the transfer was successfully queued, or non-zero
50039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * if all of the scatter-gather slots are already in use.
50139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
50239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusint omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
50339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    const struct scatterlist *sglist, int sglen,
50439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    int len, sgdma_callback_t callback, void *arg)
50539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
50639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	unsigned long flags;
50739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	struct sgdma_state *sg_state;
50839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
5099aa7705c966c750dda3d5c8d8a20f8e46668911cJoe Perches	if ((sglen < 0) || ((sglen > 0) && !sglist))
51039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		return -EINVAL;
51139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
51239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock_irqsave(&sgdma->lock, flags);
51339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
51439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	if (!sgdma->free_sgdma) {
51539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		spin_unlock_irqrestore(&sgdma->lock, flags);
51639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		return -EBUSY;
51739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
51839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
51939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state = sgdma->sg_state + sgdma->next_sgdma;
52039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
52139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->sglist = sglist;
52239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->sglen = sglen;
52339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->next_sglist = 0;
52439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->bytes_read = 0;
52539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->len = len;
52639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->queued_sglist = 0;
52739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->csr = 0;
52839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->callback = callback;
52939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sg_state->arg = arg;
53039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
53139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA;
53239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sgdma->free_sgdma--;
53339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
53439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_unlock_irqrestore(&sgdma->lock, flags);
53539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
53639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_sgdma_process(sgdma);
53739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
53839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	return 0;
53939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
54039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
54139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress.
54239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * Any queued scatter-gather DMA transactions that have not yet been started
54339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * will remain queued.  The DMA controller will be idle after this routine
54439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * completes.  When the scatter-gather queue is restarted, the next
54539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus * scatter-gather DMA transfer will begin at the start of a new transaction.
54639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus */
54739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusvoid omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
54839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
54939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	unsigned long flags;
55039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int sgslot;
55139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	struct sgdma_state *sg_state;
55239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	u32 csr = CAMDMA_CSR_TRANS_ERR;
55339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
55439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	/* stop any DMA transfers in progress */
55539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_dma_stop(&sgdma->dma, csr);
55639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
55739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock_irqsave(&sgdma->lock, flags);
55839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
55939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	if (sgdma->free_sgdma < NUM_SG_DMA) {
56039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
56139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sg_state = sgdma->sg_state + sgslot;
56239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		if (sg_state->next_sglist != 0) {
56339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			/* This DMA transfer was in progress, so abort it. */
56439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			sgdma_callback_t callback = sg_state->callback;
56539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			void *arg = sg_state->arg;
56639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			sgdma->free_sgdma++;
56739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			if (callback) {
56839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				/* leave interrupts masked */
56939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				spin_unlock(&sgdma->lock);
57039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				(*callback) (sgdma, csr, arg);
57139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus				spin_lock(&sgdma->lock);
57239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			}
57339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		}
57439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
57539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
57639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_unlock_irqrestore(&sgdma->lock, flags);
57739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
57839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
57939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailusvoid omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
58039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    unsigned long base,
58139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    void (*reset_callback)(unsigned long data),
58239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus			    unsigned long reset_callback_data)
58339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus{
58439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	int sg;
58539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
58639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	spin_lock_init(&sgdma->lock);
58739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sgdma->free_sgdma = NUM_SG_DMA;
58839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	sgdma->next_sgdma = 0;
58939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	for (sg = 0; sg < NUM_SG_DMA; sg++) {
59039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sgdma->sg_state[sg].sglen = 0;
59139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sgdma->sg_state[sg].next_sglist = 0;
59239aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sgdma->sg_state[sg].bytes_read = 0;
59339aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sgdma->sg_state[sg].queued_sglist = 0;
59439aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sgdma->sg_state[sg].csr = 0;
59539aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sgdma->sg_state[sg].callback = NULL;
59639aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus		sgdma->sg_state[sg].arg = NULL;
59739aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	}
59839aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus
59939aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	omap24xxcam_dma_init(&sgdma->dma, base);
60039aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus	setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data);
60139aee69a166b775a38ed0053596cdb8e717ae315Sakari Ailus}
602