linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] media: Add timberdale video-in driver
@ 2010-04-16 16:28 Richard Röjfors
  2010-04-25 15:24 ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 5+ messages in thread
From: Richard Röjfors @ 2010-04-16 16:28 UTC (permalink / raw)
  To: Linux Media Mailing List
  Cc: Linux Kernel Mailing List, Mauro Carvalho Chehab,
	Douglas Schilling Landgraf, Samuel Ortiz, Williams, Dan J

This patch adds the timberdale video-in driver.

The video IP of timberdale delivers the video data via DMA.
The driver uses the DMA api to handle DMA transfers, and make use
of the V4L2 videobuffers to handle buffers against user space.
Due to some timing constraint it makes sense to do DMA into an
intermediate buffer and then copy the data to vmalloc:ed buffers.

If available the driver uses an encoder to get/set the video standard

Signed-off-by: Richard Röjfors <richard.rojfors@pelagicore.com>
---
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index f8fc865..ba895cc 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -949,6 +949,15 @@ config VIDEO_OMAP2
 	---help---
 	  This is a v4l2 driver for the TI OMAP2 camera capture interface
 
+config VIDEO_TIMBERDALE
+	tristate "Support for timberdale Video In/LogiWIN"
+	depends on VIDEO_V4L2 && I2C
+	select TIMB_DMA
+	select VIDEO_ADV7180
+	select VIDEOBUF_VMALLOC
+	---help---
+	Add support for the Video In peripherial of the timberdale FPGA.
+
 #
 # USB Multimedia device configuration
 #
diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c
new file mode 100644
index 0000000..3f30ff6
--- /dev/null
+++ b/drivers/media/video/timblogiw.c
@@ -0,0 +1,920 @@
+/*
+ * timblogiw.c timberdale FPGA LogiWin Video In driver
+ * Copyright (c) 2009-2010 Intel Corporation
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA LogiWin Video In
+ */
+
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/scatterlist.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/timb_video.h>
+
+#define DRIVER_NAME			"timb-video"
+
+#define TIMBLOGIWIN_NAME		"Timberdale Video-In"
+#define TIMBLOGIW_VERSION_CODE		0x04
+
+#define TIMBLOGIW_BYTES_PER_LINE	(720 * 2)
+#define TIMBLOGIW_DMA_BUFFER_SIZE	(TIMBLOGIW_BYTES_PER_LINE * 576)
+#define TIMBLOGIW_LINES_PER_DESC	44
+#define TIMBLOGIW_MAX_VIDEO_MEM		16
+
+#define TIMBLOGIW_VIDEO_FORMAT		V4L2_PIX_FMT_UYVY
+
+#define TIMBLOGIW_HAS_DECODER(lw)	(lw->pdata.encoder.module_name)
+
+
+struct timblogiw {
+	struct video_device		video_dev;
+	struct v4l2_device		v4l2_dev; /* mutual exclusion */
+	struct mutex			lock;
+	struct device			*dev;
+	struct timb_video_platform_data pdata;
+	struct v4l2_subdev		*sd_enc;	/* encoder */
+	bool				opened;
+};
+
+struct timblogiw_tvnorm {
+	v4l2_std_id std;
+	u16     width;
+	u16     height;
+	u8	fps;
+};
+
+struct timblogiw_fh {
+	struct videobuf_queue		vb_vidq;
+	struct timblogiw_tvnorm const	*cur_norm;
+	struct list_head		capture;
+	struct dma_chan			*chan;
+	spinlock_t			queue_lock; /* mutual exclusion */
+	struct tasklet_struct		tasklet;
+	struct {
+		void			*buf;
+		struct scatterlist	sg[16];
+		struct device		*dev;
+		dma_cookie_t		cookie;
+	} dma;
+	unsigned int			frame_count;
+};
+
+const struct timblogiw_tvnorm timblogiw_tvnorms[] = {
+	{
+		.std			= V4L2_STD_PAL,
+		.width			= 720,
+		.height			= 576,
+		.fps			= 25
+	},
+	{
+		.std			= V4L2_STD_NTSC,
+		.width			= 720,
+		.height			= 480,
+		.fps			= 26
+	}
+};
+
+static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm)
+{
+	return norm->width * 2;
+}
+
+
+static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm)
+{
+	return norm->height * timblogiw_bytes_per_line(norm);
+}
+
+static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
+		if (timblogiw_tvnorms[i].std & std)
+			return timblogiw_tvnorms + i;
+
+	/* default to first element */
+	return timblogiw_tvnorms;
+}
+
+static void timblogiw_dma_cb(void *data)
+{
+	struct timblogiw_fh *fh = data;
+
+	tasklet_schedule(&fh->tasklet);
+}
+
+static int __timblogiw_alloc_dma(struct timblogiw_fh *fh, struct device *dev)
+{
+	dma_addr_t addr;
+	int err, i, pos;
+	int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
+		timblogiw_bytes_per_line(fh->cur_norm);
+
+	fh->dma.cookie = -1;
+	fh->dma.dev = dev;
+
+	fh->dma.buf = kzalloc(TIMBLOGIW_DMA_BUFFER_SIZE, GFP_KERNEL);
+	if (!fh->dma.buf)
+		return -ENOMEM;
+
+	sg_init_table(fh->dma.sg, ARRAY_SIZE(fh->dma.sg));
+
+	/* map up the DMA buffers */
+	addr = dma_map_single(dev, fh->dma.buf, TIMBLOGIW_DMA_BUFFER_SIZE,
+		DMA_FROM_DEVICE);
+	err = dma_mapping_error(dev, addr);
+	if (err) {
+		kfree(fh->dma.buf);
+		return err;
+	}
+
+	for (i = 0, pos = 0; pos < TIMBLOGIW_DMA_BUFFER_SIZE; i++) {
+		sg_dma_address(fh->dma.sg + i) = addr + pos;
+		pos += bytes_per_desc;
+		sg_dma_len(fh->dma.sg + i) = (pos > TIMBLOGIW_DMA_BUFFER_SIZE) ?
+			(bytes_per_desc - (pos - TIMBLOGIW_DMA_BUFFER_SIZE)) :
+			bytes_per_desc;
+	}
+
+	return 0;
+}
+
+static void __timblogiw_free_dma(struct timblogiw_fh *fh)
+{
+	int size = timblogiw_frame_size(fh->cur_norm);
+
+	dma_unmap_single(fh->dma.dev, sg_dma_address(fh->dma.sg), size,
+		DMA_FROM_DEVICE);
+	kfree(fh->dma.buf);
+	fh->dma.buf = NULL;
+}
+
+static int __timblogiw_start_dma(struct timblogiw_fh *fh)
+{
+	struct dma_async_tx_descriptor *desc;
+	int sg_elems;
+	int bytes_per_desc =
+		TIMBLOGIW_LINES_PER_DESC *
+		timblogiw_bytes_per_line(fh->cur_norm);
+
+	sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc;
+	sg_elems +=
+		(timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0;
+
+	desc = fh->chan->device->device_prep_slave_sg(fh->chan,
+		fh->dma.sg, sg_elems, DMA_FROM_DEVICE,
+		DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
+	if (!desc)
+		return -ENOMEM;
+
+	desc->callback_param = fh;
+	desc->callback = timblogiw_dma_cb;
+	fh->dma.cookie = desc->tx_submit(desc);
+
+	return 0;
+}
+
+static void timblogiw_stop_dma(struct timblogiw_fh *fh)
+{
+	dma_cookie_t cookie;
+	spin_lock_bh(&fh->queue_lock);
+	/* mark that DMA is stopped */
+	cookie = fh->dma.cookie;
+	fh->dma.cookie = -1;
+	spin_unlock_bh(&fh->queue_lock);
+
+	dma_sync_wait(fh->chan, cookie);
+}
+
+static void timblogiw_handleframe(unsigned long arg)
+{
+	struct timblogiw_fh *fh = (struct timblogiw_fh *)arg;
+
+	if (fh->dma.cookie == -1)
+		return;
+
+	fh->frame_count++;
+
+	spin_lock(&fh->queue_lock);
+
+	if (!list_empty(&fh->capture)) {
+		struct videobuf_buffer *vb = list_entry(fh->capture.next,
+			struct videobuf_buffer, queue);
+		int framesize = timblogiw_frame_size(fh->cur_norm);
+
+		list_del(&vb->queue);
+
+		dma_sync_single_for_cpu(fh->dma.dev, sg_dma_address(fh->dma.sg),
+			framesize, DMA_FROM_DEVICE);
+		memcpy(videobuf_to_vmalloc(vb), fh->dma.buf, framesize);
+
+		do_gettimeofday(&vb->ts);
+		vb->field_count = fh->frame_count * 2;
+		vb->state = VIDEOBUF_DONE;
+
+		wake_up(&vb->done);
+	}
+
+	__timblogiw_start_dma(fh);
+
+	spin_unlock(&fh->queue_lock);
+}
+
+static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param)
+{
+	return chan->chan_id == (int)filter_param;
+}
+
+/* IOCTL functions */
+
+static int timblogiw_g_fmt(struct file *file, void  *priv,
+	struct v4l2_format *format)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev, "%s entry\n", __func__);
+
+	if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	format->fmt.pix.width = fh->cur_norm->width;
+	format->fmt.pix.height = fh->cur_norm->height;
+	format->fmt.pix.pixelformat = TIMBLOGIW_VIDEO_FORMAT;
+	format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm);
+	format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm);
+	format->fmt.pix.field = V4L2_FIELD_NONE;
+	return 0;
+}
+
+static int timblogiw_try_fmt(struct file *file, void  *priv,
+	struct v4l2_format *format)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct v4l2_pix_format *pix = &format->fmt.pix;
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev,
+		"%s - width=%d, height=%d, pixelformat=%d, field=%d\n"
+		"bytes per line %d, size image: %d, colorspace: %d\n",
+		__func__,
+		pix->width, pix->height, pix->pixelformat, pix->field,
+		pix->bytesperline, pix->sizeimage, pix->colorspace);
+
+	if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	if (pix->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+
+	if (pix->pixelformat != TIMBLOGIW_VIDEO_FORMAT)
+		return -EINVAL;
+
+	if ((fh->cur_norm->height != pix->height) ||
+		(fh->cur_norm->width != pix->width)) {
+		pix->width = fh->cur_norm->width;
+		pix->height = fh->cur_norm->height;
+	}
+
+	return 0;
+}
+
+static int timblogiw_s_fmt(struct file *file, void  *priv,
+	struct v4l2_format *format)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+	int err;
+
+	err = timblogiw_try_fmt(file, priv, format);
+	if (err)
+		return err;
+
+	if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+		dev_err(&vdev->dev, "%s queue busy\n", __func__);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int timblogiw_querycap(struct file *file, void  *priv,
+	struct v4l2_capability *cap)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
+	memset(cap, 0, sizeof(*cap));
+	strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1);
+	strncpy(cap->driver, DRIVER_NAME, sizeof(cap->card)-1);
+	cap->version = TIMBLOGIW_VERSION_CODE;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+		V4L2_CAP_READWRITE;
+
+	return 0;
+}
+
+static int timblogiw_enum_fmt(struct file *file, void  *priv,
+	struct v4l2_fmtdesc *fmt)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	dev_dbg(&vdev->dev, "%s, index: %d\n",  __func__, fmt->index);
+
+	if (fmt->index != 0)
+		return -EINVAL;
+	memset(fmt, 0, sizeof(*fmt));
+	fmt->index = 0;
+	fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	strncpy(fmt->description, "4:2:2, packed, YUYV",
+		sizeof(fmt->description)-1);
+	fmt->pixelformat = TIMBLOGIW_VIDEO_FORMAT;
+
+	return 0;
+}
+
+static int timblogiw_g_parm(struct file *file, void *priv,
+	struct v4l2_streamparm *sp)
+{
+	struct timblogiw_fh *fh = priv;
+	struct v4l2_captureparm *cp = &sp->parm.capture;
+
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+	cp->timeperframe.numerator = 1;
+	cp->timeperframe.denominator = fh->cur_norm->fps;
+
+	return 0;
+}
+
+static int timblogiw_reqbufs(struct file *file, void  *priv,
+	struct v4l2_requestbuffers *rb)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
+
+	return videobuf_reqbufs(&fh->vb_vidq, rb);
+}
+
+static int timblogiw_querybuf(struct file *file, void  *priv,
+	struct v4l2_buffer *b)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
+
+	return videobuf_querybuf(&fh->vb_vidq, b);
+}
+
+static int timblogiw_qbuf(struct file *file, void  *priv, struct v4l2_buffer *b)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
+
+	return videobuf_qbuf(&fh->vb_vidq, b);
+}
+
+static int timblogiw_dqbuf(struct file *file, void  *priv,
+	struct v4l2_buffer *b)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
+
+	return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int timblogiw_g_std(struct file *file, void  *priv, v4l2_std_id *std)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
+
+	*std = fh->cur_norm->std;
+	return 0;
+}
+
+static int timblogiw_s_std(struct file *file, void  *priv, v4l2_std_id *std)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw *lw = video_get_drvdata(vdev);
+	struct timblogiw_fh *fh = priv;
+	int err = 0;
+
+	dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
+
+	if (TIMBLOGIW_HAS_DECODER(lw))
+		err = v4l2_subdev_call(lw->sd_enc, core, s_std, *std);
+
+	if (!err)
+		fh->cur_norm = timblogiw_get_norm(*std);
+
+	return err;
+}
+
+static int timblogiw_enuminput(struct file *file, void  *priv,
+	struct v4l2_input *inp)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
+
+	if (inp->index != 0)
+		return -EINVAL;
+
+	memset(inp, 0, sizeof(*inp));
+	inp->index = 0;
+
+	strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1);
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+	inp->std = V4L2_STD_ALL;
+
+	return 0;
+}
+
+static int timblogiw_g_input(struct file *file, void  *priv,
+	unsigned int *input)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
+
+	*input = 0;
+
+	return 0;
+}
+
+static int timblogiw_s_input(struct file *file, void  *priv, unsigned int input)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
+
+	if (input != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int timblogiw_streamon(struct file *file, void  *priv, unsigned int type)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+	int err;
+
+	dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
+
+	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		dev_dbg(&vdev->dev, "%s - No capture device\n", __func__);
+		return -EINVAL;
+	}
+
+	err = __timblogiw_start_dma(fh);
+
+	if (!err)
+		err = videobuf_streamon(&fh->vb_vidq);
+	fh->frame_count = 0;
+
+	return err;
+}
+
+static int timblogiw_streamoff(struct file *file, void  *priv,
+	unsigned int type)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev, "%s entry\n",  __func__);
+
+	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	timblogiw_stop_dma(fh);
+
+	return videobuf_streamoff(&fh->vb_vidq);
+}
+
+static int timblogiw_querystd(struct file *file, void  *priv, v4l2_std_id *std)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw *lw = video_get_drvdata(vdev);
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev, "%s entry\n",  __func__);
+
+	if (TIMBLOGIW_HAS_DECODER(lw))
+		return v4l2_subdev_call(lw->sd_enc, video, querystd, std);
+	else {
+		*std = fh->cur_norm->std;
+		return 0;
+	}
+}
+
+static int timblogiw_enum_framesizes(struct file *file, void  *priv,
+	struct v4l2_frmsizeenum *fsize)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = priv;
+
+	dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n",  __func__,
+		fsize->index, fsize->pixel_format);
+
+	if ((fsize->index != 0) ||
+		(fsize->pixel_format != TIMBLOGIW_VIDEO_FORMAT))
+		return -EINVAL;
+
+	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+	fsize->discrete.width = fh->cur_norm->width;
+	fsize->discrete.height = fh->cur_norm->height;
+
+	return 0;
+}
+
+/* Video buffer functions */
+
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+	unsigned int *size)
+{
+	struct timblogiw_fh *fh = vq->priv_data;
+
+	*size = timblogiw_frame_size(fh->cur_norm);
+
+	if (!*count)
+		*count = 32;
+
+	while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024)
+		(*count)--;
+
+	return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+	enum v4l2_field field)
+{
+	struct timblogiw_fh *fh = vq->priv_data;
+	unsigned int data_size = timblogiw_frame_size(fh->cur_norm);
+	int err = 0;
+
+	if (vb->baddr && vb->bsize < data_size)
+		/* User provided buffer, but it is too small */
+		return -ENOMEM;
+
+	vb->size = data_size;
+	vb->width = fh->cur_norm->width;
+	vb->height = fh->cur_norm->height;
+	vb->field = field;
+
+	if (vb->state == VIDEOBUF_NEEDS_INIT) {
+		err = videobuf_iolock(vq, vb, NULL);
+		if (err)
+			goto err;
+	}
+
+	vb->state = VIDEOBUF_PREPARED;
+
+	return 0;
+
+err:
+	videobuf_vmalloc_free(vb);
+	vb->state = VIDEOBUF_NEEDS_INIT;
+	return err;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+	struct timblogiw_fh *fh = vq->priv_data;
+
+	vb->state = VIDEOBUF_QUEUED;
+
+	list_add_tail(&vb->queue, &fh->capture);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+	struct videobuf_buffer *vb)
+{
+	videobuf_vmalloc_free(vb);
+	vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops timblogiw_video_qops = {
+	.buf_setup      = buffer_setup,
+	.buf_prepare    = buffer_prepare,
+	.buf_queue      = buffer_queue,
+	.buf_release    = buffer_release,
+};
+
+/* Device Operations functions */
+
+static int timblogiw_open(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw *lw = video_get_drvdata(vdev);
+	struct timblogiw_fh *fh;
+	v4l2_std_id std;
+	dma_cap_mask_t mask;
+	int err = 0;
+
+	dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+	mutex_lock(&lw->lock);
+	if (lw->opened) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) {
+		struct i2c_adapter *adapt;
+
+		/* find the video decoder */
+		adapt = i2c_get_adapter(lw->pdata.i2c_adapter);
+		if (!adapt) {
+			dev_err(&vdev->dev, "No I2C bus #%d\n",
+				lw->pdata.i2c_adapter);
+			err = -ENODEV;
+			goto out;
+		}
+
+		/* now find the encoder */
+		lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt,
+			lw->pdata.encoder.module_name, lw->pdata.encoder.info,
+			NULL);
+
+		i2c_put_adapter(adapt);
+
+		if (!lw->sd_enc) {
+			dev_err(&vdev->dev, "Failed to get encoder: %s\n",
+				lw->pdata.encoder.module_name);
+			err = -ENODEV;
+			goto out;
+		}
+	}
+
+	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+	if (!fh) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	fh->cur_norm = timblogiw_tvnorms;
+	timblogiw_querystd(file, fh, &std);
+	fh->cur_norm = timblogiw_get_norm(std);
+
+	INIT_LIST_HEAD(&fh->capture);
+	spin_lock_init(&fh->queue_lock);
+	tasklet_init(&fh->tasklet, timblogiw_handleframe, (unsigned long)fh);
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	dma_cap_set(DMA_PRIVATE, mask);
+
+	/* find the DMA channel */
+	fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn,
+			(void *)lw->pdata.dma_channel);
+	if (!fh->chan) {
+		dev_err(&vdev->dev, "Failed to get DMA channel\n");
+		kfree(fh);
+		err = -ENODEV;
+		goto out;
+	}
+
+	err = __timblogiw_alloc_dma(fh, lw->dev);
+	if (err) {
+		dev_err(&vdev->dev, "Failed to allocate DMA buffers\n");
+		dma_release_channel(fh->chan);
+		kfree(fh);
+		goto out;
+	}
+
+	file->private_data = fh;
+
+	videobuf_queue_vmalloc_init(&fh->vb_vidq, &timblogiw_video_qops,
+			NULL, &fh->queue_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			V4L2_FIELD_NONE, sizeof(struct videobuf_buffer), fh);
+
+	lw->opened = true;
+out:
+	mutex_unlock(&lw->lock);
+
+	return err;
+}
+
+static int timblogiw_close(struct file *file)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw *lw = video_get_drvdata(vdev);
+	struct timblogiw_fh *fh = file->private_data;
+
+	dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
+
+	videobuf_stop(&fh->vb_vidq);
+	videobuf_mmap_free(&fh->vb_vidq);
+
+	dma_release_channel(fh->chan);
+
+	tasklet_kill(&fh->tasklet);
+
+	__timblogiw_free_dma(fh);
+
+	kfree(fh);
+
+	mutex_lock(&lw->lock);
+	lw->opened = false;
+	mutex_unlock(&lw->lock);
+	return 0;
+}
+
+static ssize_t timblogiw_read(struct file *file, char __user *data,
+	size_t count, loff_t *ppos)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = file->private_data;
+
+	dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
+
+	return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
+		file->f_flags & O_NONBLOCK);
+}
+
+static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct timblogiw_fh *fh = file->private_data;
+
+	dev_dbg(&vdev->dev, "%s: entry\n", __func__);
+
+	return videobuf_mmap_mapper(&fh->vb_vidq, vma);
+}
+
+/* Platform device functions */
+
+static const __devinitdata struct v4l2_ioctl_ops timblogiw_ioctl_ops = {
+	.vidioc_querycap		= timblogiw_querycap,
+	.vidioc_enum_fmt_vid_cap	= timblogiw_enum_fmt,
+	.vidioc_g_fmt_vid_cap		= timblogiw_g_fmt,
+	.vidioc_try_fmt_vid_cap		= timblogiw_try_fmt,
+	.vidioc_s_fmt_vid_cap		= timblogiw_s_fmt,
+	.vidioc_g_parm			= timblogiw_g_parm,
+	.vidioc_reqbufs			= timblogiw_reqbufs,
+	.vidioc_querybuf		= timblogiw_querybuf,
+	.vidioc_qbuf			= timblogiw_qbuf,
+	.vidioc_dqbuf			= timblogiw_dqbuf,
+	.vidioc_g_std			= timblogiw_g_std,
+	.vidioc_s_std			= timblogiw_s_std,
+	.vidioc_enum_input		= timblogiw_enuminput,
+	.vidioc_g_input			= timblogiw_g_input,
+	.vidioc_s_input			= timblogiw_s_input,
+	.vidioc_streamon		= timblogiw_streamon,
+	.vidioc_streamoff		= timblogiw_streamoff,
+	.vidioc_querystd		= timblogiw_querystd,
+	.vidioc_enum_framesizes		= timblogiw_enum_framesizes,
+};
+
+static const __devinitdata struct v4l2_file_operations timblogiw_fops = {
+	.owner		= THIS_MODULE,
+	.open		= timblogiw_open,
+	.release	= timblogiw_close,
+	.ioctl		= video_ioctl2, /* V4L2 ioctl handler */
+	.mmap		= timblogiw_mmap,
+	.read		= timblogiw_read,
+};
+
+static const __devinitdata struct video_device timblogiw_template = {
+	.name		= TIMBLOGIWIN_NAME,
+	.fops		= &timblogiw_fops,
+	.ioctl_ops	= &timblogiw_ioctl_ops,
+	.release	= video_device_release_empty,
+	.minor		= -1,
+	.tvnorms	= V4L2_STD_PAL | V4L2_STD_NTSC
+};
+
+static int __devinit timblogiw_probe(struct platform_device *pdev)
+{
+	int err;
+	struct timblogiw *lw = NULL;
+	struct timb_video_platform_data *pdata = pdev->dev.platform_data;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data\n");
+		err = -EINVAL;
+		goto err;
+	}
+
+	if (!pdata->encoder.module_name)
+		dev_info(&pdev->dev, "Running without decoder\n");
+
+	lw = kzalloc(sizeof(*lw), GFP_KERNEL);
+	if (!lw) {
+		err = -ENOMEM;
+		goto err;
+	}
+
+	if (pdev->dev.parent)
+		lw->dev = pdev->dev.parent;
+	else
+		lw->dev = &pdev->dev;
+
+	memcpy(&lw->pdata, pdata, sizeof(lw->pdata));
+
+	mutex_init(&lw->lock);
+
+	lw->video_dev = timblogiw_template;
+
+	strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));
+	err = v4l2_device_register(NULL, &lw->v4l2_dev);
+	if (err)
+		goto err_register;
+
+	lw->video_dev.v4l2_dev = &lw->v4l2_dev;
+
+	err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0);
+	if (err) {
+		dev_err(&pdev->dev, "Error reg video: %d\n", err);
+		goto err_request;
+	}
+
+	platform_set_drvdata(pdev, lw);
+	video_set_drvdata(&lw->video_dev, lw);
+
+	return 0;
+
+err_request:
+	v4l2_device_unregister(&lw->v4l2_dev);
+err_register:
+	kfree(lw);
+err:
+	dev_err(&pdev->dev, "Failed to register: %d\n", err);
+
+	return err;
+}
+
+static int __devexit timblogiw_remove(struct platform_device *pdev)
+{
+	struct timblogiw *lw = platform_get_drvdata(pdev);
+
+	video_unregister_device(&lw->video_dev);
+
+	v4l2_device_unregister(&lw->v4l2_dev);
+
+	kfree(lw);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver timblogiw_platform_driver = {
+	.driver = {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= timblogiw_probe,
+	.remove		= __devexit_p(timblogiw_remove),
+};
+
+/* Module functions */
+
+static int __init timblogiw_init(void)
+{
+	return platform_driver_register(&timblogiw_platform_driver);
+}
+
+static void __exit timblogiw_exit(void)
+{
+	platform_driver_unregister(&timblogiw_platform_driver);
+}
+
+module_init(timblogiw_init);
+module_exit(timblogiw_exit);
+
+MODULE_DESCRIPTION(TIMBLOGIWIN_NAME);
+MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:"DRIVER_NAME);
diff --git a/include/media/timb_video.h b/include/media/timb_video.h
new file mode 100644
index 0000000..55334ad
--- /dev/null
+++ b/include/media/timb_video.h
@@ -0,0 +1,34 @@
+/*
+ * timb_video.h Platform struct for the Timberdale video driver
+ * Copyright (c) 2009-2010 Intel Corporation
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _TIMB_VIDEO_
+#define _TIMB_VIDEO_ 1
+
+#include <linux/i2c.h>
+
+struct timb_video_platform_data {
+	int dma_channel;
+	int i2c_adapter; /* The I2C adapter where the encoder is attached */
+	struct {
+		const char *module_name;
+		struct i2c_board_info *info;
+	} encoder;
+};
+
+#endif
+


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] media: Add timberdale video-in driver
  2010-04-16 16:28 [PATCH 1/2] media: Add timberdale video-in driver Richard Röjfors
@ 2010-04-25 15:24 ` Mauro Carvalho Chehab
  2010-04-25 16:01   ` Richard Röjfors
  0 siblings, 1 reply; 5+ messages in thread
From: Mauro Carvalho Chehab @ 2010-04-25 15:24 UTC (permalink / raw)
  To: Richard Röjfors
  Cc: Linux Media Mailing List, Linux Kernel Mailing List,
	Douglas Schilling Landgraf, Samuel Ortiz, Williams, Dan J

Richard Röjfors wrote:
> This patch adds the timberdale video-in driver.
> 
> The video IP of timberdale delivers the video data via DMA.
> The driver uses the DMA api to handle DMA transfers, and make use
> of the V4L2 videobuffers to handle buffers against user space.
> Due to some timing constraint it makes sense to do DMA into an
> intermediate buffer and then copy the data to vmalloc:ed buffers.
> 
> If available the driver uses an encoder to get/set the video standard
> 
> Signed-off-by: Richard Röjfors <richard.rojfors@pelagicore.com>
> +#define TIMBLOGIW_DMA_BUFFER_SIZE	(TIMBLOGIW_BYTES_PER_LINE * 576)

...

> +static int __timblogiw_alloc_dma(struct timblogiw_fh *fh, struct device *dev)
> +{
> +	dma_addr_t addr;
> +	int err, i, pos;
> +	int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
> +		timblogiw_bytes_per_line(fh->cur_norm);
> +
> +	fh->dma.cookie = -1;
> +	fh->dma.dev = dev;
> +
> +	fh->dma.buf = kzalloc(TIMBLOGIW_DMA_BUFFER_SIZE, GFP_KERNEL);
> +	if (!fh->dma.buf)
> +		return -ENOMEM;


Why do you need a fixed DMA buffer size? Just allocate the buffer size dynamically at
buffer_prepare callback.
> +	videobuf_queue_vmalloc_init(&fh->vb_vidq, &timblogiw_video_qops,
> +			NULL, &fh->queue_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
> +			V4L2_FIELD_NONE, sizeof(struct videobuf_buffer), fh);

You should be using, instead, videobuf_dma_sg or videobuf_cont, instead of
using videobuf-vmalloc. This way, you'll avoid double buffering.

-- 

Cheers,
Mauro

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] media: Add timberdale video-in driver
  2010-04-25 15:24 ` Mauro Carvalho Chehab
@ 2010-04-25 16:01   ` Richard Röjfors
  2010-04-25 16:13     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 5+ messages in thread
From: Richard Röjfors @ 2010-04-25 16:01 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Linux Media Mailing List, Linux Kernel Mailing List,
	Douglas Schilling Landgraf, Samuel Ortiz, Williams, Dan J

On 04/25/2010 05:24 PM, Mauro Carvalho Chehab wrote:
> Richard Röjfors wrote:
>> This patch adds the timberdale video-in driver.
>>
>> The video IP of timberdale delivers the video data via DMA.
>> The driver uses the DMA api to handle DMA transfers, and make use
>> of the V4L2 videobuffers to handle buffers against user space.
>> Due to some timing constraint it makes sense to do DMA into an
>> intermediate buffer and then copy the data to vmalloc:ed buffers.
>>
>> If available the driver uses an encoder to get/set the video standard
>>
>> Signed-off-by: Richard Röjfors<richard.rojfors@pelagicore.com>
>> +#define TIMBLOGIW_DMA_BUFFER_SIZE	(TIMBLOGIW_BYTES_PER_LINE * 576)
>
> ...
>
>> +static int __timblogiw_alloc_dma(struct timblogiw_fh *fh, struct device *dev)
>> +{
>> +	dma_addr_t addr;
>> +	int err, i, pos;
>> +	int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
>> +		timblogiw_bytes_per_line(fh->cur_norm);
>> +
>> +	fh->dma.cookie = -1;
>> +	fh->dma.dev = dev;
>> +
>> +	fh->dma.buf = kzalloc(TIMBLOGIW_DMA_BUFFER_SIZE, GFP_KERNEL);
>> +	if (!fh->dma.buf)
>> +		return -ENOMEM;
>
>
> Why do you need a fixed DMA buffer size? Just allocate the buffer size dynamically at
> buffer_prepare callback.
>> +	videobuf_queue_vmalloc_init(&fh->vb_vidq,&timblogiw_video_qops,
>> +			NULL,&fh->queue_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
>> +			V4L2_FIELD_NONE, sizeof(struct videobuf_buffer), fh);
>
> You should be using, instead, videobuf_dma_sg or videobuf_cont, instead of
> using videobuf-vmalloc. This way, you'll avoid double buffering.

1. dma_sg can not be used, the DMA engine requires the memory blocks to be aligned on a factor of 
bytes per line, so 4K pages wouldn't work.

2.
I tried using videobuf-dma-contig, but got poor performance. I can not really explain why, I though 
it's due to the fact that the contiguous buffer is allocated coherent -> no caching.
I saw both gstreamer and mplayer perform very badly.
The frame grabber requires the DMA transfer for a frame beeing started while the frame is decoded. 
When I tested using contigous buffers gstreamer sometimes was that slow that it sometimes missed to 
have a frame queued when a transfer was finished, so I got frame drops. Any other ideas of the poor 
performance? otherwise I would like to go for the double buffered solution.

Thanks
--Richard

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] media: Add timberdale video-in driver
  2010-04-25 16:01   ` Richard Röjfors
@ 2010-04-25 16:13     ` Mauro Carvalho Chehab
  2010-05-26 14:19       ` Richard Röjfors
  0 siblings, 1 reply; 5+ messages in thread
From: Mauro Carvalho Chehab @ 2010-04-25 16:13 UTC (permalink / raw)
  To: Richard Röjfors
  Cc: Linux Media Mailing List, Linux Kernel Mailing List,
	Douglas Schilling Landgraf, Samuel Ortiz, Williams, Dan J

Richard Röjfors wrote:
> On 04/25/2010 05:24 PM, Mauro Carvalho Chehab wrote:
>> Richard Röjfors wrote:
>>> This patch adds the timberdale video-in driver.
>>>
>>> The video IP of timberdale delivers the video data via DMA.
>>> The driver uses the DMA api to handle DMA transfers, and make use
>>> of the V4L2 videobuffers to handle buffers against user space.
>>> Due to some timing constraint it makes sense to do DMA into an
>>> intermediate buffer and then copy the data to vmalloc:ed buffers.
>>>
>>> If available the driver uses an encoder to get/set the video standard
>>>
>>> Signed-off-by: Richard Röjfors<richard.rojfors@pelagicore.com>
>>> +#define TIMBLOGIW_DMA_BUFFER_SIZE    (TIMBLOGIW_BYTES_PER_LINE * 576)
>>
>> ...
>>
>>> +static int __timblogiw_alloc_dma(struct timblogiw_fh *fh, struct
>>> device *dev)
>>> +{
>>> +    dma_addr_t addr;
>>> +    int err, i, pos;
>>> +    int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
>>> +        timblogiw_bytes_per_line(fh->cur_norm);
>>> +
>>> +    fh->dma.cookie = -1;
>>> +    fh->dma.dev = dev;
>>> +
>>> +    fh->dma.buf = kzalloc(TIMBLOGIW_DMA_BUFFER_SIZE, GFP_KERNEL);
>>> +    if (!fh->dma.buf)
>>> +        return -ENOMEM;
>>
>>
>> Why do you need a fixed DMA buffer size? Just allocate the buffer size
>> dynamically at
>> buffer_prepare callback.
>>> +    videobuf_queue_vmalloc_init(&fh->vb_vidq,&timblogiw_video_qops,
>>> +            NULL,&fh->queue_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
>>> +            V4L2_FIELD_NONE, sizeof(struct videobuf_buffer), fh);
>>
>> You should be using, instead, videobuf_dma_sg or videobuf_cont,
>> instead of
>> using videobuf-vmalloc. This way, you'll avoid double buffering.
> 
> 1. dma_sg can not be used, the DMA engine requires the memory blocks to
> be aligned on a factor of bytes per line, so 4K pages wouldn't work.
> 
> 2.
> I tried using videobuf-dma-contig, but got poor performance. I can not
> really explain why, I though it's due to the fact that the contiguous
> buffer is allocated coherent -> no caching.
> I saw both gstreamer and mplayer perform very badly.
> The frame grabber requires the DMA transfer for a frame beeing started
> while the frame is decoded. When I tested using contigous buffers
> gstreamer sometimes was that slow that it sometimes missed to have a
> frame queued when a transfer was finished, so I got frame drops. Any
> other ideas of the poor performance? otherwise I would like to go for
> the double buffered solution.

The better is to fix videobuf-dma_contig to better work on your hardware.
It makes sense to add a flag to allow specifying if it should use coherent
or non-coherent memory for the dma buffer alloc/free calls.

-- 

Cheers,
Mauro

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] media: Add timberdale video-in driver
  2010-04-25 16:13     ` Mauro Carvalho Chehab
@ 2010-05-26 14:19       ` Richard Röjfors
  0 siblings, 0 replies; 5+ messages in thread
From: Richard Röjfors @ 2010-05-26 14:19 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Linux Media Mailing List, Linux Kernel Mailing List,
	Douglas Schilling Landgraf, Samuel Ortiz, Williams, Dan J

Hi Mauro,

On Sun, 2010-04-25 at 13:13 -0300, Mauro Carvalho Chehab wrote:
> > 
> > 2.
> > I tried using videobuf-dma-contig, but got poor performance. I can not
> > really explain why, I though it's due to the fact that the contiguous
> > buffer is allocated coherent -> no caching.
> > I saw both gstreamer and mplayer perform very badly.
> > The frame grabber requires the DMA transfer for a frame beeing started
> > while the frame is decoded. When I tested using contigous buffers
> > gstreamer sometimes was that slow that it sometimes missed to have a
> > frame queued when a transfer was finished, so I got frame drops. Any
> > other ideas of the poor performance? otherwise I would like to go for
> > the double buffered solution.
> 
> The better is to fix videobuf-dma_contig to better work on your hardware.
> It makes sense to add a flag to allow specifying if it should use coherent
> or non-coherent memory for the dma buffer alloc/free calls.
> 

I have verified the performance issue and it goes back to non cached coherent buffers.
I did an update to the videobuf-dma-contig. I did it by adding another init function,
which calls videobuf_queue_core_init with another set of qops.
The other set uses the same functions as the uncached version, but a sync function
is added, and the alloc_functions are different.

What do you think?

Signed-off-by: Richard Röjfors <richard.rojfors@pelagicore.com>
---
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index dce4f3a..2fc923c 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -27,6 +27,7 @@ struct videobuf_dma_contig_memory {
 	u32 magic;
 	void *vaddr;
 	dma_addr_t dma_handle;
+	bool cached;
 	unsigned long size;
 	int is_userptr;
 };
@@ -38,6 +39,54 @@ struct videobuf_dma_contig_memory {
 		BUG();							    \
 	}
 
+static int __videobuf_dc_alloc(struct device *dev,
+	struct videobuf_dma_contig_memory *mem, unsigned long size)
+{
+	mem->size = size;
+	if (mem->cached) {
+		mem->vaddr = kmalloc(mem->size, GFP_KERNEL);
+		if (mem->vaddr) {
+			int err;
+
+			mem->dma_handle = dma_map_single(dev, mem->vaddr,
+				mem->size, DMA_FROM_DEVICE);
+			err = dma_mapping_error(dev, mem->dma_handle);
+			if (err) {
+				dev_err(dev, "dma_map_single failed\n");
+
+				kfree(mem->vaddr);
+				mem->vaddr = 0;
+				return err;
+			}
+		}
+	} else
+		mem->vaddr = dma_alloc_coherent(dev, mem->size,
+			&mem->dma_handle, GFP_KERNEL);
+
+	if (!mem->vaddr) {
+		dev_err(dev, "memory alloc size %ld failed\n",
+			mem->size);
+		return -ENOMEM;
+	}
+
+	dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size);
+
+	return 0;
+}
+
+static void __videobuf_dc_free(struct device *dev,
+	struct videobuf_dma_contig_memory *mem)
+{
+	if (mem->cached) {
+		dma_unmap_single(dev, mem->dma_handle, mem->size,
+			DMA_FROM_DEVICE);
+		kfree(mem->vaddr);
+	} else
+		dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle);
+
+	mem->vaddr = NULL;
+}
+
 static void
 videobuf_vm_open(struct vm_area_struct *vma)
 {
@@ -92,9 +141,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
 				dev_dbg(map->q->dev, "buf[%d] freeing %p\n",
 					i, mem->vaddr);
 
-				dma_free_coherent(q->dev, mem->size,
-						  mem->vaddr, mem->dma_handle);
-				mem->vaddr = NULL;
+				__videobuf_dc_free(q->dev, mem);
 			}
 
 			q->bufs[i]->map   = NULL;
@@ -190,7 +237,7 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
 	return ret;
 }
 
-static void *__videobuf_alloc(size_t size)
+static void *__videobuf_alloc(size_t size, bool cached)
 {
 	struct videobuf_dma_contig_memory *mem;
 	struct videobuf_buffer *vb;
@@ -199,11 +246,22 @@ static void *__videobuf_alloc(size_t size)
 	if (vb) {
 		mem = vb->priv = ((char *)vb) + size;
 		mem->magic = MAGIC_DC_MEM;
+		mem->cached = cached;
 	}
 
 	return vb;
 }
 
+static void *__videobuf_alloc_uncached(size_t size)
+{
+	return __videobuf_alloc(size, false);
+}
+
+static void *__videobuf_alloc_cached(size_t size)
+{
+	return __videobuf_alloc(size, true);
+}
+
 static void *__videobuf_to_vmalloc(struct videobuf_buffer *buf)
 {
 	struct videobuf_dma_contig_memory *mem = buf->priv;
@@ -241,17 +299,8 @@ static int __videobuf_iolock(struct videobuf_queue *q,
 			return videobuf_dma_contig_user_get(mem, vb);
 
 		/* allocate memory for the read() method */
-		mem->size = PAGE_ALIGN(vb->size);
-		mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
-						&mem->dma_handle, GFP_KERNEL);
-		if (!mem->vaddr) {
-			dev_err(q->dev, "dma_alloc_coherent %ld failed\n",
-					 mem->size);
+		if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size)))
 			return -ENOMEM;
-		}
-
-		dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n",
-			mem->vaddr, mem->size);
 		break;
 	case V4L2_MEMORY_OVERLAY:
 	default:
@@ -276,6 +325,20 @@ static int __videobuf_mmap_free(struct videobuf_queue *q)
 	return 0;
 }
 
+static int __videobuf_sync(struct videobuf_queue *q,
+			   struct videobuf_buffer *buf)
+{
+	struct videobuf_dma_contig_memory *mem = buf->priv;
+	BUG_ON(!mem);
+	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+	dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size,
+		DMA_FROM_DEVICE);
+
+    return 0;
+}
+
+
 static int __videobuf_mmap_mapper(struct videobuf_queue *q,
 				  struct vm_area_struct *vma)
 {
@@ -321,30 +384,22 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
 	BUG_ON(!mem);
 	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
 
-	mem->size = PAGE_ALIGN(q->bufs[first]->bsize);
-	mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
-					&mem->dma_handle, GFP_KERNEL);
-	if (!mem->vaddr) {
-		dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
-			mem->size);
+	if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(q->bufs[first]->bsize)))
 		goto error;
-	}
-	dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n",
-		mem->vaddr, mem->size);
 
 	/* Try to remap memory */
 
 	size = vma->vm_end - vma->vm_start;
 	size = (size < mem->size) ? size : mem->size;
 
-	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	if (!mem->cached)
+		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 	retval = remap_pfn_range(vma, vma->vm_start,
 				 mem->dma_handle >> PAGE_SHIFT,
 				 size, vma->vm_page_prot);
 	if (retval) {
 		dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
-		dma_free_coherent(q->dev, mem->size,
-				  mem->vaddr, mem->dma_handle);
+		__videobuf_dc_free(q->dev, mem);
 		goto error;
 	}
 
@@ -422,9 +477,22 @@ static int __videobuf_copy_stream(struct videobuf_queue *q,
 static struct videobuf_qtype_ops qops = {
 	.magic        = MAGIC_QTYPE_OPS,
 
-	.alloc        = __videobuf_alloc,
+	.alloc        = __videobuf_alloc_uncached,
+	.iolock       = __videobuf_iolock,
+	.mmap_free    = __videobuf_mmap_free,
+	.mmap_mapper  = __videobuf_mmap_mapper,
+	.video_copy_to_user = __videobuf_copy_to_user,
+	.copy_stream  = __videobuf_copy_stream,
+	.vmalloc      = __videobuf_to_vmalloc,
+};
+
+static struct videobuf_qtype_ops qops_cached = {
+	.magic        = MAGIC_QTYPE_OPS,
+
+	.alloc        = __videobuf_alloc_cached,
 	.iolock       = __videobuf_iolock,
 	.mmap_free    = __videobuf_mmap_free,
+	.sync         = __videobuf_sync,
 	.mmap_mapper  = __videobuf_mmap_mapper,
 	.video_copy_to_user = __videobuf_copy_to_user,
 	.copy_stream  = __videobuf_copy_stream,
@@ -445,6 +513,20 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
 }
 EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
 
+void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q,
+				    const struct videobuf_queue_ops *ops,
+				    struct device *dev,
+				    spinlock_t *irqlock,
+				    enum v4l2_buf_type type,
+				    enum v4l2_field field,
+				    unsigned int msize,
+				    void *priv)
+{
+	videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+				 priv, &qops_cached);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached);
+
 dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
 {
 	struct videobuf_dma_contig_memory *mem = buf->priv;
@@ -481,9 +563,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q,
 		return;
 	}
 
-	/* read() method */
-	dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
-	mem->vaddr = NULL;
+	__videobuf_dc_free(q->dev, mem);
 }
 EXPORT_SYMBOL_GPL(videobuf_dma_contig_free);
 
diff --git a/include/media/videobuf-dma-contig.h b/include/media/videobuf-dma-contig.h
index ebaa9bc..43b94cd 100644
--- a/include/media/videobuf-dma-contig.h
+++ b/include/media/videobuf-dma-contig.h
@@ -25,6 +25,15 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
 				    unsigned int msize,
 				    void *priv);
 
+void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q,
+				    const struct videobuf_queue_ops *ops,
+				    struct device *dev,
+				    spinlock_t *irqlock,
+				    enum v4l2_buf_type type,
+				    enum v4l2_field field,
+				    unsigned int msize,
+				    void *priv);
+
 dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf);
 void videobuf_dma_contig_free(struct videobuf_queue *q,
 			      struct videobuf_buffer *buf);


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2010-05-26 15:03 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-16 16:28 [PATCH 1/2] media: Add timberdale video-in driver Richard Röjfors
2010-04-25 15:24 ` Mauro Carvalho Chehab
2010-04-25 16:01   ` Richard Röjfors
2010-04-25 16:13     ` Mauro Carvalho Chehab
2010-05-26 14:19       ` Richard Röjfors

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).