From: David Cohen <david.cohen@indt.org.br>
To: "Linux-omap-open-source@linux.omap.com"
<Linux-omap-open-source@linux.omap.com>
Subject: [PATCH] omap camera
Date: Fri, 10 Feb 2006 17:03:23 -0400 [thread overview]
Message-ID: <43ECFF9B.80509@indt.org.br> (raw)
[-- Attachment #1: Type: text/plain, Size: 443 bytes --]
Hi all,
I've updated the driver for OMAP 24xx camera from TI (kernel version
2.6.10). It is now working with the public omap tree.
Is there anybody else working with camera driver?
I don't think it is appropriate to use a common driver (camera_core) for
the omap 2420 and 1611/1710. I guess creating a common driver for omap1
(current camera_core) and other for omap2 (an updated camera_core) could
be better. Any comment?
BR,
David Cohen
[-- Attachment #2: diff_omap24xxcam.patch --]
[-- Type: text/plain, Size: 109694 bytes --]
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 93cfb54..5988ca3 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -60,7 +60,7 @@ obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
-obj-$(CONFIG_VIDEO_OMAP_CAMERA) += omap/
+obj-$(CONFIG_VIDEO_OMAP) += omap/
obj-$(CONFIG_VIDEO_DECODER) += saa7115.o cx25840/ saa7127.o
diff --git a/drivers/media/video/omap/Kconfig b/drivers/media/video/omap/Kconfig
index 809193b..ecb7f53 100644
--- a/drivers/media/video/omap/Kconfig
+++ b/drivers/media/video/omap/Kconfig
@@ -1,7 +1,11 @@
+config VIDEO_OMAP
+ tristate "Video devices for OMAP1/2"
+ select VIDEO_BUF
+ depends on VIDEO_DEV
+
config VIDEO_OMAP_CAMERA
tristate "OMAP Camera support (EXPERIMENTAL)"
- select VIDEO_BUF
- depends on VIDEO_DEV && (ARCH_OMAP16XX || ARCH_OMAP24XX)
+ depends on VIDEO_OMAP && (ARCH_OMAP16XX || ARCH_OMAP24XX)
help
V4L2 camera driver support for OMAP1/2 based boards.
diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile
index c4c5a81..6c052ce 100644
--- a/drivers/media/video/omap/Makefile
+++ b/drivers/media/video/omap/Makefile
@@ -2,12 +2,16 @@
obj-$(CONFIG_VIDEO_OMAP_CAMERA) += omapcamera.o
obj-$(CONFIG_VIDEO_CAMERA_SENSOR_OV9640) += sensor_ov9640.o
+obj-$(CONFIG_VIDEO_OMAP2_OUT) += omapvout.o
-objs-yy := camera_core.o
+objs-y$(CONFIG_ARCH_OMAP16XX) += camera_core.o
objs-y$(CONFIG_ARCH_OMAP16XX) += omap16xxcam.o
+objs-y$(CONFIG_ARCH_OMAP24XX) += omap24xxcam.o
objs-y$(CONFIG_MACH_OMAP_H3) += h3_sensor_power.o
+objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_MACH_OMAP_H4) += omap24xxvout.o omap24xxlib.o
omapcamera-objs := $(objs-yy)
+omapvout-objs := $(objs-yy)
EXTRA_CFLAGS = -I$(src)/..
diff --git a/drivers/media/video/omap/omap24xxcam.c b/drivers/media/video/omap/omap24xxcam.c
new file mode 100644
index 0000000..8133804
--- /dev/null
+++ b/drivers/media/video/omap/omap24xxcam.c
@@ -0,0 +1,2603 @@
+/*
+ * drivers/media/video/omap/omap24xxcam.c
+ *
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP24xx camera controller.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * January 2006 - Modified for new sensor interface.
+ * Copyright (C) 2006 Texas Instruments, Inc.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/videodev.h>
+#include <media/video-buf.h>
+#include <linux/dma-mapping.h>
+#include <linux/device.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/scatterlist.h>
+#include <asm/irq.h>
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+
+#include <asm/arch/clock.h>
+#include <asm/arch/display.h>
+
+#include "omap24xxcam.h"
+#include "sensor_if.h"
+#include "omap24xxlib.h"
+
+#define DEFAULT_CAM_FUNC_CLK 96000000 /* 96MHz */
+#define DEFAULT_SENSOR_XCLK 12000000 /* 12MHz */
+
+#define QVGA_SIZE 320*240*2 /* memory needed for QVGA image */
+#define VGA_SIZE QVGA_SIZE*4 /* memory needed for VGA image */
+#define SXGA_SIZE 1280*960*2 /* memory needed for SXGA image */
+
+#define CAM_NAME "omap24xxcam"
+
+/* this is the sensor ops implemented by the associated sesnor driver */
+extern struct camera_sensor camera_sensor_if;
+
+static void omap24xxcam_cleanup(void);
+
+/* global variables */
+static struct omap24xxcam_device *saved_cam;
+
+/* module parameters */
+static int video_nr = -1; /* video device minor (-1 ==> auto assign) */
+
+/* Maximum amount of memory to use for capture buffers.
+ * Default is 4800KB, enough to double-buffer SXGA. */
+static int capture_mem = SXGA_SIZE * 2;
+
+/* Size of video overlay framebuffer. This determines the maximum image size
+ * that can be previewed. Default is 600KB, enough for VGA. */
+static int overlay_mem = VGA_SIZE;
+
+/* -------------------------------------------------------------------------- */
+#ifdef CONFIG_OMAP24XX_DPM
+#define omap24xxcam_suspend_lockout(s,f) \
+ if ((s)->suspended) {\
+ if ((f)->f_flags & O_NONBLOCK)\
+ return -EBUSY;\
+ wait_event_interruptible((s)->suspend_wq,\
+ (s)->suspended == 0);\
+ }
+#else
+#define omap24xxcam_suspend_lockout(s, f) do {} while(0)
+#endif
+
+/* Set the value of the CC_CTRL register in cam->cc_ctrl that is required to
+ * support the currently selected capture format in cam->pix. The CC_CTRL bits
+ * which must be configured are: NOBT_SYNCHRO, BT_CORRECT, PAR_ORDERCAM,
+ * PAR_CLK_POL, NOBT_HS_POL, NOBT_VS_POL, PAR_MODE, and CCP_MODE. The CC_RST,
+ * CC_FRAME_TRIG, and CC_EN bits are actively managed by the driver and should
+ * be set to zero by this routine.
+ */
+static void
+omap24xxcam_sensor_cc_ctrl(struct omap24xxcam_device *cam)
+{
+ struct v4l2_pix_format *pix = &cam->pix;
+ struct camera_sensor *sensor = cam->cam_sensor;
+
+ /* basic mode */
+ cam->cc_ctrl = sensor->parallel_mode << CC_CTRL_PAR_MODE_SHIFT;
+ /* sync polarity */
+ cam->cc_ctrl |= (sensor->hs_polarity << CC_CTRL_NOBT_HS_POL_SHIFT);
+ cam->cc_ctrl |= (sensor->vs_polarity << CC_CTRL_NOBT_VS_POL_SHIFT);
+ /* swap */
+ cam->cc_ctrl |= (sensor->image_swap ? CC_CTRL_PAR_ORDERCAM : 0);
+ /* based on BT or NOBT */
+ if ((sensor->parallel_mode == PAR_MODE_BT8) ||
+ (sensor->parallel_mode == PAR_MODE_BT10))
+ /* BT correction enable */
+ cam->cc_ctrl |= (sensor->bt_correction ? CC_CTRL_BT_CORRECT : 0);
+ else
+ /* always use the rising edger to trigger the acquisition
+ in NOBT modes. This is recommended */
+ cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB555:
+ default:
+ /* These formats need a 16-bit byte swap */
+ cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_RGB555X:
+ /* These formats don't need a 16-bit byte swap */
+ break;
+ }
+}
+
+/*
+ * camera core register I/O routines
+ */
+
+static __inline__ u32
+cc_reg_in(const struct omap24xxcam_device *cam, u32 offset)
+{
+ return readl(cam->cam_mmio_base + CC_REG_OFFSET + offset);
+}
+
+static __inline__ u32
+cc_reg_out(const struct omap24xxcam_device *cam, u32 offset, u32 val)
+{
+ writel(val, cam->cam_mmio_base + CC_REG_OFFSET + offset);
+ return val;
+}
+
+static __inline__ u32
+cc_reg_merge(const struct omap24xxcam_device *cam, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 addr = cam->cam_mmio_base + CC_REG_OFFSET + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+static void
+cc_init(const struct omap24xxcam_device *cam)
+{
+ cc_reg_out(cam, CC_SYSCONFIG, CC_SYSCONFIG_AUTOIDLE);
+}
+
+/*
+ * camera DMA register I/O routines
+ */
+
+static __inline__ u32
+camdma_reg_in(const struct omap24xxcam_device *cam, u32 offset)
+{
+ return readl(cam->cam_mmio_base + CAMDMA_REG_OFFSET + offset);
+}
+
+static __inline__ u32
+camdma_reg_out(const struct omap24xxcam_device *cam, u32 offset, u32 val)
+{
+ writel(val, cam->cam_mmio_base + CAMDMA_REG_OFFSET + offset);
+ return val;
+}
+
+static __inline__ u32
+camdma_reg_merge(const struct omap24xxcam_device *cam, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 addr = cam->cam_mmio_base + CAMDMA_REG_OFFSET + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+static void
+camdma_init(const struct omap24xxcam_device *cam)
+{
+ camdma_reg_out(cam, CAMDMA_OCP_SYSCONFIG,
+ CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
+ | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
+ | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
+
+ camdma_reg_merge(cam, CAMDMA_GCR, 0x10,
+ CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
+}
+
+/*
+ * camera MMU register I/O routines
+ */
+
+static __inline__ u32
+cammmu_reg_in(const struct omap24xxcam_device *cam, u32 offset)
+{
+ return readl(cam->cam_mmio_base + CAMMMU_REG_OFFSET + offset);
+}
+
+static __inline__ u32
+cammmu_reg_out(const struct omap24xxcam_device *cam, u32 offset, u32 val)
+{
+ writel(val, cam->cam_mmio_base + CAMMMU_REG_OFFSET + offset);
+ return val;
+}
+
+static __inline__ u32
+cammmu_reg_merge(const struct omap24xxcam_device *cam, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 addr = cam->cam_mmio_base + CAMMMU_REG_OFFSET + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+static void
+cammmu_init(const struct omap24xxcam_device *cam)
+{
+ /* set the camera MMU autoidle bit */
+ cammmu_reg_out(cam, CAMMMU_SYSCONFIG, CAMMMU_SYSCONFIG_AUTOIDLE);
+}
+
+/*
+ * camera subsystem register I/O routines
+ */
+
+static __inline__ u32
+cam_reg_in(const struct omap24xxcam_device *cam, u32 offset)
+{
+ return readl(cam->cam_mmio_base + offset);
+}
+
+static __inline__ u32
+cam_reg_out(const struct omap24xxcam_device *cam, u32 offset, u32 val)
+{
+ writel(val, cam->cam_mmio_base + offset);
+ return val;
+}
+
+static __inline__ u32
+cam_reg_merge(const struct omap24xxcam_device *cam, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 addr = cam->cam_mmio_base + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+/* Reset the camera subsystem (camera core, camera DMA, and camera MMU) */
+static void
+cam_reset(const struct omap24xxcam_device *cam, unsigned long timeout_ticks)
+{
+ unsigned long timeout;
+
+ cam_reg_out(cam, CAM_SYSCONFIG, CAM_SYSCONFIG_SOFTRESET);
+ /* wait for reset to complete */
+ timeout = jiffies + timeout_ticks;
+ while (!(cam_reg_in(cam, CAM_SYSSTATUS) & CAM_SYSSTATUS_RESETDONE)
+ && time_before(jiffies, timeout)) {
+ if (!in_atomic()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ } else
+ udelay(10);
+ }
+ if (unlikely
+ (!(cam_reg_in(cam, CAM_SYSSTATUS) & CAM_SYSSTATUS_RESETDONE))) {
+ printk(KERN_WARNING CAM_NAME
+ ": timeout waiting for camera subsystem reset\n");
+ }
+
+ return;
+}
+
+/* Initialize the camera subsystem (camera core, camera DMA, and camera MMU) */
+static void
+cam_init(const struct omap24xxcam_device *cam)
+{
+ /* reset the camera subsystem with a timeout of 200ms */
+ cam_reset(cam, HZ / 5);
+
+ /* set the camera subsystem autoidle bit */
+ cam_reg_out(cam, CAM_SYSCONFIG, CAM_SYSCONFIG_AUTOIDLE);
+
+ /* initialize the camera MMU */
+ cammmu_init(cam);
+
+ /* initialize the camera DMA controller */
+ camdma_init(cam);
+
+ /* initialize the camera core module */
+ cc_init(cam);
+}
+
+/* Program the camera interface xclk for the frequency cam->xclk based on
+ * the functional clock frequency cam->mclk. If the specifed cam->xclk
+ * frequency is not possible based on the value of cam->mclk, then the
+ * closest xclk frequency lower than the specified xclk will be selected.
+ * The actual xclk frequency is returned in cam->xclk. If cam->xclk is zero,
+ * then xclk is turned off (stable low value).
+ */
+static void
+omap24xxcam_set_xclk(struct omap24xxcam_device *cam)
+{
+ unsigned long divisor;
+
+ if (cam->mclk == 0)
+ cam->mclk = DEFAULT_CAM_FUNC_CLK; /* supply a default mclk */
+
+ if (cam->xclk == 0) {
+ cc_reg_out(cam, CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+ return;
+ }
+
+ if (cam->xclk > cam->mclk)
+ cam->xclk = cam->mclk;
+
+ divisor = cam->mclk / cam->xclk;
+ if (cam->xclk * divisor < cam->mclk)
+ divisor += 1;
+ if (divisor > (CC_CTRL_XCLK_DIV >> CC_CTRL_XCLK_DIV_SHIFT))
+ divisor = CC_CTRL_XCLK_DIV >> CC_CTRL_XCLK_DIV_SHIFT;
+ cam->xclk = cam->mclk / divisor;
+ if (divisor == 1)
+ cc_reg_out(cam, CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_BYPASS);
+ else
+ cc_reg_out(cam, CC_CTRL_XCLK, divisor);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Start a DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully started, or non-zero if all
+ * DMA channels are already in use or starting is currently inhibited.
+ */
+static int
+omap24xxcam_dma_start(struct omap24xxcam_device *cam, dma_addr_t start,
+ unsigned long len, dma_callback_t callback, void *arg)
+{
+ unsigned long irqflags;
+ int dmach;
+ void (*dma_notify) (struct omap24xxcam_device * cam);
+
+ spin_lock_irqsave(&cam->dma_lock, irqflags);
+
+ if (!cam->free_dmach || cam->dma_stop) {
+ spin_unlock_irqrestore(&cam->dma_lock, irqflags);
+ return -EBUSY;
+ }
+
+ dmach = cam->next_dmach;
+
+ cam->camdma[dmach].callback = callback;
+ cam->camdma[dmach].arg = arg;
+
+ camdma_reg_out(cam, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_FS | CAMDMA_CCR_SYNCHRO_CAMERA);
+ camdma_reg_out(cam, CAMDMA_CLNK_CTRL(dmach), 0);
+ camdma_reg_out(cam, CAMDMA_CEN(dmach), len);
+ camdma_reg_out(cam, CAMDMA_CFN(dmach), 1);
+ camdma_reg_out(cam, CAMDMA_CSDP(dmach),
+ CAMDMA_CSDP_WRITE_MODE_POSTED
+ | CAMDMA_CSDP_DST_BURST_EN_16
+ | CAMDMA_CSDP_DST_PACKED
+ | CAMDMA_CSDP_SRC_BURST_EN_16
+ | CAMDMA_CSDP_SRC_PACKED
+ | CAMDMA_CSDP_DATA_TYPE_8BITS);
+ camdma_reg_out(cam, CAMDMA_CSSA(dmach), 0);
+ camdma_reg_out(cam, CAMDMA_CDSA(dmach), start);
+ camdma_reg_out(cam, CAMDMA_CSEI(dmach), 0);
+ camdma_reg_out(cam, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
+ camdma_reg_out(cam, CAMDMA_CDEI(dmach), 0);
+ camdma_reg_out(cam, CAMDMA_CDFI(dmach), 0);
+ camdma_reg_out(cam, CAMDMA_CSR(dmach),
+ CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR
+ | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR
+ | CAMDMA_CSR_BLOCK | CAMDMA_CSR_DROP);
+ camdma_reg_out(cam, CAMDMA_CICR(dmach),
+ CAMDMA_CICR_MISALIGNED_ERR_IE
+ | CAMDMA_CICR_SUPERVISOR_ERR_IE
+ | CAMDMA_CICR_SECURE_ERR_IE
+ | CAMDMA_CICR_TRANS_ERR_IE
+ | CAMDMA_CICR_BLOCK_IE | CAMDMA_CICR_DROP_IE);
+
+ /* We're ready to start the DMA transfer. */
+ if (cam->free_dmach < NUM_CAMDMA_CHANNELS) {
+ /* A transfer is already in progress, so try to chain to it. */
+ int prev_dmach, ch;
+
+ if (dmach == 0)
+ prev_dmach = NUM_CAMDMA_CHANNELS - 1;
+ else
+ prev_dmach = dmach - 1;
+ camdma_reg_out(cam, CAMDMA_CLNK_CTRL(prev_dmach),
+ CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
+ /* Did we chain the DMA transfer before the previous one
+ * finished? Scan the in-use channel list. ch is the first one.
+ */
+ ch = (dmach + cam->free_dmach) % NUM_CAMDMA_CHANNELS;
+ /* dmach is guranteed to start as long as we find one channel
+ is still enabled */
+ while (!(camdma_reg_in(cam, CAMDMA_CCR(ch))
+ & CAMDMA_CCR_ENABLE)) {
+ if (ch == dmach) {
+ /* The previous transfers have ended and this one
+ * hasn't started, so we must not have chained
+ * to the previous one in time. We'll have to
+ * start it now.
+ */
+ camdma_reg_out(cam, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_ENABLE
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+ break;
+ } else
+ ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
+ }
+ } else {
+ /* No transfer is in progress, so we'll just start this one
+ * now.
+ */
+ camdma_reg_out(cam, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_ENABLE
+ | CAMDMA_CCR_FS | CAMDMA_CCR_SYNCHRO_CAMERA);
+ }
+
+ cam->next_dmach = (cam->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
+ cam->free_dmach--;
+
+ dma_notify = cam->dma_notify;
+ cam->dma_notify = NULL;
+
+ spin_unlock_irqrestore(&cam->dma_lock, irqflags);
+
+ if (dma_notify)
+ (*dma_notify) (cam);
+
+ return 0;
+}
+
+/* DMA completion routine for the scatter-gather DMA fragments. */
+static void
+omap24xxcam_sg_dma_callback(struct omap24xxcam_device *cam, unsigned long csr,
+ void *arg)
+{
+ int sgslot = (int) arg;
+ struct sgdma_state *sgdma;
+ const unsigned long csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&cam->sg_lock);
+
+ sgdma = cam->sgdma + sgslot;
+ if (!sgdma->queued_sglist) {
+ spin_unlock(&cam->sg_lock);
+ printk(KERN_DEBUG CAM_NAME
+ ": sgdma completed when none queued!\n");
+ return;
+ }
+
+ sgdma->csr |= csr;
+ if (!--sgdma->queued_sglist) {
+ /* Queue for this sglist is empty, so check to see if we're
+ * done.
+ */
+ if ((sgdma->next_sglist == sgdma->sglen)
+ || (sgdma->csr & csr_error)) {
+ dma_callback_t callback = sgdma->callback;
+ void *arg = sgdma->arg;
+ unsigned long sg_csr = sgdma->csr;
+ /* All done with this sglist */
+ cam->free_sgdma++;
+ if (callback) {
+ spin_unlock(&cam->sg_lock);
+ (*callback) (cam, sg_csr, arg);
+ return;
+ }
+ }
+ }
+
+ spin_unlock(&cam->sg_lock);
+}
+
+/* Process the scatter-gather DMA queue by starting queued transfers. */
+static void
+omap24xxcam_sg_dma_process(struct omap24xxcam_device *cam)
+{
+ unsigned long irqflags;
+ int queued_sgdma, sgslot;
+ struct sgdma_state *sgdma;
+ const unsigned long csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock_irqsave(&cam->sg_lock, irqflags);
+
+ queued_sgdma = NUM_SG_DMA - cam->free_sgdma;
+ sgslot = (cam->next_sgdma + cam->free_sgdma) % NUM_SG_DMA;
+ while (queued_sgdma > 0) {
+ sgdma = cam->sgdma + sgslot;
+ while ((sgdma->next_sglist < sgdma->sglen) &&
+ !(sgdma->csr & csr_error)) {
+ const struct scatterlist *sglist;
+
+ sglist = sgdma->sglist + sgdma->next_sglist;
+ /* try to start the next DMA transfer */
+ if (omap24xxcam_dma_start(cam, sg_dma_address(sglist),
+ sg_dma_len(sglist),
+ omap24xxcam_sg_dma_callback,
+ (void *) sgslot)) {
+ /* DMA start failed */
+ spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+ return;
+ } else {
+ /* DMA start was successful */
+ sgdma->next_sglist++;
+ sgdma->queued_sglist++;
+ }
+ }
+ queued_sgdma--;
+ sgslot = (sgslot + 1) % NUM_SG_DMA;
+ }
+
+ spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+}
+
+/* Queue a scatter-gather DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully queued, or
+ * non-zero if all of the scatter-gather slots are already in use.
+ */
+static int
+omap24xxcam_sg_dma_queue(struct omap24xxcam_device *cam,
+ const struct scatterlist *sglist, int sglen,
+ dma_callback_t callback, void *arg)
+{
+ unsigned long irqflags;
+ struct sgdma_state *sgdma;
+
+ if ((sglen < 0) || ((sglen > 0) & !sglist))
+ return -EINVAL;
+
+ spin_lock_irqsave(&cam->sg_lock, irqflags);
+
+ if (!cam->free_sgdma) {
+ spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+ return -EBUSY;
+ }
+
+ sgdma = cam->sgdma + cam->next_sgdma;
+
+ sgdma->sglist = sglist;
+ sgdma->sglen = sglen;
+ sgdma->next_sglist = 0;
+ sgdma->queued_sglist = 0;
+ sgdma->csr = 0;
+ sgdma->callback = callback;
+ sgdma->arg = arg;
+
+ cam->next_sgdma = (cam->next_sgdma + 1) % NUM_SG_DMA;
+ cam->free_sgdma--;
+
+ spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+
+ omap24xxcam_sg_dma_process(cam);
+
+ return 0;
+}
+
+/* Abort all chained DMA transfers. After all transfers have been aborted and
+ * the DMA controller is idle, the completion routines for any aborted transfers
+ * will be called in sequence. The DMA controller may not be idle after this
+ * routine completes, because the completion routines might start new transfers.
+ */
+static void
+omap24xxcam_dma_abort(struct omap24xxcam_device *cam, unsigned long csr)
+{
+ unsigned long irqflags;
+ int dmach, i, free_dmach;
+ dma_callback_t callback;
+ void *arg;
+
+ spin_lock_irqsave(&cam->dma_lock, irqflags);
+
+ /* stop any DMA transfers in progress */
+ dmach = (cam->next_dmach + cam->free_dmach) % NUM_CAMDMA_CHANNELS;
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
+ /* mask all interrupts from this channel */
+ camdma_reg_out(cam, CAMDMA_CICR(dmach), 0);
+ /* unlink this channel */
+ camdma_reg_merge(cam, CAMDMA_CLNK_CTRL(dmach), 0,
+ CAMDMA_CLNK_CTRL_ENABLE_LNK);
+ /* disable this channel */
+ camdma_reg_merge(cam, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
+ dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS;
+ }
+
+ /* We have to be careful here because the callback routine might start
+ * a new DMA transfer, and we only want to abort transfers that were
+ * started before this routine was called.
+ */
+ free_dmach = cam->free_dmach;
+ while ((cam->free_dmach < NUM_CAMDMA_CHANNELS) &&
+ (free_dmach < NUM_CAMDMA_CHANNELS)) {
+ dmach = (cam->next_dmach + cam->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ callback = cam->camdma[dmach].callback;
+ arg = cam->camdma[dmach].arg;
+ cam->free_dmach++;
+ free_dmach++;
+ if (callback) {
+ /* leave interrupts disabled during callback */
+ spin_unlock(&cam->dma_lock);
+ (*callback) (cam, csr, arg);
+ spin_lock(&cam->dma_lock);
+ }
+ }
+
+ spin_unlock_irqrestore(&cam->dma_lock, irqflags);
+}
+
+/* Abort all chained DMA transfers. After all transfers have been aborted and
+ * the DMA controller is idle, the completion routines for any aborted transfers
+ * will be called in sequence. If the completion routines attempt to start a
+ * new DMA transfer it will fail, so the DMA controller will be idle after this
+ * routine completes.
+ */
+static void
+omap24xxcam_dma_stop(struct omap24xxcam_device *cam, unsigned long csr)
+{
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&cam->dma_lock, irqflags);
+ cam->dma_stop++;
+ spin_unlock_irqrestore(&cam->dma_lock, irqflags);
+ omap24xxcam_dma_abort(cam, csr);
+ spin_lock_irqsave(&cam->dma_lock, irqflags);
+ cam->dma_stop--;
+ spin_unlock_irqrestore(&cam->dma_lock, irqflags);
+}
+
+/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress.
+ * Any queued scatter-gather DMA transactions that have not yet been started
+ * will remain queued. The DMA controller will be idle after this routine
+ * completes. When the scatter-gather queue is restarted, the next
+ * scatter-gather DMA transfer will begin at the start of a new transaction.
+ */
+static void
+omap24xxcam_sg_dma_sync(struct omap24xxcam_device *cam, unsigned long csr)
+{
+ unsigned long irqflags;
+ int sgslot;
+ struct sgdma_state *sgdma;
+
+ /* stop any DMA transfers in progress */
+ omap24xxcam_dma_stop(cam, csr);
+
+ spin_lock_irqsave(&cam->sg_lock, irqflags);
+
+ if (cam->free_sgdma < NUM_SG_DMA) {
+ sgslot = (cam->next_sgdma + cam->free_sgdma) % NUM_SG_DMA;
+ sgdma = cam->sgdma + sgslot;
+ if (sgdma->next_sglist != 0) {
+ /* This DMA transfer was in progress, so abort it. */
+ dma_callback_t callback = sgdma->callback;
+ void *arg = sgdma->arg;
+ cam->free_sgdma++;
+ if (callback) {
+ /* leave interrupts masked */
+ spin_unlock(&cam->sg_lock);
+ (*callback) (cam, csr, arg);
+ spin_lock(&cam->sg_lock);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+}
+
+/* Register a routine to be called once immediately after a DMA transfer is
+ * started. The purpose of this is to allow the camera interface to be
+ * started only after a DMA transaction has been queued in order to avoid
+ * DMA overruns. The registered callback routine will only be called one
+ * time and then discarded. Only one callback routine may be registered at a
+ * time.
+ * Returns zero if successful, or a non-zero error code if a different callback
+ * routine has already been registered.
+ */
+static int
+omap24xxcam_dma_notify(struct omap24xxcam_device *cam,
+ void (*callback) (struct omap24xxcam_device * cam))
+{
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&cam->dma_lock, irqflags);
+
+ if (cam->dma_notify && (cam->dma_notify != callback)) {
+ spin_unlock_irqrestore(&cam->dma_lock, irqflags);
+ return -EBUSY;
+ }
+
+ cam->dma_notify = callback;
+
+ spin_unlock_irqrestore(&cam->dma_lock, irqflags);
+
+ return 0;
+}
+
+/* Camera DMA interrupt service routine. */
+static void
+omap24xxcam_dma_isr(struct omap24xxcam_device *cam)
+{
+ int dmach, i;
+ dma_callback_t callback;
+ void *arg;
+ unsigned long csr;
+ const unsigned long csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&cam->dma_lock);
+ if (cam->free_dmach == NUM_CAMDMA_CHANNELS) {
+ /* A camera DMA interrupt occurred while all channels are idle,
+ * so we'll acknowledge the interrupt in the IRQSTATUS register
+ * and exit.
+ */
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
+ csr = camdma_reg_in(cam, CAMDMA_CSR(i));
+ /* ack interrupt in CSR */
+ camdma_reg_out(cam, CAMDMA_CSR(i), csr);
+ }
+ camdma_reg_out(cam, CAMDMA_IRQSTATUS_L0, 0xffffffff);
+ spin_unlock(&cam->dma_lock);
+ return;
+ }
+
+ while (cam->free_dmach < NUM_CAMDMA_CHANNELS) {
+ dmach = (cam->next_dmach + cam->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ if (camdma_reg_in(cam, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE) {
+ /* This buffer hasn't finished yet, so we're done. */
+ break;
+ }
+ csr = camdma_reg_in(cam, CAMDMA_CSR(dmach));
+ /* ack interrupt in CSR */
+ camdma_reg_out(cam, CAMDMA_CSR(dmach), csr);
+ /* ack interrupt in IRQSTATUS */
+ camdma_reg_out(cam, CAMDMA_IRQSTATUS_L0, (1 << dmach));
+ if (csr & csr_error) {
+ /* A DMA error occurred, so stop all DMA transfers in
+ * progress.
+ */
+ spin_unlock(&cam->dma_lock);
+ omap24xxcam_dma_stop(cam, csr);
+ return;
+ } else {
+ callback = cam->camdma[dmach].callback;
+ arg = cam->camdma[dmach].arg;
+ cam->free_dmach++;
+ if (callback) {
+ spin_unlock(&cam->dma_lock);
+ (*callback) (cam, csr, arg);
+ spin_lock(&cam->dma_lock);
+ }
+ }
+ }
+
+ spin_unlock(&cam->dma_lock);
+
+ omap24xxcam_sg_dma_process(cam);
+}
+
+/* Shutdown the camera DMA driver and controller. */
+static void
+omap24xxcam_dma_exit(struct omap24xxcam_device *cam)
+{
+ int ch;
+
+ /* Mask all DMA interrupts */
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L0, 0);
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L1, 0);
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L2, 0);
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L3, 0);
+
+ /* disable all DMA channels */
+ for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++)
+ camdma_reg_out(cam, CAMDMA_CCR(ch), 0);
+}
+
+/* Initialize the camera DMA driver. */
+static void
+omap24xxcam_dma_init(struct omap24xxcam_device *cam)
+{
+ int ch, sg;
+
+ /* group all channels on DMA IRQ0 and unmask irq */
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L0, 0xffffffff);
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L1, 0);
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L2, 0);
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L3, 0);
+
+ spin_lock_init(&cam->dma_lock);
+ cam->free_dmach = NUM_CAMDMA_CHANNELS;
+ cam->next_dmach = 0;
+ for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
+ cam->camdma[ch].callback = NULL;
+ cam->camdma[ch].arg = NULL;
+ }
+
+ spin_lock_init(&cam->sg_lock);
+ cam->free_sgdma = NUM_SG_DMA;
+ cam->next_sgdma = 0;
+ for (sg = 0; sg < NUM_SG_DMA; sg++) {
+ cam->sgdma[sg].sglen = 0;
+ cam->sgdma[sg].next_sglist = 0;
+ cam->sgdma[sg].queued_sglist = 0;
+ cam->sgdma[sg].csr = 0;
+ cam->sgdma[sg].callback = NULL;
+ cam->sgdma[sg].arg = NULL;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Callback routine for overlay DMA completion. We just start another DMA
+ * transfer unless overlay has been turned off.
+ */
+static void
+omap24xxcam_overlay_callback(struct omap24xxcam_device *cam, unsigned long csr,
+ void *arg)
+{
+ int err;
+ unsigned long irqflags;
+ const unsigned long csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock_irqsave(&cam->overlay_lock, irqflags);
+
+ if (cam->overlay_cnt > 0)
+ --cam->overlay_cnt;
+
+ if (!cam->previewing || (csr & csr_error)) {
+ spin_unlock_irqrestore(&cam->overlay_lock, irqflags);
+ return;
+ }
+
+ sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys;
+ sg_dma_len(&cam->overlay_sglist) = cam->pix.sizeimage;
+
+ while (cam->overlay_cnt < 2) {
+ err = omap24xxcam_sg_dma_queue(cam, &cam->overlay_sglist, 1,
+ omap24xxcam_overlay_callback,
+ NULL);
+ if (err)
+ break;
+ ++cam->overlay_cnt;
+ }
+
+ spin_unlock_irqrestore(&cam->overlay_lock, irqflags);
+}
+
+/* Begin DMA'ing into the camera overlay framebuffer. We queue up two DMA
+ * transfers so that all frames will be captured.
+ */
+static void
+omap24xxcam_start_overlay_dma(struct omap24xxcam_device *cam)
+{
+ int err;
+ unsigned long irqflags;
+
+ if (!cam->previewing)
+ return;
+
+ if (cam->pix.sizeimage > cam->overlay_size)
+ return;
+
+ spin_lock_irqsave(&cam->overlay_lock, irqflags);
+
+ sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys;
+ sg_dma_len(&cam->overlay_sglist) = cam->pix.sizeimage;
+
+ while (cam->overlay_cnt < 2) {
+ err = omap24xxcam_sg_dma_queue(cam, &cam->overlay_sglist, 1,
+ omap24xxcam_overlay_callback,
+ NULL);
+ if (err)
+ break;
+ ++cam->overlay_cnt;
+ }
+
+ spin_unlock_irqrestore(&cam->overlay_lock, irqflags);
+}
+
+/* Enable the camera core interface. */
+static void
+omap24xxcam_cc_enable(struct omap24xxcam_device *cam)
+{
+ /* Get the CC_CTRL register value for the current capture format. */
+ omap24xxcam_sensor_cc_ctrl(cam);
+
+ /* program the camera interface DMA packet size */
+ cc_reg_out(cam, CC_CTRL_DMA, CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
+
+ /* enable camera core error interrupts */
+ cc_reg_out(cam, CC_IRQENABLE, CC_IRQENABLE_FW_ERR_IRQ
+ | CC_IRQENABLE_FSC_ERR_IRQ | CC_IRQENABLE_SSC_ERR_IRQ
+ | CC_IRQENABLE_FIFO_OF_IRQ);
+
+ /* enable the camera interface */
+ cc_reg_out(cam, CC_CTRL, cam->cc_ctrl | CC_CTRL_CC_EN);
+}
+
+/* Error recovery for DMA errors and camera interface errors. */
+static void
+omap24xxcam_error_handler(struct omap24xxcam_device *cam)
+{
+ /* Get the CC_CTRL register value for the current capture format. */
+ omap24xxcam_sensor_cc_ctrl(cam);
+
+ /* Disable and reset the camera interface. */
+ cc_reg_out(cam, CC_CTRL,
+ cam->cc_ctrl & ~(CC_CTRL_CC_EN | CC_CTRL_CC_FRAME_TRIG));
+ cc_reg_out(cam, CC_CTRL, (cam->cc_ctrl | CC_CTRL_CC_RST)
+ & ~(CC_CTRL_CC_EN | CC_CTRL_CC_FRAME_TRIG));
+
+ /* Stop the DMA controller and frame sync scatter-gather DMA. */
+ omap24xxcam_sg_dma_sync(cam, CAMDMA_CSR_TRANS_ERR);
+
+ /* Reset and re-initialize the entire camera subsystem.
+ * Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
+ * register is supposed to be sufficient to recover from a
+ * camera interface error, but it doesn't seem to be enough. If
+ * we only do that then subsequent image captures are out of sync
+ * by either one or two times DMA_THRESHOLD bytes. Resetting and
+ * re-initializing the entire camera subsystem prevents the problem
+ * with frame synchronization. Calling cam_init() from an ISR is
+ * undesirable since it waits an indeterminate amount of time for the
+ * camera subsystem reset to complete. If we ever figure out exactly
+ * what needs to be reset to recover from a camera interface error,
+ * maybe we can replace this global reset with something less drastic.
+ */
+ cam_init(cam);
+ omap24xxcam_set_xclk(cam);
+ /* group all channels on DMA IRQ0 and unmask irq */
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L0, 0xffffffff);
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L1, 0);
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L2, 0);
+ camdma_reg_out(cam, CAMDMA_IRQENABLE_L3, 0);
+
+ if (cam->previewing || cam->streaming) {
+ /* Preview or capture is in progress, so we need to register
+ * our routine to restart the camera interface the next time a
+ * DMA transfer is queued.
+ */
+ omap24xxcam_dma_notify(cam, omap24xxcam_cc_enable);
+ }
+
+ /* Restart overlay DMA if preview is enabled. */
+ if (cam->previewing)
+ omap24xxcam_start_overlay_dma(cam);
+
+ /* Restart the scatter-gather DMA queue. */
+ omap24xxcam_sg_dma_process(cam);
+}
+
+/* Interrupt service routine for camera core interrupts. */
+static void
+omap24xxcam_cc_isr(struct omap24xxcam_device *cam)
+{
+ unsigned long cc_irqstatus;
+ const unsigned long cc_irqstatus_err = CC_IRQSTATUS_FW_ERR_IRQ
+ | CC_IRQSTATUS_FSC_ERR_IRQ | CC_IRQSTATUS_SSC_ERR_IRQ
+ | CC_IRQSTATUS_FIFO_OF_IRQ;
+
+ cc_irqstatus = cc_reg_in(cam, CC_IRQSTATUS);
+ cc_reg_out(cam, CC_IRQSTATUS, cc_irqstatus);
+
+ if (cc_irqstatus & cc_irqstatus_err)
+ omap24xxcam_error_handler(cam);
+}
+
+static irqreturn_t
+omap24xxcam_isr(int irq, void *arg, struct pt_regs *regs)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *) arg;
+ unsigned long irqstatus;
+ unsigned int irqhandled = 0;
+
+ irqstatus = cam_reg_in(cam, CAM_IRQSTATUS);
+ if (irqstatus &
+ (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
+ | CAM_IRQSTATUS_DMA_IRQ0)) {
+ omap24xxcam_dma_isr(cam);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
+ omap24xxcam_cc_isr(cam);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
+ printk(KERN_ERR CAM_NAME ": Unhandled camera MMU interrupt!\n");
+
+ return IRQ_RETVAL(irqhandled);
+}
+
+static void
+omap24xxcam_adjust_xclk(struct omap24xxcam_device *cam)
+{
+ unsigned long divisor;
+
+ if (cam->xclk > cam->mclk)
+ cam->xclk = cam->mclk;
+ divisor = cam->mclk / cam->xclk;
+ if (cam->xclk * divisor < cam->mclk)
+ divisor += 1;
+ if (divisor > 30)
+ divisor = 30;
+
+ cam->xclk = cam->mclk / divisor;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* This routine is called from interrupt context when a scatter-gather DMA
+ * transfer of a videobuf_buffer completes.
+ */
+static void
+omap24xxcam_vbq_complete(struct omap24xxcam_device *cam, unsigned long csr,
+ void *arg)
+{
+ struct videobuf_buffer *vb = (struct videobuf_buffer *) arg;
+ const unsigned long csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&cam->vbq_lock);
+
+ do_gettimeofday(&vb->ts);
+ vb->field_count = cam->field_count;
+ cam->field_count += 2;
+ if (csr & csr_error) {
+ vb->state = STATE_ERROR;
+ omap24xxcam_error_handler(cam);
+ } else
+ vb->state = STATE_DONE;
+ wake_up(&vb->done);
+ spin_unlock(&cam->vbq_lock);
+}
+
+static void
+omap24xxcam_vbq_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ videobuf_waiton(vb, 0, 0);
+ videobuf_dma_pci_unmap(NULL, &vb->dma);
+ videobuf_dma_free(&vb->dma);
+
+ vb->state = STATE_NEEDS_INIT;
+}
+
+/* Limit the number of available kernel image capture buffers based on the
+ * number requested, the currently selected image size, and the maximum
+ * amount of memory permitted for kernel capture buffers.
+ */
+static int
+omap24xxcam_vbq_setup(struct videobuf_queue *q, unsigned int *cnt, unsigned int *size)
+{
+ struct omap24xxcam_fh *fh = q->priv_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ unsigned long irqflags;
+
+ if (*cnt <= 0)
+ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+
+ if (*cnt > VIDEO_MAX_FRAME)
+ *cnt = VIDEO_MAX_FRAME;
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->still_capture && cam->cam_sensor->try_format_still_capture)
+ *size = cam->pix2.sizeimage;
+ else
+ *size = cam->pix.sizeimage;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+
+ while (*size * *cnt > capture_mem)
+ (*cnt)--;
+
+ return 0;
+}
+
+static int
+omap24xxcam_vbq_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap24xxcam_fh *fh = q->priv_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ unsigned int size;
+ unsigned long irqflags;
+ int err = 0;
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->still_capture && cam->cam_sensor->try_format_still_capture)
+ size = cam->pix2.sizeimage;
+ else
+ size = cam->pix.sizeimage;
+
+ if (vb->baddr) {
+ /* This is a userspace buffer. */
+ if (size > vb->bsize) {
+ /* The buffer isn't big enough. */
+ err = -EINVAL;
+ } else
+ vb->size = size;
+ } else if (!vb->baddr) {
+ if (vb->state != STATE_NEEDS_INIT) {
+ /* We have a kernel bounce buffer that has already been
+ * allocated.
+ */
+ if (size > vb->size) {
+ /* The image size has been changed to a larger
+ * size since this buffer was allocated, so we
+ * need to free and reallocate it.
+ */
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ omap24xxcam_vbq_release(q, vb);
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ vb->size = size;
+ }
+ } else {
+ /* We need to allocate a new kernel bounce buffer. */
+ vb->size = size;
+ }
+ }
+
+ if (cam->still_capture && cam->cam_sensor->try_format_still_capture) {
+ vb->width = cam->pix2.width;
+ vb->height = cam->pix2.height;
+ }
+ else {
+ vb->width = cam->pix.width;
+ vb->height = cam->pix.height;
+ }
+ vb->field = field;
+
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+
+ if (err)
+ return err;
+
+ if (vb->state == STATE_NEEDS_INIT)
+ err = videobuf_iolock(NULL, vb, NULL);
+
+ if (!err) {
+ vb->state = STATE_PREPARED;
+ if (vb->baddr) {
+ /* sync a userspace buffer */
+ consistent_sync((void *) vb->baddr, vb->bsize,
+ vb->dma.direction);
+ } else {
+ /* sync a kernel buffer */
+ consistent_sync(vb->dma.vmalloc, vb->dma.sglen/*size*/,
+ vb->dma.direction);
+ }
+ } else
+ omap24xxcam_vbq_release(q, vb);
+
+ return err;
+}
+
+static void
+omap24xxcam_vbq_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct omap24xxcam_fh *fh = q->priv_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ enum videobuf_state state = vb->state;
+ int err;
+
+ vb->state = STATE_QUEUED;
+ err = omap24xxcam_sg_dma_queue(cam, vb->dma.sglist, vb->dma.sglen,
+ omap24xxcam_vbq_complete, vb);
+ if (err) {
+ /* Oops. We're not supposed to get any errors here. The only
+ * way we could get an error is if we ran out of scatter-gather
+ * DMA slots, but we are supposed to have at least as many
+ * scatter-gather DMA slots as video buffers so that can't
+ * happen.
+ */
+ printk(KERN_DEBUG CAM_NAME
+ ": Failed to queue a video buffer for DMA!\n");
+ vb->state = state;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int
+omap24xxcam_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ void *arg)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ unsigned long irqflags;
+ int err;
+
+ switch (cmd) {
+ /* for time being below IOCTL cmd is here */
+
+ case VIDIOC_ENUMINPUT:
+ {
+ /* default handler assumes 1 video input (the camera) */
+ struct v4l2_input *input = (struct v4l2_input *) arg;
+ int index = input->index;
+
+ memset(input, 0, sizeof (*input));
+ input->index = index;
+
+ if (index > 0)
+ return -EINVAL;
+
+ strncpy(input->name, "camera", sizeof (input->name));
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+ }
+
+ case VIDIOC_G_INPUT:
+ {
+ unsigned int *input = arg;
+ *input = 0;
+
+ return 0;
+ }
+
+ case VIDIOC_S_INPUT:
+ {
+ unsigned int *input = arg;
+
+ if (*input > 0)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap =
+ (struct v4l2_capability *) arg;
+
+ memset(cap, 0, sizeof (*cap));
+ strncpy(cap->driver, CAM_NAME, sizeof (cap->driver));
+ strncpy(cap->card, cam->vfd->name, sizeof (cap->card));
+ cap->bus_info[0] = '\0';
+ cap->version = KERNEL_VERSION(0, 0, 0);
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ return 0;
+ }
+
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *) arg;
+ switch (f->type) {
+ case V4L2_BUF_TYPE_STILL_CAPTURE:
+ {
+ if (cam->cam_sensor->try_format_still_capture){
+ struct v4l2_pix_format *pix =
+ &f->fmt.pix;
+ memset(pix, 0, sizeof (*pix));
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ *pix = cam->pix2;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return 0;
+ }
+ /* else fall through */
+ }
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ struct v4l2_pix_format *pix =
+ &f->fmt.pix;
+ memset(pix, 0, sizeof (*pix));
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ *pix = cam->pix;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return 0;
+ }
+
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ {
+ struct v4l2_window *win = &f->fmt.win;
+ memset(win, 0, sizeof (*win));
+ /* The API has a bit of a problem here.
+ * We're returning a v4l2_window
+ * structure, but that structure
+ * contains pointers to variable-sized
+ * objects for clipping rectangles and
+ * clipping bitmaps. We will just
+ * return NULLs for those pointers.
+ */
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ win->w = cam->win.w;
+ win->field = cam->win.field;
+ win->chromakey = cam->win.chromakey;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return 0;
+ }
+
+ default:
+ {
+ return -EINVAL;
+ }
+ }
+ }
+
+ case VIDIOC_TRY_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *) arg;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ {
+ struct v4l2_window *win = &f->fmt.win;
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ err =
+ omap24xxvout_try_window(&cam->fbuf,
+ win);
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return err;
+ }
+
+ case V4L2_BUF_TYPE_STILL_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ return cam->cam_sensor->try_format(&f->
+ fmt.
+ pix,
+ cam->
+ sensor);
+ }
+
+ default:
+ {
+ return -EINVAL;
+ }
+ }
+ }
+
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *) arg;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ {
+ struct v4l2_window *win = &f->fmt.win;
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->previewing || cam->streaming) {
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return -EBUSY;
+ }
+ /* Get the panel parameters.
+ * They can change from LCD to TV
+ * or TV to LCD
+ */
+ omap2_disp_get_panel_size(
+ omap2_disp_get_output_dev(cam->vid_preview),
+ &(cam->fbuf.fmt.width),
+ &(cam->fbuf.fmt.height));
+
+ err =
+ omap24xxvout_new_window(&cam->crop,
+ &cam->win,
+ &cam->fbuf,
+ win);
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return err;
+ }
+
+ case V4L2_BUF_TYPE_STILL_CAPTURE:
+ {
+ if (cam->cam_sensor->try_format_still_capture &&
+ cam->cam_sensor->configure_still_capture) {
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ cam->cam_sensor->try_format_still_capture(&f->fmt.pix,
+ cam->
+ sensor);
+ /* set the new capture format */
+ cam->pix2 = f->fmt.pix;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ err =
+ cam->cam_sensor->configure_still_capture
+ (&cam->
+ pix2,
+ cam->
+ xclk,
+ &cam->
+ cparm.
+ timeperframe,
+ cam->
+ sensor);
+ return err;
+ }
+ /* else fall through */
+ }
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->streaming || cam->previewing) {
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return -EBUSY;
+ }
+ cam->cam_sensor->try_format(&f->fmt.pix,
+ cam->
+ sensor);
+ /* set the new capture format */
+ cam->pix = f->fmt.pix;
+ /* adjust the capture frame rate */
+ cam->xclk =
+ cam->cam_sensor->calc_xclk(&cam->
+ pix,
+ &cam->
+ nominal_timeperframe,
+ cam->
+ sensor);
+ omap24xxcam_adjust_xclk(cam);
+ cam->cparm.timeperframe =
+ cam->nominal_timeperframe;
+ /* Get the panel parameters.
+ * They can change from LCD to TV
+ * or TV to LCD
+ */
+ omap2_disp_get_panel_size(
+ omap2_disp_get_output_dev(cam->vid_preview),
+ &(cam->fbuf.fmt.width),
+ &(cam->fbuf.fmt.height));
+
+ /* intialize the preview parameters */
+ omap24xxvout_new_format(&cam->pix,
+ &cam->fbuf,
+ &cam->crop,
+ &cam->win);
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ /* program xclk */
+ omap24xxcam_set_xclk(cam);
+ /* program the sensor */
+ err =
+ cam->cam_sensor->configure(&cam->
+ pix,
+ cam->
+ xclk,
+ &cam->
+ cparm.
+ timeperframe,
+ cam->
+ sensor);
+ return err;
+ }
+
+ default:
+ {
+ return -EINVAL;
+ }
+ }
+ }
+
+ case VIDIOC_G_FBUF:
+ {
+ struct v4l2_framebuffer *fbuf =
+ (struct v4l2_framebuffer *) arg;
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ *fbuf = cam->fbuf;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return 0;
+ }
+
+ case VIDIOC_S_FBUF:
+ {
+ struct v4l2_framebuffer *fbuf =
+ (struct v4l2_framebuffer *) arg;
+ unsigned int flags = fbuf->flags;
+
+ /* The only field the user is allowed to change is
+ * fbuf->flags.
+ */
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->previewing) {
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return -EBUSY;
+ }
+ if (flags & V4L2_FBUF_FLAG_CHROMAKEY)
+ cam->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+ else
+ cam->fbuf.flags &= ~V4L2_FBUF_FLAG_CHROMAKEY;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return 0;
+ }
+
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cropcap =
+ (struct v4l2_cropcap *) arg;
+ enum v4l2_buf_type type = cropcap->type;
+
+ memset(cropcap, 0, sizeof (*cropcap));
+ cropcap->type = type;
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ {
+ struct v4l2_pix_format *pix = &cam->pix;
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ cropcap->bounds.width = pix->width & ~1;
+ cropcap->bounds.height =
+ pix->height & ~1;
+ omap24xxvout_default_crop(&cam->pix,
+ &cam->fbuf,
+ &cropcap->
+ defrect);
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ cropcap->pixelaspect.numerator = 1;
+ cropcap->pixelaspect.denominator = 1;
+ return 0;
+ }
+
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ /* We're required to support the CROPCAP
+ * ioctl even though the G_CROP/S_CROP
+ * ioctls are optional, which seems a
+ * little strange. We don't support
+ * cropping of captured images.
+ */
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ cropcap->bounds.width = cam->pix.width;
+ cropcap->bounds.height =
+ cam->pix.height;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ cropcap->defrect.width =
+ cropcap->bounds.width;
+ cropcap->defrect.height =
+ cropcap->bounds.height;
+ cropcap->pixelaspect.numerator = 1;
+ cropcap->pixelaspect.denominator = 1;
+ return 0;
+ }
+
+ default:
+ {
+ return -EINVAL;
+ }
+ }
+ }
+
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = (struct v4l2_crop *) arg;
+
+ switch (crop->type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ {
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ crop->c = cam->crop;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return 0;
+ }
+
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ /* We don't support cropping of captured
+ * images.
+ */
+ return -EINVAL;
+ }
+
+ default:
+ {
+ return -EINVAL;
+ }
+ }
+ }
+
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = (struct v4l2_crop *) arg;
+
+ switch (crop->type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ {
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->previewing) {
+ spin_unlock(&cam->img_lock);
+ return -EBUSY;
+ }
+ err = omap24xxvout_new_crop(&cam->pix,
+ &cam->crop,
+ &cam->win,
+ &cam->fbuf,
+ &crop->c);
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return err;
+ }
+
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ /* We don't support cropping of captured
+ * images.
+ */
+ return -EINVAL;
+ }
+
+ default:
+ {
+ return -EINVAL;
+ }
+ }
+ }
+
+ case VIDIOC_G_PARM:
+ {
+ struct v4l2_streamparm *parm =
+ (struct v4l2_streamparm *) arg;
+ enum v4l2_buf_type type = parm->type;
+
+ memset(parm, 0, sizeof (*parm));
+ parm->type = type;
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ parm->parm.capture = cam->cparm;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return 0;
+ }
+
+ case VIDIOC_S_PARM:
+ {
+ struct v4l2_streamparm *parm =
+ (struct v4l2_streamparm *) arg;
+ struct v4l2_captureparm *cparm = &parm->parm.capture;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->streaming || cam->previewing) {
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return -EBUSY;
+ }
+ cam->cparm.capturemode = cparm->capturemode;
+ if (cparm->timeperframe.numerator
+ && cparm->timeperframe.denominator) {
+ cam->nominal_timeperframe = cparm->timeperframe;
+ /* adjust the capture frame rate */
+ cam->xclk =
+ cam->cam_sensor->calc_xclk(&cam->pix,
+ &cam->
+ nominal_timeperframe,
+ cam->sensor);
+ omap24xxcam_adjust_xclk(cam);
+ cam->cparm.timeperframe =
+ cam->nominal_timeperframe;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ /* program xclk */
+ omap24xxcam_set_xclk(cam);
+ /* program the sensor */
+ err =
+ cam->cam_sensor->configure(&cam->pix,
+ cam->xclk,
+ &cam->cparm.
+ timeperframe,
+ cam->sensor);
+ cparm->timeperframe = cam->cparm.timeperframe;
+ } else {
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ }
+ return 0;
+ }
+
+ case VIDIOC_OVERLAY:
+ {
+ int *on = arg;
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+
+ if (!cam->previewing && *on) {
+ if (cam->pix.sizeimage <= cam->overlay_size) {
+ /* V1 is the default for preview */
+ cam->vid_preview = (*on == 2) ?
+ OMAP2_VIDEO2 : OMAP2_VIDEO1;
+ if (!omap2_disp_request_layer
+ (cam->vid_preview)) {
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return -EBUSY;
+ }
+ omap2_disp_get_dss();
+
+ /* Turn on the overlay window */
+ omap2_disp_config_vlayer(cam->
+ vid_preview,
+ &cam->pix,
+ &cam->crop,
+ &cam->win, -1,
+ 0);
+ omap2_disp_start_vlayer(cam->
+ vid_preview,
+ &cam->pix,
+ &cam->crop,
+ cam->
+ overlay_base_phys,
+ -1, 0);
+ cam->previewing = fh;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ /* start the camera interface */
+ omap24xxcam_dma_notify(cam,
+ omap24xxcam_cc_enable);
+ omap24xxcam_start_overlay_dma(cam);
+ } else {
+ /* Image size is bigger than overlay
+ * buffer.
+ */
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return -EINVAL;
+ }
+ } else if (!*on) {
+ /* turn overlay off */
+ omap2_disp_disable_layer(cam->vid_preview);
+ omap2_disp_release_layer(cam->vid_preview);
+ omap2_disp_put_dss();
+ cam->previewing = NULL;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ } else
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return 0;
+ }
+
+ case VIDIOC_REQBUFS:
+ return videobuf_reqbufs(&fh->vbq, arg);
+
+ case VIDIOC_QUERYBUF:
+ return videobuf_querybuf(&fh->vbq, arg);
+
+ case VIDIOC_QBUF:
+ return videobuf_qbuf(&fh->vbq, arg);
+
+ case VIDIOC_DQBUF:
+ return videobuf_dqbuf(&fh->vbq, arg, file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_STREAMON:
+ {
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->streaming) {
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return -EBUSY;
+ } else
+ cam->streaming = fh;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+
+ omap24xxcam_dma_notify(cam, omap24xxcam_cc_enable);
+ return videobuf_streamon(&fh->vbq);
+ }
+
+ case VIDIOC_STREAMOFF:
+ {
+ struct videobuf_queue *q = &fh->vbq;
+ int i;
+
+ /* video-buf lib has trouble to turn off streaming while
+ any buffer is still in QUEUED state. Let's wait until
+ all queued buffers are filled.
+ */
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ while (q->bufs[i]->state == STATE_QUEUED) ;
+ }
+
+ err = videobuf_streamoff(q);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ if (q->bufs[i]->memory == V4L2_MEMORY_USERPTR)
+ omap24xxcam_vbq_release(q,
+ q->bufs[i]);
+ }
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->streaming == fh)
+ cam->streaming = NULL;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+
+ return 0;
+ }
+
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *vc = arg;
+ return cam->cam_sensor->get_control(vc, cam->sensor);
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *vc = arg;
+ return cam->cam_sensor->set_control(vc, cam->sensor);
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+ return cam->cam_sensor->query_control(qc, cam->sensor);
+ }
+ case VIDIOC_QUERYMENU:
+ {
+ return -EINVAL;
+ }
+
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_QUERYSTD:
+ {
+ /* Digital cameras don't have an analog video standard,
+ * so we don't need to implement these ioctls.
+ */
+ return -EINVAL;
+ }
+
+ case VIDIOC_G_AUDIO:
+ case VIDIOC_S_AUDIO:
+ case VIDIOC_G_AUDOUT:
+ case VIDIOC_S_AUDOUT:
+ {
+ /* we don't have any audio inputs or outputs */
+ return -EINVAL;
+ }
+
+ case VIDIOC_G_JPEGCOMP:
+ case VIDIOC_S_JPEGCOMP:
+ {
+ /* JPEG compression is not supported */
+ return -EINVAL;
+ }
+
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_MODULATOR:
+ case VIDIOC_S_MODULATOR:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ {
+ /* we don't have a tuner or modulator */
+ return -EINVAL;
+ }
+
+ case VIDIOC_ENUMOUTPUT:
+ case VIDIOC_G_OUTPUT:
+ case VIDIOC_S_OUTPUT:
+ {
+ /* we don't have any video outputs */
+ return -EINVAL;
+ }
+
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *fmt = arg;
+ return cam->cam_sensor->enum_pixformat(fmt,
+ cam->sensor);
+ }
+
+ default:
+ {
+ /* unrecognized ioctl */
+ return -ENOIOCTLCMD;
+ }
+ }
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+ /*
+ * file operations
+ */
+
+static unsigned
+ int
+omap24xxcam_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_buffer *vb;
+ enum v4l2_field field;
+ unsigned long irqflags;
+
+ omap24xxcam_suspend_lockout(cam, file);
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+
+ if (cam->streaming == fh) {
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ /* streaming capture */
+ if (list_empty(&fh->vbq.stream))
+ return POLLERR;
+ vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer,
+ stream);
+ } else if (cam->streaming) {
+ /* streaming I/O is in progress on another file descriptor */
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return POLLERR;
+ } else {
+ /* read() capture */
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ down(&fh->vbq.lock);
+ if (fh->vbq.read_buf == NULL) {
+ /* need to capture a new image */
+ fh->vbq.read_buf = videobuf_alloc(fh->vbq.msize);
+ if (fh->vbq.read_buf == NULL) {
+ up(&fh->vbq.lock);
+ return POLLERR;
+ }
+ fh->vbq.read_buf->memory = V4L2_MEMORY_USERPTR;
+ field = videobuf_next_field(&fh->vbq);
+ if (fh->vbq.ops->buf_prepare(&fh->vbq, fh->vbq.read_buf,
+ field) != 0) {
+ up(&fh->vbq.lock);
+ return POLLERR;
+ }
+
+ omap24xxcam_dma_notify(cam, omap24xxcam_cc_enable);
+ fh->vbq.ops->buf_queue(&fh->vbq, fh->vbq.read_buf);
+ fh->vbq.read_off = 0;
+ }
+ up(&fh->vbq.lock);
+ vb = (struct videobuf_buffer *) fh->vbq.read_buf;
+ }
+
+ poll_wait(file, &vb->done, wait);
+ if (vb->state == STATE_DONE || vb->state == STATE_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static ssize_t
+omap24xxcam_read(struct file *file, char *data, size_t count, loff_t * ppos)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct omap24xxcam_fh *preview_fh;
+ unsigned long irqflags;
+ int free_sgdma, err;
+
+ omap24xxcam_suspend_lockout(cam, file);
+
+ /* user buffer has to be word aligned */
+ if (((unsigned int) data & 0x3) != 0)
+ return -EIO;
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->streaming) {
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ return -EBUSY;
+ }
+ preview_fh = NULL;
+ if (cam->previewing) {
+ preview_fh = cam->previewing;
+ /* stop preview */
+ cam->previewing = NULL;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ /* We need wait until sgdmas used by preview are freed.
+ * To minimize the shot-to-shot delay, we don't want to
+ * yield. Just want to start one-shot capture as soon as
+ * possible. An alternative is to stop the dma but the
+ * sync error would require a reset of the camera system.
+ */
+ do {
+ /* prevent the race with dma handler */
+ spin_lock_irqsave(&cam->sg_lock, irqflags);
+ free_sgdma = cam->free_sgdma;
+ spin_unlock_irqrestore(&cam->sg_lock, irqflags);
+ } while (NUM_SG_DMA != free_sgdma);
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ }
+ cam->still_capture = 1;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+
+ if (cam->cam_sensor->enter_still_capture)
+ cam->cam_sensor->enter_still_capture(16, cam->sensor);
+ omap24xxcam_dma_notify(cam, omap24xxcam_cc_enable);
+ if (((unsigned int) data & (DMA_THRESHOLD - 1)) == 0) {
+ /* use zero-copy if user buffer is aligned with DMA_THRESHOLD */
+ err = videobuf_read_one(&fh->vbq, data, count, ppos, file->f_flags & O_NONBLOCK);
+ } else {
+ /* if user buffer is not aligned with DMA_THRESHOLD, the last DMA
+ transfer to the first page frame is less than DMA_THRESHOLD.
+ Camera DMA physical channel 1 doesn't seem to handle this
+ partial transfer correctly. We end up with sync problem with
+ an offset of DMA_THRESHOLD. For this case, We will use kernel
+ bounce buffer to capture the image pretending that we want to
+ read one pixel less than the actual image size.
+ */
+ err = videobuf_read_one(&fh->vbq, data, count - 2, ppos, file->f_flags & O_NONBLOCK);
+ }
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ cam->still_capture = 0;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+
+ if (cam->cam_sensor->exit_still_capture)
+ cam->cam_sensor->exit_still_capture(cam->sensor);
+
+ /* if previwing was on, re-start it after the read */
+ if (preview_fh) { /* was previewing */
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ cam->previewing = preview_fh;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ omap24xxcam_dma_notify(cam, omap24xxcam_cc_enable);
+ omap24xxcam_start_overlay_dma(cam);
+ }
+
+ return err;
+}
+
+static int
+omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+
+ omap24xxcam_suspend_lockout(cam, file);
+ return videobuf_mmap_mapper(&fh->vbq, vma);
+}
+
+static int
+omap24xxcam_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ omap24xxcam_suspend_lockout(cam, file);
+ return video_usercopy(inode, file, cmd, arg, omap24xxcam_do_ioctl);
+}
+
+static int
+omap24xxcam_release(struct inode *inode, struct file *file)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_queue *q = &fh->vbq;
+ unsigned long irqflags;
+ int i;
+
+ omap24xxcam_suspend_lockout(cam, file);
+
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ /* turn off overlay */
+ if (cam->previewing == fh) {
+ cam->previewing = NULL;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ omap2_disp_disable_layer(cam->vid_preview);
+ omap2_disp_release_layer(cam->vid_preview);
+ omap2_disp_put_dss();
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ }
+
+ /* stop streaming capture */
+ if (cam->streaming == fh) {
+ cam->streaming = NULL;
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ if (q->bufs[i]->memory == V4L2_MEMORY_USERPTR)
+ omap24xxcam_vbq_release(q, q->bufs[i]);
+ }
+ videobuf_streamoff(q);
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ }
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+
+ /* release read_buf videobuf_buffer struct */
+ if (fh->vbq.read_buf) {
+ omap24xxcam_vbq_release(q, fh->vbq.read_buf);
+ kfree(fh->vbq.read_buf);
+ }
+
+ /* free video_buffer objects */
+ videobuf_mmap_free(q);
+
+ file->private_data = NULL;
+ kfree(fh);
+
+ return 0;
+}
+
+static int
+omap24xxcam_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct omap24xxcam_device *cam = saved_cam;
+ struct omap24xxcam_fh *fh;
+
+ if (!cam || !cam->vfd || (cam->vfd->minor != minor))
+ return -ENODEV;
+
+ omap24xxcam_suspend_lockout(cam, file);
+
+ /* allocate per-filehandle data */
+ fh = kmalloc(sizeof (*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+ file->private_data = fh;
+ fh->cam = cam;
+ fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ videobuf_queue_init(&fh->vbq, &cam->vbq_ops, NULL, &cam->vbq_lock,
+ fh->type, V4L2_FIELD_NONE,
+ sizeof (struct videobuf_buffer), fh);
+
+ return 0;
+}
+
+static struct file_operations omap24xxcam_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = omap24xxcam_read,
+ .poll = omap24xxcam_poll,
+ .ioctl = omap24xxcam_ioctl,
+ .mmap = omap24xxcam_mmap,
+ .open = omap24xxcam_open,
+ .release = omap24xxcam_release,
+};
+
+/* -------------------------------------------------------------------------- */
+static int
+omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+ unsigned long irqflags;
+
+ /*if (level != SUSPEND_POWER_DOWN)
+ return 0;*/
+
+ if (cam->suspended)
+ return 0;
+ /* lock-out applications during suspend */
+ cam->suspended = 1;
+
+ /* stall previewing */
+ spin_lock_irqsave(&cam->img_lock, irqflags);
+ if (cam->previewing) {
+ omap2_disp_disable_layer(cam->vid_preview);
+ /* we still hold the video layer */
+ omap2_disp_put_dss();
+ }
+ spin_unlock_irqrestore(&cam->img_lock, irqflags);
+
+ /* Get the CC_CTRL register value for the current capture format. */
+ omap24xxcam_sensor_cc_ctrl(cam);
+
+ /* Disable the camera interface. */
+ cc_reg_out(cam, CC_CTRL,
+ cam->cc_ctrl & ~(CC_CTRL_CC_EN | CC_CTRL_CC_FRAME_TRIG));
+
+ /* Stop the DMA controller and frame sync scatter-gather DMA. */
+ omap24xxcam_sg_dma_sync(cam, CAMDMA_CSR_TRANS_ERR);
+
+ /* power down the sensor */
+ cam->cam_sensor->power_off(cam->sensor);
+
+ /* stop XCLK */
+ cc_reg_out(cam, CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+
+ clk_disable(cam->cami);
+ clk_disable(cam->camf);
+
+ return 0;
+}
+static int
+omap24xxcam_resume(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ /*if (level != RESUME_POWER_ON)
+ return 0;*/
+
+ if (!cam->suspended)
+ return 0;
+
+ clk_enable(cam->cami);
+ clk_enable(cam->camf);
+
+ cam_init(cam);
+
+ /* set XCLK */
+ omap24xxcam_set_xclk(cam);
+
+ /* power up the sensor */
+ cam->cam_sensor->power_on(cam->sensor);
+
+ if (cam->streaming || cam->previewing) {
+ /* capture or previewing was in progress, so we need to register
+ * our routine to restart the camera interface the next time a
+ * DMA transfer is queued.
+ */
+ omap24xxcam_dma_notify(cam, omap24xxcam_cc_enable);
+ }
+ if (cam->previewing) {
+ omap2_disp_get_dss();
+ omap2_disp_enable_layer(cam->vid_preview);
+ omap24xxcam_start_overlay_dma(cam);
+ }
+ omap24xxcam_sg_dma_process(cam);
+
+ /* camera interface will be enabled through dma_notify function
+ ** automatically when new dma starts
+ */
+
+ /* wake up applications waiting on suspend queue */
+ cam->suspended = 0;
+ wake_up(&cam->suspend_wq);
+ return 0;
+}
+
+static int
+omap24xxcam_probe(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam;
+ struct video_device *vfd;
+
+ cam = kmalloc(sizeof (struct omap24xxcam_device), GFP_KERNEL);
+ if (!cam) {
+ printk(KERN_ERR CAM_NAME ": could not allocate memory\n");
+ goto init_error;
+ }
+ memset(cam, 0, sizeof (struct omap24xxcam_device));
+ saved_cam = cam;
+
+ cam->suspended = 0;
+ init_waitqueue_head(&cam->suspend_wq);
+
+ /* initialize the video_device struct */
+ vfd = cam->vfd = video_device_alloc();
+ if (!vfd) {
+ printk(KERN_ERR CAM_NAME
+ ": could not allocate video device struct\n");
+ goto init_error;
+ }
+ vfd->release = video_device_release;
+
+ strncpy(vfd->name, CAM_NAME, sizeof (vfd->name));
+ vfd->type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CHROMAKEY;
+ /* need to register for a VID_HARDWARE_* ID in videodev.h */
+ vfd->hardware = 0;
+ vfd->fops = &omap24xxcam_fops;
+ video_set_drvdata(vfd, cam);
+ vfd->minor = -1;
+
+ /* initialize the videobuf queue ops */
+ cam->vbq_ops.buf_setup = omap24xxcam_vbq_setup;
+ cam->vbq_ops.buf_prepare = omap24xxcam_vbq_prepare;
+ cam->vbq_ops.buf_queue = omap24xxcam_vbq_queue;
+ cam->vbq_ops.buf_release = omap24xxcam_vbq_release;
+ spin_lock_init(&cam->vbq_lock);
+
+ /* Impose a lower limit on the amount of memory allocated for capture.
+ * We require at least enough memory to double-buffer QVGA (300KB).
+ */
+ if (capture_mem < QVGA_SIZE * 2)
+ capture_mem = QVGA_SIZE * 2;
+
+ /* request the mem region for the camera registers */
+ if (!request_mem_region(CAM_REG_BASE, CAM_REG_SIZE, vfd->name)) {
+ printk(KERN_ERR CAM_NAME
+ ": cannot reserve camera register I/O region\n");
+ goto init_error;
+ }
+ cam->cam_mmio_base_phys = CAM_REG_BASE;
+ cam->cam_mmio_size = CAM_REG_SIZE;
+
+ /* map the region */
+ cam->cam_mmio_base = (unsigned long)
+ ioremap(cam->cam_mmio_base_phys, cam->cam_mmio_size);
+ if (!cam->cam_mmio_base) {
+ printk(KERN_ERR CAM_NAME
+ ": cannot map camera register I/O region\n");
+ goto init_error;
+ }
+
+ /* allocate coherent memory for the overlay framebuffer */
+ cam->overlay_size = overlay_mem;
+ if (cam->overlay_size > 0) {
+ cam->overlay_base = (unsigned long) dma_alloc_coherent(NULL,
+ cam->overlay_size,
+ (dma_addr_t *) &cam->overlay_base_phys,
+ GFP_KERNEL | GFP_DMA);
+ if (!cam->overlay_base) {
+ printk(KERN_ERR CAM_NAME
+ ": cannot allocate overlay framebuffer\n");
+ goto init_error;
+ }
+ }
+ memset((void *) cam->overlay_base, 0, cam->overlay_size);
+
+ /* initialize the overlay spinlock */
+ spin_lock_init(&cam->overlay_lock);
+
+ /* Enable interface & functional clocks */
+ cam->cami = clk_get(NULL,"cam_ick");
+ cam->camf = clk_get(NULL,"cam_fck");
+ clk_enable(cam->cami);
+ clk_enable(cam->camf);
+
+ /* initialize the camera interface */
+ cam_init(cam);
+
+ /* initialize the spinlock used to serialize access to the image
+ * parameters
+ */
+ spin_lock_init(&cam->img_lock);
+ cam->cam_sensor = &camera_sensor_if;
+
+ /* initialize the camera interface functional clock frequency */
+ cam->mclk = DEFAULT_CAM_FUNC_CLK;
+
+ /* initialize the streaming capture parameters */
+ cam->cparm.readbuffers = 1;
+ cam->cparm.capability = V4L2_CAP_TIMEPERFRAME;
+
+ /* Enable the xclk output. The sensor may (and does, in the case of
+ * the OV9640) require an xclk input in order for its initialization
+ * routine to work.
+ */
+ cam->xclk = DEFAULT_SENSOR_XCLK; /* choose an arbitrary xclk frequency */
+ omap24xxcam_set_xclk(cam);
+
+ /* get the framebuffer parameters in case the sensor init routine
+ * needs them
+ */
+ /* the display controller video layer used for camera preview */
+ cam->vid_preview = OMAP2_VIDEO1;
+ omap2_disp_get_panel_size(omap2_disp_get_output_dev(cam->vid_preview),
+ &(cam->fbuf.fmt.width),
+ &(cam->fbuf.fmt.height));
+
+ /* select an arbitrary default capture frame rate of 15fps */
+ cam->nominal_timeperframe.numerator = 1;
+ cam->nominal_timeperframe.denominator = 15;
+
+ /* initialize the sensor and define a default capture format cam->pix */
+ cam->sensor = cam->cam_sensor->init(&cam->pix, &cam->pix2);
+ if (!cam->sensor) {
+ printk(KERN_ERR CAM_NAME ": cannot initialize sensor\n");
+ goto init_error;
+ }
+ printk(KERN_INFO "Sensor is %s\n", cam->cam_sensor->name);
+
+ /* calculate xclk based on the default capture format and default
+ * frame rate
+ */
+ cam->xclk = cam->cam_sensor->calc_xclk(&cam->pix,
+ &cam->nominal_timeperframe,
+ cam->sensor);
+ omap24xxcam_adjust_xclk(cam);
+ cam->cparm.timeperframe = cam->nominal_timeperframe;
+
+ /* program the camera interface to generate the new xclk frequency */
+ omap24xxcam_set_xclk(cam);
+
+ /* initialize the image preview parameters based on the default capture
+ * format
+ */
+ omap24xxvout_new_format(&cam->pix, &cam->fbuf, &cam->crop, &cam->win);
+
+ /* program the sensor for the default capture format and rate */
+ cam->cam_sensor->configure(&cam->pix, cam->xclk,
+ &cam->cparm.timeperframe, cam->sensor);
+ if (cam->cam_sensor->configure_still_capture)
+ cam->cam_sensor->configure_still_capture(&cam->pix2, cam->xclk,
+ &cam->cparm.timeperframe, cam->sensor);
+
+ /* initialize the DMA driver */
+ omap24xxcam_dma_init(cam);
+
+ /* install the interrupt service routine */
+ if (request_irq(INT_24XX_CAMDMA_IRQ24, omap24xxcam_isr, 0, CAM_NAME, cam)) {
+ printk(KERN_ERR CAM_NAME
+ ": could not install interrupt service routine\n");
+ goto init_error;
+ }
+ cam->irq = INT_24XX_CAMDMA_IRQ24;
+
+ if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+ printk(KERN_ERR CAM_NAME
+ ": could not register Video for Linux device\n");
+ vfd->minor = -1;
+ goto init_error;
+ }
+ /* set driver specific data to use in power management functions */
+ platform_set_drvdata(pdev, cam);
+
+ printk(KERN_INFO CAM_NAME
+ ": registered device video%d [v4l2]\n", vfd->minor);
+
+#undef CAMERA_TEST
+#ifdef CAMERA_TEST
+ if (cam->pix.sizeimage <= cam->overlay_size) {
+ printk(KERN_INFO CAM_NAME ": Camera test--enabling overlay\n");
+ /* turn on the video overlay */
+ omap2_disp_config_vlayer(cam->vid_preview, &cam->pix,
+ &cam->crop, &cam->win, -1, 0);
+ omap2_disp_start_vlayer(cam->vid_preview, &cam->pix, &cam->crop,
+ cam->overlay_base_phys, -1, 0);
+ cam->previewing = (struct omap24xxcam_fh *) 1;
+ /* start the camera interface */
+ omap24xxcam_dma_notify(cam, omap24xxcam_cc_enable);
+ omap24xxcam_start_overlay_dma(cam);
+ } else {
+ printk(KERN_INFO CAM_NAME
+ ": Can't start camera test--overlay buffer too"
+ " small\n");
+ }
+#endif
+
+ return 0;
+
+ init_error:
+ omap24xxcam_cleanup();
+ return -ENODEV;
+}
+
+static int omap24xxcam_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver omap24xxcam_pdriver = {
+ .probe = omap24xxcam_probe,
+ .remove = omap24xxcam_remove,
+#ifdef CONFIG_PM
+ .suspend = omap24xxcam_suspend,
+ .resume = omap24xxcam_resume,
+#else
+ .suspend = NULL,
+ .resume = NULL,
+#endif
+ .shutdown = NULL,
+ .driver {
+ .name = CAM_NAME,
+ },
+};
+
+static struct platform_device omap24xxcam_dev = {
+ .name = CAM_NAME,
+ .dev =
+ {
+ .release = NULL,
+ },
+ .id = 0,
+};
+
+int __init
+omap24xxcam_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&omap24xxcam_pdriver);
+ if (ret != 0)
+ return ret;
+ ret = platform_device_register(&omap24xxcam_dev);
+ if ( ret != 0) {
+ platform_driver_unregister(&omap24xxcam_pdriver);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+omap24xxcam_cleanup(void)
+{
+ struct omap24xxcam_device *cam = saved_cam;
+ struct video_device *vfd;
+
+ if (!cam)
+ return;
+ vfd = cam->vfd;
+
+ if (vfd) {
+ if (vfd->minor == -1) {
+ /* The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(vfd);
+ } else {
+ /* The unregister function will release the video_device
+ * struct as well as unregistering it.
+ */
+ video_unregister_device(vfd);
+ }
+ cam->vfd = NULL;
+ }
+
+ if (cam->irq) {
+ free_irq(cam->irq, cam);
+ cam->irq = 0;
+ }
+
+ omap24xxcam_dma_exit(cam);
+ if (cam->sensor)
+ cam->cam_sensor->cleanup(cam->sensor);
+ /* sensor allocated private data is gone */
+ cam->sensor = NULL;
+
+ if (cam->overlay_base) {
+ dma_free_coherent(NULL, cam->overlay_size,
+ (void *)cam->overlay_base, cam->overlay_base_phys);
+ cam->overlay_base = 0;
+ }
+ cam->overlay_base_phys = 0;
+
+ if (cam->cam_mmio_base) {
+ iounmap((void *) cam->cam_mmio_base);
+ cam->cam_mmio_base = 0;
+ }
+ if (cam->cam_mmio_base_phys) {
+ release_mem_region(cam->cam_mmio_base_phys, cam->cam_mmio_size);
+ cam->cam_mmio_base_phys = 0;
+ }
+ platform_device_unregister(&omap24xxcam_dev);
+ platform_driver_unregister(&omap24xxcam_pdriver);
+
+ /*clk_put(cam->cami);
+ clk_put(cam->camf);*/
+ clk_disable(cam->cami);
+ clk_disable(cam->camf);
+
+ kfree(cam);
+ saved_cam = NULL;
+}
+
+MODULE_AUTHOR("MontaVista Software, Inc.");
+MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
+MODULE_LICENSE("GPL");
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr,
+ "Minor number for video device (-1 ==> auto assign)");
+module_param(capture_mem, int, 0);
+MODULE_PARM_DESC(capture_mem,
+ "Maximum amount of memory for capture buffers (default 4800KB)");
+module_param(overlay_mem, int, 0);
+MODULE_PARM_DESC(overlay_mem,
+ "Preview overlay framebuffer size (default 600KB)");
+
+module_init(omap24xxcam_init);
+module_exit(omap24xxcam_cleanup);
diff --git a/drivers/media/video/omap/omap24xxcam.h b/drivers/media/video/omap/omap24xxcam.h
new file mode 100644
index 0000000..83d2886
--- /dev/null
+++ b/drivers/media/video/omap/omap24xxcam.h
@@ -0,0 +1,550 @@
+/*
+ * drivers/media/video/omap/omap24xxcam.h
+ *
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP24xx camera controller.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * August 2005 - Modified for new display code.
+ * January 2006 - Enhanced to support new sensor interface
+ * Copyright (C) 2006 Texas Instruments, Inc.
+ *
+ */
+
+#ifndef OMAP24XXCAM_H
+#define OMAP24XXCAM_H
+
+/* physical memory map definitions */
+ /* camera subsystem */
+#define CAM_REG_BASE 0x48052000
+#define CAM_REG_SIZE 0x00001000
+ /* camera core */
+#define CC_REG_OFFSET 0x00000400
+ /* camera DMA */
+#define CAMDMA_REG_OFFSET 0x00000800
+ /* camera MMU */
+#define CAMMMU_REG_OFFSET 0x00000C00
+
+/* define camera subsystem register offsets */
+#define CAM_REVISION 0x000
+#define CAM_SYSCONFIG 0x010
+#define CAM_SYSSTATUS 0x014
+#define CAM_IRQSTATUS 0x018
+#define CAM_GPO 0x040
+#define CAM_GPI 0x050
+
+/* define camera core register offsets */
+#define CC_REVISION 0x000
+#define CC_SYSCONFIG 0x010
+#define CC_SYSSTATUS 0x014
+#define CC_IRQSTATUS 0x018
+#define CC_IRQENABLE 0x01C
+#define CC_CTRL 0x040
+#define CC_CTRL_DMA 0x044
+#define CC_CTRL_XCLK 0x048
+#define CC_FIFODATA 0x04C
+#define CC_TEST 0x050
+#define CC_GENPAR 0x054
+#define CC_CCPFSCR 0x058
+#define CC_CCPFECR 0x05C
+#define CC_CCPLSCR 0x060
+#define CC_CCPLECR 0x064
+#define CC_CCPDFR 0x068
+
+/* define camera dma register offsets */
+#define CAMDMA_REVISION 0x000
+#define CAMDMA_IRQSTATUS_L0 0x008
+#define CAMDMA_IRQSTATUS_L1 0x00C
+#define CAMDMA_IRQSTATUS_L2 0x010
+#define CAMDMA_IRQSTATUS_L3 0x014
+#define CAMDMA_IRQENABLE_L0 0x018
+#define CAMDMA_IRQENABLE_L1 0x01C
+#define CAMDMA_IRQENABLE_L2 0x020
+#define CAMDMA_IRQENABLE_L3 0x024
+#define CAMDMA_SYSSTATUS 0x028
+#define CAMDMA_OCP_SYSCONFIG 0x02C
+#define CAMDMA_CAPS_0 0x064
+#define CAMDMA_CAPS_2 0x06C
+#define CAMDMA_CAPS_3 0x070
+#define CAMDMA_CAPS_4 0x074
+#define CAMDMA_GCR 0x078
+#define CAMDMA_CCR(n) (0x080 + (n)*0x60)
+#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60)
+#define CAMDMA_CICR(n) (0x088 + (n)*0x60)
+#define CAMDMA_CSR(n) (0x08C + (n)*0x60)
+#define CAMDMA_CSDP(n) (0x090 + (n)*0x60)
+#define CAMDMA_CEN(n) (0x094 + (n)*0x60)
+#define CAMDMA_CFN(n) (0x098 + (n)*0x60)
+#define CAMDMA_CSSA(n) (0x09C + (n)*0x60)
+#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60)
+#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60)
+#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60)
+#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60)
+#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60)
+#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60)
+#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60)
+#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60)
+#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60)
+#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60)
+
+/* define camera mmu register offsets */
+#define CAMMMU_REVISION 0x000
+#define CAMMMU_SYSCONFIG 0x010
+#define CAMMMU_SYSSTATUS 0x014
+#define CAMMMU_IRQSTATUS 0x018
+#define CAMMMU_IRQENABLE 0x01C
+#define CAMMMU_WALKING_ST 0x040
+#define CAMMMU_CNTL 0x044
+#define CAMMMU_FAULT_AD 0x048
+#define CAMMMU_TTB 0x04C
+#define CAMMMU_LOCK 0x050
+#define CAMMMU_LD_TLB 0x054
+#define CAMMMU_CAM 0x058
+#define CAMMMU_RAM 0x05C
+#define CAMMMU_GFLUSH 0x060
+#define CAMMMU_FLUSH_ENTRY 0x064
+#define CAMMMU_READ_CAM 0x068
+#define CAMMMU_READ_RAM 0x06C
+#define CAMMMU_EMU_FAULT_AD 0x070
+
+/* Define bit fields within selected registers */
+#define CAM_REVISION_MAJOR (15 << 4)
+#define CAM_REVISION_MAJOR_SHIFT 4
+#define CAM_REVISION_MINOR (15 << 0)
+#define CAM_REVISION_MINOR_SHIFT 0
+
+#define CAM_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAM_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAM_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAM_IRQSTATUS_CC_IRQ (1 << 4)
+#define CAM_IRQSTATUS_MMU_IRQ (1 << 3)
+#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2)
+#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1)
+#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0)
+
+#define CAM_GPO_CAM_S_P_EN (1 << 1)
+#define CAM_GPO_CAM_CCP_MODE (1 << 0)
+
+#define CAM_GPI_CC_DMA_REQ1 (1 << 24)
+#define CAP_GPI_CC_DMA_REQ0 (1 << 23)
+#define CAP_GPI_CAM_MSTANDBY (1 << 21)
+#define CAP_GPI_CAM_WAIT (1 << 20)
+#define CAP_GPI_CAM_S_DATA (1 << 17)
+#define CAP_GPI_CAM_S_CLK (1 << 16)
+#define CAP_GPI_CAM_P_DATA (0xFFF << 3)
+#define CAP_GPI_CAM_P_DATA_SHIFT 3
+#define CAP_GPI_CAM_P_VS (1 << 2)
+#define CAP_GPI_CAM_P_HS (1 << 1)
+#define CAP_GPI_CAM_P_CLK (1 << 0)
+
+#define CC_REVISION_MAJOR (15 << 4)
+#define CC_REVISION_MAJOR_SHIFT 4
+#define CC_REVISION_MINOR (15 << 0)
+#define CC_REVISION_MINOR_SHIFT 0
+
+#define CC_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CC_SYSCONFIG_SOFTRESET (1 << 1)
+#define CC_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CC_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CC_IRQSTATUS_FS_IRQ (1 << 19)
+#define CC_IRQSTATUS_LE_IRQ (1 << 18)
+#define CC_IRQSTATUS_LS_IRQ (1 << 17)
+#define CC_IRQSTATUS_FE_IRQ (1 << 16)
+#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10)
+#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0)
+
+#define CC_IRQENABLE_FS_IRQ (1 << 19)
+#define CC_IRQENABLE_LE_IRQ (1 << 18)
+#define CC_IRQENABLE_LS_IRQ (1 << 17)
+#define CC_IRQENABLE_FE_IRQ (1 << 16)
+#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10)
+#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0)
+
+#define CC_CTRL_CC_RST (1 << 18)
+#define CC_CTRL_CC_FRAME_TRIG (1 << 17)
+#define CC_CTRL_CC_EN (1 << 16)
+#define CC_CTRL_NOBT_SYNCHRO (1 << 13)
+#define CC_CTRL_BT_CORRECT (1 << 12)
+#define CC_CTRL_PAR_ORDERCAM (1 << 11)
+#define CC_CTRL_PAR_CLK_POL (1 << 10)
+#define CC_CTRL_NOBT_HS_POL_SHIFT 9
+#define CC_CTRL_NOBT_HS_POL (1 << 9)
+#define CC_CTRL_NOBT_VS_POL_SHIFT 8
+#define CC_CTRL_NOBT_VS_POL (1 << 8)
+#define CC_CTRL_PAR_MODE (7 << 1)
+#define CC_CTRL_PAR_MODE_SHIFT 1
+#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1)
+#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1)
+#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1)
+#define CC_CTRL_PAR_MODE_BT8 (4 << 1)
+#define CC_CTRL_PAR_MODE_BT10 (5 << 1)
+#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1)
+#define CC_CTRL_CCP_MODE (1 << 0)
+
+#define CC_CTRL_DMA_EN (1 << 8)
+#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0)
+#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0
+
+#define CC_CTRL_XCLK_DIV (0x1F << 0)
+#define CC_CTRL_XCLK_DIV_SHIFT 0
+#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0)
+#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0)
+#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0)
+
+#define CC_TEST_FIFO_RD_POINTER (0xFF << 24)
+#define CC_TEST_FIFO_RD_POINTER_SHIFT 24
+#define CC_TEST_FIFO_WR_POINTER (0xFF << 16)
+#define CC_TEST_FIFO_WR_POINTER_SHIFT 16
+#define CC_TEST_FIFO_LEVEL (0xFF << 8)
+#define CC_TEST_FIFO_LEVEL_SHIFT 8
+#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0)
+#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0
+
+#define CC_GENPAR_FIFO_DEPTH (7 << 0)
+#define CC_GENPAR_FIFO_DEPTH_SHIFT 0
+
+#define CC_CCPDFR_ALPHA (0xFF << 8)
+#define CC_CCPDFR_ALPHA_SHIFT 8
+#define CC_CCPDFR_DATAFORMAT (15 << 0)
+#define CC_CCPDFR_DATAFORMAT_SHIFT 0
+#define CC_CCPDFR_DATAFORMAT_YUV422BE ( 0 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV422 ( 1 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV420 ( 2 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB444 ( 4 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB565 ( 5 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888NDE ( 6 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888 ( 7 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8NDE ( 8 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8 ( 9 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0)
+#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0)
+
+#define CAMDMA_REVISION_MAJOR (15 << 4)
+#define CAMDMA_REVISION_MAJOR_SHIFT 4
+#define CAMDMA_REVISION_MINOR (15 << 0)
+#define CAMDMA_REVISION_MINOR_SHIFT 0
+
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12)
+#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9)
+#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8)
+#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16)
+#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0)
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0
+
+#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24)
+#define CAMDMA_CCR_PREFETCH (1 << 23)
+#define CAMDMA_CCR_SUPERVISOR (1 << 22)
+#define CAMDMA_CCR_SECURE (1 << 21)
+#define CAMDMA_CCR_BS (1 << 18)
+#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17)
+#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16)
+#define CAMDMA_CCR_DST_AMODE (3 << 14)
+#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14)
+#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14)
+#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14)
+#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14)
+#define CAMDMA_CCR_SRC_AMODE (3 << 12)
+#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12)
+#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12)
+#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12)
+#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12)
+#define CAMDMA_CCR_WR_ACTIVE (1 << 10)
+#define CAMDMA_CCR_RD_ACTIVE (1 << 9)
+#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8)
+#define CAMDMA_CCR_ENABLE (1 << 7)
+#define CAMDMA_CCR_PRIO (1 << 6)
+#define CAMDMA_CCR_FS (1 << 5)
+#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0))
+#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01
+
+#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0
+
+#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11)
+#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10)
+#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9)
+#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8)
+#define CAMDMA_CICR_PACKET_IE (1 << 7)
+#define CAMDMA_CICR_BLOCK_IE (1 << 5)
+#define CAMDMA_CICR_LAST_IE (1 << 4)
+#define CAMDMA_CICR_FRAME_IE (1 << 3)
+#define CAMDMA_CICR_HALF_IE (1 << 2)
+#define CAMDMA_CICR_DROP_IE (1 << 1)
+
+#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11)
+#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10)
+#define CAMDMA_CSR_SECURE_ERR (1 << 9)
+#define CAMDMA_CSR_TRANS_ERR (1 << 8)
+#define CAMDMA_CSR_PACKET (1 << 7)
+#define CAMDMA_CSR_SYNC (1 << 6)
+#define CAMDMA_CSR_BLOCK (1 << 5)
+#define CAMDMA_CSR_LAST (1 << 4)
+#define CAMDMA_CSR_FRAME (1 << 3)
+#define CAMDMA_CSR_HALF (1 << 2)
+#define CAMDMA_CSR_DROP (1 << 1)
+
+#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21)
+#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20)
+#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19)
+#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18)
+#define CAMDMA_CSDP_WRITE_MODE (3 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16)
+#define CAMDMA_CSDP_DST_BURST_EN (3 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14)
+#define CAMDMA_CSDP_DST_PACKED (1 << 13)
+#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9)
+#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9)
+#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7)
+#define CAMDMA_CSDP_SRC_PACKED (1 << 6)
+#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2)
+#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2)
+#define CAMDMA_CSDP_DATA_TYPE (3 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0)
+
+#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0)
+
+struct omap24xx_cc_regs {
+ u32 revision; /* 0x000 */
+ u32 res1[3];
+ u32 sysconfig; /* 0x010 */
+ u32 sysstatus; /* 0x014 */
+ u32 irqstatus; /* 0x018 */
+ u32 irqenable; /* 0x01C */
+ u32 res2[8];
+ u32 ctrl; /* 0x040 */
+ u32 ctrl_dma; /* 0x044 */
+ u32 ctrl_xclk; /* 0x048 */
+ u32 fifodata; /* 0x04C */
+ u32 test; /* 0x050 */
+ u32 genpar; /* 0x054 */
+ u32 ccpfscr; /* 0x058 */
+ u32 ccpfecr; /* 0x05C */
+ u32 ccplscr; /* 0x060 */
+ u32 ccplecr; /* 0x064 */
+ u32 ccpdfr; /* 0x068 */
+};
+
+/* forward declarations */
+struct omap24xxcam_fh;
+struct omap24xxcam_device;
+
+
+/* camera DMA definitions */
+#define DMA_THRESHOLD 32 /* number of bytes transferred per DMA request */
+/* NUM_CAMDMA_CHANNELS is the number of logical channels provided by the camera
+ * DMA controller.
+ */
+#define NUM_CAMDMA_CHANNELS 4
+/* NUM_SG_DMA is the number of scatter-gather DMA transfers that can be queued.
+ * We need it to be 2 greater than the maximum number of video frames so that
+ * we can use 2 slots for overlay while still having VIDEO_MAX_FRAME slots left
+ * for streaming.
+ */
+#define NUM_SG_DMA (VIDEO_MAX_FRAME+2)
+
+typedef void (*dma_callback_t)(struct omap24xxcam_device *cam,
+ unsigned long status, void *arg);
+
+struct camdma_state {
+ dma_callback_t callback;
+ void *arg;
+};
+struct sgdma_state {
+ const struct scatterlist *sglist;
+ int sglen; /* number of sglist entries */
+ int next_sglist; /* index of next sglist entry to process */
+ int queued_sglist; /* number of sglist entries queued for DMA */
+ unsigned long csr; /* DMA return code */
+ dma_callback_t callback;
+ void *arg;
+};
+
+/* per-device data structure */
+struct omap24xxcam_device {
+ struct clk *cami;
+ struct clk *camf;
+ unsigned int irq;
+
+ unsigned long cam_mmio_base;
+ unsigned long cam_mmio_base_phys;
+ unsigned long cam_mmio_size;
+
+ unsigned long overlay_base;
+ unsigned long overlay_base_phys;
+ unsigned long overlay_size;
+
+ /* camera DMA management */
+ spinlock_t dma_lock;
+ /* While dma_stop!=0, an attempt to start a new DMA transfer will
+ * fail.
+ */
+ int dma_stop;
+ int free_dmach; /* number of dma channels free */
+ int next_dmach; /* index of next dma channel to use */
+ struct camdma_state camdma[NUM_CAMDMA_CHANNELS];
+ /* dma_notify is a pointer to a callback routine for notification when
+ * a DMA transfer has been started.
+ */
+ void (*dma_notify)(struct omap24xxcam_device *cam);
+
+ /* frequncy (in Hz) of camera interface functional clock (MCLK) */
+ unsigned long mclk;
+
+ struct device dev;
+ struct video_device *vfd;
+
+ spinlock_t overlay_lock; /* spinlock for overlay DMA counter */
+ int overlay_cnt; /* count of queued overlay DMA xfers */
+ struct scatterlist overlay_sglist;
+
+ spinlock_t vbq_lock; /* spinlock for videobuf queues */
+ struct videobuf_queue_ops vbq_ops; /* videobuf queue operations */
+ unsigned long field_count; /* field counter for videobuf_buffer */
+
+ /* scatter-gather DMA management */
+ spinlock_t sg_lock;
+ int free_sgdma; /* number of free sg dma slots */
+ int next_sgdma; /* index of next sg dma slot to use */
+ struct sgdma_state sgdma[NUM_SG_DMA];
+
+ /* The img_lock is used to serialize access to the image parameters for
+ * overlay and capture.
+ */
+ spinlock_t img_lock;
+
+ /* Access to everything below here is locked by img_lock */
+
+ /* We allow streaming from at most one filehandle at a time.
+ * non-NULL means streaming is in progress.
+ */
+ struct omap24xxcam_fh *streaming;
+ /* We allow previewing from at most one filehandle at a time.
+ * non-NULL means previewing is in progress.
+ */
+ struct omap24xxcam_fh *previewing;
+
+ /* capture parameters (frame rate, number of buffers) */
+ struct v4l2_captureparm cparm;
+ struct v4l2_captureparm cparm2;
+
+ /* This is the frame period actually requested by the user. */
+ struct v4l2_fract nominal_timeperframe;
+
+ /* frequency (in Hz) of camera interface xclk output */
+ unsigned long xclk;
+
+ /* pointer to camera sensor interface interface */
+ struct camera_sensor *cam_sensor;
+ /* blind pointer to private data structure for sensor */
+ void *sensor;
+
+ /* pix defines the size and pixel format of the image captured by the
+ * sensor. This also defines the size of the framebuffers. The
+ * same pool of framebuffers is used for video capture and video
+ * overlay. These parameters are set/queried by the
+ * VIDIOC_S_FMT/VIDIOC_G_FMT ioctls with a CAPTURE buffer type.
+ */
+ struct v4l2_pix_format pix;
+ struct v4l2_pix_format pix2;
+ int still_capture;
+
+ /* crop defines the size and offset of the video overlay source window
+ * within the framebuffer. These parameters are set/queried by the
+ * VIDIOC_S_CROP/VIDIOC_G_CROP ioctls with an OVERLAY buffer type.
+ * The cropping rectangle allows a subset of the captured image to be
+ * previewed. It only affects the portion of the image previewed, not
+ * captured; the entire camera image is always captured.
+ */
+ struct v4l2_rect crop;
+
+ /* win defines the size and offset of the video overlay target window
+ * within the video display. These parameters are set/queried by the
+ * VIDIOC_S_FMT/VIDIOC_G_FMT ioctls with an OVERLAY buffer type.
+ */
+ struct v4l2_window win;
+
+ /* fbuf reflects the size of the video display. It is queried with the
+ * VIDIOC_G_FBUF ioctl. The size of the video display cannot be
+ * changed with the VIDIOC_S_FBUF ioctl.
+ */
+ struct v4l2_framebuffer fbuf;
+
+
+ /* value of CC_CTRL register required to support current capture
+ * format
+ */
+ unsigned long cc_ctrl;
+
+ /* the display controller video layer for camera preview
+ */
+ int vid_preview;
+
+ /* Power management suspend lockout */
+ int suspended;
+ wait_queue_head_t suspend_wq;
+
+};
+
+/* per-filehandle data structure */
+struct omap24xxcam_fh {
+ struct omap24xxcam_device *cam;
+ enum v4l2_buf_type type;
+ struct videobuf_queue vbq;
+};
+
+#endif /* ifndef OMAP24XXCAM_H */
diff --git a/drivers/media/video/omap/omap24xxlib.c b/drivers/media/video/omap/omap24xxlib.c
new file mode 100644
index 0000000..4818909
--- /dev/null
+++ b/drivers/media/video/omap/omap24xxlib.c
@@ -0,0 +1,269 @@
+/*
+ * drivers/media/video/omap/omap24xxlib.c
+ *
+ * Copyright (C) 2005 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * Based on the OMAP2 camera driver
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP24xx camera controller.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/kdev_t.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/videodev.h>
+
+//#include "display.h"
+
+#include <asm/byteorder.h>
+#include <asm/semaphore.h>
+
+/* Return the default overlay cropping rectangle in crop given the image
+ * size in pix and the video display size in fbuf. The default
+ * cropping rectangle is the largest rectangle no larger than the capture size
+ * that will fit on the display. The default cropping rectangle is centered in
+ * the image. All dimensions and offsets are rounded down to even numbers.
+ */
+void
+omap24xxvout_default_crop(struct v4l2_pix_format *pix,
+ struct v4l2_framebuffer *fbuf, struct v4l2_rect *crop)
+{
+ crop->width = (pix->width < fbuf->fmt.width) ?
+ pix->width : fbuf->fmt.width;
+ crop->height = (pix->height < fbuf->fmt.height) ?
+ pix->height : fbuf->fmt.height;
+ crop->width &= ~1;
+ crop->height &= ~1;
+ crop->left = ((pix->width - crop->width) >> 1) & ~1;
+ crop->top = ((pix->height - crop->height) >> 1) & ~1;
+}
+
+/* Given a new render window in new_win, adjust the window to the
+ * nearest supported configuration. The adjusted window parameters are
+ * returned in new_win.
+ * Returns zero if succesful, or -EINVAL if the requested window is
+ * impossible and cannot reasonably be adjusted.
+ */
+int
+omap24xxvout_try_window(struct v4l2_framebuffer *fbuf,
+ struct v4l2_window *new_win)
+{
+ struct v4l2_rect try_win;
+
+ /* make a working copy of the new_win rectangle */
+ try_win = new_win->w;
+
+ /* adjust the preview window so it fits on the display by clipping any
+ * offscreen areas
+ */
+ if (try_win.left < 0) {
+ try_win.width += try_win.left;
+ try_win.left = 0;
+ }
+ if (try_win.top < 0) {
+ try_win.height += try_win.top;
+ try_win.top = 0;
+ }
+ try_win.width = (try_win.width < fbuf->fmt.width) ?
+ try_win.width : fbuf->fmt.width;
+ try_win.height = (try_win.height < fbuf->fmt.height) ?
+ try_win.height : fbuf->fmt.height;
+ if (try_win.left + try_win.width > fbuf->fmt.width)
+ try_win.width = fbuf->fmt.width - try_win.left;
+ if (try_win.top + try_win.height > fbuf->fmt.height)
+ try_win.height = fbuf->fmt.height - try_win.top;
+ try_win.width &= ~1;
+ try_win.height &= ~1;
+
+ if (try_win.width <= 0 || try_win.height <= 0)
+ return -EINVAL;
+
+ /* We now have a valid preview window, so go with it */
+ new_win->w = try_win;
+ new_win->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
+/* Given a new render window in new_win, adjust the window to the
+ * nearest supported configuration. The image cropping window in crop
+ * will also be adjusted if necessary. Preference is given to keeping the
+ * the window as close to the requested configuration as possible. If
+ * successful, new_win, vout->win, and crop are updated.
+ * Returns zero if succesful, or -EINVAL if the requested preview window is
+ * impossible and cannot reasonably be adjusted.
+ */
+int
+omap24xxvout_new_window(struct v4l2_rect *crop,
+ struct v4l2_window *win,
+ struct v4l2_framebuffer *fbuf,
+ struct v4l2_window *new_win)
+{
+ int err;
+
+ err = omap24xxvout_try_window(fbuf, new_win);
+ if (err) {
+ return err;
+ }
+
+ /* update our preview window */
+ win->w = new_win->w;
+ win->field = new_win->field;
+ win->chromakey = new_win->chromakey;
+
+ /* adjust the cropping window to allow for resizing limitations */
+ if (crop->height / win->w.height >= 2) {
+ /* The maximum vertical downsizing ratio is 2:1 */
+ crop->height = win->w.height * 2;
+ }
+ if (crop->width / win->w.width >= 2) {
+ /* The maximum horizontal downsizing ratio is 2:1 */
+ crop->width = win->w.width * 2;
+ }
+ if (crop->width > 768) {
+ /* The OMAP2420 vertical resizing line buffer is 768 pixels
+ * wide. If the cropped image is wider than 768 pixels then it
+ * cannot be vertically resized.
+ */
+ if (crop->height != win->w.height)
+ crop->width = 768;
+ }
+ return 0;
+}
+
+/* Given a new cropping rectangle in new_crop, adjust the cropping rectangle to
+ * the nearest supported configuration. The image render window in win will
+ * also be adjusted if necessary. The preview window is adjusted such that the
+ * horizontal and vertical rescaling ratios stay constant. If the render
+ * window would fall outside the display boundaries, the cropping rectangle will
+ * also be adjusted to maintain the rescaling ratios. If successful, crop
+ * and win are updated.
+ * Returns zero if succesful, or -EINVAL if the requested cropping rectangle is
+ * impossible and cannot reasonably be adjusted.
+ */
+int
+omap24xxvout_new_crop(struct v4l2_pix_format *pix,
+ struct v4l2_rect *crop,
+ struct v4l2_window *win,
+ struct v4l2_framebuffer *fbuf,
+ const struct v4l2_rect *new_crop)
+{
+ struct v4l2_rect try_crop;
+ unsigned long vresize, hresize;
+
+ /* make a working copy of the new_crop rectangle */
+ try_crop = *new_crop;
+
+ /* adjust the cropping rectangle so it fits in the image */
+ if (try_crop.left < 0) {
+ try_crop.width += try_crop.left;
+ try_crop.left = 0;
+ }
+ if (try_crop.top < 0) {
+ try_crop.height += try_crop.top;
+ try_crop.top = 0;
+ }
+ try_crop.width = (try_crop.width < pix->width) ?
+ try_crop.width : pix->width;
+ try_crop.height = (try_crop.height < pix->height) ?
+ try_crop.height : pix->height;
+ if (try_crop.left + try_crop.width > pix->width)
+ try_crop.width = pix->width - try_crop.left;
+ if (try_crop.top + try_crop.height > pix->height)
+ try_crop.height = pix->height - try_crop.top;
+ try_crop.width &= ~1;
+ try_crop.height &= ~1;
+ if (try_crop.width <= 0 || try_crop.height <= 0)
+ return -EINVAL;
+
+ if (crop->height != win->w.height) {
+ /* If we're resizing vertically, we can't support a crop width
+ * wider than 768 pixels.
+ */
+ if (try_crop.width > 768)
+ try_crop.width = 768;
+ }
+ /* vertical resizing */
+ vresize = (1024 * crop->height) / win->w.height;
+ if (vresize > 2048)
+ vresize = 2048;
+ else if (vresize == 0)
+ vresize = 1;
+ win->w.height = ((1024 * try_crop.height) / vresize) & ~1;
+ if (win->w.height == 0)
+ win->w.height = 2;
+ if (win->w.height + win->w.top > fbuf->fmt.height) {
+ /* We made the preview window extend below the bottom of the
+ * display, so clip it to the display boundary and resize the
+ * cropping height to maintain the vertical resizing ratio.
+ */
+ win->w.height = (fbuf->fmt.height - win->w.top) & ~1;
+ try_crop.height = ((vresize * win->w.height) / 1024) & ~1;
+ if (try_crop.height == 0)
+ try_crop.height = 2;
+ }
+ /* horizontal resizing */
+ hresize = (1024 * crop->width) / win->w.width;
+ if (hresize > 2048)
+ hresize = 2048;
+ else if (hresize == 0)
+ hresize = 1;
+ win->w.width = ((1024 * try_crop.width) / hresize) & ~1;
+ if (win->w.width == 0)
+ win->w.width = 2;
+ if (win->w.width + win->w.left > fbuf->fmt.width) {
+ /* We made the preview window extend past the right side of the
+ * display, so clip it to the display boundary and resize the
+ * cropping width to maintain the horizontal resizing ratio.
+ */
+ win->w.width = (fbuf->fmt.width - win->w.left) & ~1;
+ try_crop.width = ((hresize * win->w.width) / 1024) & ~1;
+ if (try_crop.width == 0)
+ try_crop.width = 2;
+ }
+ /* update our cropping rectangle and we're done */
+ *crop = try_crop;
+ return 0;
+}
+
+/* Given a new format in pix and fbuf, crop and win
+ * structures are initialized to default values. crop
+ * is initialized to the largest window size that will fit on the display. The
+ * crop window is centered in the image. win is initialized to
+ * the same size as crop and is centered on the display.
+ * All sizes and offsets are constrained to be even numbers.
+ */
+void
+omap24xxvout_new_format(struct v4l2_pix_format *pix,
+ struct v4l2_framebuffer *fbuf,
+ struct v4l2_rect *crop, struct v4l2_window *win)
+{
+ /* crop defines the preview source window in the image capture
+ * buffer
+ */
+ omap24xxvout_default_crop(pix, fbuf, crop);
+
+ /* win defines the preview target window on the display */
+ win->w.width = crop->width;
+ win->w.height = crop->height;
+ win->w.left = ((fbuf->fmt.width - win->w.width) >> 1) & ~1;
+ win->w.top = ((fbuf->fmt.height - win->w.height) >> 1) & ~1;
+}
diff --git a/drivers/media/video/omap/omap24xxlib.h b/drivers/media/video/omap/omap24xxlib.h
new file mode 100644
index 0000000..6c1f906
--- /dev/null
+++ b/drivers/media/video/omap/omap24xxlib.h
@@ -0,0 +1,34 @@
+/*
+ * drivers/media/video/omap/omap24xxlib.h
+ *
+ * Copyright (C) 2005 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#ifndef OMAP24XXLIB_VIDEO_H
+#define OMAP24XXLIB_VIDEO_H
+
+extern void
+ omap24xxvout_default_crop(struct v4l2_pix_format *pix, struct v4l2_framebuffer *fbuf,
+ struct v4l2_rect *crop);
+
+extern int
+ omap24xxvout_new_crop(struct v4l2_pix_format *pix, struct v4l2_rect *crop,
+ struct v4l2_window *win, struct v4l2_framebuffer *fbuf,
+ const struct v4l2_rect *new_crop);
+
+extern int
+ omap24xxvout_try_window(struct v4l2_framebuffer *fbuf, struct v4l2_window *new_win);
+
+extern int
+ omap24xxvout_new_window(struct v4l2_rect *crop,struct v4l2_window *win,
+ struct v4l2_framebuffer *fbuf, struct v4l2_window *new_win);
+
+extern void
+ omap24xxvout_new_format(struct v4l2_pix_format *pix, struct v4l2_framebuffer *fbuf,
+ struct v4l2_rect *crop, struct v4l2_window *win);
+#endif
diff --git a/drivers/media/video/omap/sensor_if.h b/drivers/media/video/omap/sensor_if.h
index 984ba55..e595ccf 100644
--- a/drivers/media/video/omap/sensor_if.h
+++ b/drivers/media/video/omap/sensor_if.h
@@ -2,7 +2,7 @@
/*
* drivers/media/video/omap/sensor_if.h
*
- * Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2004-2005 Texas Instruments, Inc.
*
* Sensor interface to OMAP camera capture drivers
* Sensor driver should implement this interface
@@ -21,29 +21,68 @@
#define LEN_SENSOR_NAME 31
+#define PAR_MODE_NOBT8 0
+#define PAR_MODE_NOBT10 1
+#define PAR_MODE_NOBT12 2
+#define PAR_MODE_BT8 4
+#define PAR_MODE_BT10 5
+
+#define SYNC_ACTIVE_HIGH 0
+#define SYNC_ACTIVE_LOW 1
+
+#define V4L2_BUF_TYPE_STILL_CAPTURE V4L2_BUF_TYPE_PRIVATE
+
struct camera_sensor {
unsigned int version;
char name[LEN_SENSOR_NAME + 1];
- void *(*init)(struct v4l2_pix_format *);
+ int parallel_mode; /* parallel I/F mode */
+ int hs_polarity; /* horizontal sync polarity */
+ int vs_polarity; /* vertical sync polarity */
+ int image_swap; /* image swap or not */
+ int bt_correction; /* BT correction enabled or not */
+
+ /* init the sensor with the passed pix format. A none zero private
+ pointer must be returned on success. The same pointer is passed
+ back for all other functions. This gives a sensor driver the
+ chance to handle multiple sensor. */
+ void *(*init)(struct v4l2_pix_format *, struct v4l2_pix_format *);
+ /* clean up the sensor */
int (*cleanup)(void *);
+ /* These are for power management */
int (*power_on)(void *);
int (*power_off)(void *);
+ /* Handle V4L2 fmt IOCTLs */
int (*enum_pixformat)(struct v4l2_fmtdesc *, void *);
int (*try_format) (struct v4l2_pix_format *, void *);
+ /* Calculated the needed xclk for the pix format passed and the
+ desired capture rate. */
unsigned long (*calc_xclk) (struct v4l2_pix_format *,
struct v4l2_fract *, void *);
+ /* Configure the sensor to generate the passed pix format at the
+ passed capture rate with the passed xclk */
int (*configure) (struct v4l2_pix_format *, unsigned long,
struct v4l2_fract *, void *);
-
+ /* These handle V4L2 control IOCTLs */
int (*query_control) (struct v4l2_queryctrl *, void *);
int (*get_control) (struct v4l2_control *, void *);
int (*set_control) (struct v4l2_control *, void *);
+ /* These are only for those sensors that use a different sensor context
+ for still image capture. A simple sensor driver doesn't have to
+ implement them. */
+ int (*try_format_still_capture) (struct v4l2_pix_format *, void *);
+ int (*configure_still_capture) (struct v4l2_pix_format *, unsigned long,
+ struct v4l2_fract *, void *);
+ unsigned long (*calc_xclk_still_capture) (struct v4l2_pix_format *,
+ struct v4l2_fract *, void *);
+ int (*enter_still_capture) (int, void *);
+ int (*exit_still_capture) (void *);
+
};
#endif
diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h
index 42098d9..4f47249 100644
--- a/include/asm-arm/arch-omap/irqs.h
+++ b/include/asm-arm/arch-omap/irqs.h
@@ -237,6 +237,7 @@
#define INT_24XX_SDMA_IRQ1 13
#define INT_24XX_SDMA_IRQ2 14
#define INT_24XX_SDMA_IRQ3 15
+#define INT_24XX_CAM_MPU_IRQ 24
#define INT_24XX_DSS_IRQ 25
#define INT_24XX_GPIO_BANK1 29
#define INT_24XX_GPIO_BANK2 30
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
next reply other threads:[~2006-02-10 21:03 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-02-10 21:03 David Cohen [this message]
2006-02-11 6:34 ` [PATCH] omap camera Paul Mundt
2006-02-11 13:53 ` Komal Shah
2006-02-11 13:50 ` Komal Shah
-- strict thread matches above, loose matches on Subject: below --
2006-02-10 21:17 Zhang, Jian
2006-02-13 16:01 Zhang, Jian
2006-02-13 20:53 ` Paul Mundt
2006-02-13 21:13 Zhang, Jian
2006-02-13 21:15 Zhang, Jian
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=43ECFF9B.80509@indt.org.br \
--to=david.cohen@indt.org.br \
--cc=Linux-omap-open-source@linux.omap.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox