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
This commit is contained in:
Alhad Purnapatre
2012-04-07 17:06:38 -07:00
parent ee2c76e889
commit c55856c548
14 changed files with 1404 additions and 28 deletions
@@ -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 @@ static struct resource msm_fb_resources[] = {
}
};
#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 @@ static struct platform_device msm_fb_device = {
}
};
#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 @@ static struct platform_device *msm_fb_devices[] __initdata = {
#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 @@ void __init msm_msm7627a_allocate_memory_regions(void)
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 = {
+36
View File
@@ -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 @@ static struct resource msm_fb_resources[] = {
}
};
#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 @@ static struct platform_device msm_fb_device = {
}
};
#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 @@ static struct platform_device *devices[] __initdata = {
#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 @@ static void __init msm7x30_allocate_memory_regions(void)
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)
+6
View File
@@ -188,5 +188,11 @@ struct msm_mddi_bridge_platform_data {
};
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
+8
View File
@@ -251,3 +251,11 @@ config IMX091
---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
+1
View File
@@ -52,3 +52,4 @@ obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o
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
+947
View File
@@ -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");
+59
View File
@@ -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
+6
View File
@@ -794,4 +794,10 @@ static inline void mdp_dsi_cmd_overlay_suspend(void)
}
#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 */
+7
View File
@@ -748,4 +748,11 @@ void mdp4_free_writeback_buf(struct msm_fb_data_type *mfd, u32 mix_num);
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 */
+109
View File
@@ -2946,3 +2946,112 @@ void mdp4_iommu_attach(void)
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;
}
+123 -28
View File
@@ -1314,39 +1314,21 @@ void put_img(struct file *p_src_file)
}
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 @@ int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req)
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 @@ int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req)
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;
}
+54
View File
@@ -3603,4 +3603,58 @@ int __init msm_fb_init(void)
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);
+1
View File
@@ -3,3 +3,4 @@ header-y += tavarua.h
header-y += msm_camera.h
header-y += msm_isp.h
header-y += msm_gemini.h
header-y += msm_v4l2_overlay.h
+9
View File
@@ -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