forked from rubenslte/android_kernel_samsung_msm8226
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:
@@ -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 = {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user