From c55856c548e0cec8b29e415a08a353a0ab1ac4c8 Mon Sep 17 00:00:00 2001 From: Alhad Purnapatre Date: Tue, 28 Feb 2012 13:24:57 -0800 Subject: [PATCH] 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 Change-Id: Iab69d0a5acfe993d13cb7a585e292b9a87eb90ee --- arch/arm/mach-msm/board-msm7627a-display.c | 38 + arch/arm/mach-msm/board-msm7x30.c | 36 + arch/arm/mach-msm/include/mach/msm_fb.h | 6 + drivers/media/video/msm/Kconfig | 8 + drivers/media/video/msm/Makefile | 1 + drivers/media/video/msm/msm_v4l2_video.c | 947 +++++++++++++++++++++ drivers/media/video/msm/msm_v4l2_video.h | 59 ++ drivers/video/msm/mdp.h | 6 + drivers/video/msm/mdp4.h | 7 + drivers/video/msm/mdp4_overlay.c | 109 +++ drivers/video/msm/mdp_ppp.c | 151 +++- drivers/video/msm/msm_fb.c | 54 ++ include/media/Kbuild | 1 + include/media/msm_v4l2_overlay.h | 9 + 14 files changed, 1404 insertions(+), 28 deletions(-) create mode 100644 drivers/media/video/msm/msm_v4l2_video.c create mode 100644 drivers/media/video/msm/msm_v4l2_video.h create mode 100644 include/media/msm_v4l2_overlay.h diff --git a/arch/arm/mach-msm/board-msm7627a-display.c b/arch/arm/mach-msm/board-msm7627a-display.c index 86343f5fb20..96d798e1803 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 @@ 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 = { diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index cd1e5b87ff4..972cb22a7fe 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 @@ 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) diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index 339fa463a45..3bbaa25da47 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 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 diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig index a93031e6bb5..ec034b11b2b 100644 --- a/drivers/media/video/msm/Kconfig +++ b/drivers/media/video/msm/Kconfig @@ -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 diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile index 2843d30d60e..f817561a708 100644 --- a/drivers/media/video/msm/Makefile +++ b/drivers/media/video/msm/Makefile @@ -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 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 00000000000..c4e6c621e7b --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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 00000000000..a7baa756685 --- /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 +#include +#include + + +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 a5165ab6265..be57ee0f9a3 100644 --- a/drivers/video/msm/mdp.h +++ b/drivers/video/msm/mdp.h @@ -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 */ diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h index 6ab4edf0a63..9c047e5e14b 100644 --- a/drivers/video/msm/mdp4.h +++ b/drivers/video/msm/mdp4.h @@ -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 */ diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c index a4db73e4c7f..b4a9512aa7e 100644 --- a/drivers/video/msm/mdp4_overlay.c +++ b/drivers/video/msm/mdp4_overlay.c @@ -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; +} + diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index a6f03badbd4..4009ffc7844 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -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; +} diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 6ec5d70aa25..74f7ead5205 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -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); diff --git a/include/media/Kbuild b/include/media/Kbuild index f4a8a86e2ca..a60b86c117f 100644 --- a/include/media/Kbuild +++ b/include/media/Kbuild @@ -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 diff --git a/include/media/msm_v4l2_overlay.h b/include/media/msm_v4l2_overlay.h new file mode 100644 index 00000000000..c83cfb7b191 --- /dev/null +++ b/include/media/msm_v4l2_overlay.h @@ -0,0 +1,9 @@ +#ifndef LINUX_MSM_V4L2_OVERLAY +#define LINUX_MSM_V4L2_OVERLAY + +#include + +#define VIDIOC_MSM_USERPTR_QBUF \ +_IOWR('V', BASE_VIDIOC_PRIVATE, struct v4l2_buffer) + +#endif