summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux/drivers/media/video/Kconfig18
-rw-r--r--linux/drivers/media/video/Makefile4
-rw-r--r--linux/drivers/media/video/omap24xxcam-dma.c601
-rw-r--r--linux/drivers/media/video/omap24xxcam.c1908
-rw-r--r--linux/drivers/media/video/omap24xxcam.h593
-rw-r--r--linux/drivers/media/video/tvp514x.c1569
-rw-r--r--linux/drivers/media/video/tvp514x_regs.h297
-rw-r--r--linux/include/media/tvp514x.h118
-rw-r--r--linux/include/media/v4l2-int-device.h6
-rw-r--r--v4l2-apps/util/v4l2-ctl.cpp37
10 files changed, 5150 insertions, 1 deletions
diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig
index c4ee10c37..cc224744b 100644
--- a/linux/drivers/media/video/Kconfig
+++ b/linux/drivers/media/video/Kconfig
@@ -361,6 +361,17 @@ config VIDEO_SAA7191
To compile this driver as a module, choose M here: the
module will be called saa7191.
+config VIDEO_TVP514X
+ tristate "Texas Instruments TVP514x video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the TI TVP5146/47
+ decoder. It is currently working with the TI OMAP3 camera
+ controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp514x.
+
config VIDEO_TVP5150
tristate "Texas Instruments TVP5150 video decoder"
depends on VIDEO_V4L2 && I2C
@@ -770,6 +781,13 @@ config VIDEO_SH_MOBILE_CEU
---help---
This is a v4l2 driver for the SuperH Mobile CEU Interface
+config VIDEO_OMAP2
+ tristate "OMAP2 Camera Capture Interface driver"
+ depends on VIDEO_DEV && ARCH_OMAP2
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP2 camera capture interface
+
#
# USB Multimedia device configuration
#
diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile
index 53d43f9e0..492ab3dce 100644
--- a/linux/drivers/media/video/Makefile
+++ b/linux/drivers/media/video/Makefile
@@ -8,6 +8,8 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o
stkwebcam-objs := stk-webcam.o stk-sensor.o
+omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
+
videodev-objs := v4l2-dev.o v4l2-ioctl.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-compat-ioctl32.o v4l2-int-device.o
@@ -67,6 +69,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
+obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
@@ -130,6 +133,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
diff --git a/linux/drivers/media/video/omap24xxcam-dma.c b/linux/drivers/media/video/omap24xxcam-dma.c
new file mode 100644
index 000000000..1d54b86c9
--- /dev/null
+++ b/linux/drivers/media/video/omap24xxcam-dma.c
@@ -0,0 +1,601 @@
+/*
+ * drivers/media/video/omap24xxcam-dma.c
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com> and
+ * David Cohen <david.cohen@indt.org.br>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+
+#include "omap24xxcam.h"
+
+/*
+ *
+ * DMA hardware.
+ *
+ */
+
+/* Ack all interrupt on CSR and IRQSTATUS_L0 */
+static void omap24xxcam_dmahw_ack_all(unsigned long base)
+{
+ u32 csr;
+ int i;
+
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr);
+ }
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf);
+}
+
+/* Ack dmach on CSR and IRQSTATUS_L0 */
+static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach)
+{
+ u32 csr;
+
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr);
+ /* ack interrupt in IRQSTATUS */
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach));
+
+ return csr;
+}
+
+static int omap24xxcam_dmahw_running(unsigned long base, int dmach)
+{
+ return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE;
+}
+
+static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach,
+ dma_addr_t start, u32 len)
+{
+ omap24xxcam_reg_out(base, 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_WR_ACTIVE
+ | CAMDMA_CCR_RD_ACTIVE
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len);
+ omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1);
+ omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach),
+ CAMDMA_CSDP_WRITE_MODE_POSTED
+ | CAMDMA_CSDP_DST_BURST_EN_32
+ | CAMDMA_CSDP_DST_PACKED
+ | CAMDMA_CSDP_SRC_BURST_EN_32
+ | CAMDMA_CSDP_SRC_PACKED
+ | CAMDMA_CSDP_DATA_TYPE_8BITS);
+ omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start);
+ omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
+ omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach),
+ CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR
+ | CAMDMA_CSR_BLOCK
+ | CAMDMA_CSR_DROP);
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach),
+ CAMDMA_CICR_MISALIGNED_ERR_IE
+ | CAMDMA_CICR_SECURE_ERR_IE
+ | CAMDMA_CICR_TRANS_ERR_IE
+ | CAMDMA_CICR_BLOCK_IE
+ | CAMDMA_CICR_DROP_IE);
+}
+
+static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach)
+{
+ omap24xxcam_reg_out(base, 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);
+}
+
+static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach,
+ int free_dmach)
+{
+ int prev_dmach, ch;
+
+ if (dmach == 0)
+ prev_dmach = NUM_CAMDMA_CHANNELS - 1;
+ else
+ prev_dmach = dmach - 1;
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach),
+ CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
+ /* Did we chain the DMA transfer before the previous one
+ * finished?
+ */
+ ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS;
+ while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch))
+ & CAMDMA_CCR_ENABLE)) {
+ if (ch == dmach) {
+ /* The previous transfer has 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.
+ */
+ omap24xxcam_dmahw_transfer_start(base, dmach);
+ break;
+ } else
+ ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
+ }
+}
+
+/* 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_dmahw_abort_ch(unsigned long base, int dmach)
+{
+ /* mask all interrupts from this channel */
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
+ /* unlink this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0,
+ CAMDMA_CLNK_CTRL_ENABLE_LNK);
+ /* disable this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
+}
+
+static void omap24xxcam_dmahw_init(unsigned long base)
+{
+ omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
+ CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
+ | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
+ | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10,
+ CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
+
+ omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf);
+}
+
+/*
+ *
+ * Individual DMA channel handling.
+ *
+ */
+
+/* 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_dma *dma, dma_addr_t start,
+ u32 len, dma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ int dmach;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ if (!dma->free_dmach || atomic_read(&dma->dma_stop)) {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ return -EBUSY;
+ }
+
+ dmach = dma->next_dmach;
+
+ dma->ch_state[dmach].callback = callback;
+ dma->ch_state[dmach].arg = arg;
+
+ omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len);
+
+ /* We're ready to start the DMA transfer. */
+
+ if (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ /* A transfer is already in progress, so try to chain to it. */
+ omap24xxcam_dmahw_transfer_chain(dma->base, dmach,
+ dma->free_dmach);
+ } else {
+ /* No transfer is in progress, so we'll just start this one
+ * now.
+ */
+ omap24xxcam_dmahw_transfer_start(dma->base, dmach);
+ }
+
+ dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
+ dma->free_dmach--;
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ 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_dma *dma, u32 csr)
+{
+ unsigned long flags;
+ int dmach, i, free_dmach;
+ dma_callback_t callback;
+ void *arg;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ /* stop any DMA transfers in progress */
+ dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS;
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
+ omap24xxcam_dmahw_abort_ch(dma->base, dmach);
+ 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 = dma->free_dmach;
+ while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) &&
+ (free_dmach < NUM_CAMDMA_CHANNELS)) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ free_dmach++;
+ if (callback) {
+ /* leave interrupts disabled during callback */
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+/* 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_dma *dma, u32 csr)
+{
+ atomic_inc(&dma->dma_stop);
+ omap24xxcam_dma_abort(dma, csr);
+ atomic_dec(&dma->dma_stop);
+}
+
+/* Camera DMA interrupt service routine. */
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma)
+{
+ int dmach;
+ dma_callback_t callback;
+ void *arg;
+ u32 csr;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&dma->lock);
+
+ if (dma->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.
+ */
+ omap24xxcam_dmahw_ack_all(dma->base);
+ spin_unlock(&dma->lock);
+ return;
+ }
+
+ while (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ if (omap24xxcam_dmahw_running(dma->base, dmach)) {
+ /* This buffer hasn't finished yet, so we're done. */
+ break;
+ }
+ csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach);
+ if (csr & csr_error) {
+ /* A DMA error occurred, so stop all DMA
+ * transfers in progress.
+ */
+ spin_unlock(&dma->lock);
+ omap24xxcam_dma_stop(dma, csr);
+ return;
+ } else {
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ if (callback) {
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+ }
+
+ spin_unlock(&dma->lock);
+
+ omap24xxcam_sgdma_process(
+ container_of(dma, struct omap24xxcam_sgdma, dma));
+}
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ omap24xxcam_dmahw_init(dma->base);
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
+ unsigned long base)
+{
+ int ch;
+
+ /* group all channels on DMA IRQ0 and unmask irq */
+ spin_lock_init(&dma->lock);
+ dma->base = base;
+ dma->free_dmach = NUM_CAMDMA_CHANNELS;
+ dma->next_dmach = 0;
+ for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
+ dma->ch_state[ch].callback = NULL;
+ dma->ch_state[ch].arg = NULL;
+ }
+}
+
+/*
+ *
+ * Scatter-gather DMA.
+ *
+ * High-level DMA construct for transferring whole picture frames to
+ * memory that is discontinuous.
+ *
+ */
+
+/* DMA completion routine for the scatter-gather DMA fragments. */
+static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr,
+ void *arg)
+{
+ struct omap24xxcam_sgdma *sgdma =
+ container_of(dma, struct omap24xxcam_sgdma, dma);
+ int sgslot = (int)arg;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&sgdma->lock);
+
+ /* We got an interrupt, we can remove the timer */
+ del_timer(&sgdma->reset_timer);
+
+ sg_state = sgdma->sg_state + sgslot;
+ if (!sg_state->queued_sglist) {
+ spin_unlock(&sgdma->lock);
+ printk(KERN_ERR "%s: sgdma completed when none queued!\n",
+ __func__);
+ return;
+ }
+
+ sg_state->csr |= csr;
+ if (!--sg_state->queued_sglist) {
+ /* Queue for this sglist is empty, so check to see if we're
+ * done.
+ */
+ if ((sg_state->next_sglist == sg_state->sglen)
+ || (sg_state->csr & csr_error)) {
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ u32 sg_csr = sg_state->csr;
+ /* All done with this sglist */
+ sgdma->free_sgdma++;
+ if (callback) {
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, sg_csr, arg);
+ return;
+ }
+ }
+ }
+
+ spin_unlock(&sgdma->lock);
+}
+
+/* Start queued scatter-gather DMA transfers. */
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int queued_sgdma, sgslot;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma;
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ while (queued_sgdma > 0) {
+ sg_state = sgdma->sg_state + sgslot;
+ while ((sg_state->next_sglist < sg_state->sglen) &&
+ !(sg_state->csr & csr_error)) {
+ const struct scatterlist *sglist;
+ unsigned int len;
+
+ sglist = sg_state->sglist + sg_state->next_sglist;
+ /* try to start the next DMA transfer */
+ if (sg_state->next_sglist + 1 == sg_state->sglen) {
+ /*
+ * On the last sg, we handle the case where
+ * cam->img.pix.sizeimage % PAGE_ALIGN != 0
+ */
+ len = sg_state->len - sg_state->bytes_read;
+ } else {
+ len = sg_dma_len(sglist);
+ }
+
+ if (omap24xxcam_dma_start(&sgdma->dma,
+ sg_dma_address(sglist),
+ len,
+ omap24xxcam_sgdma_callback,
+ (void *)sgslot)) {
+ /* DMA start failed */
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return;
+ } else {
+ unsigned long expires;
+ /* DMA start was successful */
+ sg_state->next_sglist++;
+ sg_state->bytes_read += len;
+ sg_state->queued_sglist++;
+
+ /* We start the reset timer */
+ expires = jiffies + HZ;
+ mod_timer(&sgdma->reset_timer, expires);
+ }
+ }
+ queued_sgdma--;
+ sgslot = (sgslot + 1) % NUM_SG_DMA;
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+/*
+ * 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.
+ */
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ struct sgdma_state *sg_state;
+
+ if ((sglen < 0) || ((sglen > 0) & !sglist))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (!sgdma->free_sgdma) {
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return -EBUSY;
+ }
+
+ sg_state = sgdma->sg_state + sgdma->next_sgdma;
+
+ sg_state->sglist = sglist;
+ sg_state->sglen = sglen;
+ sg_state->next_sglist = 0;
+ sg_state->bytes_read = 0;
+ sg_state->len = len;
+ sg_state->queued_sglist = 0;
+ sg_state->csr = 0;
+ sg_state->callback = callback;
+ sg_state->arg = arg;
+
+ sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA;
+ sgdma->free_sgdma--;
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+
+ omap24xxcam_sgdma_process(sgdma);
+
+ return 0;
+}
+
+/* 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.
+ */
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int sgslot;
+ struct sgdma_state *sg_state;
+ u32 csr = CAMDMA_CSR_TRANS_ERR;
+
+ /* stop any DMA transfers in progress */
+ omap24xxcam_dma_stop(&sgdma->dma, csr);
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (sgdma->free_sgdma < NUM_SG_DMA) {
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ sg_state = sgdma->sg_state + sgslot;
+ if (sg_state->next_sglist != 0) {
+ /* This DMA transfer was in progress, so abort it. */
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ sgdma->free_sgdma++;
+ if (callback) {
+ /* leave interrupts masked */
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, csr, arg);
+ spin_lock(&sgdma->lock);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data)
+{
+ int sg;
+
+ spin_lock_init(&sgdma->lock);
+ sgdma->free_sgdma = NUM_SG_DMA;
+ sgdma->next_sgdma = 0;
+ for (sg = 0; sg < NUM_SG_DMA; sg++) {
+ sgdma->sg_state[sg].sglen = 0;
+ sgdma->sg_state[sg].next_sglist = 0;
+ sgdma->sg_state[sg].bytes_read = 0;
+ sgdma->sg_state[sg].queued_sglist = 0;
+ sgdma->sg_state[sg].csr = 0;
+ sgdma->sg_state[sg].callback = NULL;
+ sgdma->sg_state[sg].arg = NULL;
+ }
+
+ omap24xxcam_dma_init(&sgdma->dma, base);
+ setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data);
+}
diff --git a/linux/drivers/media/video/omap24xxcam.c b/linux/drivers/media/video/omap24xxcam.c
new file mode 100644
index 000000000..85c3c7c92
--- /dev/null
+++ b/linux/drivers/media/video/omap24xxcam.c
@@ -0,0 +1,1908 @@
+/*
+ * drivers/media/video/omap24xxcam.c
+ *
+ * OMAP 2 camera block driver.
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007-2008 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/pci.h> /* needed for videobufs */
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "omap24xxcam.h"
+
+#define OMAP24XXCAM_VERSION KERNEL_VERSION(0, 0, 0)
+
+#define RESET_TIMEOUT_NS 10000
+
+static void omap24xxcam_reset(struct omap24xxcam_device *cam);
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
+static int omap24xxcam_remove(struct platform_device *pdev);
+
+/* 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 = 1280 * 960 * 2 * 2;
+
+static struct v4l2_int_device omap24xxcam;
+
+/*
+ *
+ * Clocks.
+ *
+ */
+
+static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
+{
+ if (cam->ick != NULL && !IS_ERR(cam->ick))
+ clk_put(cam->ick);
+ if (cam->fck != NULL && !IS_ERR(cam->fck))
+ clk_put(cam->fck);
+
+ cam->ick = cam->fck = NULL;
+}
+
+static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
+{
+ int rval = 0;
+
+ cam->fck = clk_get(cam->dev, "cam_fck");
+ if (IS_ERR(cam->fck)) {
+ dev_err(cam->dev, "can't get cam_fck");
+ rval = PTR_ERR(cam->fck);
+ omap24xxcam_clock_put(cam);
+ return rval;
+ }
+
+ cam->ick = clk_get(cam->dev, "cam_ick");
+ if (IS_ERR(cam->ick)) {
+ dev_err(cam->dev, "can't get cam_ick");
+ rval = PTR_ERR(cam->ick);
+ omap24xxcam_clock_put(cam);
+ }
+
+ return rval;
+}
+
+static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
+{
+ clk_enable(cam->fck);
+ clk_enable(cam->ick);
+}
+
+static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
+{
+ clk_disable(cam->fck);
+ clk_disable(cam->ick);
+}
+
+/*
+ *
+ * Camera core
+ *
+ */
+
+/*
+ * Set xclk.
+ *
+ * To disable xclk, use value zero.
+ */
+static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
+ u32 xclk)
+{
+ if (xclk) {
+ u32 divisor = CAM_MCLK / xclk;
+
+ if (divisor == 1)
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK,
+ CC_CTRL_XCLK_DIV_BYPASS);
+ else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, divisor);
+ } else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+}
+
+static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
+{
+ /*
+ * Setting the camera core AUTOIDLE bit causes problems with frame
+ * synchronization, so we will clear the AUTOIDLE bit instead.
+ */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
+ CC_SYSCONFIG_AUTOIDLE);
+
+ /* program the camera interface DMA packet size */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
+ CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
+
+ /* enable camera core error interrupts */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, 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 core.
+ *
+ * Data transfer to the camera DMA starts from next starting frame.
+ */
+static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
+{
+
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ cam->cc_ctrl);
+}
+
+/*
+ * Disable camera core.
+ *
+ * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
+ * core internal state machines will be reset. Use
+ * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
+ * frame completely.
+ */
+static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
+{
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ CC_CTRL_CC_RST);
+}
+
+/* Interrupt service routine for camera core interrupts. */
+static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
+{
+ u32 cc_irqstatus;
+ const u32 cc_irqstatus_err =
+ CC_IRQSTATUS_FW_ERR_IRQ
+ | CC_IRQSTATUS_FSC_ERR_IRQ
+ | CC_IRQSTATUS_SSC_ERR_IRQ
+ | CC_IRQSTATUS_FIFO_UF_IRQ
+ | CC_IRQSTATUS_FIFO_OF_IRQ;
+
+ cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
+ CC_IRQSTATUS);
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
+ cc_irqstatus);
+
+ if (cc_irqstatus & cc_irqstatus_err
+ && !atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
+ cc_irqstatus);
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ *
+ * videobuf_buffer handling.
+ *
+ * Memory for mmapped videobuf_buffers is not allocated
+ * conventionally, but by several kmalloc allocations and then
+ * creating the scatterlist on our own. User-space buffers are handled
+ * normally.
+ *
+ */
+
+/*
+ * Free the memory-mapped buffer memory allocated for a
+ * videobuf_buffer and the associated scatterlist.
+ */
+static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ size_t alloc_size;
+ struct page *page;
+ int i;
+
+ if (dma->sglist == NULL)
+ return;
+
+ i = dma->sglen;
+ while (i) {
+ i--;
+ alloc_size = sg_dma_len(&dma->sglist[i]);
+ page = sg_page(&dma->sglist[i]);
+ do {
+ ClearPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ __free_pages(sg_page(&dma->sglist[i]),
+ get_order(sg_dma_len(&dma->sglist[i])));
+ }
+
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+}
+
+/* Release all memory related to the videobuf_queue. */
+static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
+{
+ int i;
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == vbq->bufs[i])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
+ continue;
+ vbq->ops->buf_release(vbq, vbq->bufs[i]);
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ kfree(vbq->bufs[i]);
+ vbq->bufs[i] = NULL;
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ videobuf_mmap_free(vbq);
+}
+
+/*
+ * Allocate physically as contiguous as possible buffer for video
+ * frame and allocate and build DMA scatter-gather list for it.
+ */
+static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
+{
+ unsigned int order;
+ size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
+ struct page *page;
+ int max_pages, err = 0, i = 0;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /*
+ * allocate maximum size scatter-gather list. Note this is
+ * overhead. We may not use as many entries as we allocate
+ */
+ max_pages = vb->bsize >> PAGE_SHIFT;
+ dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
+ if (dma->sglist == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ while (size) {
+ order = get_order(size);
+ /*
+ * do not over-allocate even if we would get larger
+ * contiguous chunk that way
+ */
+ if ((PAGE_SIZE << order) > size)
+ order--;
+
+ /* try to allocate as many contiguous pages as possible */
+ page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ /* if allocation fails, try to allocate smaller amount */
+ while (page == NULL) {
+ order--;
+ page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ if (page == NULL && !order) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ size -= (PAGE_SIZE << order);
+
+ /* append allocated chunk of pages into scatter-gather list */
+ sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
+ dma->sglen++;
+ i++;
+
+ alloc_size = (PAGE_SIZE << order);
+
+ /* clear pages before giving them to user space */
+ memset(page_address(page), 0, alloc_size);
+
+ /* mark allocated pages reserved */
+ do {
+ SetPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ }
+ /*
+ * REVISIT: not fully correct to assign nr_pages == sglen but
+ * video-buf is passing nr_pages for e.g. unmap_sg calls
+ */
+ dma->nr_pages = dma->sglen;
+ dma->direction = PCI_DMA_FROMDEVICE;
+
+ return 0;
+
+out:
+ omap24xxcam_vbq_free_mmap_buffer(vb);
+ return err;
+}
+
+static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
+ unsigned int count)
+{
+ int i, err = 0;
+ struct omap24xxcam_fh *fh =
+ container_of(vbq, struct omap24xxcam_fh, vbq);
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < count; i++) {
+ err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
+ if (err)
+ goto out;
+ dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
+ videobuf_to_dma(vbq->bufs[i])->sglen, i);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return 0;
+out:
+ while (i) {
+ i--;
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+/*
+ * 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_sgdma *sgdma,
+ u32 csr, void *arg)
+{
+ struct omap24xxcam_device *cam =
+ container_of(sgdma, struct omap24xxcam_device, sgdma);
+ struct omap24xxcam_fh *fh = cam->streaming->private_data;
+ struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (--cam->sgdma_in_queue == 0)
+ omap24xxcam_core_disable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ do_gettimeofday(&vb->ts);
+ vb->field_count = atomic_add_return(2, &fh->field_count);
+ if (csr & csr_error) {
+ vb->state = VIDEOBUF_ERROR;
+ if (!atomic_read(&fh->cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
+ omap24xxcam_reset(cam);
+ }
+ } else
+ vb->state = VIDEOBUF_DONE;
+ wake_up(&vb->done);
+}
+
+static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /* wait for buffer, especially to get out of the sgdma queue */
+ videobuf_waiton(vb, 0, 0);
+ if (vb->memory == V4L2_MEMORY_MMAP) {
+ dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
+ dma->direction);
+ dma->direction = DMA_NONE;
+ } else {
+ videobuf_dma_unmap(vbq, videobuf_to_dma(vb));
+ videobuf_dma_free(videobuf_to_dma(vb));
+ }
+
+ vb->state = VIDEOBUF_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 *vbq, unsigned int *cnt,
+ unsigned int *size)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+
+ if (*cnt <= 0)
+ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+
+ if (*cnt > VIDEO_MAX_FRAME)
+ *cnt = VIDEO_MAX_FRAME;
+
+ *size = fh->pix.sizeimage;
+
+ /* accessing fh->cam->capture_mem is ok, it's constant */
+ while (*size * *cnt > fh->cam->capture_mem)
+ (*cnt)--;
+
+ return 0;
+}
+
+static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
+ struct videobuf_dmabuf *dma)
+{
+ int err = 0;
+
+ dma->direction = PCI_DMA_FROMDEVICE;
+ if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+ dma->sglen = 0;
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ int err = 0;
+
+ /*
+ * Accessing pix here is okay since it's constant while
+ * streaming is on (and we only get called then).
+ */
+ if (vb->baddr) {
+ /* This is a userspace buffer. */
+ if (fh->pix.sizeimage > vb->bsize) {
+ /* The buffer isn't big enough. */
+ err = -EINVAL;
+ } else
+ vb->size = fh->pix.sizeimage;
+ } else {
+ if (vb->state != VIDEOBUF_NEEDS_INIT) {
+ /*
+ * We have a kernel bounce buffer that has
+ * already been allocated.
+ */
+ if (fh->pix.sizeimage > 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.
+ */
+ omap24xxcam_vbq_release(vbq, vb);
+ vb->size = fh->pix.sizeimage;
+ }
+ } else {
+ /* We need to allocate a new kernel bounce buffer. */
+ vb->size = fh->pix.sizeimage;
+ }
+ }
+
+ if (err)
+ return err;
+
+ vb->width = fh->pix.width;
+ vb->height = fh->pix.height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ if (vb->memory == V4L2_MEMORY_MMAP)
+ /*
+ * we have built the scatter-gather list by ourself so
+ * do the scatter-gather mapping as well
+ */
+ err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
+ else
+ err = videobuf_iolock(vbq, vb, NULL);
+ }
+
+ if (!err)
+ vb->state = VIDEOBUF_PREPARED;
+ else
+ omap24xxcam_vbq_release(vbq, vb);
+
+ return err;
+}
+
+static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ enum videobuf_state state = vb->state;
+ unsigned long flags;
+ int err;
+
+ /*
+ * FIXME: We're marking the buffer active since we have no
+ * pretty way of marking it active exactly when the
+ * scatter-gather transfer starts.
+ */
+ vb->state = VIDEOBUF_ACTIVE;
+
+ err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
+ videobuf_to_dma(vb)->sglist,
+ videobuf_to_dma(vb)->sglen, vb->size,
+ omap24xxcam_vbq_complete, vb);
+
+ if (!err) {
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (++cam->sgdma_in_queue == 1
+ && !atomic_read(&cam->in_reset))
+ omap24xxcam_core_enable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ } else {
+ /*
+ * 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.
+ */
+ dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
+ dev_err(cam->dev, "likely a bug in the driver!\n");
+ vb->state = state;
+ }
+}
+
+static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
+ .buf_setup = omap24xxcam_vbq_setup,
+ .buf_prepare = omap24xxcam_vbq_prepare,
+ .buf_queue = omap24xxcam_vbq_queue,
+ .buf_release = omap24xxcam_vbq_release,
+};
+
+/*
+ *
+ * OMAP main camera system
+ *
+ */
+
+/*
+ * Reset camera block to power-on state.
+ */
+static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
+{
+ int max_loop = RESET_TIMEOUT_NS;
+
+ /* Reset whole camera subsystem */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAM_SYSCONFIG,
+ CAM_SYSCONFIG_SOFTRESET);
+
+ /* Wait till it's finished */
+ while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE)
+ && --max_loop) {
+ ndelay(1);
+ }
+
+ if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE))
+ dev_err(cam->dev, "camera soft reset timeout\n");
+}
+
+/*
+ * (Re)initialise the camera block.
+ */
+static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_poweron_reset(cam);
+
+ /* set the camera subsystem autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
+ CAM_SYSCONFIG_AUTOIDLE);
+
+ /* set the camera MMU autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
+ CAMMMU_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_core_hwinit(cam);
+
+ omap24xxcam_dma_hwinit(&cam->sgdma.dma);
+}
+
+/*
+ * Callback for dma transfer stalling.
+ */
+static void omap24xxcam_stalled_dma_reset(unsigned long data)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
+
+ if (!atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "dma stalled, resetting camera\n");
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ * Stop capture. Mark we're doing a reset, stop DMA transfers and
+ * core. (No new scatter-gather transfers will be queued whilst
+ * in_reset is non-zero.)
+ *
+ * If omap24xxcam_capture_stop is called from several places at
+ * once, only the first call will have an effect. Similarly, the last
+ * call omap24xxcam_streaming_cont will have effect.
+ *
+ * Serialisation is ensured by using cam->core_enable_disable_lock.
+ */
+static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_inc_return(&cam->in_reset) != 1) {
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ return;
+ }
+
+ omap24xxcam_core_disable(cam);
+
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ omap24xxcam_sgdma_sync(&cam->sgdma);
+}
+
+/*
+ * Reset and continue streaming.
+ *
+ * Note: 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.
+ */
+static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_read(&cam->in_reset) != 1)
+ goto out;
+
+ omap24xxcam_hwinit(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ omap24xxcam_sgdma_process(&cam->sgdma);
+
+ if (cam->sgdma_in_queue)
+ omap24xxcam_core_enable(cam);
+
+out:
+ atomic_dec(&cam->in_reset);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+}
+
+static ssize_t
+omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap24xxcam_device *cam = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive");
+}
+static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
+
+/*
+ * Stop capture and restart it. I.e. reset the camera during use.
+ */
+static void omap24xxcam_reset(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_capture_stop(cam);
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ * The main interrupt handler.
+ */
+static irqreturn_t omap24xxcam_isr(int irq, void *arg)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
+ u32 irqstatus;
+ unsigned int irqhandled = 0;
+
+ irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
+
+ if (irqstatus &
+ (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
+ | CAM_IRQSTATUS_DMA_IRQ0)) {
+ omap24xxcam_dma_isr(&cam->sgdma.dma);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
+ omap24xxcam_core_isr(cam);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
+ dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
+
+ return IRQ_RETVAL(irqhandled);
+}
+
+/*
+ *
+ * Sensor handling.
+ *
+ */
+
+/*
+ * Enable the external sensor interface. Try to negotiate interface
+ * parameters with the sensor and start using the new ones. The calls
+ * to sensor_if_enable and sensor_if_disable need not to be balanced.
+ */
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+ struct v4l2_ifparm p;
+
+ rval = vidioc_int_g_ifparm(cam->sdev, &p);
+ if (rval) {
+ dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
+ return rval;
+ }
+
+ cam->if_type = p.if_type;
+
+ cam->cc_ctrl = CC_CTRL_CC_EN;
+
+ switch (p.if_type) {
+ case V4L2_IF_TYPE_BT656:
+ if (p.u.bt656.frame_start_on_rising_vs)
+ cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
+ if (p.u.bt656.bt_sync_correct)
+ cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
+ if (p.u.bt656.swap)
+ cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
+ if (p.u.bt656.latch_clk_inv)
+ cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
+ if (p.u.bt656.nobt_hs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
+ if (p.u.bt656.nobt_vs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
+
+ switch (p.u.bt656.mode) {
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
+ break;
+ default:
+ dev_err(cam->dev,
+ "bt656 interface mode %d not supported\n",
+ p.u.bt656.mode);
+ return -EINVAL;
+ }
+ /*
+ * The clock rate that the sensor wants has changed.
+ * We have to adjust the xclk from OMAP 2 side to
+ * match the sensor's wish as closely as possible.
+ */
+ if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
+ u32 xclk = p.u.bt656.clock_curr;
+ u32 divisor;
+
+ if (xclk == 0)
+ return -EINVAL;
+
+ if (xclk > CAM_MCLK)
+ xclk = CAM_MCLK;
+
+ divisor = CAM_MCLK / xclk;
+ if (divisor * xclk < CAM_MCLK)
+ divisor++;
+ if (CAM_MCLK / divisor < p.u.bt656.clock_min
+ && divisor > 1)
+ divisor--;
+ if (divisor > 30)
+ divisor = 30;
+
+ xclk = CAM_MCLK / divisor;
+
+ if (xclk < p.u.bt656.clock_min
+ || xclk > p.u.bt656.clock_max)
+ return -EINVAL;
+
+ cam->if_u.bt656.xclk = xclk;
+ }
+ omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
+ break;
+ default:
+ /* FIXME: how about other interfaces? */
+ dev_err(cam->dev, "interface type %d not supported\n",
+ p.if_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
+{
+ switch (cam->if_type) {
+ case V4L2_IF_TYPE_BT656:
+ omap24xxcam_core_xclk_set(cam, 0);
+ break;
+ }
+}
+
+/*
+ * Initialise the sensor hardware.
+ */
+static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
+{
+ int err = 0;
+ struct v4l2_int_device *sdev = cam->sdev;
+
+ omap24xxcam_clock_on(cam);
+ err = omap24xxcam_sensor_if_enable(cam);
+ if (err) {
+ dev_err(cam->dev, "sensor interface could not be enabled at "
+ "initialisation, %d\n", err);
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ /* power up sensor during sensor initialization */
+ vidioc_int_s_power(sdev, 1);
+
+ err = vidioc_int_dev_init(sdev);
+ if (err) {
+ dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
+ /* Sensor init failed --- it's nonexistent to us! */
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ dev_info(cam->dev, "sensor is %s\n", sdev->name);
+
+out:
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+
+ vidioc_int_s_power(sdev, 0);
+
+ return err;
+}
+
+static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
+{
+ if (cam->sdev)
+ vidioc_int_dev_exit(cam->sdev);
+}
+
+static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+ vidioc_int_s_power(cam->sdev, 0);
+}
+
+/*
+ * Power-up and configure camera sensor. It's ready for capturing now.
+ */
+static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+
+ omap24xxcam_clock_on(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ rval = vidioc_int_s_power(cam->sdev, 1);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_init(cam->sdev);
+ if (rval)
+ goto out;
+
+ return 0;
+
+out:
+ omap24xxcam_sensor_disable(cam);
+
+ return rval;
+}
+
+static void omap24xxcam_sensor_reset_work(struct work_struct *work)
+{
+ struct omap24xxcam_device *cam =
+ container_of(work, struct omap24xxcam_device,
+ sensor_reset_work);
+
+ if (atomic_read(&cam->reset_disable))
+ return;
+
+ omap24xxcam_capture_stop(cam);
+
+ if (vidioc_int_reset(cam->sdev) == 0) {
+ vidioc_int_init(cam->sdev);
+ } else {
+ /* Can't reset it by vidioc_int_reset. */
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_sensor_enable(cam);
+ }
+
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ *
+ * IOCTL interface.
+ *
+ */
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+
+ strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
+ cap->version = OMAP24XXCAM_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
+
+ return rval;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = vidioc_int_s_fmt_cap(cam->sdev, f);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ if (!rval) {
+ mutex_lock(&ofh->vbq.vb_lock);
+ ofh->pix = f->fmt.pix;
+ mutex_unlock(&ofh->vbq.vb_lock);
+ }
+
+ memset(f, 0, sizeof(*f));
+ vidioc_g_fmt_vid_cap(file, fh, f);
+
+ return rval;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_try_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+ mutex_unlock(&cam->mutex);
+
+ rval = videobuf_reqbufs(&ofh->vbq, b);
+
+ /*
+ * Either videobuf_reqbufs failed or the buffers are not
+ * memory-mapped (which would need special attention).
+ */
+ if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
+ goto out;
+
+ rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
+ if (rval)
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+
+out:
+ return rval;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_querybuf(&ofh->vbq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_qbuf(&ofh->vbq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_buffer *vb;
+ int rval;
+
+videobuf_dqbuf_again:
+ rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
+ if (rval)
+ goto out;
+
+ vb = ofh->vbq.bufs[b->index];
+
+ mutex_lock(&cam->mutex);
+ /* _needs_reset returns -EIO if reset is required. */
+ rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
+ mutex_unlock(&cam->mutex);
+ if (rval == -EIO)
+ schedule_work(&cam->sensor_reset_work);
+ else
+ rval = 0;
+
+out:
+ /*
+ * This is a hack. We don't want to show -EIO to the user
+ * space. Requeue the buffer and try again if we're not doing
+ * this in non-blocking mode.
+ */
+ if (rval == -EIO) {
+ videobuf_qbuf(&ofh->vbq, b);
+ if (!(file->f_flags & O_NONBLOCK))
+ goto videobuf_dqbuf_again;
+ /*
+ * We don't have a videobuf_buffer now --- maybe next
+ * time...
+ */
+ rval = -EAGAIN;
+ }
+
+ return rval;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ if (rval) {
+ dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
+ goto out;
+ }
+
+ rval = videobuf_streamon(&ofh->vbq);
+ if (!rval) {
+ cam->streaming = file;
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_queue *q = &ofh->vbq;
+ int rval;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_scheduled_work();
+
+ rval = videobuf_streamoff(q);
+ if (!rval) {
+ mutex_lock(&cam->mutex);
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ return rval;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp)
+{
+ if (inp->index > 0)
+ return -EINVAL;
+
+ strlcpy(inp->name, "camera", sizeof(inp->name));
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_queryctrl(cam->sdev, a);
+
+ return rval;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_s_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a) {
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_parm(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct v4l2_streamparm old_streamparm;
+ int rval;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_s_parm(cam->sdev, a);
+ if (rval)
+ goto out;
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ /*
+ * Revert to old streaming parameters if enabling sensor
+ * interface with the new ones failed.
+ */
+ if (rval)
+ vidioc_int_s_parm(cam->sdev, &old_streamparm);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+/*
+ *
+ * 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;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming != file) {
+ mutex_unlock(&cam->mutex);
+ return POLLERR;
+ }
+ mutex_unlock(&cam->mutex);
+
+ mutex_lock(&fh->vbq.vb_lock);
+ if (list_empty(&fh->vbq.stream)) {
+ mutex_unlock(&fh->vbq.vb_lock);
+ return POLLERR;
+ }
+ vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
+ mutex_unlock(&fh->vbq.vb_lock);
+
+ poll_wait(file, &vb->done, wait);
+
+ if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int omap24xxcam_mmap_buffers(struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_queue *vbq = &fh->vbq;
+ unsigned int first, last, size, i, j;
+ int err = 0;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+ mutex_unlock(&cam->mutex);
+ mutex_lock(&vbq->vb_lock);
+
+ /* look for first buffer to map */
+ for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+ if (NULL == vbq->bufs[first])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
+ continue;
+ if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+ break;
+ }
+
+ /* look for last buffer to map */
+ for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
+ if (NULL == vbq->bufs[last])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
+ continue;
+ size += vbq->bufs[last]->bsize;
+ if (size == (vma->vm_end - vma->vm_start))
+ break;
+ }
+
+ size = 0;
+ for (i = first; i <= last; i++) {
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
+
+ for (j = 0; j < dma->sglen; j++) {
+ err = remap_pfn_range(
+ vma, vma->vm_start + size,
+ page_to_pfn(sg_page(&dma->sglist[j])),
+ sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
+ if (err)
+ goto out;
+ size += sg_dma_len(&dma->sglist[j]);
+ }
+ }
+
+out:
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ int rval;
+
+ /* let the video-buf mapper check arguments and set-up structures */
+ rval = videobuf_mmap_mapper(&fh->vbq, vma);
+ if (rval)
+ return rval;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* do mapping to our allocated buffers */
+ rval = omap24xxcam_mmap_buffers(file, vma);
+ /*
+ * In case of error, free vma->vm_private_data allocated by
+ * videobuf_mmap_mapper.
+ */
+ if (rval)
+ kfree(vma->vm_private_data);
+
+ return rval;
+}
+
+static int omap24xxcam_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct omap24xxcam_device *cam = omap24xxcam.priv;
+ struct omap24xxcam_fh *fh;
+ struct v4l2_format format;
+
+ if (!cam || !cam->vfd || (cam->vfd->minor != minor))
+ return -ENODEV;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (fh == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&cam->mutex);
+ if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
+ mutex_unlock(&cam->mutex);
+ goto out_try_module_get;
+ }
+
+ if (atomic_inc_return(&cam->users) == 1) {
+ omap24xxcam_hwinit(cam);
+ if (omap24xxcam_sensor_enable(cam)) {
+ mutex_unlock(&cam->mutex);
+ goto out_omap24xxcam_sensor_enable;
+ }
+ }
+ mutex_unlock(&cam->mutex);
+
+ fh->cam = cam;
+ mutex_lock(&cam->mutex);
+ vidioc_int_g_fmt_cap(cam->sdev, &format);
+ mutex_unlock(&cam->mutex);
+ /* FIXME: how about fh->pix when there are more users? */
+ fh->pix = format.fmt.pix;
+
+ file->private_data = fh;
+
+ spin_lock_init(&fh->vbq_lock);
+
+ videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
+ &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), fh);
+
+ return 0;
+
+out_omap24xxcam_sensor_enable:
+ omap24xxcam_poweron_reset(cam);
+ module_put(cam->sdev->module);
+
+out_try_module_get:
+ kfree(fh);
+
+ return -ENODEV;
+}
+
+static int omap24xxcam_release(struct inode *inode, struct file *file)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_scheduled_work();
+
+ /* stop streaming capture */
+ videobuf_streamoff(&fh->vbq);
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming == file) {
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ } else {
+ mutex_unlock(&cam->mutex);
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
+
+ /*
+ * Make sure the reset work we might have scheduled is not
+ * pending! It may be run *only* if we have users. (And it may
+ * not be scheduled anymore since streaming is already
+ * disabled.)
+ */
+ flush_scheduled_work();
+
+ mutex_lock(&cam->mutex);
+ if (atomic_dec_return(&cam->users) == 0) {
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+ }
+ mutex_unlock(&cam->mutex);
+
+ file->private_data = NULL;
+
+ module_put(cam->sdev->module);
+ kfree(fh);
+
+ return 0;
+}
+
+static struct file_operations omap24xxcam_fops = {
+ .llseek = no_llseek,
+ .ioctl = video_ioctl2,
+ .poll = omap24xxcam_poll,
+ .mmap = omap24xxcam_mmap,
+ .open = omap24xxcam_open,
+ .release = omap24xxcam_release,
+};
+
+/*
+ *
+ * Power management.
+ *
+ */
+
+#ifdef CONFIG_PM
+static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_stop(cam);
+
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+
+ return 0;
+}
+
+static int omap24xxcam_resume(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ omap24xxcam_hwinit(cam);
+ omap24xxcam_sensor_enable(cam);
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_cont(cam);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+};
+
+/*
+ *
+ * Camera device (i.e. /dev/video).
+ *
+ */
+
+static int omap24xxcam_device_register(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+ struct video_device *vfd;
+ int rval;
+
+ /* We already have a slave. */
+ if (cam->sdev)
+ return -EBUSY;
+
+ cam->sdev = s;
+
+ if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
+ dev_err(cam->dev, "could not register sysfs entry\n");
+ rval = -EBUSY;
+ goto err;
+ }
+
+ /* initialize the video_device struct */
+ vfd = cam->vfd = video_device_alloc();
+ if (!vfd) {
+ dev_err(cam->dev, "could not allocate video device struct\n");
+ rval = -ENOMEM;
+ goto err;
+ }
+ vfd->release = video_device_release;
+
+ vfd->parent = cam->dev;
+
+ strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
+ vfd->vfl_type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY;
+ vfd->fops = &omap24xxcam_fops;
+ vfd->minor = -1;
+ vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
+
+ omap24xxcam_hwinit(cam);
+
+ rval = omap24xxcam_sensor_init(cam);
+ if (rval)
+ goto err;
+
+ if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+ dev_err(cam->dev, "could not register V4L device\n");
+ vfd->minor = -1;
+ rval = -EBUSY;
+ goto err;
+ }
+
+ omap24xxcam_poweron_reset(cam);
+
+ dev_info(cam->dev, "registered device video%d\n", vfd->minor);
+
+ return 0;
+
+err:
+ omap24xxcam_device_unregister(s);
+
+ return rval;
+}
+
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+
+ omap24xxcam_sensor_exit(cam);
+
+ if (cam->vfd) {
+ if (cam->vfd->minor == -1) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(cam->vfd);
+ } else {
+ /*
+ * The unregister function will release the
+ * video_device struct as well as
+ * unregistering it.
+ */
+ video_unregister_device(cam->vfd);
+ }
+ cam->vfd = NULL;
+ }
+
+ device_remove_file(cam->dev, &dev_attr_streaming);
+
+ cam->sdev = NULL;
+}
+
+static struct v4l2_int_master omap24xxcam_master = {
+ .attach = omap24xxcam_device_register,
+ .detach = omap24xxcam_device_unregister,
+};
+
+static struct v4l2_int_device omap24xxcam = {
+ .module = THIS_MODULE,
+ .name = CAM_NAME,
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &omap24xxcam_master
+ },
+};
+
+/*
+ *
+ * Driver initialisation and deinitialisation.
+ *
+ */
+
+static int __init omap24xxcam_probe(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam;
+ struct resource *mem;
+ int irq;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam) {
+ dev_err(&pdev->dev, "could not allocate memory\n");
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, cam);
+
+ cam->dev = &pdev->dev;
+
+ /*
+ * 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 < 320 * 240 * 2 * 2)
+ capture_mem = 320 * 240 * 2 * 2;
+ cam->capture_mem = capture_mem;
+
+ /* request the mem region for the camera registers */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(cam->dev, "no mem resource?\n");
+ goto err;
+ }
+ if (!request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->name)) {
+ dev_err(cam->dev,
+ "cannot reserve camera register I/O region\n");
+ goto err;
+ }
+ cam->mmio_base_phys = mem->start;
+ cam->mmio_size = (mem->end - mem->start) + 1;
+
+ /* map the region */
+ cam->mmio_base = (unsigned long)
+ ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
+ if (!cam->mmio_base) {
+ dev_err(cam->dev, "cannot map camera register I/O region\n");
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(cam->dev, "no irq for camera?\n");
+ goto err;
+ }
+
+ /* install the interrupt service routine */
+ if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
+ dev_err(cam->dev,
+ "could not install interrupt service routine\n");
+ goto err;
+ }
+ cam->irq = irq;
+
+ if (omap24xxcam_clock_get(cam))
+ goto err;
+
+ INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
+
+ mutex_init(&cam->mutex);
+ spin_lock_init(&cam->core_enable_disable_lock);
+
+ omap24xxcam_sgdma_init(&cam->sgdma,
+ cam->mmio_base + CAMDMA_REG_OFFSET,
+ omap24xxcam_stalled_dma_reset,
+ (unsigned long)cam);
+
+ omap24xxcam.priv = cam;
+
+ if (v4l2_int_device_register(&omap24xxcam))
+ goto err;
+
+ return 0;
+
+err:
+ omap24xxcam_remove(pdev);
+ return -ENODEV;
+}
+
+static int omap24xxcam_remove(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (!cam)
+ return 0;
+
+ if (omap24xxcam.priv != NULL)
+ v4l2_int_device_unregister(&omap24xxcam);
+ omap24xxcam.priv = NULL;
+
+ omap24xxcam_clock_put(cam);
+
+ if (cam->irq) {
+ free_irq(cam->irq, cam);
+ cam->irq = 0;
+ }
+
+ if (cam->mmio_base) {
+ iounmap((void *)cam->mmio_base);
+ cam->mmio_base = 0;
+ }
+
+ if (cam->mmio_base_phys) {
+ release_mem_region(cam->mmio_base_phys, cam->mmio_size);
+ cam->mmio_base_phys = 0;
+ }
+
+ kfree(cam);
+
+ return 0;
+}
+
+static struct platform_driver omap24xxcam_driver = {
+ .probe = omap24xxcam_probe,
+ .remove = omap24xxcam_remove,
+#ifdef CONFIG_PM
+ .suspend = omap24xxcam_suspend,
+ .resume = omap24xxcam_resume,
+#endif
+ .driver = {
+ .name = CAM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ *
+ * Module initialisation and deinitialisation
+ *
+ */
+
+static int __init omap24xxcam_init(void)
+{
+ return platform_driver_register(&omap24xxcam_driver);
+}
+
+static void __exit omap24xxcam_cleanup(void)
+{
+ platform_driver_unregister(&omap24xxcam_driver);
+}
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+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 4800kiB)");
+
+module_init(omap24xxcam_init);
+module_exit(omap24xxcam_cleanup);
diff --git a/linux/drivers/media/video/omap24xxcam.h b/linux/drivers/media/video/omap24xxcam.h
new file mode 100644
index 000000000..2ce67f5a4
--- /dev/null
+++ b/linux/drivers/media/video/omap24xxcam.h
@@ -0,0 +1,593 @@
+/*
+ * drivers/media/video/omap24xxcam.h
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP24XXCAM_H
+#define OMAP24XXCAM_H
+
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-int-device.h>
+
+/*
+ *
+ * General driver related definitions.
+ *
+ */
+
+#define CAM_NAME "omap24xxcam"
+
+#define CAM_MCLK 96000000
+
+/* number of bytes transferred per DMA request */
+#define DMA_THRESHOLD 32
+
+/*
+ * 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 don't have any overlay sglists now.)
+ */
+#define NUM_SG_DMA (VIDEO_MAX_FRAME)
+
+/*
+ *
+ * Register definitions.
+ *
+ */
+
+/* subsystem register block offsets */
+#define CC_REG_OFFSET 0x00000400
+#define CAMDMA_REG_OFFSET 0x00000800
+#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_ONE_SHOT (1 << 20)
+#define CC_CTRL_CC_IF_SYNCHRO (1 << 19)
+#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 (1 << 9)
+#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)
+
+/*
+ *
+ * Declarations.
+ *
+ */
+
+/* forward declarations */
+struct omap24xxcam_sgdma;
+struct omap24xxcam_dma;
+
+typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam,
+ u32 status, void *arg);
+typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam,
+ u32 status, void *arg);
+
+struct channel_state {
+ dma_callback_t callback;
+ void *arg;
+};
+
+/* sgdma state for each of the possible videobuf_buffers + 2 overlays */
+struct sgdma_state {
+ const struct scatterlist *sglist;
+ int sglen; /* number of sglist entries */
+ int next_sglist; /* index of next sglist entry to process */
+ unsigned int bytes_read; /* number of bytes read */
+ unsigned int len; /* total length of sglist (excluding
+ * bytes due to page alignment) */
+ int queued_sglist; /* number of sglist entries queued for DMA */
+ u32 csr; /* DMA return code */
+ sgdma_callback_t callback;
+ void *arg;
+};
+
+/* physical DMA channel management */
+struct omap24xxcam_dma {
+ spinlock_t lock; /* Lock for the whole structure. */
+
+ unsigned long base; /* base address for dma controller */
+
+ /* While dma_stop!=0, an attempt to start a new DMA transfer will
+ * fail.
+ */
+ atomic_t dma_stop;
+ int free_dmach; /* number of dma channels free */
+ int next_dmach; /* index of next dma channel to use */
+ struct channel_state ch_state[NUM_CAMDMA_CHANNELS];
+};
+
+/* scatter-gather DMA (scatterlist stuff) management */
+struct omap24xxcam_sgdma {
+ struct omap24xxcam_dma dma;
+
+ spinlock_t lock; /* Lock for the fields below. */
+ int free_sgdma; /* number of free sg dma slots */
+ int next_sgdma; /* index of next sg dma slot to use */
+ struct sgdma_state sg_state[NUM_SG_DMA];
+
+ /* Reset timer data */
+ struct timer_list reset_timer;
+};
+
+/* per-device data structure */
+struct omap24xxcam_device {
+ /*** mutex ***/
+ /*
+ * mutex serialises access to this structure. Also camera
+ * opening and releasing is synchronised by this.
+ */
+ struct mutex mutex;
+
+ /*** general driver state information ***/
+ atomic_t users;
+ /*
+ * Lock to serialise core enabling and disabling and access to
+ * sgdma_in_queue.
+ */
+ spinlock_t core_enable_disable_lock;
+ /*
+ * Number or sgdma requests in scatter-gather queue, protected
+ * by the lock above.
+ */
+ int sgdma_in_queue;
+ /*
+ * Sensor interface parameters: interface type, CC_CTRL
+ * register value and interface specific data.
+ */
+ int if_type;
+ union {
+ struct parallel {
+ u32 xclk;
+ } bt656;
+ } if_u;
+ u32 cc_ctrl;
+
+ /*** subsystem structures ***/
+ struct omap24xxcam_sgdma sgdma;
+
+ /*** hardware resources ***/
+ unsigned int irq;
+ unsigned long mmio_base;
+ unsigned long mmio_base_phys;
+ unsigned long mmio_size;
+
+ /*** interfaces and device ***/
+ struct v4l2_int_device *sdev;
+ struct device *dev;
+ struct video_device *vfd;
+
+ /*** camera and sensor reset related stuff ***/
+ struct work_struct sensor_reset_work;
+ /*
+ * We're in the middle of a reset. Don't enable core if this
+ * is non-zero! This exists to help decisionmaking in a case
+ * where videobuf_qbuf is called while we are in the middle of
+ * a reset.
+ */
+ atomic_t in_reset;
+ /*
+ * Non-zero if we don't want any resets for now. Used to
+ * prevent reset work to run when we're about to stop
+ * streaming.
+ */
+ atomic_t reset_disable;
+
+ /*** video device parameters ***/
+ int capture_mem;
+
+ /*** camera module clocks ***/
+ struct clk *fck;
+ struct clk *ick;
+
+ /*** capture data ***/
+ /* file handle, if streaming is on */
+ struct file *streaming;
+};
+
+/* Per-file handle data. */
+struct omap24xxcam_fh {
+ spinlock_t vbq_lock; /* spinlock for the videobuf queue */
+ struct videobuf_queue vbq;
+ struct v4l2_pix_format pix; /* serialise pix by vbq->lock */
+ atomic_t field_count; /* field counter for videobuf_buffer */
+ /* accessing cam here doesn't need serialisation: it's constant */
+ struct omap24xxcam_device *cam;
+};
+
+/*
+ *
+ * Register I/O functions.
+ *
+ */
+
+static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset,
+ u32 val)
+{
+ writel(val, base + offset);
+ return val;
+}
+
+static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 addr = base + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+/*
+ *
+ * Function prototypes.
+ *
+ */
+
+/* dma prototypes */
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma);
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma);
+
+/* sgdma prototypes */
+
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma);
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg);
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma);
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data);
+void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma);
+
+#endif
diff --git a/linux/drivers/media/video/tvp514x.c b/linux/drivers/media/video/tvp514x.c
new file mode 100644
index 000000000..ac9aa40d0
--- /dev/null
+++ b/linux/drivers/media/video/tvp514x.c
@@ -0,0 +1,1569 @@
+/*
+ * drivers/media/video/tvp514x.c
+ *
+ * TI TVP5146/47 decoder driver
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-int-device.h>
+#include <media/tvp514x.h>
+
+#include "tvp514x_regs.h"
+
+/* Module Name */
+#define TVP514X_MODULE_NAME "tvp514x"
+
+/* Private macros for TVP */
+#define I2C_RETRY_COUNT (5)
+#define LOCK_RETRY_COUNT (5)
+#define LOCK_RETRY_DELAY (200)
+
+/* Debug functions */
+static int debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dump_reg(client, reg, val) \
+ do { \
+ val = tvp514x_read_reg(client, reg); \
+ v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \
+ } while (0)
+
+/**
+ * enum tvp514x_std - enum for supported standards
+ */
+enum tvp514x_std {
+ STD_NTSC_MJ = 0,
+ STD_PAL_BDGHIN,
+ STD_INVALID
+};
+
+/**
+ * enum tvp514x_state - enum for different decoder states
+ */
+enum tvp514x_state {
+ STATE_NOT_DETECTED,
+ STATE_DETECTED
+};
+
+/**
+ * struct tvp514x_std_info - Structure to store standard informations
+ * @width: Line width in pixels
+ * @height:Number of active lines
+ * @video_std: Value to write in REG_VIDEO_STD register
+ * @standard: v4l2 standard structure information
+ */
+struct tvp514x_std_info {
+ unsigned long width;
+ unsigned long height;
+ u8 video_std;
+ struct v4l2_standard standard;
+};
+
+/**
+ * struct tvp514x_decoded - TVP5146/47 decoder object
+ * @v4l2_int_device: Slave handle
+ * @pdata: Board specific
+ * @client: I2C client data
+ * @id: Entry from I2C table
+ * @ver: Chip version
+ * @state: TVP5146/47 decoder state - detected or not-detected
+ * @pix: Current pixel format
+ * @num_fmts: Number of formats
+ * @fmt_list: Format list
+ * @current_std: Current standard
+ * @num_stds: Number of standards
+ * @std_list: Standards list
+ * @route: input and output routing at chip level
+ */
+struct tvp514x_decoder {
+ struct v4l2_int_device *v4l2_int_device;
+ const struct tvp514x_platform_data *pdata;
+ struct i2c_client *client;
+
+ struct i2c_device_id *id;
+
+ int ver;
+ enum tvp514x_state state;
+
+ struct v4l2_pix_format pix;
+ int num_fmts;
+ const struct v4l2_fmtdesc *fmt_list;
+
+ enum tvp514x_std current_std;
+ int num_stds;
+ struct tvp514x_std_info *std_list;
+
+ struct v4l2_routing route;
+};
+
+/* TVP514x default register values */
+static struct tvp514x_reg tvp514x_reg_list[] = {
+ {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */
+ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F},
+ {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F},
+ {TOK_WRITE, REG_COLOR_KILLER, 0x10},
+ {TOK_WRITE, REG_LUMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL2, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL3, 0x02},
+ {TOK_WRITE, REG_BRIGHTNESS, 0x80},
+ {TOK_WRITE, REG_CONTRAST, 0x80},
+ {TOK_WRITE, REG_SATURATION, 0x80},
+ {TOK_WRITE, REG_HUE, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E},
+ {TOK_SKIP, 0x0F, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80},
+ {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80},
+ {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80},
+ {TOK_SKIP, 0x13, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80},
+ {TOK_SKIP, 0x15, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */
+ {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03},
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */
+ {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */
+ {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00},
+ {TOK_SKIP, 0x26, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x27, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC},
+ {TOK_SKIP, 0x29, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00},
+ {TOK_SKIP, 0x2B, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_SCART_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_CONTROL, 0x00},
+ {TOK_SKIP, 0x2F, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x30, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x31, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF},
+ {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF},
+ {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */
+ {TOK_TERM, 0, 0},
+};
+
+/* List of image formats supported by TVP5146/47 decoder
+ * Currently we are using 8 bit mode only, but can be
+ * extended to 10/20 bit mode.
+ */
+static const struct v4l2_fmtdesc tvp514x_fmt_list[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "8-bit UYVY 4:2:2 Format",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+
+/*
+ * Supported standards -
+ *
+ * Currently supports two standards only, need to add support for rest of the
+ * modes, like SECAM, etc...
+ */
+static struct tvp514x_std_info tvp514x_std_list[] = {
+ /* Standard: STD_NTSC_MJ */
+ [STD_NTSC_MJ] = {
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_NTSC_MJ_BIT,
+ .standard = {
+ .index = 0,
+ .id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .frameperiod = {1001, 30000},
+ .framelines = 525
+ },
+ /* Standard: STD_PAL_BDGHIN */
+ },
+ [STD_PAL_BDGHIN] = {
+ .width = PAL_NUM_ACTIVE_PIXELS,
+ .height = PAL_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_PAL_BDGHIN_BIT,
+ .standard = {
+ .index = 1,
+ .id = V4L2_STD_PAL,
+ .name = "PAL",
+ .frameperiod = {1, 25},
+ .framelines = 625
+ },
+ },
+ /* Standard: need to add for additional standard */
+};
+/*
+ * Control structure for Auto Gain
+ * This is temporary data, will get replaced once
+ * v4l2_ctrl_query_fill supports it.
+ */
+static const struct v4l2_queryctrl tvp514x_autogain_ctrl = {
+ .id = V4L2_CID_AUTOGAIN,
+ .name = "Gain, Automatic",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+};
+
+/*
+ * Read a value from a register in an TVP5146/47 decoder device.
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int tvp514x_read_reg(struct i2c_client *client, u8 reg)
+{
+ int err;
+ int retry = 0;
+read_again:
+
+ err = i2c_smbus_read_byte_data(client, reg);
+ if (err == -1) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l_warn(client, "Read: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto read_again;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * Write a value to a register in an TVP5146/47 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int err;
+ int retry = 0;
+write_again:
+
+ err = i2c_smbus_write_byte_data(client, reg, val);
+ if (err) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l_warn(client, "Write: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto write_again;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * tvp514x_write_regs : Initializes a list of TVP5146/47 registers
+ * if token is TOK_TERM, then entire write operation terminates
+ * if token is TOK_DELAY, then a delay of 'val' msec is introduced
+ * if token is TOK_SKIP, then the register write is skipped
+ * if token is TOK_WRITE, then the register write is performed
+ *
+ * reglist - list of registers to be written
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_regs(struct i2c_client *client,
+ const struct tvp514x_reg reglist[])
+{
+ int err;
+ const struct tvp514x_reg *next = reglist;
+
+ for (; next->token != TOK_TERM; next++) {
+ if (next->token == TOK_DELAY) {
+ msleep(next->val);
+ continue;
+ }
+
+ if (next->token == TOK_SKIP)
+ continue;
+
+ err = tvp514x_write_reg(client, next->reg, (u8) next->val);
+ if (err) {
+ v4l_err(client, "Write failed. Err[%d]\n", err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * tvp514x_get_current_std:
+ * Returns the current standard detected by TVP5146/47
+ */
+static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder
+ *decoder)
+{
+ u8 std, std_status;
+
+ std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD);
+ if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) {
+ /* use the standard status register */
+ std_status = tvp514x_read_reg(decoder->client,
+ REG_VIDEO_STD_STATUS);
+ } else
+ std_status = std; /* use the standard register itself */
+
+ switch (std_status & VIDEO_STD_MASK) {
+ case VIDEO_STD_NTSC_MJ_BIT:
+ return STD_NTSC_MJ;
+
+ case VIDEO_STD_PAL_BDGHIN_BIT:
+ return STD_PAL_BDGHIN;
+
+ default:
+ return STD_INVALID;
+ }
+
+ return STD_INVALID;
+}
+
+/*
+ * TVP5146/47 register dump function
+ */
+static void tvp514x_reg_dump(struct tvp514x_decoder *decoder)
+{
+ u8 value;
+
+ dump_reg(decoder->client, REG_INPUT_SEL, value);
+ dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value);
+ dump_reg(decoder->client, REG_VIDEO_STD, value);
+ dump_reg(decoder->client, REG_OPERATION_MODE, value);
+ dump_reg(decoder->client, REG_COLOR_KILLER, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL1, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL2, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL3, value);
+ dump_reg(decoder->client, REG_BRIGHTNESS, value);
+ dump_reg(decoder->client, REG_CONTRAST, value);
+ dump_reg(decoder->client, REG_SATURATION, value);
+ dump_reg(decoder->client, REG_HUE, value);
+ dump_reg(decoder->client, REG_CHROMA_CONTROL1, value);
+ dump_reg(decoder->client, REG_CHROMA_CONTROL2, value);
+ dump_reg(decoder->client, REG_COMP_PR_SATURATION, value);
+ dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value);
+ dump_reg(decoder->client, REG_COMP_PB_SATURATION, value);
+ dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value);
+ dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value);
+ dump_reg(decoder->client, REG_SYNC_CONTROL, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value);
+ dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value);
+}
+
+/*
+ * Configure the TVP5146/47 with the current register settings
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_configure(struct tvp514x_decoder *decoder)
+{
+ int err;
+
+ /* common register initialization */
+ err =
+ tvp514x_write_regs(decoder->client, tvp514x_reg_list);
+ if (err)
+ return err;
+
+ if (debug)
+ tvp514x_reg_dump(decoder);
+
+ return 0;
+}
+
+/*
+ * Detect if an tvp514x is present, and if so which revision.
+ * A device is considered to be detected if the chip ID (LSB and MSB)
+ * registers match the expected values.
+ * Any value of the rom version register is accepted.
+ * Returns ENODEV error number if no device is detected, or zero
+ * if a device is detected.
+ */
+static int tvp514x_detect(struct tvp514x_decoder *decoder)
+{
+ u8 chip_id_msb, chip_id_lsb, rom_ver;
+
+ chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB);
+ chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB);
+ rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION);
+
+ v4l_dbg(1, debug, decoder->client,
+ "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n",
+ chip_id_msb, chip_id_lsb, rom_ver);
+ if ((chip_id_msb != TVP514X_CHIP_ID_MSB)
+ || ((chip_id_lsb != TVP5146_CHIP_ID_LSB)
+ && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) {
+ /* We didn't read the values we expected, so this must not be
+ * an TVP5146/47.
+ */
+ v4l_err(decoder->client,
+ "chip id mismatch msb:0x%x lsb:0x%x\n",
+ chip_id_msb, chip_id_lsb);
+ return -ENODEV;
+ }
+
+ decoder->ver = rom_ver;
+ decoder->state = STATE_DETECTED;
+
+ v4l_info(decoder->client,
+ "%s found at 0x%x (%s)\n", decoder->client->name,
+ decoder->client->addr << 1,
+ decoder->client->adapter->name);
+ return 0;
+}
+
+/*
+ * Following are decoder interface functions implemented by
+ * TVP5146/47 decoder driver.
+ */
+
+/**
+ * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @std_id: standard V4L2 std_id ioctl enum
+ *
+ * Returns the current standard detected by TVP5146/47. If no active input is
+ * detected, returns -EINVAL
+ */
+static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ enum tvp514x_std current_std;
+ enum tvp514x_input input_sel;
+ u8 sync_lock_status, lock_mask;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ input_sel = decoder->route.input;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+ /* check whether signal is locked */
+ sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1);
+ if (lock_mask != (sync_lock_status & lock_mask))
+ return -EINVAL; /* No input detected */
+
+ decoder->current_std = current_std;
+ *std_id = decoder->std_list[current_std].standard.id;
+
+ v4l_dbg(1, debug, decoder->client, "Current STD: %s",
+ decoder->std_list[current_std].standard.name);
+ return 0;
+}
+
+/**
+ * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @std_id: standard V4L2 v4l2_std_id ioctl enum
+ *
+ * If std_id is supported, sets the requested standard. Otherwise, returns
+ * -EINVAL
+ */
+static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err, i;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < decoder->num_stds; i++)
+ if (*std_id & decoder->std_list[i].standard.id)
+ break;
+
+ if ((i == decoder->num_stds) || (i == STD_INVALID))
+ return -EINVAL;
+
+ err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD,
+ decoder->std_list[i].video_std);
+ if (err)
+ return err;
+
+ decoder->current_std = i;
+ tvp514x_reg_list[REG_VIDEO_STD].val = decoder->std_list[i].video_std;
+
+ v4l_dbg(1, debug, decoder->client, "Standard set to: %s",
+ decoder->std_list[i].standard.name);
+ return 0;
+}
+
+/**
+ * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @index: number of the input
+ *
+ * If index is valid, selects the requested input. Otherwise, returns -EINVAL if
+ * the input is not supported or there is no active signal present in the
+ * selected input.
+ */
+static int ioctl_s_routing(struct v4l2_int_device *s,
+ struct v4l2_routing *route)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err;
+ enum tvp514x_input input_sel;
+ enum tvp514x_output output_sel;
+ enum tvp514x_std current_std = STD_INVALID;
+ u8 sync_lock_status, lock_mask;
+ int try_count = LOCK_RETRY_COUNT;
+
+ if ((!route) || (route->input >= INPUT_INVALID) ||
+ (route->output >= OUTPUT_INVALID))
+ return -EINVAL; /* Index out of bound */
+
+ input_sel = route->input;
+ output_sel = route->output;
+
+ err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel);
+ if (err)
+ return err;
+
+ output_sel |= tvp514x_read_reg(decoder->client,
+ REG_OUTPUT_FORMATTER1) & 0x7;
+ err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1,
+ output_sel);
+ if (err)
+ return err;
+
+ tvp514x_reg_list[REG_INPUT_SEL].val = input_sel;
+ tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel;
+
+ /* Clear status */
+ msleep(LOCK_RETRY_DELAY);
+ err =
+ tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01);
+ if (err)
+ return err;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+
+ while (try_count-- > 0) {
+ /* Allow decoder to sync up with new input */
+ msleep(LOCK_RETRY_DELAY);
+
+ /* get the current standard for future reference */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ continue;
+
+ sync_lock_status = tvp514x_read_reg(decoder->client,
+ REG_STATUS1);
+ if (lock_mask == (sync_lock_status & lock_mask))
+ break; /* Input detected */
+ }
+
+ if ((current_std == STD_INVALID) || (try_count < 0))
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+ decoder->route.input = route->input;
+ decoder->route.output = route->output;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Input set to: %d, std : %d",
+ input_sel, current_std);
+
+ return 0;
+}
+
+/**
+ * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qctrl: standard V4L2 v4l2_queryctrl structure
+ *
+ * If the requested control is supported, returns the control information.
+ * Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = -EINVAL;
+
+ if (qctrl == NULL)
+ return err;
+
+ switch (qctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ /* Brightness supported is same as standard one (0-255),
+ * so make use of standard API provided.
+ */
+ err = v4l2_ctrl_query_fill_std(qctrl);
+ break;
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ /* Saturation and Contrast supported is -
+ * Contrast: 0 - 255 (Default - 128)
+ * Saturation: 0 - 255 (Default - 128)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
+ break;
+ case V4L2_CID_HUE:
+ /* Hue Supported is -
+ * Hue - -180 - +180 (Default - 0, Step - +180)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ /* Autogain is either 0 or 1*/
+ memcpy(qctrl, &tvp514x_autogain_ctrl,
+ sizeof(struct v4l2_queryctrl));
+ err = 0;
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", qctrl->id);
+ return err;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Query Control: %s : Min - %d, Max - %d, Def - %d",
+ qctrl->name,
+ qctrl->minimum,
+ qctrl->maximum,
+ qctrl->default_value);
+
+ return err;
+}
+
+/**
+ * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the decoder. Otherwise, returns -EINVAL if the control is not
+ * supported.
+ */
+static int
+ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (ctrl == NULL)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = tvp514x_reg_list[REG_CONTRAST].val;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = tvp514x_reg_list[REG_SATURATION].val;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = tvp514x_reg_list[REG_HUE].val;
+ if (ctrl->value == 0x7F)
+ ctrl->value = 180;
+ else if (ctrl->value == 0x80)
+ ctrl->value = -180;
+ else
+ ctrl->value = 0;
+
+ break;
+ case V4L2_CID_AUTOGAIN:
+ ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val;
+ if ((ctrl->value & 0x3) == 3)
+ ctrl->value = 1;
+ else
+ ctrl->value = 0;
+
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", ctrl->id);
+ return -EINVAL;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Get Control: ID - %d - %d",
+ ctrl->id, ctrl->value);
+ return 0;
+}
+
+/**
+ * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW. Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = -EINVAL, value;
+
+ if (ctrl == NULL)
+ return err;
+
+ value = (__s32) ctrl->value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid brightness setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_BRIGHTNESS].val = value;
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid contrast setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_CONTRAST,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_CONTRAST].val = value;
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid saturation setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_SATURATION,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_SATURATION].val = value;
+ break;
+ case V4L2_CID_HUE:
+ if (value == 180)
+ value = 0x7F;
+ else if (value == -180)
+ value = 0x80;
+ else if (value == 0)
+ value = 0;
+ else {
+ v4l_err(decoder->client,
+ "invalid hue setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_HUE,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_HUE].val = value;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (value == 1)
+ value = 0x0F;
+ else if (value == 0)
+ value = 0x0C;
+ else {
+ v4l_err(decoder->client,
+ "invalid auto gain setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value;
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", ctrl->id);
+ return err;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Set Control: ID - %d - %d",
+ ctrl->id, ctrl->value);
+
+ return err;
+}
+
+/**
+ * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure
+ *
+ * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats
+ */
+static int
+ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int index;
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ index = fmt->index;
+ if ((index >= decoder->num_fmts) || (index < 0))
+ return -EINVAL; /* Index out of bound */
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ memcpy(fmt, &decoder->fmt_list[index],
+ sizeof(struct v4l2_fmtdesc));
+
+ v4l_dbg(1, debug, decoder->client,
+ "Current FMT: index - %d (%s)",
+ decoder->fmt_list[index].index,
+ decoder->fmt_list[index].description);
+ return 0;
+}
+
+/**
+ * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
+ *
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int
+ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int ifmt;
+ struct v4l2_pix_format *pix;
+ enum tvp514x_std current_std;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ pix = &f->fmt.pix;
+
+ /* Calculate height and width based on current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+ pix->width = decoder->std_list[current_std].width;
+ pix->height = decoder->std_list[current_std].height;
+
+ for (ifmt = 0; ifmt < decoder->num_fmts; ifmt++) {
+ if (pix->pixelformat ==
+ decoder->fmt_list[ifmt].pixelformat)
+ break;
+ }
+ if (ifmt == decoder->num_fmts)
+ ifmt = 0; /* None of the format matched, select default */
+ pix->pixelformat = decoder->fmt_list[ifmt].pixelformat;
+
+ pix->field = V4L2_FIELD_INTERLACED;
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->priv = 0;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Try FMT: pixelformat - %s, bytesperline - %d"
+ "Width - %d, Height - %d",
+ decoder->fmt_list[ifmt].description, pix->bytesperline,
+ pix->width, pix->height);
+ return 0;
+}
+
+/**
+ * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
+ *
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+static int
+ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_pix_format *pix;
+ int rval;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ pix = &f->fmt.pix;
+ rval = ioctl_try_fmt_cap(s, f);
+ if (rval)
+ return rval;
+
+ decoder->pix = *pix;
+
+ return rval;
+}
+
+/**
+ * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the decoder's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int
+ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ f->fmt.pix = decoder->pix;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Current FMT: bytesperline - %d"
+ "Width - %d, Height - %d",
+ decoder->pix.bytesperline,
+ decoder->pix.width, decoder->pix.height);
+ return 0;
+}
+
+/**
+ * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the decoder's video CAPTURE parameters.
+ */
+static int
+ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_captureparm *cparm;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ cparm = &a->parm.capture;
+ cparm->capability = V4L2_CAP_TIMEPERFRAME;
+ cparm->timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the decoder to use the input parameters, if possible. If
+ * not possible, returns the appropriate error code.
+ */
+static int
+ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_fract *timeperframe;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ timeperframe = &a->parm.capture.timeperframe;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ *timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num
+ * @s: pointer to standard V4L2 device structure
+ * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int rval;
+
+ if (p == NULL)
+ return -EINVAL;
+
+ if (NULL == decoder->pdata->ifparm)
+ return -EINVAL;
+
+ rval = decoder->pdata->ifparm(p);
+ if (rval) {
+ v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval);
+ return rval;
+ }
+
+ p->u.bt656.clock_curr = TVP514X_XCLK_BT656;
+
+ return 0;
+}
+
+/**
+ * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num
+ * @s: pointer to standard V4L2 device structure
+ * @p: void pointer to hold decoder's private data address
+ *
+ * Returns device's (decoder's) private data area address in p parameter
+ */
+static int ioctl_g_priv(struct v4l2_int_device *s, void *p)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (NULL == decoder->pdata->priv_data_set)
+ return -EINVAL;
+
+ return decoder->pdata->priv_data_set(p);
+}
+
+/**
+ * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = 0;
+
+ switch (on) {
+ case V4L2_POWER_OFF:
+ /* Power Down Sequence */
+ err =
+ tvp514x_write_reg(decoder->client, REG_OPERATION_MODE,
+ 0x01);
+ /* Disable mux for TVP5146/47 decoder data path */
+ if (decoder->pdata->power_set)
+ err |= decoder->pdata->power_set(on);
+ decoder->state = STATE_NOT_DETECTED;
+ break;
+
+ case V4L2_POWER_STANDBY:
+ if (decoder->pdata->power_set)
+ err = decoder->pdata->power_set(on);
+ break;
+
+ case V4L2_POWER_ON:
+ /* Enable mux for TVP5146/47 decoder data path */
+ if ((decoder->pdata->power_set) &&
+ (decoder->state == STATE_NOT_DETECTED)) {
+ int i;
+ struct tvp514x_init_seq *int_seq =
+ (struct tvp514x_init_seq *)
+ decoder->id->driver_data;
+
+ err = decoder->pdata->power_set(on);
+
+ /* Power Up Sequence */
+ for (i = 0; i < int_seq->no_regs; i++) {
+ err |= tvp514x_write_reg(decoder->client,
+ int_seq->init_reg_seq[i].reg,
+ int_seq->init_reg_seq[i].val);
+ }
+ /* Detect the sensor is not already detected */
+ err |= tvp514x_detect(decoder);
+ if (err) {
+ v4l_err(decoder->client,
+ "Unable to detect decoder\n");
+ return err;
+ }
+ }
+ err |= tvp514x_configure(decoder);
+ break;
+
+ default:
+ err = -ENODEV;
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialize the decoder device (calls tvp514x_configure())
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ /* Set default standard to auto */
+ tvp514x_reg_list[REG_VIDEO_STD].val =
+ VIDEO_STD_AUTO_SWITCH_BIT;
+
+ return tvp514x_configure(decoder);
+}
+
+/**
+ * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ return 0;
+}
+
+/**
+ * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master. Returns 0 if
+ * TVP5146/47 device could be found, otherwise returns appropriate error.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err;
+
+ err = tvp514x_detect(decoder);
+ if (err < 0) {
+ v4l_err(decoder->client,
+ "Unable to detect decoder\n");
+ return err;
+ }
+
+ v4l_info(decoder->client,
+ "chip version 0x%.2x detected\n", decoder->ver);
+
+ return 0;
+}
+
+static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = {
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init},
+ {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit},
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power},
+ {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm},
+ {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init},
+ {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap},
+ {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_try_fmt_cap},
+ {vidioc_int_g_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_g_fmt_cap},
+ {vidioc_int_s_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_s_fmt_cap},
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm},
+ {vidioc_int_queryctrl_num,
+ (v4l2_int_ioctl_func *) ioctl_queryctrl},
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl},
+ {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd},
+ {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std},
+ {vidioc_int_s_video_routing_num,
+ (v4l2_int_ioctl_func *) ioctl_s_routing},
+};
+
+static struct v4l2_int_slave tvp514x_slave = {
+ .ioctls = tvp514x_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc),
+};
+
+static struct tvp514x_decoder tvp514x_dev = {
+ .state = STATE_NOT_DETECTED,
+
+ .fmt_list = tvp514x_fmt_list,
+ .num_fmts = ARRAY_SIZE(tvp514x_fmt_list),
+
+ .pix = { /* Default to NTSC 8-bit YUV 422 */
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2,
+ .sizeimage =
+ NTSC_NUM_ACTIVE_PIXELS * 2 * NTSC_NUM_ACTIVE_LINES,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ },
+
+ .current_std = STD_NTSC_MJ,
+ .std_list = tvp514x_std_list,
+ .num_stds = ARRAY_SIZE(tvp514x_std_list),
+
+};
+
+static struct v4l2_int_device tvp514x_int_device = {
+ .module = THIS_MODULE,
+ .name = TVP514X_MODULE_NAME,
+ .priv = &tvp514x_dev,
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &tvp514x_slave,
+ },
+};
+
+/**
+ * tvp514x_probe - decoder driver i2c probe handler
+ * @client: i2c driver client device structure
+ *
+ * Register decoder as an i2c client device and V4L2
+ * device.
+ */
+static int
+tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct tvp514x_decoder *decoder = &tvp514x_dev;
+ int err;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ decoder->pdata = client->dev.platform_data;
+ if (!decoder->pdata) {
+ v4l_err(client, "No platform data\n!!");
+ return -ENODEV;
+ }
+ /*
+ * Fetch platform specific data, and configure the
+ * tvp514x_reg_list[] accordingly. Since this is one
+ * time configuration, no need to preserve.
+ */
+ tvp514x_reg_list[REG_OUTPUT_FORMATTER2].val |=
+ (decoder->pdata->clk_polarity << 1);
+ tvp514x_reg_list[REG_SYNC_CONTROL].val |=
+ ((decoder->pdata->hs_polarity << 2) |
+ (decoder->pdata->vs_polarity << 3));
+ /*
+ * Save the id data, required for power up sequence
+ */
+ decoder->id = (struct i2c_device_id *)id;
+ /* Attach to Master */
+ strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master);
+ decoder->v4l2_int_device = &tvp514x_int_device;
+ decoder->client = client;
+ i2c_set_clientdata(client, decoder);
+
+ /* Register with V4L2 layer as slave device */
+ err = v4l2_int_device_register(decoder->v4l2_int_device);
+ if (err) {
+ i2c_set_clientdata(client, NULL);
+ v4l_err(client,
+ "Unable to register to v4l2. Err[%d]\n", err);
+
+ } else
+ v4l_info(client, "Registered to v4l2 master %s!!\n",
+ decoder->pdata->master);
+
+ return 0;
+}
+
+/**
+ * tvp514x_remove - decoder driver i2c remove handler
+ * @client: i2c driver client device structure
+ *
+ * Unregister decoder as an i2c client device and V4L2
+ * device. Complement of tvp514x_probe().
+ */
+static int __exit tvp514x_remove(struct i2c_client *client)
+{
+ struct tvp514x_decoder *decoder = i2c_get_clientdata(client);
+
+ if (!client->adapter)
+ return -ENODEV; /* our client isn't attached */
+
+ v4l2_int_device_unregister(decoder->v4l2_int_device);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+/*
+ * TVP5146 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp5146_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp5146_init = {
+ .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq),
+ .init_reg_seq = tvp5146_init_reg_seq,
+};
+/*
+ * TVP5147 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp5147_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp5147_init = {
+ .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq),
+ .init_reg_seq = tvp5147_init_reg_seq,
+};
+/*
+ * TVP5146M2/TVP5147M1 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp514xm_init_reg_seq[] = {
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp514xm_init = {
+ .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq),
+ .init_reg_seq = tvp514xm_init_reg_seq,
+};
+/*
+ * I2C Device Table -
+ *
+ * name - Name of the actual device/chip.
+ * driver_data - Driver data
+ */
+static const struct i2c_device_id tvp514x_id[] = {
+ {"tvp5146", (unsigned long)&tvp5146_init},
+ {"tvp5146m2", (unsigned long)&tvp514xm_init},
+ {"tvp5147", (unsigned long)&tvp5147_init},
+ {"tvp5147m1", (unsigned long)&tvp514xm_init},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tvp514x_id);
+
+static struct i2c_driver tvp514x_i2c_driver = {
+ .driver = {
+ .name = TVP514X_MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tvp514x_probe,
+ .remove = __exit_p(tvp514x_remove),
+ .id_table = tvp514x_id,
+};
+
+/**
+ * tvp514x_init
+ *
+ * Module init function
+ */
+static int __init tvp514x_init(void)
+{
+ return i2c_add_driver(&tvp514x_i2c_driver);
+}
+
+/**
+ * tvp514x_cleanup
+ *
+ * Module exit function
+ */
+static void __exit tvp514x_cleanup(void)
+{
+ i2c_del_driver(&tvp514x_i2c_driver);
+}
+
+module_init(tvp514x_init);
+module_exit(tvp514x_cleanup);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TVP514X linux decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/tvp514x_regs.h b/linux/drivers/media/video/tvp514x_regs.h
new file mode 100644
index 000000000..351620aee
--- /dev/null
+++ b/linux/drivers/media/video/tvp514x_regs.h
@@ -0,0 +1,297 @@
+/*
+ * drivers/media/video/tvp514x_regs.h
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _TVP514X_REGS_H
+#define _TVP514X_REGS_H
+
+/*
+ * TVP5146/47 registers
+ */
+#define REG_INPUT_SEL (0x00)
+#define REG_AFE_GAIN_CTRL (0x01)
+#define REG_VIDEO_STD (0x02)
+#define REG_OPERATION_MODE (0x03)
+#define REG_AUTOSWITCH_MASK (0x04)
+
+#define REG_COLOR_KILLER (0x05)
+#define REG_LUMA_CONTROL1 (0x06)
+#define REG_LUMA_CONTROL2 (0x07)
+#define REG_LUMA_CONTROL3 (0x08)
+
+#define REG_BRIGHTNESS (0x09)
+#define REG_CONTRAST (0x0A)
+#define REG_SATURATION (0x0B)
+#define REG_HUE (0x0C)
+
+#define REG_CHROMA_CONTROL1 (0x0D)
+#define REG_CHROMA_CONTROL2 (0x0E)
+
+/* 0x0F Reserved */
+
+#define REG_COMP_PR_SATURATION (0x10)
+#define REG_COMP_Y_CONTRAST (0x11)
+#define REG_COMP_PB_SATURATION (0x12)
+
+/* 0x13 Reserved */
+
+#define REG_COMP_Y_BRIGHTNESS (0x14)
+
+/* 0x15 Reserved */
+
+#define REG_AVID_START_PIXEL_LSB (0x16)
+#define REG_AVID_START_PIXEL_MSB (0x17)
+#define REG_AVID_STOP_PIXEL_LSB (0x18)
+#define REG_AVID_STOP_PIXEL_MSB (0x19)
+
+#define REG_HSYNC_START_PIXEL_LSB (0x1A)
+#define REG_HSYNC_START_PIXEL_MSB (0x1B)
+#define REG_HSYNC_STOP_PIXEL_LSB (0x1C)
+#define REG_HSYNC_STOP_PIXEL_MSB (0x1D)
+
+#define REG_VSYNC_START_LINE_LSB (0x1E)
+#define REG_VSYNC_START_LINE_MSB (0x1F)
+#define REG_VSYNC_STOP_LINE_LSB (0x20)
+#define REG_VSYNC_STOP_LINE_MSB (0x21)
+
+#define REG_VBLK_START_LINE_LSB (0x22)
+#define REG_VBLK_START_LINE_MSB (0x23)
+#define REG_VBLK_STOP_LINE_LSB (0x24)
+#define REG_VBLK_STOP_LINE_MSB (0x25)
+
+/* 0x26 - 0x27 Reserved */
+
+#define REG_FAST_SWTICH_CONTROL (0x28)
+
+/* 0x29 Reserved */
+
+#define REG_FAST_SWTICH_SCART_DELAY (0x2A)
+
+/* 0x2B Reserved */
+
+#define REG_SCART_DELAY (0x2C)
+#define REG_CTI_DELAY (0x2D)
+#define REG_CTI_CONTROL (0x2E)
+
+/* 0x2F - 0x31 Reserved */
+
+#define REG_SYNC_CONTROL (0x32)
+#define REG_OUTPUT_FORMATTER1 (0x33)
+#define REG_OUTPUT_FORMATTER2 (0x34)
+#define REG_OUTPUT_FORMATTER3 (0x35)
+#define REG_OUTPUT_FORMATTER4 (0x36)
+#define REG_OUTPUT_FORMATTER5 (0x37)
+#define REG_OUTPUT_FORMATTER6 (0x38)
+#define REG_CLEAR_LOST_LOCK (0x39)
+
+#define REG_STATUS1 (0x3A)
+#define REG_STATUS2 (0x3B)
+
+#define REG_AGC_GAIN_STATUS_LSB (0x3C)
+#define REG_AGC_GAIN_STATUS_MSB (0x3D)
+
+/* 0x3E Reserved */
+
+#define REG_VIDEO_STD_STATUS (0x3F)
+#define REG_GPIO_INPUT1 (0x40)
+#define REG_GPIO_INPUT2 (0x41)
+
+/* 0x42 - 0x45 Reserved */
+
+#define REG_AFE_COARSE_GAIN_CH1 (0x46)
+#define REG_AFE_COARSE_GAIN_CH2 (0x47)
+#define REG_AFE_COARSE_GAIN_CH3 (0x48)
+#define REG_AFE_COARSE_GAIN_CH4 (0x49)
+
+#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A)
+#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D)
+#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E)
+#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51)
+
+/* 0x52 - 0x68 Reserved */
+
+#define REG_FBIT_VBIT_CONTROL1 (0x69)
+
+/* 0x6A - 0x6B Reserved */
+
+#define REG_BACKEND_AGC_CONTROL (0x6C)
+
+/* 0x6D - 0x6E Reserved */
+
+#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F)
+#define REG_ROM_VERSION (0x70)
+
+/* 0x71 - 0x73 Reserved */
+
+#define REG_AGC_WHITE_PEAK_PROCESSING (0x74)
+#define REG_FBIT_VBIT_CONTROL2 (0x75)
+#define REG_VCR_TRICK_MODE_CONTROL (0x76)
+#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77)
+#define REG_AGC_INCREMENT_SPEED (0x78)
+#define REG_AGC_INCREMENT_DELAY (0x79)
+
+/* 0x7A - 0x7F Reserved */
+
+#define REG_CHIP_ID_MSB (0x80)
+#define REG_CHIP_ID_LSB (0x81)
+
+/* 0x82 Reserved */
+
+#define REG_CPLL_SPEED_CONTROL (0x83)
+
+/* 0x84 - 0x96 Reserved */
+
+#define REG_STATUS_REQUEST (0x97)
+
+/* 0x98 - 0x99 Reserved */
+
+#define REG_VERTICAL_LINE_COUNT_LSB (0x9A)
+#define REG_VERTICAL_LINE_COUNT_MSB (0x9B)
+
+/* 0x9C - 0x9D Reserved */
+
+#define REG_AGC_DECREMENT_DELAY (0x9E)
+
+/* 0x9F - 0xB0 Reserved */
+
+#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1)
+#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2)
+#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3)
+#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4)
+#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5)
+#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6)
+#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7)
+#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8)
+#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9)
+#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA)
+#define REG_VDP_TTX_FILTER_CONTROL (0xBB)
+#define REG_VDP_FIFO_WORD_COUNT (0xBC)
+#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD)
+
+/* 0xBE Reserved */
+
+#define REG_VDP_FIFO_RESET (0xBF)
+#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0)
+#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1)
+#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2)
+#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3)
+
+/* 0xC4 - 0xD5 Reserved */
+
+#define REG_VDP_LINE_START (0xD6)
+#define REG_VDP_LINE_STOP (0xD7)
+#define REG_VDP_GLOBAL_LINE_MODE (0xD8)
+#define REG_VDP_FULL_FIELD_ENABLE (0xD9)
+#define REG_VDP_FULL_FIELD_MODE (0xDA)
+
+/* 0xDB - 0xDF Reserved */
+
+#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0)
+#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1)
+#define REG_FIFO_READ_DATA (0xE2)
+
+/* 0xE3 - 0xE7 Reserved */
+
+#define REG_VBUS_ADDRESS_ACCESS1 (0xE8)
+#define REG_VBUS_ADDRESS_ACCESS2 (0xE9)
+#define REG_VBUS_ADDRESS_ACCESS3 (0xEA)
+
+/* 0xEB - 0xEF Reserved */
+
+#define REG_INTERRUPT_RAW_STATUS0 (0xF0)
+#define REG_INTERRUPT_RAW_STATUS1 (0xF1)
+#define REG_INTERRUPT_STATUS0 (0xF2)
+#define REG_INTERRUPT_STATUS1 (0xF3)
+#define REG_INTERRUPT_MASK0 (0xF4)
+#define REG_INTERRUPT_MASK1 (0xF5)
+#define REG_INTERRUPT_CLEAR0 (0xF6)
+#define REG_INTERRUPT_CLEAR1 (0xF7)
+
+/* 0xF8 - 0xFF Reserved */
+
+/*
+ * Mask and bit definitions of TVP5146/47 registers
+ */
+/* The ID values we are looking for */
+#define TVP514X_CHIP_ID_MSB (0x51)
+#define TVP5146_CHIP_ID_LSB (0x46)
+#define TVP5147_CHIP_ID_LSB (0x47)
+
+#define VIDEO_STD_MASK (0x07)
+#define VIDEO_STD_AUTO_SWITCH_BIT (0x00)
+#define VIDEO_STD_NTSC_MJ_BIT (0x01)
+#define VIDEO_STD_PAL_BDGHIN_BIT (0x02)
+#define VIDEO_STD_PAL_M_BIT (0x03)
+#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04)
+#define VIDEO_STD_NTSC_4_43_BIT (0x05)
+#define VIDEO_STD_SECAM_BIT (0x06)
+#define VIDEO_STD_PAL_60_BIT (0x07)
+
+/*
+ * Status bit
+ */
+#define STATUS_TV_VCR_BIT (1<<0)
+#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1)
+#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2)
+#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3)
+#define STATUS_LOST_LOCK_DETECT_BIT (1<<4)
+#define STATUS_FEILD_RATE_BIT (1<<5)
+#define STATUS_LINE_ALTERNATING_BIT (1<<6)
+#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7)
+
+/* Tokens for register write */
+#define TOK_WRITE (0) /* token for write operation */
+#define TOK_TERM (1) /* terminating token */
+#define TOK_DELAY (2) /* delay token for reg list */
+#define TOK_SKIP (3) /* token to skip a register */
+/**
+ * struct tvp514x_reg - Structure for TVP5146/47 register initialization values
+ * @token - Token: TOK_WRITE, TOK_TERM etc..
+ * @reg - Register offset
+ * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY
+ */
+struct tvp514x_reg {
+ u8 token;
+ u8 reg;
+ u32 val;
+};
+
+/**
+ * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up
+ * Sequence.
+ * @ no_regs - Number of registers to write for power up sequence.
+ * @ init_reg_seq - Array of registers and respective value to write.
+ */
+struct tvp514x_init_seq {
+ unsigned int no_regs;
+ const struct tvp514x_reg *init_reg_seq;
+};
+#endif /* ifndef _TVP514X_REGS_H */
diff --git a/linux/include/media/tvp514x.h b/linux/include/media/tvp514x.h
new file mode 100644
index 000000000..5e7ee968c
--- /dev/null
+++ b/linux/include/media/tvp514x.h
@@ -0,0 +1,118 @@
+/*
+ * drivers/media/video/tvp514x.h
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _TVP514X_H
+#define _TVP514X_H
+
+/*
+ * Other macros
+ */
+#define TVP514X_MODULE_NAME "tvp514x"
+
+#define TVP514X_XCLK_BT656 (27000000)
+
+/* Number of pixels and number of lines per frame for different standards */
+#define NTSC_NUM_ACTIVE_PIXELS (720)
+#define NTSC_NUM_ACTIVE_LINES (480)
+#define PAL_NUM_ACTIVE_PIXELS (720)
+#define PAL_NUM_ACTIVE_LINES (576)
+
+/**
+ * enum tvp514x_input - enum for different decoder input pin
+ * configuration.
+ */
+enum tvp514x_input {
+ /*
+ * CVBS input selection
+ */
+ INPUT_CVBS_VI1A = 0x0,
+ INPUT_CVBS_VI1B,
+ INPUT_CVBS_VI1C,
+ INPUT_CVBS_VI2A = 0x04,
+ INPUT_CVBS_VI2B,
+ INPUT_CVBS_VI2C,
+ INPUT_CVBS_VI3A = 0x08,
+ INPUT_CVBS_VI3B,
+ INPUT_CVBS_VI3C,
+ INPUT_CVBS_VI4A = 0x0C,
+ /*
+ * S-Video input selection
+ */
+ INPUT_SVIDEO_VI2A_VI1A = 0x44,
+ INPUT_SVIDEO_VI2B_VI1B,
+ INPUT_SVIDEO_VI2C_VI1C,
+ INPUT_SVIDEO_VI2A_VI3A = 0x54,
+ INPUT_SVIDEO_VI2B_VI3B,
+ INPUT_SVIDEO_VI2C_VI3C,
+ INPUT_SVIDEO_VI4A_VI1A = 0x4C,
+ INPUT_SVIDEO_VI4A_VI1B,
+ INPUT_SVIDEO_VI4A_VI1C,
+ INPUT_SVIDEO_VI4A_VI3A = 0x5C,
+ INPUT_SVIDEO_VI4A_VI3B,
+ INPUT_SVIDEO_VI4A_VI3C,
+
+ /* Need to add entries for
+ * RGB, YPbPr and SCART.
+ */
+ INPUT_INVALID
+};
+
+/**
+ * enum tvp514x_output - enum for output format
+ * supported.
+ *
+ */
+enum tvp514x_output {
+ OUTPUT_10BIT_422_EMBEDDED_SYNC = 0,
+ OUTPUT_20BIT_422_SEPERATE_SYNC,
+ OUTPUT_10BIT_422_SEPERATE_SYNC = 3,
+ OUTPUT_INVALID
+};
+
+/**
+ * struct tvp514x_platform_data - Platform data values and access functions.
+ * @power_set: Power state access function, zero is off, non-zero is on.
+ * @ifparm: Interface parameters access function.
+ * @priv_data_set: Device private data (pointer) access function.
+ * @clk_polarity: Clock polarity of the current interface.
+ * @ hs_polarity: HSYNC Polarity configuration for current interface.
+ * @ vs_polarity: VSYNC Polarity configuration for current interface.
+ */
+struct tvp514x_platform_data {
+ char *master;
+ int (*power_set) (enum v4l2_power on);
+ int (*ifparm) (struct v4l2_ifparm *p);
+ int (*priv_data_set) (void *);
+ /* Interface control params */
+ bool clk_polarity;
+ bool hs_polarity;
+ bool vs_polarity;
+};
+
+
+#endif /* ifndef _TVP514X_H */
diff --git a/linux/include/media/v4l2-int-device.h b/linux/include/media/v4l2-int-device.h
index 9c2df41db..ecda3c725 100644
--- a/linux/include/media/v4l2-int-device.h
+++ b/linux/include/media/v4l2-int-device.h
@@ -183,6 +183,9 @@ enum v4l2_int_ioctl_num {
vidioc_int_s_crop_num,
vidioc_int_g_parm_num,
vidioc_int_s_parm_num,
+ vidioc_int_querystd_num,
+ vidioc_int_s_std_num,
+ vidioc_int_s_video_routing_num,
/*
*
@@ -284,6 +287,9 @@ V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *);
V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *);
V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *);
V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *);
+V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *);
+V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *);
+V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *);
V4L2_INT_WRAPPER_0(dev_init);
V4L2_INT_WRAPPER_0(dev_exit);
diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp
index 417721c57..7d5988571 100644
--- a/v4l2-apps/util/v4l2-ctl.cpp
+++ b/v4l2-apps/util/v4l2-ctl.cpp
@@ -1096,6 +1096,7 @@ static void list_devices()
DIR *dp;
struct dirent *ep;
dev_vec files;
+ dev_map links;
dev_map cards;
struct v4l2_capability vcap;
@@ -1119,6 +1120,37 @@ static void list_devices()
}
#endif
+ /* Find device nodes which are links to other device nodes */
+ for (dev_vec::iterator iter = files.begin();
+ iter != files.end(); ) {
+ char link[64+1];
+ int link_len;
+ std::string target;
+
+ link_len = readlink(iter->c_str(), link, 64);
+ if (link_len < 0) { /* Not a link or error */
+ iter++;
+ continue;
+ }
+ link[link_len] = '\0';
+
+ /* Only remove from files list if target itself is in list */
+ if (link[0] != '/') /* Relative link */
+ target = std::string("/dev/");
+ target += link;
+ if (find(files.begin(), files.end(), target) == files.end()) {
+ iter++;
+ continue;
+ }
+
+ /* Move the device node from files to links */
+ if (links[target].empty())
+ links[target] = *iter;
+ else
+ links[target] += ", " + *iter;
+ files.erase(iter);
+ }
+
std::sort(files.begin(), files.end(), sort_on_device_name);
for (dev_vec::iterator iter = files.begin();
@@ -1133,7 +1165,10 @@ static void list_devices()
bus_info = (const char *)vcap.bus_info;
if (cards[bus_info].empty())
cards[bus_info] += std::string((char *)vcap.card) + " (" + bus_info + "):\n";
- cards[bus_info] += "\t" + (*iter) + "\n";
+ cards[bus_info] += "\t" + (*iter);
+ if (!(links[*iter].empty()))
+ cards[bus_info] += " <- " + links[*iter];
+ cards[bus_info] += "\n";
}
for (dev_map::iterator iter = cards.begin();
iter != cards.end(); ++iter) {