msm_fb: display: MSM V4l2 video overlay driver
Provides a V4L2 device that uses the MDP overlay pipes (on MDP4),
or the PPP interface (MDP3) to overlay frames on top of display
framebuffer.
Signed-off-by: Alhad Purnapatre <alhadp@codeaurora.org>
Change-Id: Iab69d0a5acfe993d13cb7a585e292b9a87eb90ee
diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c
index 86343f5..96d798e 100644
--- a/arch/arm/mach-msm/board-msm7627a-display.c
+++ b/arch/arm/mach-msm/board-msm7627a-display.c
@@ -35,6 +35,12 @@
#define MSM7x25A_MSM_FB_SIZE 0x96000
#endif
+/*
+ * Reserve enough v4l2 space for a double buffered full screen
+ * res image (864x480x1.5x2)
+ */
+#define MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE 1244160
+
static unsigned fb_size = MSM_FB_SIZE;
static int __init fb_size_setup(char *p)
{
@@ -459,6 +465,14 @@
}
};
+#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE
+static struct resource msm_v4l2_video_overlay_resources[] = {
+ {
+ .flags = IORESOURCE_DMA,
+ }
+};
+#endif
+
#define LCDC_TOSHIBA_FWVGA_PANEL_NAME "lcdc_toshiba_fwvga_pt"
#define MIPI_CMD_RENESAS_FWVGA_PANEL_NAME "mipi_cmd_renesas_fwvga"
@@ -522,6 +536,16 @@
}
};
+#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE
+static struct platform_device msm_v4l2_video_overlay_device = {
+ .name = "msm_v4l2_overlay_pd",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(msm_v4l2_video_overlay_resources),
+ .resource = msm_v4l2_video_overlay_resources,
+ };
+#endif
+
+
#ifdef CONFIG_FB_MSM_MIPI_DSI
static int mipi_renesas_set_bl(int level)
{
@@ -579,6 +603,9 @@
#ifdef CONFIG_FB_MSM_MIPI_DSI
&mipi_dsi_renesas_panel_device,
#endif
+#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE
+ &msm_v4l2_video_overlay_device,
+#endif
};
static struct platform_device *qrd_fb_devices[] __initdata = {
@@ -610,6 +637,17 @@
msm_fb_resources[0].end = msm_fb_resources[0].start + fb_size - 1;
pr_info("allocating %lu bytes at %p (%lx physical) for fb\n", fb_size,
addr, __pa(addr));
+
+#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE
+ fb_size = MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE;
+ addr = alloc_bootmem_align(fb_size, 0x1000);
+ msm_v4l2_video_overlay_resources[0].start = __pa(addr);
+ msm_v4l2_video_overlay_resources[0].end =
+ msm_v4l2_video_overlay_resources[0].start + fb_size - 1;
+ pr_debug("allocating %lu bytes at %p (%lx physical) for v4l2\n",
+ fb_size, addr, __pa(addr));
+#endif
+
}
static struct msm_panel_common_pdata mdp_pdata = {
diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c
index cd1e5b8..972cb22 100644
--- a/arch/arm/mach-msm/board-msm7x30.c
+++ b/arch/arm/mach-msm/board-msm7x30.c
@@ -99,6 +99,11 @@
#else
#define MSM_FB_SIZE 0x500000
#endif
+/*
+ * Reserve space for double buffered full screen
+ * res V4L2 video overlay - i.e. 1280x720x1.5x2
+ */
+#define MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE 2764800
#define MSM_PMEM_ADSP_SIZE 0x1E00000
#define MSM_FLUID_PMEM_ADSP_SIZE 0x2800000
#define PMEM_KERNEL_EBI0_SIZE 0x600000
@@ -3770,6 +3775,14 @@
}
};
+#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE
+static struct resource msm_v4l2_video_overlay_resources[] = {
+ {
+ .flags = IORESOURCE_DMA,
+ }
+};
+#endif
+
static int msm_fb_detect_panel(const char *name)
{
if (machine_is_msm7x30_fluid()) {
@@ -3803,6 +3816,16 @@
}
};
+#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE
+
+static struct platform_device msm_v4l2_video_overlay_device = {
+ .name = "msm_v4l2_overlay_pd",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(msm_v4l2_video_overlay_resources),
+ .resource = msm_v4l2_video_overlay_resources,
+};
+#endif
+
static struct platform_device msm_migrate_pages_device = {
.name = "msm_migrate_pages",
.id = -1,
@@ -5192,6 +5215,9 @@
#endif
&android_pmem_device,
&msm_fb_device,
+#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE
+ &msm_v4l2_video_overlay_device,
+#endif
&msm_migrate_pages_device,
&mddi_toshiba_device,
&lcdc_toshiba_panel_device,
@@ -7025,6 +7051,16 @@
msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1;
pr_info("allocating %lu bytes at %p (%lx physical) for fb\n",
size, addr, __pa(addr));
+
+#ifdef CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE
+ size = MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE;
+ addr = alloc_bootmem_align(size, 0x1000);
+ msm_v4l2_video_overlay_resources[0].start = __pa(addr);
+ msm_v4l2_video_overlay_resources[0].end =
+ msm_v4l2_video_overlay_resources[0].start + size - 1;
+ pr_debug("allocating %lu bytes at %p (%lx physical) for v4l2\n",
+ size, addr, __pa(addr));
+#endif
}
static void __init msm7x30_map_io(void)
diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h
index 339fa46..3bbaa25 100644
--- a/arch/arm/mach-msm/include/mach/msm_fb.h
+++ b/arch/arm/mach-msm/include/mach/msm_fb.h
@@ -188,5 +188,11 @@
};
+struct mdp_v4l2_req;
+int msm_fb_v4l2_enable(struct mdp_overlay *req, bool enable, void **par);
+int msm_fb_v4l2_update(void *par,
+ unsigned long srcp0_addr, unsigned long srcp0_size,
+ unsigned long srcp1_addr, unsigned long srcp1_size,
+ unsigned long srcp2_addr, unsigned long srcp2_size);
#endif
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index a93031e..ec034b1 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -251,3 +251,11 @@
---help---
Sony 13MP sensor back camera that uses 4 mipi lanes,
runs at 30 fps preview and 14 fps snapshot
+
+config MSM_V4L2_VIDEO_OVERLAY_DEVICE
+ tristate "Qualcomm MSM V4l2 video overlay device"
+ ---help---
+ Enables support for the MSM V4L2 video
+ overlay driver. This allows video rendering
+ apps to render overlaid video using Video4Linux2
+ APIs, by using /dev/videoX device
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
index 2843d30..f817561 100644
--- a/drivers/media/video/msm/Makefile
+++ b/drivers/media/video/msm/Makefile
@@ -52,3 +52,4 @@
obj-$(CONFIG_MT9D113) += mt9d113.o mt9d113_reg.o
obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += wfd/
+obj-$(CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE) += msm_v4l2_video.o
diff --git a/drivers/media/video/msm/msm_v4l2_video.c b/drivers/media/video/msm/msm_v4l2_video.c
new file mode 100644
index 0000000..c4e6c62
--- /dev/null
+++ b/drivers/media/video/msm/msm_v4l2_video.c
@@ -0,0 +1,947 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/msm_mdp.h>
+#include <linux/sched.h>
+#include <linux/capability.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-dev.h>
+#include <media/msm_v4l2_overlay.h>
+
+#include <mach/board.h>
+#include <mach/msm_fb.h>
+
+#include "msm_v4l2_video.h"
+
+#define MSM_VIDEO -1
+
+static struct msm_v4l2_overlay_device *saved_vout0;
+
+static struct mutex msmfb_lock;
+static char *v4l2_ram_phys;
+static unsigned int v4l2_ram_size;
+
+static int msm_v4l2_overlay_mapformat(uint32_t pixelformat);
+
+static int msm_v4l2_overlay_startstreaming(struct msm_v4l2_overlay_device *vout)
+{
+ vout->req.src.width = vout->pix.width;
+ vout->req.src.height = vout->pix.height;
+
+ vout->req.src_rect.x = vout->crop_rect.left;
+ vout->req.src_rect.y = vout->crop_rect.top;
+ vout->req.src_rect.w = vout->crop_rect.width;
+ vout->req.src_rect.h = vout->crop_rect.height;
+
+ vout->req.src.format =
+ msm_v4l2_overlay_mapformat(vout->pix.pixelformat);
+
+ vout->req.dst_rect.x = vout->win.w.left;
+ vout->req.dst_rect.y = vout->win.w.top;
+ vout->req.dst_rect.w = vout->win.w.width;
+ vout->req.dst_rect.h = vout->win.w.height;
+
+ vout->req.alpha = MDP_ALPHA_NOP;
+ vout->req.transp_mask = MDP_TRANSP_NOP;
+
+ pr_debug("msm_v4l2_overlay:startstreaming:enabling fb\n");
+ mutex_lock(&msmfb_lock);
+ msm_fb_v4l2_enable(&vout->req, true, &vout->par);
+ mutex_unlock(&msmfb_lock);
+
+ vout->streaming = 1;
+
+ return 0;
+}
+
+static int msm_v4l2_overlay_stopstreaming(struct msm_v4l2_overlay_device *vout)
+{
+ if (!vout->streaming)
+ return 0;
+
+ pr_debug("msm_v4l2_overlay:startstreaming:disabling fb\n");
+ mutex_lock(&msmfb_lock);
+ msm_fb_v4l2_enable(&vout->req, false, &vout->par);
+ mutex_unlock(&msmfb_lock);
+
+ vout->streaming = 0;
+
+ return 0;
+}
+
+static int msm_v4l2_overlay_mapformat(uint32_t pixelformat)
+{
+ int mdp_format;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ mdp_format = MDP_RGB_565;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ mdp_format = MDP_ARGB_8888;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ mdp_format = MDP_RGB_888;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ mdp_format = MDP_Y_CRCB_H2V2;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ mdp_format = MDP_Y_CBCR_H2V2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ mdp_format = MDP_Y_CR_CB_H2V2;
+ break;
+ default:
+ pr_err("%s:Unrecognized format %u\n", __func__, pixelformat);
+ mdp_format = MDP_Y_CBCR_H2V2;
+ break;
+ }
+
+ return mdp_format;
+}
+
+static int
+msm_v4l2_overlay_fb_update(struct msm_v4l2_overlay_device *vout,
+ struct v4l2_buffer *buffer)
+{
+ int ret;
+ unsigned long src_addr, src_size;
+ struct msm_v4l2_overlay_userptr_buffer up_buffer;
+
+ if (!buffer ||
+ (buffer->memory == V4L2_MEMORY_MMAP &&
+ buffer->index >= vout->numbufs))
+ return -EINVAL;
+
+ mutex_lock(&msmfb_lock);
+ switch (buffer->memory) {
+ case V4L2_MEMORY_MMAP:
+ src_addr = (unsigned long)v4l2_ram_phys
+ + vout->bufs[buffer->index].offset;
+ src_size = buffer->bytesused;
+ ret = msm_fb_v4l2_update(vout->par, src_addr, src_size,
+ 0, 0, 0, 0);
+ break;
+ case V4L2_MEMORY_USERPTR:
+ if (copy_from_user(&up_buffer,
+ (void __user *)buffer->m.userptr,
+ sizeof(struct msm_v4l2_overlay_userptr_buffer))) {
+ mutex_unlock(&msmfb_lock);
+ return -EINVAL;
+ }
+ ret = msm_fb_v4l2_update(vout->par,
+ (unsigned long)up_buffer.base[0], up_buffer.length[0],
+ (unsigned long)up_buffer.base[1], up_buffer.length[1],
+ (unsigned long)up_buffer.base[2], up_buffer.length[2]);
+ break;
+ default:
+ mutex_unlock(&msmfb_lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&msmfb_lock);
+
+ if (buffer->memory == V4L2_MEMORY_MMAP) {
+ vout->bufs[buffer->index].queued = 1;
+ buffer->flags |= V4L2_BUF_FLAG_MAPPED;
+ }
+ buffer->flags |= V4L2_BUF_FLAG_QUEUED;
+
+ return ret;
+}
+
+static int
+msm_v4l2_overlay_vidioc_dqbuf(struct file *file,
+ struct msm_v4l2_overlay_fh *fh, void *arg)
+{
+ struct msm_v4l2_overlay_device *vout = fh->vout;
+ struct v4l2_buffer *buffer = arg;
+ int i;
+
+ if (!vout->streaming) {
+ pr_err("%s: Video Stream not enabled\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!buffer || buffer->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ if (buffer->memory == V4L2_MEMORY_MMAP) {
+ for (i = 0; i < vout->numbufs; i++) {
+ if (vout->bufs[i].queued == 1) {
+ vout->bufs[i].queued = 0;
+ /* Call into fb to remove this buffer? */
+ break;
+ }
+ }
+
+ /*
+ * This should actually block, unless O_NONBLOCK was
+ * specified in open, but fine for now, especially
+ * since this is not a capturing device
+ */
+ if (i == vout->numbufs)
+ return -EAGAIN;
+ }
+
+ buffer->flags &= ~V4L2_BUF_FLAG_QUEUED;
+
+ return 0;
+}
+
+
+static int
+msm_v4l2_overlay_vidioc_qbuf(struct file *file, struct msm_v4l2_overlay_fh* fh,
+ void *arg, bool bUserPtr)
+{
+ struct msm_v4l2_overlay_device *vout = fh->vout;
+ struct v4l2_buffer *buffer = arg;
+ int ret;
+
+ if (!bUserPtr && buffer->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (!vout->streaming) {
+ pr_err("%s: Video Stream not enabled\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!buffer || buffer->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ /* maybe allow only one qbuf at a time? */
+ ret = msm_v4l2_overlay_fb_update(vout, buffer);
+
+ return 0;
+}
+
+static int
+msm_v4l2_overlay_vidioc_querycap(struct file *file, void *arg)
+{
+ struct v4l2_capability *buffer = arg;
+
+ memset(buffer, 0, sizeof(struct v4l2_capability));
+ strlcpy(buffer->driver, "msm_v4l2_video_overlay",
+ ARRAY_SIZE(buffer->driver));
+ strlcpy(buffer->card, "MSM MDP",
+ ARRAY_SIZE(buffer->card));
+ buffer->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_VIDEO_OVERLAY;
+ return 0;
+}
+
+static int
+msm_v4l2_overlay_vidioc_fbuf(struct file *file,
+ struct msm_v4l2_overlay_device *vout, void *arg, bool get)
+{
+ struct v4l2_framebuffer *fb = arg;
+
+ if (fb == NULL)
+ return -EINVAL;
+
+ if (get) {
+ mutex_lock(&vout->update_lock);
+ memcpy(&fb->fmt, &vout->pix, sizeof(struct v4l2_pix_format));
+ mutex_unlock(&vout->update_lock);
+ }
+ /* The S_FBUF request does not store anything right now */
+ return 0;
+}
+
+static long msm_v4l2_overlay_calculate_bufsize(struct v4l2_pix_format *pix)
+{
+ int bpp;
+ long bufsize;
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ bpp = 12;
+ break;
+
+ case V4L2_PIX_FMT_RGB565:
+ bpp = 16;
+ break;
+
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_YUV444:
+ bpp = 24;
+ break;
+
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ bpp = 32;
+ break;
+ default:
+ pr_err("%s: Unrecognized format %u\n", __func__,
+ pix->pixelformat);
+ bpp = 0;
+ }
+
+ bufsize = (pix->width * pix->height * bpp)/8;
+
+ return bufsize;
+}
+
+static long
+msm_v4l2_overlay_vidioc_reqbufs(struct file *file,
+ struct msm_v4l2_overlay_device *vout, void *arg)
+
+{
+ struct v4l2_requestbuffers *rqb = arg;
+ long bufsize;
+ int i;
+
+ if (rqb == NULL || rqb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ if (rqb->memory == V4L2_MEMORY_MMAP) {
+ if (rqb->count == 0) {
+ /* Deallocate allocated buffers */
+ mutex_lock(&vout->update_lock);
+ vout->numbufs = 0;
+ kfree(vout->bufs);
+ /*
+ * There should be a way to look at bufs[i]->mapped,
+ * and prevent userspace from mmaping and directly
+ * calling this ioctl without unmapping. Maybe kernel
+ * handles for us, but needs to be checked out
+ */
+ mutex_unlock(&vout->update_lock);
+ } else {
+ /*
+ * Keep it simple for now - need to deallocate
+ * before reallocate
+ */
+ if (vout->bufs)
+ return -EINVAL;
+
+ mutex_lock(&vout->update_lock);
+ bufsize =
+ msm_v4l2_overlay_calculate_bufsize(&vout->pix);
+ mutex_unlock(&vout->update_lock);
+
+ if (bufsize == 0
+ || (bufsize * rqb->count) > v4l2_ram_size) {
+ pr_err("%s: Unsupported format or buffer size too large\n",
+ __func__);
+ pr_err("%s: bufsize %lu ram_size %u count %u\n",
+ __func__, bufsize, v4l2_ram_size, rqb->count);
+ return -EINVAL;
+ }
+
+ /*
+ * We don't support multiple open of one vout,
+ * but there are probably still some MT problems here,
+ * (what if same fh is shared between two userspace
+ * threads and they both call REQBUFS etc)
+ */
+
+ mutex_lock(&vout->update_lock);
+ vout->numbufs = rqb->count;
+ vout->bufs =
+ kmalloc(rqb->count *
+ sizeof(struct msm_v4l2_overlay_buffer),
+ GFP_KERNEL);
+
+ for (i = 0; i < rqb->count; i++) {
+ struct msm_v4l2_overlay_buffer *b =
+ (struct msm_v4l2_overlay_buffer *)vout->bufs
+ + i;
+ b->mapped = 0;
+ b->queued = 0;
+ b->offset = PAGE_ALIGN(bufsize*i);
+ b->bufsize = bufsize;
+ }
+
+ mutex_unlock(&vout->update_lock);
+
+ }
+ }
+
+ return 0;
+}
+
+static long
+msm_v4l2_overlay_vidioc_querybuf(struct file *file,
+ struct msm_v4l2_overlay_device *vout,
+ void *arg)
+{
+ struct v4l2_buffer *buf = arg;
+ struct msm_v4l2_overlay_buffer *mbuf;
+
+ if (buf == NULL || buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT
+ || buf->memory == V4L2_MEMORY_USERPTR
+ || buf->index >= vout->numbufs)
+ return -EINVAL;
+
+ mutex_lock(&vout->update_lock);
+
+ mbuf = (struct msm_v4l2_overlay_buffer *)vout->bufs + buf->index;
+ buf->flags = 0;
+ if (mbuf->mapped)
+ buf->flags |= V4L2_BUF_FLAG_MAPPED;
+ if (mbuf->queued)
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->length = mbuf->bufsize;
+ buf->m.offset = mbuf->offset;
+
+ mutex_unlock(&vout->update_lock);
+
+ return 0;
+}
+
+static long
+msm_v4l2_overlay_do_ioctl(struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct msm_v4l2_overlay_fh *fh = file->private_data;
+ struct msm_v4l2_overlay_device *vout = fh->vout;
+ int ret;
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ return msm_v4l2_overlay_vidioc_querycap(file, arg);
+
+ case VIDIOC_G_FBUF:
+ return msm_v4l2_overlay_vidioc_fbuf(file, vout, arg, true);
+
+ case VIDIOC_S_FBUF:
+ return msm_v4l2_overlay_vidioc_fbuf(file, vout, arg, false);
+
+ case VIDIOC_REQBUFS:
+ return msm_v4l2_overlay_vidioc_reqbufs(file, vout, arg);
+
+ case VIDIOC_QUERYBUF:
+ return msm_v4l2_overlay_vidioc_querybuf(file, vout, arg);
+
+ case VIDIOC_QBUF:
+ mutex_lock(&vout->update_lock);
+ ret = msm_v4l2_overlay_vidioc_qbuf(file, fh, arg, false);
+ mutex_unlock(&vout->update_lock);
+
+ return ret;
+
+ case VIDIOC_MSM_USERPTR_QBUF:
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ mutex_lock(&vout->update_lock);
+ ret = msm_v4l2_overlay_vidioc_qbuf(file, fh, arg, true);
+ mutex_unlock(&vout->update_lock);
+
+ return ret;
+
+ case VIDIOC_DQBUF:
+ mutex_lock(&vout->update_lock);
+ ret = msm_v4l2_overlay_vidioc_dqbuf(file, fh, arg);
+ mutex_unlock(&vout->update_lock);
+ break;
+
+ case VIDIOC_S_FMT: {
+ struct v4l2_format *f = arg;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ mutex_lock(&vout->update_lock);
+ memcpy(&vout->win, &f->fmt.win,
+ sizeof(struct v4l2_window));
+ mutex_unlock(&vout->update_lock);
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ mutex_lock(&vout->update_lock);
+ memcpy(&vout->pix, &f->fmt.pix,
+ sizeof(struct v4l2_pix_format));
+ mutex_unlock(&vout->update_lock);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+ case VIDIOC_G_FMT: {
+ struct v4l2_format *f = arg;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT: {
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ memset(pix, 0, sizeof(*pix));
+ *pix = vout->pix;
+ break;
+ }
+
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY: {
+ struct v4l2_window *win = &f->fmt.win;
+ memset(win, 0, sizeof(*win));
+ win->w = vout->win.w;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case VIDIOC_CROPCAP: {
+ struct v4l2_cropcap *cr = arg;
+ if (cr->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ cr->bounds.left = 0;
+ cr->bounds.top = 0;
+ cr->bounds.width = vout->crop_rect.width;
+ cr->bounds.height = vout->crop_rect.height;
+
+ cr->defrect.left = 0;
+ cr->defrect.top = 0;
+ cr->defrect.width = vout->crop_rect.width;
+ cr->defrect.height = vout->crop_rect.height;
+
+ cr->pixelaspect.numerator = 1;
+ cr->pixelaspect.denominator = 1;
+ break;
+ }
+
+ case VIDIOC_S_CROP: {
+ struct v4l2_crop *crop = arg;
+
+ switch (crop->type) {
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+
+ mutex_lock(&vout->update_lock);
+ memcpy(&vout->crop_rect, &crop->c,
+ sizeof(struct v4l2_rect));
+ mutex_unlock(&vout->update_lock);
+
+ break;
+
+ default:
+
+ return -EINVAL;
+ }
+ break;
+ }
+ case VIDIOC_G_CROP: {
+ struct v4l2_crop *crop = arg;
+
+ switch (crop->type) {
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ memcpy(&crop->c, &vout->crop_rect,
+ sizeof(struct v4l2_rect));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case VIDIOC_S_CTRL: {
+ struct v4l2_control *ctrl = arg;
+ int32_t rotflag;
+
+ switch (ctrl->id) {
+
+ case V4L2_CID_ROTATE:
+ switch (ctrl->value) {
+ case 0:
+ rotflag = MDP_ROT_NOP;
+ break;
+ case 90:
+ rotflag = MDP_ROT_90;
+ break;
+ case 180:
+ rotflag = MDP_ROT_180;
+ break;
+ case 270:
+ rotflag = MDP_ROT_270;
+ break;
+ default:
+ pr_err("%s: V4L2_CID_ROTATE invalid rotation value %d.\n",
+ __func__, ctrl->value);
+ return -ERANGE;
+ }
+
+ mutex_lock(&vout->update_lock);
+ /* Clear the rotation flags */
+ vout->req.flags &= ~MDP_ROT_NOP;
+ vout->req.flags &= ~MDP_ROT_90;
+ vout->req.flags &= ~MDP_ROT_180;
+ vout->req.flags &= ~MDP_ROT_270;
+ /* Set the new rotation flag */
+ vout->req.flags |= rotflag;
+ mutex_unlock(&vout->update_lock);
+
+ break;
+
+ case V4L2_CID_HFLIP:
+ mutex_lock(&vout->update_lock);
+ /* Clear the flip flag */
+ vout->req.flags &= ~MDP_FLIP_LR;
+ if (true == ctrl->value)
+ vout->req.flags |= MDP_FLIP_LR;
+ mutex_unlock(&vout->update_lock);
+
+ break;
+
+ case V4L2_CID_VFLIP:
+ mutex_lock(&vout->update_lock);
+ /* Clear the flip flag */
+ vout->req.flags &= ~MDP_FLIP_UD;
+ if (true == ctrl->value)
+ vout->req.flags |= MDP_FLIP_UD;
+ mutex_unlock(&vout->update_lock);
+
+ break;
+
+ default:
+ pr_err("%s: VIDIOC_S_CTRL invalid control ID %d.\n",
+ __func__, ctrl->id);
+ return -EINVAL;
+ }
+ break;
+ }
+ case VIDIOC_G_CTRL: {
+ struct v4l2_control *ctrl = arg;
+ __s32 rotation;
+
+ switch (ctrl->id) {
+
+ case V4L2_CID_ROTATE:
+ if (MDP_ROT_NOP == (vout->req.flags & MDP_ROT_NOP))
+ rotation = 0;
+ if (MDP_ROT_90 == (vout->req.flags & MDP_ROT_90))
+ rotation = 90;
+ if (MDP_ROT_180 == (vout->req.flags & MDP_ROT_180))
+ rotation = 180;
+ if (MDP_ROT_270 == (vout->req.flags & MDP_ROT_270))
+ rotation = 270;
+
+ ctrl->value = rotation;
+ break;
+
+ case V4L2_CID_HFLIP:
+ if (MDP_FLIP_LR == (vout->req.flags & MDP_FLIP_LR))
+ ctrl->value = true;
+ break;
+
+ case V4L2_CID_VFLIP:
+ if (MDP_FLIP_UD == (vout->req.flags & MDP_FLIP_UD))
+ ctrl->value = true;
+ break;
+
+ default:
+ pr_err("%s: VIDIOC_G_CTRL invalid control ID %d.\n",
+ __func__, ctrl->id);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case VIDIOC_STREAMON: {
+
+ if (vout->streaming) {
+ pr_err("%s: VIDIOC_STREAMON: already streaming.\n",
+ __func__);
+ return -EBUSY;
+ }
+
+ mutex_lock(&vout->update_lock);
+ msm_v4l2_overlay_startstreaming(vout);
+ mutex_unlock(&vout->update_lock);
+
+ break;
+ }
+
+ case VIDIOC_STREAMOFF: {
+
+ if (!vout->streaming) {
+ pr_err("%s: VIDIOC_STREAMOFF: not currently streaming.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&vout->update_lock);
+ msm_v4l2_overlay_stopstreaming(vout);
+ mutex_unlock(&vout->update_lock);
+
+ break;
+ }
+
+ default:
+ return -ENOIOCTLCMD;
+
+ } /* switch */
+
+ return 0;
+}
+
+static long
+msm_v4l2_overlay_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_v4l2_overlay_do_ioctl);
+}
+
+static int
+msm_v4l2_overlay_mmap(struct file *filp, struct vm_area_struct * vma)
+{
+ unsigned long start = (unsigned long)v4l2_ram_phys;
+
+ /*
+ * vm_pgoff is the offset (>>PAGE_SHIFT) that we provided
+ * during REQBUFS. off therefore should equal the offset we
+ * provided in REQBUFS, since last (PAGE_SHIFT) bits of off
+ * should be 0
+ */
+ unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+ u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + v4l2_ram_size);
+
+ /*
+ * This is probably unnecessary now - the last PAGE_SHIFT
+ * bits of start should be 0 now, since we are page aligning
+ * v4l2_ram_phys
+ */
+ start &= PAGE_MASK;
+
+ pr_debug("v4l2 map req for phys(%p,%p) offset %u to virt (%p,%p)\n",
+ (void *)(start+off), (void *)(start+off+(vma->vm_end - vma->vm_start)),
+ (unsigned int)off, (void *)vma->vm_start, (void *)vma->vm_end);
+
+ if ((vma->vm_end - vma->vm_start + off) > len) {
+ pr_err("v4l2 map request, memory requested too big\n");
+ return -EINVAL;
+ }
+
+ start += off;
+ vma->vm_pgoff = start >> PAGE_SHIFT;
+ /* This is an IO map - tell maydump to skip this VMA */
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Remap the frame buffer I/O range */
+ if (io_remap_pfn_range(vma, vma->vm_start, start >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int
+msm_v4l2_overlay_release(struct file *file)
+{
+ struct msm_v4l2_overlay_fh *fh = file->private_data;
+ struct msm_v4l2_overlay_device *vout = fh->vout;
+
+ if (vout->streaming)
+ msm_v4l2_overlay_stopstreaming(vout);
+
+ vout->ref_count--;
+
+ kfree(vout->bufs);
+ vout->numbufs = 0;
+ kfree(fh);
+
+ return 0;
+}
+
+static int
+msm_v4l2_overlay_open(struct file *file)
+{
+ struct msm_v4l2_overlay_device *vout = NULL;
+ struct v4l2_pix_format *pix = NULL;
+ struct msm_v4l2_overlay_fh *fh;
+
+ vout = saved_vout0;
+ vout->id = 0;
+
+ if (vout->ref_count) {
+ pr_err("%s: multiple open currently is not"
+ "supported!\n", __func__);
+ return -EBUSY;
+ }
+
+ vout->ref_count++;
+
+ /* allocate per-filehandle data */
+ fh = kmalloc(sizeof(struct msm_v4l2_overlay_fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ fh->vout = vout;
+ fh->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+ file->private_data = fh;
+
+ vout->streaming = 0;
+ vout->crop_rect.left = vout->crop_rect.top = 0;
+ vout->crop_rect.width = vout->screen_width;
+ vout->crop_rect.height = vout->screen_height;
+
+ pix = &vout->pix;
+ pix->width = vout->screen_width;
+ pix->height = vout->screen_height;
+ pix->pixelformat = V4L2_PIX_FMT_RGB32;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = pix->width * 4;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->priv = 0;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+
+ vout->win.w.left = 0;
+ vout->win.w.top = 0;
+ vout->win.w.width = vout->screen_width;
+ vout->win.w.height = vout->screen_height;
+
+ vout->fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY
+ | V4L2_FBUF_CAP_LOCAL_ALPHA;
+ vout->fb.flags = V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ vout->fb.base = 0;
+ memcpy(&vout->fb.fmt, pix, sizeof(struct v4l2_format));
+
+ vout->bufs = 0;
+ vout->numbufs = 0;
+
+ mutex_init(&vout->update_lock);
+
+ return 0;
+}
+
+
+static int __devinit
+msm_v4l2_overlay_probe(struct platform_device *pdev)
+{
+ char *v4l2_ram_phys_unaligned;
+ if ((pdev->id == 0) && (pdev->num_resources > 0)) {
+ v4l2_ram_size =
+ pdev->resource[0].end - pdev->resource[0].start + 1;
+ v4l2_ram_phys_unaligned = (char *)pdev->resource[0].start;
+ v4l2_ram_phys =
+ (char *)PAGE_ALIGN((unsigned int)v4l2_ram_phys_unaligned);
+ /*
+ * We are (fwd) page aligning the start of v4l2 memory.
+ * Therefore we have that much less physical memory available
+ */
+ v4l2_ram_size -= (unsigned int)v4l2_ram_phys
+ - (unsigned int)v4l2_ram_phys_unaligned;
+
+
+ }
+ return 0;
+}
+
+static int __devexit
+msm_v4l2_overlay_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static void msm_v4l2_overlay_videodev_release(struct video_device *vfd)
+{
+ return;
+}
+
+static const struct v4l2_file_operations msm_v4l2_overlay_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_v4l2_overlay_open,
+ .release = msm_v4l2_overlay_release,
+ .mmap = msm_v4l2_overlay_mmap,
+ .ioctl = msm_v4l2_overlay_ioctl,
+};
+
+static struct video_device msm_v4l2_overlay_vid_device0 = {
+ .name = "msm_v4l2_overlay",
+ .fops = &msm_v4l2_overlay_fops,
+ .minor = -1,
+ .release = msm_v4l2_overlay_videodev_release,
+};
+
+static struct platform_driver msm_v4l2_overlay_platform_driver = {
+ .probe = msm_v4l2_overlay_probe,
+ .remove = msm_v4l2_overlay_remove,
+ .driver = {
+ .name = "msm_v4l2_overlay_pd",
+ },
+};
+
+static int __init msm_v4l2_overlay_init(void)
+{
+ int ret;
+
+
+ saved_vout0 = kzalloc(sizeof(struct msm_v4l2_overlay_device),
+ GFP_KERNEL);
+
+ if (!saved_vout0)
+ return -ENOMEM;
+
+ ret = platform_driver_register(&msm_v4l2_overlay_platform_driver);
+ if (ret < 0)
+ goto end;
+
+ /*
+ * Register the device with videodev.
+ * Videodev will make IOCTL calls on application requests
+ */
+ ret = video_register_device(&msm_v4l2_overlay_vid_device0,
+ VFL_TYPE_GRABBER, MSM_VIDEO);
+
+ if (ret < 0) {
+ pr_err("%s: V4L2 video overlay device registration failure(%d)\n",
+ __func__, ret);
+ goto end_unregister;
+ }
+
+ mutex_init(&msmfb_lock);
+
+ return 0;
+
+end_unregister:
+ platform_driver_unregister(&msm_v4l2_overlay_platform_driver);
+
+end:
+ kfree(saved_vout0);
+ return ret;
+}
+
+static void __exit msm_v4l2_overlay_exit(void)
+{
+ video_unregister_device(&msm_v4l2_overlay_vid_device0);
+ platform_driver_unregister(&msm_v4l2_overlay_platform_driver);
+ mutex_destroy(&msmfb_lock);
+ kfree(saved_vout0);
+}
+
+module_init(msm_v4l2_overlay_init);
+module_exit(msm_v4l2_overlay_exit);
+
+MODULE_DESCRIPTION("MSM V4L2 Video Overlay Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_v4l2_video.h b/drivers/media/video/msm/msm_v4l2_video.h
new file mode 100644
index 0000000..a7baa75
--- /dev/null
+++ b/drivers/media/video/msm/msm_v4l2_video.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+#ifndef MSM_V4L2_VIDEO_H
+#define MSM_V4L2_VIDEO_H
+
+#include <linux/mm.h>
+#include <linux/msm_mdp.h>
+#include <linux/videodev2.h>
+
+
+struct msm_v4l2_overlay_buffer {
+ int mapped;
+ int queued;
+ int offset;
+ int bufsize;
+};
+
+struct msm_v4l2_overlay_device {
+ struct device dev;
+
+ int ref_count;
+ int id;
+
+ int screen_width;
+ int screen_height;
+ int streaming;
+
+ struct v4l2_pix_format pix;
+ struct v4l2_window win;
+ struct v4l2_rect crop_rect;
+ struct v4l2_framebuffer fb;
+ struct msm_v4l2_overlay_buffer *bufs;
+ int numbufs;
+ struct mdp_overlay req;
+ void *par;
+
+ struct mutex update_lock;
+};
+
+struct msm_v4l2_overlay_fh {
+ struct msm_v4l2_overlay_device *vout;
+ enum v4l2_buf_type type;
+};
+
+struct msm_v4l2_overlay_userptr_buffer {
+ uint base[3];
+ size_t length[3];
+};
+
+#endif
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
index a5165ab..be57ee0 100644
--- a/drivers/video/msm/mdp.h
+++ b/drivers/video/msm/mdp.h
@@ -794,4 +794,10 @@
}
#endif
+int mdp_ppp_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req);
+int mdp_ppp_v4l2_overlay_clear(void);
+int mdp_ppp_v4l2_overlay_play(struct fb_info *info,
+ unsigned long srcp0_addr, unsigned long srcp0_size,
+ unsigned long srcp1_addr, unsigned long srcp1_size);
+
#endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 6ab4edf..9c047e5 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -748,4 +748,11 @@
int mdp4_igc_lut_config(struct mdp_igc_lut_data *cfg);
void mdp4_iommu_unmap(struct mdp4_overlay_pipe *pipe);
void mdp4_iommu_attach(void);
+int mdp4_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req,
+ struct mdp4_overlay_pipe **ppipe);
+void mdp4_v4l2_overlay_clear(struct mdp4_overlay_pipe *pipe);
+int mdp4_v4l2_overlay_play(struct fb_info *info, struct mdp4_overlay_pipe *pipe,
+ unsigned long srcp0_addr, unsigned long srcp1_addr,
+ unsigned long srcp2_addr);
+
#endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index a4db73e..b4a9512 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -2946,3 +2946,112 @@
done = 1;
}
}
+
+int mdp4_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req,
+ struct mdp4_overlay_pipe **ppipe)
+{
+ struct mdp4_overlay_pipe *pipe;
+ int err;
+ struct msm_fb_data_type *mfb = info->par;
+
+ req->z_order = 0;
+ req->id = MSMFB_NEW_REQUEST;
+ req->is_fg = false;
+ req->alpha = 0xff;
+ err = mdp4_overlay_req2pipe(req, MDP4_MIXER0, &pipe, mfb);
+ if (err < 0) {
+ pr_err("%s:Could not allocate MDP overlay pipe\n", __func__);
+ return err;
+ }
+
+ mdp4_mixer_blend_setup(pipe);
+ *ppipe = pipe;
+
+ return 0;
+}
+
+void mdp4_v4l2_overlay_clear(struct mdp4_overlay_pipe *pipe)
+{
+ mdp4_mixer_stage_down(pipe);
+ mdp4_overlay_pipe_free(pipe);
+}
+
+int mdp4_v4l2_overlay_play(struct fb_info *info, struct mdp4_overlay_pipe *pipe,
+ unsigned long srcp0_addr, unsigned long srcp1_addr,
+ unsigned long srcp2_addr)
+{
+ struct msm_fb_data_type *mfd = info->par;
+ int err;
+
+ if (mutex_lock_interruptible(&mfd->dma->ov_mutex))
+ return -EINTR;
+
+ switch (pipe->src_format) {
+ case MDP_Y_CR_CB_H2V2:
+ /* YUV420 */
+ pipe->srcp0_addr = srcp0_addr;
+ pipe->srcp0_ystride = pipe->src_width;
+ /*
+ * For YUV420, the luma plane is 1 byte per pixel times
+ * num of pixels in the image Also, the planes are
+ * switched in MDP, srcp2 is actually first chroma plane
+ */
+ pipe->srcp2_addr = srcp1_addr ? srcp1_addr :
+ pipe->srcp0_addr + (pipe->src_width * pipe->src_height);
+ pipe->srcp2_ystride = pipe->src_width/2;
+ /*
+ * The chroma planes are half the size of the luma
+ * planes
+ */
+ pipe->srcp1_addr = srcp2_addr ? srcp2_addr :
+ pipe->srcp2_addr +
+ (pipe->src_width * pipe->src_height / 4);
+ pipe->srcp1_ystride = pipe->src_width/2;
+ break;
+ case MDP_Y_CRCB_H2V2:
+ /* NV12 */
+ pipe->srcp0_addr = srcp0_addr;
+ pipe->srcp0_ystride = pipe->src_width;
+ pipe->srcp1_addr = srcp1_addr ? srcp1_addr :
+ pipe->srcp0_addr +
+ (pipe->src_width * pipe->src_height);
+ pipe->srcp1_ystride = pipe->src_width;
+ break;
+ default:
+ pr_err("%s: format (%u) is not supported\n", __func__,
+ pipe->src_format);
+ err = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: pipe ndx=%d stage=%d format=%x\n", __func__,
+ pipe->pipe_ndx, pipe->mixer_stage, pipe->src_format);
+
+ if (pipe->pipe_type == OVERLAY_TYPE_VIDEO)
+ mdp4_overlay_vg_setup(pipe);
+ else
+ mdp4_overlay_rgb_setup(pipe);
+
+ mdp4_mixer_stage_up(pipe);
+
+ if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
+ mdp4_overlay_reg_flush(pipe, 1);
+ mdp4_overlay_lcdc_vsync_push(mfd, pipe);
+ } else {
+#ifdef CONFIG_FB_MSM_MIPI_DSI
+ if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
+ mdp4_dsi_cmd_dma_busy_wait(mfd);
+ mdp4_dsi_cmd_kickoff_video(mfd, pipe);
+ }
+#else
+ if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
+ mdp4_mddi_dma_busy_wait(mfd);
+ mdp4_mddi_kickoff_video(mfd, pipe);
+ }
+#endif
+ }
+done:
+ mutex_unlock(&mfd->dma->ov_mutex);
+ return err;
+}
+
diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c
index a6f03ba..4009ffc 100644
--- a/drivers/video/msm/mdp_ppp.c
+++ b/drivers/video/msm/mdp_ppp.c
@@ -1314,39 +1314,21 @@
}
-int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req)
+static int mdp_ppp_blit_addr(struct fb_info *info, struct mdp_blit_req *req,
+ unsigned long srcp0_start, unsigned long srcp0_len,
+ unsigned long srcp1_start, unsigned long srcp1_len,
+ unsigned long dst_start, unsigned long dst_len,
+ struct file *p_src_file, struct file *p_dst_file)
{
- unsigned long src_start, dst_start;
- unsigned long src_len = 0;
- unsigned long dst_len = 0;
MDPIBUF iBuf;
u32 dst_width, dst_height;
- struct file *p_src_file = 0 , *p_dst_file = 0;
- struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+ struct msm_fb_data_type *mfd = info->par;
if (req->dst.format == MDP_FB_FORMAT)
req->dst.format = mfd->fb_imgType;
if (req->src.format == MDP_FB_FORMAT)
req->src.format = mfd->fb_imgType;
- if (req->flags & MDP_BLIT_SRC_GEM)
- get_gem_img(&req->src, &src_start, &src_len);
- else
- get_img(&req->src, info, &src_start, &src_len, &p_src_file);
- if (src_len == 0) {
- printk(KERN_ERR "mdp_ppp: could not retrieve image from "
- "memory\n");
- return -1;
- }
- if (req->flags & MDP_BLIT_DST_GEM)
- get_gem_img(&req->dst, &dst_start, &dst_len);
- else
- get_img(&req->dst, info, &dst_start, &dst_len, &p_dst_file);
- if (dst_len == 0) {
- put_img(p_src_file);
- printk(KERN_ERR "mdp_ppp: could not retrieve image from "
- "memory\n");
- return -1;
- }
+
if (mdp_ppp_verify_req(req)) {
printk(KERN_ERR "mdp_ppp: invalid image!\n");
put_img(p_src_file);
@@ -1375,17 +1357,17 @@
iBuf.mdpImg.width = req->src.width;
iBuf.mdpImg.imgType = req->src.format;
- iBuf.mdpImg.bmy_addr = (uint32 *) (src_start + req->src.offset);
+ iBuf.mdpImg.bmy_addr = (uint32 *) (srcp0_start + req->src.offset);
if (iBuf.mdpImg.imgType == MDP_Y_CBCR_H2V2_ADRENO)
iBuf.mdpImg.cbcr_addr =
(uint32 *) ((uint32) iBuf.mdpImg.bmy_addr +
ALIGN((ALIGN(req->src.width, 32) *
ALIGN(req->src.height, 32)), 4096));
else
- iBuf.mdpImg.cbcr_addr =
+ iBuf.mdpImg.cbcr_addr = srcp1_start ? (uint32 *)srcp1_start :
(uint32 *) ((uint32) iBuf.mdpImg.bmy_addr +
- req->src.width * req->src.height);
+ req->src.width * req->src.height);
iBuf.mdpImg.mdpOp = MDPOP_NOP;
@@ -1589,3 +1571,116 @@
put_img(p_dst_file);
return 0;
}
+
+int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req)
+{
+ unsigned long src_start, dst_start;
+ unsigned long src_len = 0;
+ unsigned long dst_len = 0;
+ struct file *p_src_file = 0 , *p_dst_file = 0;
+
+ if (req->flags & MDP_BLIT_SRC_GEM)
+ get_gem_img(&req->src, &src_start, &src_len);
+ else
+ get_img(&req->src, info, &src_start, &src_len, &p_src_file);
+ if (src_len == 0) {
+ printk(KERN_ERR "mdp_ppp: could not retrieve image from "
+ "memory\n");
+ return -EINVAL;
+ }
+ if (req->flags & MDP_BLIT_DST_GEM)
+ get_gem_img(&req->dst, &dst_start, &dst_len);
+ else
+ get_img(&req->dst, info, &dst_start, &dst_len, &p_dst_file);
+ if (dst_len == 0) {
+ put_img(p_src_file);
+ printk(KERN_ERR "mdp_ppp: could not retrieve image from "
+ "memory\n");
+ return -EINVAL;
+ }
+
+ return mdp_ppp_blit_addr(info, req, src_start, src_len, 0, 0, dst_start,
+ dst_len, p_src_file, p_dst_file);
+}
+
+static struct mdp_blit_req overlay_req;
+static bool mdp_overlay_req_set;
+
+int mdp_ppp_v4l2_overlay_set(struct fb_info *info, struct mdp_overlay *req)
+{
+ memset(&overlay_req, 0, sizeof(struct mdp_blit_req));
+
+ overlay_req.src.width = req->src.width;
+ overlay_req.src.height = req->src.height;
+ overlay_req.src.format = req->src.format;
+
+
+ overlay_req.dst.width = req->dst_rect.w;
+ overlay_req.dst.height = req->dst_rect.h;
+ overlay_req.dst.format = MDP_FB_FORMAT;
+ overlay_req.transp_mask = req->transp_mask;
+ overlay_req.flags = req->flags;
+ overlay_req.alpha = req->alpha;
+
+ overlay_req.src_rect.x = req->src_rect.x;
+ overlay_req.src_rect.y = req->src_rect.y;
+ overlay_req.src_rect.w = req->src_rect.w;
+ overlay_req.src_rect.h = req->src_rect.h;
+ overlay_req.dst_rect.x = req->dst_rect.x;
+ overlay_req.dst_rect.y = req->dst_rect.y;
+ overlay_req.dst_rect.w = req->dst_rect.w;
+ overlay_req.dst_rect.h = req->dst_rect.h;
+ mdp_overlay_req_set = true;
+
+ pr_debug("%s: Overlay parameters:", __func__);
+ pr_debug("Src_Image (%u %u)\n", overlay_req.src.width,
+ overlay_req.src.height);
+
+ if (overlay_req.src.format == MDP_Y_CRCB_H2V2)
+ pr_debug("Overlay format MDP_Y_CRCB_H2V2\n");
+ else if (overlay_req.src.format == MDP_RGB_565)
+ pr_debug("Overlay format MDP_RGB_565\n");
+ else
+ pr_debug("Overlay format(%u) unknown\n",
+ overlay_req.src.format);
+
+ pr_debug("Dst_Image (%u %u)\n", overlay_req.dst.width,
+ overlay_req.dst.height);
+ pr_debug("Src rect: (%u,%u,%u,%u), Dst rect: (%u,%u,%u,%u)\n",
+ overlay_req.src_rect.x, overlay_req.src_rect.y,
+ overlay_req.src_rect.w, overlay_req.src_rect.h,
+ overlay_req.dst_rect.x, overlay_req.dst_rect.y,
+ overlay_req.dst_rect.w, overlay_req.dst_rect.h);
+ return 0;
+}
+
+int mdp_ppp_v4l2_overlay_clear(void)
+{
+ memset(&overlay_req, 0, sizeof(struct mdp_overlay));
+ mdp_overlay_req_set = false;
+ return 0;
+}
+
+int mdp_ppp_v4l2_overlay_play(struct fb_info *info,
+ unsigned long srcp0_addr, unsigned long srcp0_size,
+ unsigned long srcp1_addr, unsigned long srcp1_size)
+{
+ int ret;
+
+ if (!mdp_overlay_req_set) {
+ pr_err("mdp_ppp:v4l2:No overlay set, ignore play req\n");
+ return -EINVAL;
+ }
+
+ overlay_req.dst.width = info->var.xres;
+ overlay_req.dst.height = info->var.yres;
+
+ ret = mdp_ppp_blit_addr(info, &overlay_req,
+ srcp0_addr, srcp0_size, srcp1_addr, srcp1_size,
+ info->fix.smem_start, info->fix.smem_len, NULL, NULL);
+
+ if (ret)
+ pr_err("%s:Blitting overlay failed(%d)\n", __func__, ret);
+
+ return ret;
+}
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 6ec5d70..74f7ead 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -3603,4 +3603,58 @@
return 0;
}
+/* Called by v4l2 driver to enable/disable overlay pipe */
+int msm_fb_v4l2_enable(struct mdp_overlay *req, bool enable, void **par)
+{
+ int err = 0;
+#ifdef CONFIG_FB_MSM_MDP40
+ struct mdp4_overlay_pipe *pipe;
+ if (enable) {
+
+ err = mdp4_v4l2_overlay_set(fbi_list[0], req, &pipe);
+
+ *(struct mdp4_overlay_pipe **)par = pipe;
+
+ } else {
+ pipe = *(struct mdp4_overlay_pipe **)par;
+ mdp4_v4l2_overlay_clear(pipe);
+ }
+#else
+#ifdef CONFIG_FB_MSM_MDP30
+ if (enable)
+ err = mdp_ppp_v4l2_overlay_set(fbi_list[0], req);
+ else
+ err = mdp_ppp_v4l2_overlay_clear();
+#else
+ err = -EINVAL;
+#endif
+#endif
+
+ return err;
+}
+EXPORT_SYMBOL(msm_fb_v4l2_enable);
+
+/* Called by v4l2 driver to provide a frame for display */
+int msm_fb_v4l2_update(void *par,
+ unsigned long srcp0_addr, unsigned long srcp0_size,
+ unsigned long srcp1_addr, unsigned long srcp1_size,
+ unsigned long srcp2_addr, unsigned long srcp2_size)
+{
+#ifdef CONFIG_FB_MSM_MDP40
+ struct mdp4_overlay_pipe *pipe = (struct mdp4_overlay_pipe *)par;
+ return mdp4_v4l2_overlay_play(fbi_list[0], pipe,
+ srcp0_addr, srcp1_addr,
+ srcp2_addr);
+#else
+#ifdef CONFIG_FB_MSM_MDP30
+ return mdp_ppp_v4l2_overlay_play(fbi_list[0],
+ srcp0_addr, srcp0_size,
+ srcp1_addr, srcp1_size);
+#else
+ return -EINVAL;
+#endif
+#endif
+}
+EXPORT_SYMBOL(msm_fb_v4l2_update);
+
module_init(msm_fb_init);
diff --git a/include/media/Kbuild b/include/media/Kbuild
index f4a8a86..a60b86c 100644
--- a/include/media/Kbuild
+++ b/include/media/Kbuild
@@ -3,3 +3,4 @@
header-y += msm_camera.h
header-y += msm_isp.h
header-y += msm_gemini.h
+header-y += msm_v4l2_overlay.h
diff --git a/include/media/msm_v4l2_overlay.h b/include/media/msm_v4l2_overlay.h
new file mode 100644
index 0000000..c83cfb7
--- /dev/null
+++ b/include/media/msm_v4l2_overlay.h
@@ -0,0 +1,9 @@
+#ifndef LINUX_MSM_V4L2_OVERLAY
+#define LINUX_MSM_V4L2_OVERLAY
+
+#include <linux/videodev2.h>
+
+#define VIDIOC_MSM_USERPTR_QBUF \
+_IOWR('V', BASE_VIDIOC_PRIVATE, struct v4l2_buffer)
+
+#endif