@@ -683,6 +683,8 @@ source "drivers/media/video/ivtv/Kconfig"
source "drivers/media/video/cx18/Kconfig"
+source "drivers/media/video/s6dp/Kconfig"
+
config VIDEO_M32R_AR
tristate "AR devices"
depends on M32R && VIDEO_V4L1
@@ -134,6 +134,8 @@ obj-$(CONFIG_VIDEO_CX18) += cx18/
obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
+obj-$(CONFIG_VIDEO_S6000) += s6dp/
+
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
new file mode 100644
@@ -0,0 +1,6 @@
+config VIDEO_S6000
+ tristate "S6000 video"
+ depends on VIDEO_V4L2
+ default n
+ help
+ Enables the s6000 video driver.
new file mode 100644
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_S6000) += s6dp.o
new file mode 100644
@@ -0,0 +1,1664 @@
+/*
+ * Video driver for S6105 on chip data port device
+ * (c)2007 Stretch, Inc.
+ * (c)2009 emlix GmbH
+ * Authors: Fabian Godehardt <fg@emlix.com>
+ * Oskar Schirmer <os@emlix.com>
+ * Johannes Weiner <jw@emlix.com>
+ * Daniel Gloeckner <dg@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/s6dp-link.h>
+#include <linux/io.h>
+#include <variant/dmac.h>
+#include <variant/hardware.h>
+#include "s6dp.h"
+
+#define DRV_NAME "s6dp"
+#define DRIVER_VERSION_NUM KERNEL_VERSION(0, 0, 1)
+#define DRV_ERR KERN_ERR DRV_NAME ": "
+#define DRV_INFO KERN_INFO DRV_NAME ": "
+
+#define DP_NB_PORTS (S6_DPDMA_NB / S6_DP_CHAN_PER_PORT)
+
+/* device not opened */
+#define DP_STATE_UNUSED 0
+/* after open */
+#define DP_STATE_IDLE 1
+/* after reqbufs */
+#define DP_STATE_READY 2
+/* after streamon */
+#define DP_STATE_ACTIVE 3
+
+#define DP_CB_OFFSET 0
+#define DP_Y_OFFSET 1
+#define DP_CR_OFFSET 2
+#define DP_K_OFFSET 3
+
+#define CURRENT_BUF_TYPE(pd) ((pd)->ext.egress ? V4L2_BUF_TYPE_VIDEO_OUTPUT \
+ : V4L2_BUF_TYPE_VIDEO_CAPTURE)
+
+struct s6dp_frame {
+ void *data;
+ dma_addr_t dma_handle;
+ struct timeval timestamp;
+ struct list_head list;
+ u32 sequence;
+ u32 flags;
+};
+
+struct s6dp {
+ u8 port;
+ u16 irq;
+ void __iomem *dp;
+ u32 dataram;
+ u32 dmac;
+ struct s6dp_link *link;
+ struct list_head idleq;
+ struct list_head busyq;
+ struct list_head fullq;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ u32 outstanding;
+ struct {
+ u8 state;
+ u8 aligned:1;
+ u8 framerepeat:1;
+ u8 progressive:1;
+
+ u16 width;
+ u16 height;
+ u8 portsperstream;
+ u8 greyperchroma;
+ u16 pixel_total;
+ u8 pixel_offset;
+ u8 pixel_padding;
+ u16 line_total;
+ u16 line_odd_total;
+ u16 line_odd_offset;
+ u16 line_even_offset;
+ u16 odd_vsync_len;
+ u16 odd_vsync_offset;
+ u16 even_vsync_len;
+ u16 even_vsync_offset;
+ u8 odd_hsync_len;
+ u8 odd_hsync_offset;
+ u8 even_hsync_len;
+ u8 even_hsync_offset;
+
+ u32 fourcc;
+ u32 bufsize;
+ u32 chanoff[S6_DP_CHAN_PER_PORT];
+ u32 chansiz[S6_DP_CHAN_PER_PORT];
+ u32 sequence;
+ enum v4l2_field vfield;
+ enum v4l2_colorspace colorspace;
+ v4l2_std_id std_id;
+ } cur;
+ struct s6dp_frame *frames;
+ unsigned nrframes;
+ unsigned nrmapped;
+ struct {
+ u8 is_10bit:1;
+ u8 micron:1;
+ u8 egress:1;
+ u8 use_1120_line_and_crc:1;
+ u8 ext_framing:1;
+ u8 vsync_pol:1;
+ u8 hsync_pol:1;
+ u8 blank_pol:1;
+ u8 field_ctrl:1;
+ u8 blank_ctrl:1;
+ u8 relaxed_framing_mode:1;
+ u32 desc_size;
+ } ext;
+ unsigned int num_io;
+};
+
+static int s6v4l_enumstd(struct s6dp *, struct v4l2_standard *);
+static int s6v4l_s_output(struct file *, void *, unsigned int);
+static int s6v4l_s_input(struct file *, void *, unsigned int);
+static int s6v4l_streamoff(struct file *, void *, enum v4l2_buf_type);
+
+#define DP_REG_R(pd, n) readl((pd)->dp + (n))
+#define DP_REG_W(pd, n, v) writel((v), (pd)->dp + (n))
+#define DP_PREG_R(pd, n) readl((pd)->dp + S6_DP_CFG_BASE((pd)->port) + (n))
+#define DP_PREG_W(pd, n, v) writel((v), \
+ (pd)->dp + S6_DP_CFG_BASE((pd)->port) + (n))
+
+static void s6dp_try_fill_dma(struct s6dp *pd)
+{
+ struct list_head *inq;
+ if (pd->cur.state != DP_STATE_ACTIVE)
+ return;
+ inq = &pd->idleq;
+ while (!list_empty(inq)) {
+ unsigned chan = pd->port * S6_DP_CHAN_PER_PORT;
+ int i;
+ struct s6dp_frame *f;
+ for (i = 0; i < S6_DP_CHAN_PER_PORT; i++)
+ if (pd->cur.chansiz[i]
+ && s6dmac_fifo_full(pd->dmac, chan + i))
+ return;
+ f = list_first_entry(inq, struct s6dp_frame, list);
+ list_del(&f->list);
+ list_add_tail(&f->list, &pd->busyq);
+ do if (pd->cur.chansiz[--i]) {
+ u32 h, b, s, d;
+ b = (u32)f->dma_handle;
+ b += pd->cur.chanoff[i];
+ h = pd->dataram + S6_DP_CHAN_OFFSET(i);
+ if (pd->ext.egress) {
+ s = b;
+ d = h;
+ } else {
+ s = h;
+ d = b;
+ }
+ s6dmac_put_fifo(pd->dmac, chan + i, s, d,
+ pd->cur.chansiz[i]);
+ } while (i > 0); /* chan #0 must be last to push */
+ pd->outstanding++;
+ }
+}
+
+static void s6dp_err_interrupt(struct s6dp *pd)
+{
+ u32 m, r = DP_REG_R(pd, S6_DP_INT_UNMAP_RAW1);
+ m = 1 << S6_DP_INT_UNDEROVERRUN(pd->port);
+ if (r & m) {
+ printk(DRV_ERR "got overrun/underrun on lane %d\n", pd->port);
+ /* mask this interrupt source */
+ DP_REG_W(pd, S6_DP_INT_ENABLE, DP_REG_R(pd, S6_DP_INT_ENABLE)
+ & ~m);
+ }
+ m = 1;
+ switch (pd->port) {
+ case 0:
+ m <<= S6_DP_INT_UNMAP_RAW1_DP0_BT1120ERR
+ - S6_DP_INT_UNMAP_RAW1_DP2_BT1120ERR;
+ case 2:
+ m <<= S6_DP_INT_UNMAP_RAW1_DP2_BT1120ERR;
+ if (r & m)
+ printk(DRV_ERR "BT-1120 error bad CRC or line number"
+ " on lane %d\n", pd->port);
+ }
+ m = 1 << S6_DP_INT_WRONGPIXEL(pd->port);
+ if (r & m) {
+ printk(DRV_ERR "bad pixels in lines\n");
+ DP_REG_W(pd, S6_DP_INT_CLEAR, m);
+ }
+ m = 1 << S6_DP_INT_WRONGLINES(pd->port);
+ if (r & m) {
+ printk(DRV_ERR "bad lines in frame\n");
+ DP_REG_W(pd, S6_DP_INT_CLEAR, m);
+ }
+ DP_REG_W(pd, S6_DP_INT_CLEAR, 1 << S6_DP_INT_ERR_INT);
+}
+
+static void s6dp_tc_interrupt(struct s6dp *pd)
+{
+ unsigned c = S6_DP_CHAN_PER_PORT * (pd->port + 1);
+ unsigned i = S6_DP_CHAN_PER_PORT;
+ u8 event[S6_DP_CHAN_PER_PORT];
+ do {
+ c -= 1;
+ s6dmac_lowwmark_irq(pd->dmac, c);
+ s6dmac_pendcnt_irq(pd->dmac, c);
+ event[--i] = s6dmac_termcnt_irq(pd->dmac, c);
+ } while (i > 0);
+ while (!pd->cur.chansiz[i]) {
+ if (++i >= S6_DP_CHAN_PER_PORT)
+ return;
+ c += 1;
+ }
+ if (event[i]) {
+ struct timeval now;
+ u32 newfc, pending, global;
+ struct list_head *outq = &pd->fullq;
+
+ do_gettimeofday(&now);
+ global = readl(S6_REG_GREG1 + S6_GREG1_GLOBAL_TIMER);
+ DP_REG_W(pd, S6_DP_INT_CLEAR, 1 << S6_DP_INT_TERMCNT(pd->port));
+ newfc = DP_PREG_R(pd, S6_DP_FRAME_COUNT);
+ pending = s6dmac_pending_count(pd->dmac, c);
+ if (unlikely(!pending) && pd->cur.framerepeat)
+ pending = 1;
+ while (pd->outstanding > pending) {
+ struct s6dp_frame *f;
+ u32 delta;
+ delta = global - s6dmac_timestamp(pd->dmac, c);
+ if (unlikely(list_empty(&pd->busyq))) {
+ /* shouldn't happen */
+ printk(DRV_ERR "no buffers in interrupt\n");
+ break;
+ }
+ f = list_first_entry(&pd->busyq, struct s6dp_frame,
+ list);
+ f->sequence = pd->cur.sequence++;
+ f->flags &= ~V4L2_BUF_FLAG_QUEUED;
+ f->flags |= V4L2_BUF_FLAG_DONE;
+ f->timestamp = now;
+ f->timestamp.tv_usec -= delta * 10;
+ while (f->timestamp.tv_usec < 0) {
+ f->timestamp.tv_sec -= 1;
+ f->timestamp.tv_usec += 1000000;
+ }
+
+ list_del(&f->list);
+ list_add_tail(&f->list, outq);
+ pd->outstanding--;
+ }
+ pd->cur.sequence = newfc;
+ if (unlikely(list_empty(&pd->busyq)) && pending)
+ printk(DRV_ERR "no repeating frame?\n");
+ s6dp_try_fill_dma(pd);
+ if (!list_empty(&pd->fullq))
+ wake_up_interruptible(&pd->wait);
+ }
+}
+
+static irqreturn_t s6dp_interrupt(int irq, void *dev_id)
+{
+ struct video_device **devs = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ int i;
+ for (i = 0; i < DP_NB_PORTS; i++) {
+ struct video_device *dev = devs[i];
+ if (dev) {
+ struct s6dp *pd = video_get_drvdata(dev);
+ u32 s;
+ spin_lock(&pd->lock);
+ s = DP_REG_R(pd, S6_DP_INT_STATUS);
+ if (unlikely(s & (1 << S6_DP_INT_ERR_INT))) {
+ s6dp_err_interrupt(pd);
+ ret = IRQ_HANDLED;
+ }
+ if (s & (1 << S6_DP_INT_TERMCNT(pd->port))) {
+ s6dp_tc_interrupt(pd);
+ ret = IRQ_HANDLED;
+ }
+ spin_unlock(&pd->lock);
+ }
+ }
+ return ret;
+}
+
+static int s6dp_dma_init(struct video_device *dev)
+{
+ struct s6dp *pd = video_get_drvdata(dev);
+ unsigned i, n, burstsize;
+
+ n = DP_PREG_R(pd, S6_DP_CBCR_DMA_CONVERT) |
+ DP_PREG_R(pd, S6_DP_Y_DMA_CONVERT) |
+ DP_PREG_R(pd, S6_DP_ANC_DMA_CONVERT);
+ burstsize = 7;
+ for (i = (1 << (burstsize - 4)) - 1; n & i; i >>= 1)
+ burstsize--;
+
+ n = 3;
+ i = 0;
+ do {
+ int ret;
+ ret = s6dmac_request_chan(pd->dmac,
+ pd->port * S6_DP_CHAN_PER_PORT + i, /* channel */
+ 0, /* prio */
+ 0, /* pxfer */
+ pd->ext.egress, /* srcinc */
+ !pd->ext.egress, /* dstinc */
+ 0, /* sc./ga. */
+ 0, /* srcskip */
+ 0, /* dstskip */
+ burstsize, /* burstsize */
+ -1, /* bwconsv */
+ pd->ext.egress ? 4 : 2, /* lowwmark */
+ 1, /* timestamp */
+ 0); /* enable */
+ if (ret < 0) {
+ printk(DRV_ERR
+ "error - can not request DMA channel %d\n",
+ pd->port * S6_DP_CHAN_PER_PORT + i);
+ goto errdma;
+ }
+ } while (++i < n);
+
+ pd->cur.framerepeat = 1;
+ s6dmac_dp_setup_group(pd->dmac, pd->port, n, 1);
+
+ DP_REG_W(pd, S6_DP_VIDEO_DMA_CFG, (DP_REG_R(pd, S6_DP_VIDEO_DMA_CFG)
+ & ~(7 << S6_DP_VIDEO_DMA_CFG_BURST_BITS(pd->port)))
+ | (burstsize << S6_DP_VIDEO_DMA_CFG_BURST_BITS(pd->port)));
+
+ return 0;
+errdma:
+ while (i > 0) {
+ i -= 1;
+ s6dmac_release_chan(pd->dmac,
+ pd->port * S6_DP_CHAN_PER_PORT + i);
+ }
+ return -EIO;
+}
+
+static void s6dp_dma_free(struct video_device *dev)
+{
+ struct s6dp *pd = video_get_drvdata(dev);
+ unsigned i, n;
+
+ if (pd->cur.state < DP_STATE_ACTIVE)
+ return;
+
+ n = 3;
+ i = 0;
+ do {
+ s6dmac_release_chan(pd->dmac,
+ pd->port * S6_DP_CHAN_PER_PORT + i);
+ } while (++i < n);
+}
+
+static int s6dp_setup_stream(struct video_device *dev)
+{
+ struct s6dp *pd = video_get_drvdata(dev);
+ unsigned i, n, y;
+ unsigned long flags;
+
+ i = pd->cur.portsperstream;
+ if (i != 1) {
+ printk(DRV_ERR "multi port mode not implemented\n");
+ /* needs cross device checking for free channels */
+ return -EINVAL;
+ }
+ pd->cur.portsperstream = i; /* FIXME -> set_current */
+ /* write port configuration (24 regs. minus ANC stuff, see below) */
+ DP_PREG_W(pd, S6_DP_PIXEL_TOTAL, pd->cur.pixel_total);
+ DP_PREG_W(pd, S6_DP_PIXEL_ACTIVE,
+ pd->cur.width / pd->cur.greyperchroma);
+ DP_PREG_W(pd, S6_DP_PIXEL_OFFSET, pd->cur.pixel_offset);
+ DP_PREG_W(pd, S6_DP_PIXEL_PADDING, pd->cur.pixel_padding);
+ DP_PREG_W(pd, S6_DP_LINE_TOTAL, pd->cur.line_total);
+ DP_PREG_W(pd, S6_DP_LINE_ODD_TOTAL, pd->cur.line_odd_total);
+ i = pd->cur.progressive ? 0 : pd->cur.height/2;
+ DP_PREG_W(pd, S6_DP_LINE_ODD_ACTIVE, pd->cur.height - i);
+ DP_PREG_W(pd, S6_DP_LINE_ODD_OFFSET, pd->cur.line_odd_offset);
+ DP_PREG_W(pd, S6_DP_LINE_EVEN_ACTIVE, i);
+ DP_PREG_W(pd, S6_DP_LINE_EVEN_OFFSET, pd->cur.line_even_offset);
+ DP_PREG_W(pd, S6_DP_ODD_VSYNC_LENGTH, pd->cur.odd_vsync_len);
+ DP_PREG_W(pd, S6_DP_ODD_VSYNC_OFFSET, pd->cur.odd_vsync_offset);
+ DP_PREG_W(pd, S6_DP_EVEN_VSYNC_LENGTH, pd->cur.even_vsync_len);
+ DP_PREG_W(pd, S6_DP_EVEN_VSYNC_OFFSET, pd->cur.even_vsync_offset);
+ DP_PREG_W(pd, S6_DP_ODD_HSYNC_LENGTH, pd->cur.odd_hsync_len);
+ DP_PREG_W(pd, S6_DP_ODD_HSYNC_OFFSET, pd->cur.odd_hsync_offset);
+ DP_PREG_W(pd, S6_DP_EVEN_HSYNC_LENGTH, pd->cur.even_hsync_len);
+ DP_PREG_W(pd, S6_DP_EVEN_HSYNC_OFFSET, pd->cur.even_hsync_offset);
+
+ DP_PREG_W(pd, S6_DP_ANC_PIXEL_ACTIVE, 0);
+ DP_PREG_W(pd, S6_DP_ANC_PIXEL_OFFSET, 0);
+ DP_PREG_W(pd, S6_DP_LINE_ODD_ANC_ACTIVE, 0);
+ DP_PREG_W(pd, S6_DP_LINE_ODD_ANC_OFFSET, 0);
+ DP_PREG_W(pd, S6_DP_LINE_EVEN_ANC_ACTIVE, 0);
+ DP_PREG_W(pd, S6_DP_LINE_EVEN_ANC_OFFSET, 0);
+
+ /*
+ * Program the _dma_convert registers. These values calculate for the
+ * hardware the number of bursts a frame will require (in video mode).
+ * In streaming mode, cbcr_dma_convert indicates the number of 16b
+ * lines to do before issuing the last transfer.
+ */
+ n = pd->cur.width / pd->cur.greyperchroma;
+ y = pd->cur.height;
+ i = pd->ext.is_10bit ? 12 : 16;
+ DP_PREG_W(pd, S6_DP_CBCR_DMA_CONVERT, ((n + i - 1) / i) * y);
+ i /= pd->cur.greyperchroma;
+ DP_PREG_W(pd, S6_DP_Y_DMA_CONVERT, ((n + i - 1) / i) * y);
+ DP_PREG_W(pd, S6_DP_ANC_DMA_CONVERT, 0);
+
+ /* Program dp_config. Function of mode and optional configs */
+ /* Video configuration */
+ i = (pd->cur.greyperchroma == 1 ? S6_DP_VIDEO_CFG_MODE_444_SERIAL
+ : S6_DP_VIDEO_CFG_MODE_422_SERIAL)
+ << S6_DP_VIDEO_CFG_MODE;
+ i |= pd->ext.use_1120_line_and_crc << S6_DP_VIDEO_CFG_1120_VIDEO_MODE;
+ /* Progressive / interlaced */
+ /* Micron mode: Must be progressive (regardless of what mode says) */
+ i |= pd->ext.micron << S6_DP_VIDEO_CFG_MICRON_MODE;
+ i |= (pd->ext.micron | pd->cur.progressive)
+ << S6_DP_VIDEO_CFG_INTERL_OR_PROGR;
+ /* External framing */
+ if (pd->ext.ext_framing) {
+ i |= 1 << S6_DP_VIDEO_CFG_FRAMING;
+ i |= pd->ext.vsync_pol << S6_DP_VIDEO_CFG_VSYNC_POL;
+ i |= pd->ext.hsync_pol << S6_DP_VIDEO_CFG_HSYNC_POL;
+ i |= pd->ext.blank_pol << S6_DP_VIDEO_CFG_BLANK_POL;
+ i |= pd->ext.field_ctrl << S6_DP_VIDEO_CFG_FIELD_CTRL;
+ i |= pd->ext.blank_ctrl << S6_DP_VIDEO_CFG_BLANK_CTRL;
+ } else {
+ /* Embedded framing */
+ i |= pd->ext.relaxed_framing_mode << S6_DP_VIDEO_CFG_RELAX_MODE;
+ }
+ i |= pd->ext.is_10bit << S6_DP_VIDEO_CFG_8_OR_10;
+ i |= pd->ext.egress << S6_DP_VIDEO_CFG_IN_OR_OUT;
+ DP_REG_W(pd, S6_DP_VIDEO_CFG(pd->port), i);
+
+ spin_lock_irqsave(&pd->lock, flags);
+ /*
+ * Program the clk_mux in DP_CLK_SETTING.
+ * NOTE: all ports share this register, so this would need to be
+ * atomic if this function were re-entrant (which it is not).
+ */
+ i = DP_REG_R(pd, S6_DP_DP_CLK_SETTING)
+ & ~(S6_DP_DP_CLK_SETTING_CLK_MUX_MASK <<
+ S6_DP_DP_CLK_SETTING_CLK_MUX(pd->port));
+ i |= (pd->ext.egress ? 0 : 1) <<
+ S6_DP_DP_CLK_SETTING_CLK_MUX(pd->port);
+ DP_REG_W(pd, S6_DP_DP_CLK_SETTING, i);
+
+ /* Initialize DP DMA registers for this stream */
+ i = s6dp_dma_init(dev);
+ spin_unlock_irqrestore(&pd->lock, flags);
+ return i;
+}
+
+static void _s6dp_reset_port(struct s6dp *pd)
+{
+ unsigned i, m;
+ unsigned long flags;
+ struct list_head *queue[3] = {
+ &pd->idleq, &pd->busyq, &pd->fullq
+ };
+
+ spin_lock_irqsave(&pd->lock, flags);
+ DP_REG_W(pd, S6_DP_INT_ENABLE, DP_REG_R(pd, S6_DP_INT_ENABLE)
+ & ~((1 << S6_DP_INT_UNDEROVERRUN(pd->port))
+ | (1 << S6_DP_INT_WRONGPIXEL(pd->port))
+ | (1 << S6_DP_INT_WRONGLINES(pd->port))));
+
+ /* Clear the enable bit for the entire DMA group */
+ s6dmac_dp_switch_group(pd->dmac, pd->port, 0);
+ pd->outstanding = 0;
+ spin_unlock_irqrestore(&pd->lock, flags);
+ /* wait for first channel's DMA to become disabled */
+ i = 0;
+ do if (pd->cur.chansiz[i]) {
+ while (s6dmac_channel_enabled(pd->dmac,
+ i + pd->port * S6_DP_CHAN_PER_PORT))
+ ;
+ break;
+ } while (++i < S6_DP_CHAN_PER_PORT);
+
+ spin_lock_irqsave(&pd->lock, flags);
+ DP_REG_W(pd, S6_DP_VIDEO_ENABLE, DP_REG_R(pd, S6_DP_VIDEO_ENABLE)
+ & ~(1 << S6_DP_VIDEO_ENABLE_ENABLE(pd->port)));
+
+ /* FIXME, sort out true channel list: */
+ s6dmac_disable_error_irqs(pd->dmac,
+ 15 << (pd->port * S6_DP_CHAN_PER_PORT));
+
+ DP_REG_W(pd, S6_DP_INT_CLEAR,
+ (1 << S6_DP_INT_LOWWMARK(pd->port)) |
+ (1 << S6_DP_INT_PENDGCNT(pd->port)) |
+ (1 << S6_DP_INT_TERMCNT(pd->port)));
+ for (i = 0; i < 3; i++) {
+ struct s6dp_frame *f, *next;
+ list_for_each_entry_safe(f, next, queue[i], list) {
+ list_del_init(&f->list);
+ f->flags &= ~(V4L2_BUF_FLAG_QUEUED |
+ V4L2_BUF_FLAG_DONE);
+ }
+ }
+ m = 1 << S6_DP_INT_DMAERR;
+ for (i = 0; i < S6_DP_CHAN_PER_PORT; i++)
+ if (pd->cur.chansiz[i])
+ m |= (1 << (i + S6_DP_CHAN_PER_PORT * pd->port));
+ DP_REG_W(pd, S6_DP_INT_ENABLE, DP_REG_R(pd, S6_DP_INT_ENABLE) & ~m);
+ spin_unlock_irqrestore(&pd->lock, flags);
+}
+
+static void s6dp_reset_port(struct video_device *dev)
+{
+ struct s6dp *pd = video_get_drvdata(dev);
+ unsigned i;
+ unsigned long flags;
+
+ _s6dp_reset_port(pd);
+ spin_lock_irqsave(&pd->lock, flags);
+ s6dp_dma_free(dev);
+ DP_REG_W(pd, S6_DP_DP_CLK_SETTING, DP_REG_R(pd, S6_DP_DP_CLK_SETTING)
+ & ~(S6_DP_DP_CLK_SETTING_CLK_MUX_MASK
+ << S6_DP_DP_CLK_SETTING_CLK_MUX(pd->port)));
+ spin_unlock_irqrestore(&pd->lock, flags);
+ DP_REG_W(pd, S6_DP_VIDEO_CFG(pd->port), 0);
+ for (i = 0; i < 24; i++)
+ DP_PREG_W(pd, i*4, 0);
+}
+
+static int s6dp_video_open(struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct s6dp *pd = video_get_drvdata(dev);
+ unsigned long flags;
+
+ file->private_data = dev;
+ spin_lock_irqsave(&pd->lock, flags);
+ if (pd->cur.state != DP_STATE_UNUSED) {
+ spin_unlock_irqrestore(&pd->lock, flags);
+ return -EBUSY; /* TODO: v4l2 allows multiple opens */
+ }
+ pd->cur.state = DP_STATE_IDLE;
+ spin_unlock_irqrestore(&pd->lock, flags);
+
+ /* deferred initialization to avoid problems with the probing order */
+ if (!pd->cur.height) {
+ struct v4l2_cropcap cap;
+ struct v4l2_format vf;
+ v4l2_std_id first = 0;
+ int i;
+
+ if (pd->ext.egress)
+ s6v4l_s_output(file, file->private_data, 0);
+ else
+ s6v4l_s_input(file, file->private_data, 0);
+
+ for (i = 0; ; i++) {
+ struct v4l2_standard std;
+ std.index = i;
+ if (s6v4l_enumstd(pd, &std) < 0)
+ break;
+ if (!first)
+ first = std.id;
+ dev->tvnorms |= std.id;
+ }
+ if (dev->ioctl_ops->vidioc_s_std)
+ if (!dev->ioctl_ops->vidioc_s_std(file,
+ file->private_data,
+ &first))
+ dev->current_norm = first;
+ cap.type = CURRENT_BUF_TYPE(pd);
+ if (!dev->ioctl_ops->vidioc_cropcap(file, file->private_data,
+ &cap)) {
+ struct v4l2_crop vc;
+ vc.type = CURRENT_BUF_TYPE(pd);
+ vc.c = cap.defrect;
+ dev->ioctl_ops->vidioc_s_crop(file, file->private_data,
+ &vc);
+ }
+ vf.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
+ vf.fmt.pix.field = V4L2_FIELD_ANY;
+ vf.fmt.pix.width = 704;
+ vf.fmt.pix.height = 480;
+ vf.fmt.pix.bytesperline = 0;
+ vf.fmt.pix.priv = 0;
+ if (pd->ext.egress)
+ dev->ioctl_ops->vidioc_s_fmt_vid_out(file,
+ file->private_data,
+ &vf);
+ else
+ dev->ioctl_ops->vidioc_s_fmt_vid_cap(file,
+ file->private_data,
+ &vf);
+ }
+ return 0;
+}
+
+static void s6dp_relbufs(struct video_device *dev)
+{
+ struct s6dp *pd = video_get_drvdata(dev);
+ int i;
+ unsigned long flags;
+
+ if (!pd->nrframes)
+ return;
+ spin_lock_irqsave(&pd->lock, flags);
+ INIT_LIST_HEAD(&pd->idleq);
+ INIT_LIST_HEAD(&pd->busyq);
+ INIT_LIST_HEAD(&pd->fullq);
+ spin_unlock_irqrestore(&pd->lock, flags);
+ for (i = 0; i < pd->nrframes; i++)
+ dma_free_coherent(dev->parent, pd->cur.bufsize,
+ pd->frames[i].data, pd->frames[i].dma_handle);
+ kfree(pd->frames);
+ pd->nrframes = 0;
+}
+
+static int s6dp_video_close(struct file *file)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ /* reset port and free dma channels */
+ s6dp_reset_port(dev);
+
+ /* free buffer(s) */
+ s6dp_relbufs(dev);
+ pd->cur.state = DP_STATE_UNUSED;
+ return 0;
+}
+
+static void s6dp_video_vm_close(struct vm_area_struct *area)
+{
+ struct video_device *dev = area->vm_file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ struct s6dp_frame *f = area->vm_private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pd->lock, flags);
+ f->flags &= ~V4L2_BUF_FLAG_MAPPED;
+ pd->nrmapped--;
+ spin_unlock_irqrestore(&pd->lock, flags);
+}
+
+static struct vm_operations_struct s6dp_vm_ops = {
+ .close = s6dp_video_vm_close,
+};
+
+static int s6dp_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ unsigned long flags;
+ u32 buf;
+ int index;
+
+ /* we use the vma_pgoff to distinguish between the buffers */
+#define MAX_FRAMES 256
+ index = vma->vm_pgoff & 0xFF;
+ if (pd->cur.state < DP_STATE_READY || index >= pd->nrframes)
+ return -ENOMEM;
+ buf = (u32)pd->frames[index].data;
+ BUG_ON(buf & ~PAGE_MASK);
+
+ vma->vm_pgoff = buf >> PAGE_SHIFT;
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ PAGE_ALIGN(pd->cur.bufsize), vma->vm_page_prot)) {
+ printk(DRV_ERR "error - mapping frame #%d\n", index);
+ return -EAGAIN;
+ }
+ vma->vm_flags &= ~VM_IO; /* not I/O memory */
+ vma->vm_flags |= VM_MAYSHARE | VM_RESERVED; /* avoid to swap out */
+ vma->vm_ops = &s6dp_vm_ops;
+ vma->vm_private_data = pd->frames + index;
+
+ spin_lock_irqsave(&pd->lock, flags);
+ pd->nrmapped++;
+ pd->frames[index].flags |= V4L2_BUF_FLAG_MAPPED;
+ spin_unlock_irqrestore(&pd->lock, flags);
+ return 0;
+}
+
+static unsigned long s6dp_video_get_unmapped_area(struct file *file,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ int index;
+
+ index = pgoff & 0xFF;
+ if (pd->cur.state < DP_STATE_READY || index >= pd->nrframes)
+ return -ENOMEM;
+ return (unsigned long)pd->frames[index].data;
+}
+
+static unsigned int s6dp_video_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ poll_wait(file, &pd->wait, wait);
+ if (pd->cur.state < DP_STATE_ACTIVE)
+ return POLLERR;
+ if (list_empty(&pd->fullq))
+ return 0;
+ return pd->ext.egress ? (POLLOUT | POLLWRNORM) : (POLLIN | POLLRDNORM);
+}
+
+static long s6dp_video_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ if (cmd == VIDIOC_ENUMSTD) {
+ struct v4l2_standard std;
+ int ret;
+ if (copy_from_user(&std, (void __user *)arg, sizeof(std)))
+ return -EFAULT;
+ ret = s6v4l_enumstd(pd, &std);
+ if (copy_to_user((void __user *)arg, &std, sizeof(std)))
+ return -EFAULT;
+ return ret;
+ }
+ return video_ioctl2(file, cmd, arg);
+}
+
+static inline u32 s6dp_byteperline(struct s6dp *pd, int divide)
+{
+ u32 n = pd->cur.width;
+ if (divide)
+ n /= pd->cur.greyperchroma;
+ if (pd->ext.is_10bit) /* pack 3 samples into 4 bytes: */
+ n = ((n * 4) + 2) / 3;
+ return n;
+}
+
+static inline u32 s6dp_bytealigned(u32 unaligned)
+{
+ return (unaligned + 15) & ~15;
+}
+
+static inline u32 s6dp_byteperframe(struct s6dp *pd, int divide, u32 perline)
+{
+ u32 n = pd->cur.height;
+ if (divide)
+ n /= 2;
+ return n * perline;
+}
+
+static inline unsigned s6dp_set_hw2buf(struct s6dp *pd, int chan,
+ unsigned offset, unsigned asize)
+{
+ pd->cur.chanoff[chan] = offset;
+ pd->cur.chansiz[chan] = asize;
+ return 1 << chan;
+}
+
+static int s6dp_set_current(struct video_device *dev, u32 fourcc, int aligned)
+{
+ struct s6dp *pd = video_get_drvdata(dev);
+ u32 uyl, ayl, uyf, ayf, ucl, acl, acf;
+ pd->cur.fourcc = fourcc;
+ pd->cur.aligned = aligned;
+ pd->cur.chansiz[DP_K_OFFSET] = 0;
+ uyl = s6dp_byteperline(pd, 0);
+ ayl = s6dp_bytealigned(uyl);
+ ucl = s6dp_byteperline(pd, 1);
+ acl = s6dp_bytealigned(ucl);
+ uyf = s6dp_byteperframe(pd, 0, uyl);
+ ayf = s6dp_byteperframe(pd, 0, ayl);
+ if (!aligned && ayl != pd->cur.greyperchroma * acl)
+ return -EINVAL;
+ acf = s6dp_byteperframe(pd, 0, acl);
+ switch (fourcc) {
+ case V4L2_PIX_FMT_YUV444P:
+ if (aligned || uyl == ayl) {
+ s6dp_set_hw2buf(pd, DP_Y_OFFSET, 0, ayf);
+ s6dp_set_hw2buf(pd, DP_CB_OFFSET, ayf, acf);
+ s6dp_set_hw2buf(pd, DP_CR_OFFSET, ayf + acf, acf);
+ pd->cur.bufsize = ayf + 2 * acf;
+ }
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ if (aligned || ucl == acl) {
+ s6dp_set_hw2buf(pd, DP_Y_OFFSET, 0, ayf);
+ s6dp_set_hw2buf(pd, DP_CB_OFFSET, ayf, acf);
+ s6dp_set_hw2buf(pd, DP_CR_OFFSET, ayf + acf, acf);
+ pd->cur.bufsize = ayf + 2 * acf;
+ }
+ break;
+ default:
+ BUG();
+ }
+ BUG_ON(pd->cur.bufsize >= (1 << 24));
+ return 0;
+}
+
+static int s6v4l_update(struct s6dp *pd, int r)
+{
+ struct s6dp_mode mode;
+ int divi, sub;
+
+ if (r < 0)
+ return r;
+ if (!pd->link || !pd->link->g_mode)
+ return -EINVAL; /* no driver, no V4L */
+ pd->link->g_mode(pd->link->context, &mode);
+
+ pd->cur.width = mode.pixel_active;
+ pd->cur.height = mode.odd_active + mode.even_active;
+ pd->cur.progressive = mode.progressive;
+ switch (mode.type) {
+ case S6_DP_VIDEO_CFG_MODE_422_SERIAL:
+ pd->cur.portsperstream = 1;
+ divi = 2;
+ sub = 2;
+ break;
+ case S6_DP_VIDEO_CFG_MODE_444_SERIAL:
+ pd->cur.portsperstream = 1;
+ divi = 1;
+ sub = 3;
+ break;
+ case S6_DP_VIDEO_CFG_MODE_422_PARALLEL:
+ pd->cur.portsperstream = 2;
+ divi = 2;
+ sub = 4;
+ break;
+ case S6_DP_VIDEO_CFG_MODE_444_PARALLEL:
+ pd->cur.portsperstream = 3;
+ divi = 1;
+ sub = 8;
+ break;
+ default:
+ divi = 1;
+ sub = 0;
+ }
+ pd->cur.greyperchroma = divi;
+ pd->cur.pixel_total = mode.pixel_total / divi - sub;
+ pd->cur.pixel_offset = mode.pixel_offset / divi;
+ pd->cur.pixel_padding = mode.pixel_padding / divi;
+ pd->cur.line_total = mode.framelines;
+ pd->cur.line_odd_total = mode.odd_total;
+ pd->cur.line_odd_offset = mode.odd_first;
+ pd->cur.line_even_offset = mode.even_first;
+ pd->cur.odd_vsync_len = mode.odd_vsync_len;
+ pd->cur.odd_vsync_offset = mode.odd_vsync_offset;
+ pd->cur.even_vsync_len = mode.even_vsync_len;
+ pd->cur.even_vsync_offset = mode.even_vsync_offset;
+ pd->cur.odd_hsync_len = mode.hsync_len / divi;
+ pd->cur.odd_hsync_offset = mode.hsync_offset / divi;
+ pd->cur.even_hsync_len = mode.hsync_len / divi;
+ pd->cur.even_hsync_offset = mode.hsync_offset / divi;
+ pd->ext.ext_framing = !mode.embedded_sync;
+ pd->ext.micron = mode.micron_mode;
+ pd->ext.vsync_pol = mode.vsync_pol;
+ pd->ext.hsync_pol = mode.hsync_pol;
+ pd->ext.blank_pol = mode.blank_pol;
+ pd->ext.field_ctrl = mode.field_ctrl;
+ pd->ext.blank_ctrl = mode.blank_ctrl;
+ pd->ext.relaxed_framing_mode = mode.relaxed_framing;
+ pd->ext.is_10bit = mode.ten_bit;
+ pd->ext.use_1120_line_and_crc = mode.line_and_crc;
+ return 0;
+}
+
+
+static int s6v4l_enumstd(struct s6dp *pd, struct v4l2_standard *std)
+{
+ int ret = -EINVAL;
+ if (pd->link && pd->link->e_std)
+ ret = pd->link->e_std(pd->link->context, std);
+ return ret;
+}
+
+static int s6v4l_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ int ret = -EINVAL;
+ if (pd->link && pd->link->s_std)
+ ret = pd->link->s_std(pd->link->context, std,
+ pd->cur.state >= DP_STATE_READY);
+ return s6v4l_update(pd, ret);
+}
+
+static int s6v4l_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ u32 i;
+ struct s6dp_frame *f;
+ unsigned long flags;
+
+ if (pd->cur.state < DP_STATE_READY)
+ return -EINVAL;
+ if (p->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ if (p->type != CURRENT_BUF_TYPE(pd))
+ return -EINVAL;
+ i = p->index;
+ if (i >= pd->nrframes) {
+ printk(DRV_ERR "buffer index range error (%u/%u)\n",
+ i, pd->nrframes);
+ return -EINVAL;
+ }
+ f = &pd->frames[i];
+ if (!list_empty(&f->list)) {
+ printk(DRV_ERR "error - frame %d already queued\n", i);
+ return -EINVAL;
+ }
+ f->timestamp.tv_sec = 0;
+ f->timestamp.tv_usec = 0;
+ f->flags |= V4L2_BUF_FLAG_QUEUED;
+ p->flags = f->flags;
+ spin_lock_irqsave(&pd->lock, flags);
+ list_add_tail(&f->list, &pd->idleq);
+ s6dp_try_fill_dma(pd);
+ spin_unlock_irqrestore(&pd->lock, flags);
+ return 0;
+}
+
+static int s6v4l_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ struct s6dp_frame *f;
+ unsigned long flags;
+
+ if (pd->cur.state < DP_STATE_READY)
+ return -EINVAL;
+retry:
+ if (!(file->f_flags & O_NONBLOCK) &&
+ wait_event_interruptible(pd->wait, !list_empty(&pd->fullq)))
+ return -ERESTARTSYS;
+ spin_lock_irqsave(&pd->lock, flags);
+ if (list_empty(&pd->fullq)) {
+ spin_unlock_irqrestore(&pd->lock, flags);
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ goto retry;
+ }
+ f = list_first_entry(&pd->fullq, struct s6dp_frame, list);
+ list_del_init(&f->list);
+ f->flags &= ~V4L2_BUF_FLAG_DONE;
+ spin_unlock_irqrestore(&pd->lock, flags);
+
+ p->index = f - &pd->frames[0];
+ p->timestamp = f->timestamp;
+ p->sequence = f->sequence;
+ p->memory = V4L2_MEMORY_MMAP;
+ p->flags = f->flags;
+ p->field = pd->cur.vfield;
+ p->length = pd->cur.bufsize;
+ if (!pd->ext.egress)
+ p->bytesused = pd->cur.bufsize;
+ return 0;
+}
+
+static int s6v4l_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ int i;
+
+ if (req->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ if (req->type != CURRENT_BUF_TYPE(pd))
+ return -EINVAL;
+ if (pd->nrmapped)
+ return -EBUSY;
+ if (pd->cur.state > DP_STATE_READY) {
+ if (req->count)
+ return -EBUSY;
+ i = s6v4l_streamoff(file, priv, req->type);
+ if (i < 0)
+ return i;
+ }
+ if (req->count > MAX_FRAMES)
+ req->count = MAX_FRAMES;
+
+ s6dp_relbufs(dev);
+ if (req->count == 0) {
+ pd->cur.state = DP_STATE_IDLE;
+ return 0;
+ }
+
+ pd->frames =
+ kmalloc(req->count * sizeof(struct s6dp_frame), GFP_KERNEL);
+ if (!pd->frames)
+ return -ENOMEM;
+ for (i = 0; i < req->count; i++) {
+ struct s6dp_frame *f;
+ f = &pd->frames[i];
+ f->data = dma_alloc_coherent(dev->parent, pd->cur.bufsize,
+ &f->dma_handle, GFP_KERNEL);
+ if (!f->data) {
+ req->count = i;
+ break;
+ }
+ INIT_LIST_HEAD(&f->list);
+ f->flags = 0;
+ }
+ if (!i) {
+ kfree(pd->frames);
+ return -ENOMEM;
+ }
+ pd->nrframes = i;
+ pd->cur.state = DP_STATE_READY;
+ return 0;
+}
+
+static int s6v4l_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ if (p->type != CURRENT_BUF_TYPE(pd))
+ return -EINVAL;
+ if (pd->cur.state < DP_STATE_READY)
+ return -EINVAL;
+ if (p->index >= pd->nrframes)
+ return -EINVAL;
+
+ p->memory = V4L2_MEMORY_MMAP;
+ p->m.offset = p->index << PAGE_SHIFT; /*
+ * a "magic cookie" that the
+ * appl. can pass to mmap to
+ * specifiy which buffer is
+ * being mapped
+ */
+
+ p->length = pd->cur.bufsize;
+ p->flags = pd->frames[p->index].flags;
+ p->field = pd->cur.vfield;
+ return 0;
+}
+
+static int s6v4l_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type vtype)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ unsigned i, m;
+ unsigned long flags;
+
+ if (pd->cur.state != DP_STATE_READY) {
+ printk(DRV_ERR "device not ready\n");
+ return -EINVAL;
+ }
+
+ if (list_empty(&pd->idleq)) {
+ printk(DRV_ERR "no buffers queued\n");
+ return -EINVAL;
+ }
+
+ i = s6dp_setup_stream(dev);
+ if (i) {
+ printk(DRV_ERR "error - video setup failed\n");
+ return i;
+ }
+ pd->cur.sequence = 0;
+ pd->cur.state = DP_STATE_ACTIVE;
+
+ /* Set the enable bit for the entire DMA group */
+ s6dmac_dp_switch_group(pd->dmac, pd->port, 1);
+
+ m = (1 << S6_DP_INT_DMAERR)
+ | (1 << S6_DP_INT_UNDEROVERRUN(pd->port))
+ | (1 << S6_DP_INT_WRONGPIXEL(pd->port))
+ | (1 << S6_DP_INT_WRONGLINES(pd->port));
+ for (i = 0; i < S6_DP_CHAN_PER_PORT; i++)
+ if (pd->cur.chansiz[i])
+ m |= (1 << (i + S6_DP_CHAN_PER_PORT * pd->port));
+ spin_lock_irqsave(&pd->lock, flags);
+ DP_REG_W(pd, S6_DP_INT_ENABLE, DP_REG_R(pd, S6_DP_INT_ENABLE) | m);
+ DP_REG_W(pd, S6_DP_VIDEO_ENABLE, DP_REG_R(pd, S6_DP_VIDEO_ENABLE)
+ | (1 << S6_DP_VIDEO_ENABLE_ENABLE(pd->port)));
+ s6dp_try_fill_dma(pd);
+ spin_unlock_irqrestore(&pd->lock, flags);
+ return 0;
+}
+
+
+static int s6v4l_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ if (pd->cur.state != DP_STATE_ACTIVE)
+ return -EINVAL;
+ s6dp_reset_port(dev);
+ pd->cur.state = DP_STATE_READY;
+ return 0;
+}
+
+const static struct {
+ u32 pixelformat;
+ u8 *description;
+} s6dp_enum_fmt[] = {
+ { V4L2_PIX_FMT_YUV444P,
+ "YUV 4:4:4 planar",
+ },
+ { V4L2_PIX_FMT_YUV422P,
+ "YUV 4:2:2 planar",
+ },
+};
+
+static int s6v4l_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ u32 i = f->index;
+ if (i >= ARRAY_SIZE(s6dp_enum_fmt))
+ return -EINVAL;
+ f->pixelformat = s6dp_enum_fmt[i].pixelformat;
+ strlcpy(f->description, s6dp_enum_fmt[i].description,
+ sizeof(f->description));
+ f->flags = 0;
+ return 0;
+}
+
+static int s6v4l_enum_fmt_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ u32 i = f->index;
+ if (i > 0)
+ return -EINVAL;
+ /* Only 422 for now */
+ f->pixelformat = s6dp_enum_fmt[1].pixelformat;
+ strlcpy(f->description, s6dp_enum_fmt[1].description,
+ sizeof(f->description));
+ f->flags = 0;
+ return 0;
+}
+
+static int s6v4l_cropcap(struct file *file, void *priv, struct v4l2_cropcap *c)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ if (c->type != CURRENT_BUF_TYPE(pd))
+ return -EINVAL;
+
+ if (!pd->link || !pd->link->cropcap)
+ return -EINVAL;
+
+ return pd->link->cropcap(pd->link->context, c);
+}
+
+static int s6v4l_s_crop(struct file *file, void *priv, struct v4l2_crop *c)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ int ret;
+
+ if (c->type != CURRENT_BUF_TYPE(pd))
+ return -EINVAL;
+
+ if (!pd->link || !pd->link->s_crop)
+ return -EINVAL;
+
+ ret = pd->link->s_crop(pd->link->context, c,
+ pd->cur.state >= DP_STATE_READY);
+
+ return s6v4l_update(pd, ret);
+}
+
+static int s6v4l_g_crop(struct file *file, void *priv, struct v4l2_crop *c)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ if (c->type != CURRENT_BUF_TYPE(pd))
+ return -EINVAL;
+
+ if (!pd->link || !pd->link->g_crop)
+ return -EINVAL;
+
+ return pd->link->g_crop(pd->link->context, c);
+}
+
+static int s6v4l_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ int cwidth, cheight, cbytesperline, aligned = 1;
+ if (!pd->link || !pd->link->s_fmt || !pd->link->g_mode)
+ return 0;
+
+ pd->link->s_fmt(pd->link->context, 1, &f->fmt.pix, 1);
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
+ f->fmt.pix.width &= ~1;
+ break;
+ default:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV444P;
+ }
+ if (f->fmt.pix.field == V4L2_FIELD_ALTERNATE)
+ f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
+ cheight = f->fmt.pix.height;
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV444P:
+ cwidth = f->fmt.pix.width;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ cheight = f->fmt.pix.height / 2;
+ case V4L2_PIX_FMT_YUV422P:
+ cwidth = f->fmt.pix.width / 2;
+ break;
+ default:
+ cwidth = 0;
+ }
+ if (aligned) {
+ f->fmt.pix.bytesperline = s6dp_bytealigned(f->fmt.pix.width);
+ cbytesperline = s6dp_bytealigned(cwidth);
+ } else {
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ cbytesperline = cwidth;
+ }
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height
+ + cbytesperline * cheight * 2;
+ return 0;
+}
+
+static int s6v4l_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ unsigned i;
+
+ memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
+ if (pd->link && pd->link->g_fmt) {
+ i = pd->link->g_fmt(pd->link->context, &f->fmt.pix);
+ if (i < 0)
+ return i;
+ } else {
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ }
+ f->fmt.pix.field = pd->cur.vfield;
+ if (f->fmt.pix.field == V4L2_FIELD_ALTERNATE)
+ f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
+ f->fmt.pix.width = pd->cur.width;
+ f->fmt.pix.height = pd->cur.height;
+ f->fmt.pix.pixelformat = pd->cur.fourcc;
+ f->fmt.pix.bytesperline = s6dp_bytealigned(f->fmt.pix.width);
+ f->fmt.pix.priv = pd->cur.aligned;
+ f->fmt.pix.sizeimage = pd->cur.bufsize;
+ return 0;
+}
+
+static int s6v4l_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct video_device *dev = file->private_data;
+ struct s6dp *pd = video_get_drvdata(dev);
+ struct v4l2_pix_format pfmt;
+ int r, align;
+ if (pd->cur.state != DP_STATE_IDLE)
+ return -EBUSY;
+ r = s6v4l_try_fmt(file, dev, f);
+ if (r < 0)
+ return r;
+ if (pd->link && pd->link->s_fmt) {
+ pfmt = f->fmt.pix;
+ r = pd->link->s_fmt(pd->link->context, 0, &pfmt, 0);
+ }
+ r = s6v4l_update(pd, r);
+ if (r < 0)
+ return r;
+
+ align = f->fmt.pix.priv & 1;
+ pd->cur.vfield = f->fmt.pix.field;
+ pd->cur.colorspace = f->fmt.pix.colorspace;
+ r = s6dp_set_current(dev, f->fmt.pix.pixelformat, align);
+ return r;
+}
+
+static int s6v4l_enum_input(struct file *file, void *fh, struct v4l2_input *inp)
+{
+ struct video_device *dev = video_devdata(file);
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ if (!pd->link || !pd->link->dir.ingress.e_inp)
+ return -EINVAL;
+
+ return pd->link->dir.ingress.e_inp(pd->link->context, inp);
+}
+
+static int s6v4l_enum_output(struct file *file, void *fh,
+ struct v4l2_output *outp)
+{
+ struct video_device *dev = video_devdata(file);
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ if (!pd->link || !pd->link->dir.egress.e_outp)
+ return -EINVAL;
+
+ return pd->link->dir.egress.e_outp(pd->link->context, outp);
+}
+
+static int s6v4l_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct video_device *dev = video_devdata(file);
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ if (!pd->link || !pd->link->dir.ingress.s_inp)
+ return -EINVAL;
+
+ *i = pd->num_io;
+ return 0;
+}
+
+static int s6v4l_g_output(struct file *file, void *fh, unsigned int *i)
+{
+ struct video_device *dev = video_devdata(file);
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ if (!pd->link || !pd->link->dir.egress.s_outp)
+ return -EINVAL;
+
+ *i = pd->num_io;
+ return 0;
+}
+
+static int s6v4l_s_input(struct file *file, void *fh, unsigned int i)
+{
+ struct video_device *dev = video_devdata(file);
+ struct s6dp *pd = video_get_drvdata(dev);
+ int ret = -EINVAL;
+
+ if (pd->link && pd->link->dir.ingress.s_inp) {
+ ret = pd->link->dir.ingress.s_inp(pd->link->context, i,
+ pd->cur.state
+ >= DP_STATE_READY);
+ if (ret >= 0)
+ pd->num_io = i;
+ }
+
+ return s6v4l_update(pd, ret);
+}
+
+static int s6v4l_s_output(struct file *file, void *fh, unsigned int i)
+{
+ struct video_device *dev = video_devdata(file);
+ struct s6dp *pd = video_get_drvdata(dev);
+ int ret = -EINVAL;
+
+ if (pd->link && pd->link->dir.egress.s_outp) {
+ ret = pd->link->dir.egress.s_outp(pd->link->context, i,
+ pd->cur.state >= DP_STATE_READY);
+ if (ret >= 0)
+ pd->num_io = i;
+ }
+
+ return s6v4l_update(pd, ret);
+}
+
+static int s6v4l_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct video_device *dev = video_devdata(file);
+ struct s6dp *pd = video_get_drvdata(dev);
+
+ strcpy(cap->driver, "s6dp");
+ strcpy(cap->card, "Stretch data port");
+ sprintf(cap->bus_info, "Data port %i", pd->port);
+ cap->version = DRIVER_VERSION_NUM;
+ if (pd->ext.egress)
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT;
+ else
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
+ return 0;
+}
+
+static const struct v4l2_file_operations s6v4l_video_fops = {
+ .owner = THIS_MODULE,
+ .open = s6dp_video_open,
+ .release = s6dp_video_close,
+ .get_unmapped_area = s6dp_video_get_unmapped_area,
+ .mmap = s6dp_video_mmap,
+ .poll = s6dp_video_poll,
+ .ioctl = s6dp_video_ioctl,
+};
+
+static const struct v4l2_ioctl_ops capture_v4l_ioctl_ops = {
+ .vidioc_querycap = s6v4l_querycap,
+ .vidioc_enum_fmt_vid_cap = s6v4l_enum_fmt_cap,
+ .vidioc_g_fmt_vid_cap = s6v4l_g_fmt,
+ .vidioc_s_fmt_vid_cap = s6v4l_s_fmt,
+ .vidioc_try_fmt_vid_cap = s6v4l_try_fmt,
+ .vidioc_reqbufs = s6v4l_reqbufs,
+ .vidioc_querybuf = s6v4l_querybuf,
+ .vidioc_qbuf = s6v4l_qbuf,
+ .vidioc_dqbuf = s6v4l_dqbuf,
+ .vidioc_streamon = s6v4l_streamon,
+ .vidioc_streamoff = s6v4l_streamoff,
+ .vidioc_s_std = s6v4l_s_std,
+ .vidioc_enum_input = s6v4l_enum_input,
+ .vidioc_g_input = s6v4l_g_input,
+ .vidioc_s_input = s6v4l_s_input,
+ .vidioc_cropcap = s6v4l_cropcap,
+ .vidioc_g_crop = s6v4l_g_crop,
+ .vidioc_s_crop = s6v4l_s_crop,
+};
+
+static const struct v4l2_ioctl_ops output_v4l_ioctl_ops = {
+ .vidioc_querycap = s6v4l_querycap,
+ .vidioc_enum_fmt_vid_out = s6v4l_enum_fmt_out,
+ .vidioc_g_fmt_vid_out = s6v4l_g_fmt,
+ .vidioc_s_fmt_vid_out = s6v4l_s_fmt,
+ .vidioc_try_fmt_vid_out = s6v4l_try_fmt,
+ .vidioc_reqbufs = s6v4l_reqbufs,
+ .vidioc_querybuf = s6v4l_querybuf,
+ .vidioc_qbuf = s6v4l_qbuf,
+ .vidioc_dqbuf = s6v4l_dqbuf,
+ .vidioc_streamon = s6v4l_streamon,
+ .vidioc_streamoff = s6v4l_streamoff,
+ .vidioc_s_std = s6v4l_s_std,
+ .vidioc_enum_output = s6v4l_enum_output,
+ .vidioc_g_output = s6v4l_g_output,
+ .vidioc_s_output = s6v4l_s_output,
+ .vidioc_cropcap = s6v4l_cropcap,
+ .vidioc_g_crop = s6v4l_g_crop,
+ .vidioc_s_crop = s6v4l_s_crop,
+};
+
+
+static int probe_one(struct platform_device *pdev, int irq,
+ struct video_device **devs, struct s6dp_link *link,
+ void __iomem *dpbase, void __iomem *dmac, u32 physbase)
+{
+ struct video_device *dev;
+ struct s6dp *pd;
+ int index, res = -ENOMEM;
+
+ dev = video_device_alloc();
+ if (!dev) {
+ printk(DRV_ERR "video device alloc failed.\n");
+ goto err_allocd;
+ }
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+ if (!pd) {
+ printk(DRV_ERR "video device alloc failed.\n");
+ goto err_allocp;
+ }
+ pd->ext.egress = link->is_egress;
+ strlcpy(dev->name, pdev->name, sizeof(dev->name));
+ dev->fops = &s6v4l_video_fops;
+ dev->release = video_device_release;
+ dev->tvnorms = 0;
+ dev->parent = &pdev->dev;
+ if (pd->ext.egress)
+ dev->ioctl_ops = &output_v4l_ioctl_ops;
+ else
+ dev->ioctl_ops = &capture_v4l_ioctl_ops;
+ video_set_drvdata(dev, pd);
+ pd->irq = irq;
+ pd->dp = dpbase;
+ pd->dmac = (u32)dmac;
+ for (index = 0; !(link->port_mask & (1 << index)); index++)
+ ;
+ if (link->port_mask != (1 << index)) {
+ printk(DRV_ERR "multi port mode not implemented\n");
+ goto err_videor;
+ }
+ pd->port = index;
+ pd->dataram = physbase + S6_DP_DATARAM(index);
+ pd->cur.state = DP_STATE_UNUSED;
+ pd->frames = NULL;
+ pd->nrframes = 0;
+ pd->link = link;
+ INIT_LIST_HEAD(&pd->idleq);
+ INIT_LIST_HEAD(&pd->busyq);
+ INIT_LIST_HEAD(&pd->fullq);
+ init_waitqueue_head(&pd->wait);
+ spin_lock_init(&pd->lock);
+ if (video_register_device_index(dev, VFL_TYPE_GRABBER, link->minor,
+ index)) {
+ printk(DRV_ERR "video_register_device failed!\n");
+ res = -ENODEV;
+ goto err_videor;
+ }
+ s6dp_reset_port(dev);
+ *devs = dev;
+ return 0;
+
+err_videor:
+ kfree(pd);
+err_allocp:
+ video_device_release(dev);
+err_allocd:
+ return res;
+}
+
+static int __devinit s6dp_probe(struct platform_device *pdev)
+{
+ int i, ret, irq;
+ unsigned in_use;
+ struct video_device **devs;
+ struct s6dp_link *links;
+ void __iomem *dpbase, *dmacbase;
+ struct resource *res, *regs, *dmac;
+ if (!pdev->dev.platform_data) {
+ printk(DRV_ERR "no platform data given\n");
+ return -EINVAL;
+ }
+ devs = kzalloc(DP_NB_PORTS * sizeof(*devs), GFP_KERNEL);
+ if (!devs) {
+ printk(DRV_ERR "video device alloc failed.\n");
+ return -ENOMEM;
+ }
+ irq = platform_get_irq(pdev, 0);
+ ret = request_irq(irq, &s6dp_interrupt, 0, DRV_NAME, devs);
+ if (ret) {
+ printk(DRV_ERR "irq request failed: %d\n", irq);
+ goto err_free_mem;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -EINVAL;
+ goto err_free_irq;
+ }
+ regs = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (!res) {
+ ret = -EBUSY;
+ goto err_free_irq;
+ }
+ dpbase = ioremap_nocache(regs->start, regs->end - regs->start + 1);
+ if (!dpbase) {
+ ret = -ENOMEM;
+ goto err_free_regs;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ ret = -EINVAL;
+ goto err_unmap_regs;
+ }
+ dmac = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (!dmac) {
+ ret = -EBUSY;
+ goto err_unmap_regs;
+ }
+ dmacbase = ioremap_nocache(dmac->start, dmac->end - dmac->start + 1);
+ if (!dmacbase) {
+ ret = -ENOMEM;
+ goto err_free_dmac;
+ }
+ i = 0;
+ in_use = 0;
+ for (links = pdev->dev.platform_data; links->port_mask; links++) {
+ if (in_use & links->port_mask) {
+ printk(DRV_ERR "port already in use - skipping\n");
+ continue;
+ }
+ ret = probe_one(pdev, irq, &devs[i], links, dpbase, dmacbase,
+ regs->start);
+ if (ret)
+ goto err_free_devs;
+ in_use |= links->port_mask;
+ i++;
+ }
+ platform_set_drvdata(pdev, devs);
+ return 0;
+
+err_free_devs:
+ while (i--) {
+ if (devs[i]) {
+ struct s6dp *pd = video_get_drvdata(devs[i]);
+ video_unregister_device(devs[i]);
+ kfree(pd);
+ video_device_release(devs[i]);
+ }
+ }
+ iounmap(dmacbase);
+err_free_dmac:
+ release_mem_region(dmac->start, dmac->end - dmac->start + 1);
+err_unmap_regs:
+ iounmap(dpbase);
+err_free_regs:
+ release_mem_region(regs->start, regs->end - regs->start + 1);
+err_free_irq:
+ free_irq(irq, devs);
+err_free_mem:
+ kfree(devs);
+ return ret;
+}
+
+static int __devexit s6dp_remove(struct platform_device *pdev)
+{
+ struct video_device **devs = platform_get_drvdata(pdev);
+ int i;
+ platform_set_drvdata(pdev, NULL);
+ for (i = 0; i < DP_NB_PORTS; i++) {
+ struct video_device *dev = devs[i];
+ if (dev) {
+ struct s6dp *pd = video_get_drvdata(dev);
+ video_unregister_device(dev);
+ kfree(pd);
+ video_device_release(dev);
+ }
+ }
+ i = platform_get_irq(pdev, 0);
+ free_irq(i, devs);
+ kfree(devs);
+ return 0;
+}
+
+static struct platform_driver s6dp_driver = {
+ .probe = s6dp_probe,
+ .remove = __devexit_p(s6dp_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s6dp_init(void)
+{
+ printk(DRV_INFO "S6 video driver <info@emlix.com>\n");
+ return platform_driver_register(&s6dp_driver);
+}
+
+static void __exit s6dp_exit(void)
+{
+ platform_driver_unregister(&s6dp_driver);
+}
+
+module_init(s6dp_init);
+module_exit(s6dp_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("S6105 on chip video driver");
+MODULE_AUTHOR("Fabian Godehardt, Hannes Weiner, "
+ "Oskar Schirmer, Daniel Gloeckner");
new file mode 100644
@@ -0,0 +1,121 @@
+/*
+ * drivers/media/video/s6dp/s6dp.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2008 emlix GmbH <info@emlix.com>
+ * Authors: Fabian Godehardt <fg@emlix.com>
+ * Oskar Schirmer <os@emlix.com>
+ */
+
+#ifndef __ASM_XTENSA_S6105_DP_H
+#define __ASM_XTENSA_S6105_DP_H
+
+#define S6_DP_CHAN_PER_PORT 4
+
+/* global data port setup */
+#define S6_DP_INT_STATUS 0x00
+#define S6_DP_INT_LOWWMARK(p) (p)
+#define S6_DP_INT_PENDGCNT(p) ((p) + 4)
+#define S6_DP_INT_TERMCNT(p) ((p) + 8)
+#define S6_DP_INT_ERR_INT 12
+#define S6_DP_INT_ENABLE 0x04
+#define S6_DP_INT_DMAERR 16
+#define S6_DP_INT_UNDEROVERRUN(p) ((p) + 20)
+#define S6_DP_INT_WRONGPIXEL(p) ((p) * 2 + 24)
+#define S6_DP_INT_WRONGLINES(p) ((p) * 2 + 25)
+#define S6_DP_INT_RAW 0x08
+#define S6_DP_INT_CLEAR 0x0c
+#define S6_DP_INT_SET 0x10
+#define S6_DP_INT_UNMAP_RAW0 0x14
+#define S6_DP_INT_UNMAP_RAW1 0x18
+#define S6_DP_INT_UNMAP_RAW1_DP2_BT1120ERR 18
+#define S6_DP_INT_UNMAP_RAW1_DP0_BT1120ERR 19
+#define S6_DP_DP_CLK_SETTING 0x40
+#define S6_DP_DP_CLK_SETTING_CLK_MUX(p) ((p) * 4)
+#define S6_DP_DP_CLK_SETTING_CLK_MUX_MASK 3
+#define S6_DP_VIDEO_OUT_DLL_SEL 0x50
+#define S6_DP_VIDEO_REF_DLL_SEL 0x54
+#define S6_DP_VIDEO_FBK_DLL_SEL 0x58
+#define S6_DP_VIDEO_ENABLE 0x80
+#define S6_DP_VIDEO_ENABLE_ENABLE(p) ((p) * 8)
+#define S6_DP_VIDEO_DMA_CFG 0x84
+#define S6_DP_VIDEO_DMA_CFG_BURST_BITS(p) ((p) * 8)
+#define S6_DP_VIDEO_CFG(p) ((p) * 0x4 + 0x90)
+#define S6_DP_VIDEO_CFG_8_OR_10 0
+#define S6_DP_VIDEO_CFG_IN_OR_OUT 1
+#define S6_DP_VIDEO_CFG_FRAMING 2
+#define S6_DP_VIDEO_CFG_MODE 3
+#define S6_DP_VIDEO_CFG_MODE_422_SERIAL 0
+#define S6_DP_VIDEO_CFG_MODE_444_SERIAL 1
+#define S6_DP_VIDEO_CFG_MODE_422_PARALLEL 2
+#define S6_DP_VIDEO_CFG_MODE_444_PARALLEL 3
+#define S6_DP_VIDEO_CFG_MODE_422_SERIAL_CASCADE 4
+#define S6_DP_VIDEO_CFG_MODE_444_SERIAL_CASCADE 5
+#define S6_DP_VIDEO_CFG_MODE_422_PARALLEL_CASCADE 6
+#define S6_DP_VIDEO_CFG_MODE_RAW 7
+#define S6_DP_VIDEO_CFG_MODE_FIFO8 8
+#define S6_DP_VIDEO_CFG_MODE_FIFO16 9
+#define S6_DP_VIDEO_CFG_MODE_FIFO32 10
+#define S6_DP_VIDEO_CFG_MODE_STREAM8 11
+#define S6_DP_VIDEO_CFG_MODE_STREAM16 12
+#define S6_DP_VIDEO_CFG_MODE_STREAM32 13
+#define S6_DP_VIDEO_CFG_MODE_STREAM8_CASCADE 14
+#define S6_DP_VIDEO_CFG_MODE_STREAM16_CASCADE 15
+#define S6_DP_VIDEO_CFG_INTERL_OR_PROGR 8
+#define S6_DP_VIDEO_CFG_1120_VIDEO_MODE 9
+#define S6_DP_VIDEO_CFG_ANCILLARY_DATA 10
+#define S6_DP_VIDEO_CFG_VSYNC_POL 12
+#define S6_DP_VIDEO_CFG_HSYNC_POL 13
+#define S6_DP_VIDEO_CFG_BLANK_POL 14
+#define S6_DP_VIDEO_CFG_FIELD_CTRL 15
+#define S6_DP_VIDEO_CFG_BLANK_CTRL 16
+#define S6_DP_VIDEO_CFG_RELAX_MODE 21
+#define S6_DP_VIDEO_CFG_MICRON_MODE 22
+#define S6_DP_VIDEO_BLANK(p) ((p) * 0x4 + 0xa0)
+#define S6_DP_VIDEO_BAD_FRAME_NUM(p) ((p) * 0x4 + 0xc0)
+#define S6_DP_VIDEO_BAD_PIXEL_CNT(p) ((p) * 0x8 + 0xd0)
+#define S6_DP_VIDEO_BAD_LINE_CNT(p) ((p) * 0x8 + 0xd4)
+
+/* per port configuration registers */
+#define S6_DP_PIXEL_TOTAL 0x00
+#define S6_DP_PIXEL_ACTIVE 0x04
+#define S6_DP_PIXEL_OFFSET 0x08
+#define S6_DP_PIXEL_PADDING 0x0c
+#define S6_DP_ANC_PIXEL_ACTIVE 0x10
+#define S6_DP_ANC_PIXEL_OFFSET 0x14
+#define S6_DP_LINE_TOTAL 0x18
+#define S6_DP_LINE_ODD_TOTAL 0x1c
+#define S6_DP_LINE_ODD_ACTIVE 0x20
+#define S6_DP_LINE_ODD_OFFSET 0x24
+#define S6_DP_LINE_EVEN_ACTIVE 0x28
+#define S6_DP_LINE_EVEN_OFFSET 0x2c
+#define S6_DP_LINE_ODD_ANC_ACTIVE 0x30
+#define S6_DP_LINE_ODD_ANC_OFFSET 0x34
+#define S6_DP_LINE_EVEN_ANC_ACTIVE 0x38
+#define S6_DP_LINE_EVEN_ANC_OFFSET 0x3c
+#define S6_DP_ODD_VSYNC_LENGTH 0x40
+#define S6_DP_ODD_VSYNC_OFFSET 0x44
+#define S6_DP_EVEN_VSYNC_LENGTH 0x48
+#define S6_DP_EVEN_VSYNC_OFFSET 0x4c
+#define S6_DP_ODD_HSYNC_LENGTH 0x50
+#define S6_DP_ODD_HSYNC_OFFSET 0x54
+#define S6_DP_EVEN_HSYNC_LENGTH 0x58
+#define S6_DP_EVEN_HSYNC_OFFSET 0x5c
+
+#define S6_DP_FRAME_COUNT 0x60
+#define S6_DP_TSI_TIMESTAMP_UPDATE 0x64
+#define S6_DP_TSI_TIMESTAMP_HI 0x68
+#define S6_DP_TSI_TIMESTAMP_LO 0x6c
+#define S6_DP_CBCR_DMA_CONVERT 0x70
+#define S6_DP_Y_DMA_CONVERT 0x74
+#define S6_DP_ANC_DMA_CONVERT 0x78
+
+#define S6_DP_CFG_BASE(n) ((n) * 0x80 + 0x100)
+#define S6_DP_CHAN_OFFSET(n) ((n) * 0x100)
+#define S6_DP_DATARAM(port) ((port) * S6_DP_CHAN_PER_PORT * 0x100 \
+ + 0x1000)
+
+#endif /* __ASM_XTENSA_S6105_DP_H */
new file mode 100644
@@ -0,0 +1,63 @@
+#ifndef __S6DP_LINK_H__
+#define __S6DP_LINK_H__
+
+#include <linux/videodev2.h>
+
+struct s6dp_mode {
+ unsigned int type:4;
+ unsigned int progressive:1;
+ unsigned int embedded_sync:1;
+ unsigned int micron_mode:1;
+ unsigned int vsync_pol:1;
+ unsigned int hsync_pol:1;
+ unsigned int blank_pol:1;
+ unsigned int field_ctrl:1;
+ unsigned int blank_ctrl:1;
+ unsigned int relaxed_framing:1;
+ unsigned int ten_bit:1;
+ unsigned int line_and_crc:1;
+ u16 pixel_total;
+ u16 pixel_offset;
+ u16 pixel_active;
+ u16 pixel_padding;
+ u16 hsync_offset;
+ u16 hsync_len;
+ u16 framelines;
+ u16 odd_vsync_offset;
+ u16 odd_vsync_len;
+ u16 odd_first;
+ u16 odd_active;
+ u16 odd_total;
+ u16 even_vsync_offset;
+ u16 even_vsync_len;
+ u16 even_first;
+ u16 even_active;
+};
+
+struct s6dp_link {
+ void *context;
+ unsigned port_mask:4;
+ unsigned is_egress:1;
+ int minor;
+ void (*g_mode)(void *ctx, struct s6dp_mode *mode);
+ int (*cropcap)(void *ctx, struct v4l2_cropcap *cap);
+ int (*s_crop)(void *ctx, struct v4l2_crop *crop, int busy);
+ int (*g_crop)(void *ctx, struct v4l2_crop *crop);
+ int (*e_std)(void *ctx, struct v4l2_standard *std);
+ int (*s_std)(void *ctx, v4l2_std_id *mask, int busy);
+ int (*s_fmt)(void *ctx, int try_fmt, struct v4l2_pix_format *fmt,
+ int busy);
+ int (*g_fmt)(void *ctx, struct v4l2_pix_format *fmt);
+ union {
+ struct {
+ int (*e_inp)(void *ctx, struct v4l2_input *inp);
+ int (*s_inp)(void *ctx, unsigned int nr, int busy);
+ } ingress;
+ struct {
+ int (*e_outp)(void *ctx, struct v4l2_output *outp);
+ int (*s_outp)(void *ctx, unsigned int nr, int busy);
+ } egress;
+ } dir;
+};
+
+#endif