diff options
author | Ricardo Salveti de Araujo <ricardo.salveti@linaro.org> | 2012-03-25 18:58:25 -0300 |
---|---|---|
committer | Ricardo Salveti de Araujo <ricardo.salveti@linaro.org> | 2012-03-25 18:58:25 -0300 |
commit | a5f51dee3f8480b7bfb309a9cd6a05c87d013858 (patch) | |
tree | c5b886b6c1d44b2dca15ba4e92a59f7294886a93 /sgx/services4/v4l2-gfx |
Imported Upstream version 1.7.10.0.1.21 (ARMHF)upstream/1.7.10.0.1.21
Signed-off-by: Ricardo Salveti de Araujo <ricardo.salveti@linaro.org>
Diffstat (limited to 'sgx/services4/v4l2-gfx')
-rwxr-xr-x | sgx/services4/v4l2-gfx/Kbuild.mk | 7 | ||||
-rwxr-xr-x | sgx/services4/v4l2-gfx/Linux.mk | 5 | ||||
-rw-r--r-- | sgx/services4/v4l2-gfx/gfx_bc.c | 495 | ||||
-rw-r--r-- | sgx/services4/v4l2-gfx/gfx_bc.h | 76 | ||||
-rw-r--r-- | sgx/services4/v4l2-gfx/gfx_init.c | 305 | ||||
-rw-r--r-- | sgx/services4/v4l2-gfx/gfx_io.c | 1331 | ||||
-rwxr-xr-x | sgx/services4/v4l2-gfx/gfx_tiler.c | 152 | ||||
-rw-r--r-- | sgx/services4/v4l2-gfx/v4gfx.h | 181 |
8 files changed, 2552 insertions, 0 deletions
diff --git a/sgx/services4/v4l2-gfx/Kbuild.mk b/sgx/services4/v4l2-gfx/Kbuild.mk new file mode 100755 index 0000000..1f4047e --- /dev/null +++ b/sgx/services4/v4l2-gfx/Kbuild.mk @@ -0,0 +1,7 @@ +ccflags-y += -I$(TOP)/services4/v4l2-gfx + +gfx_vout_mod-y += \ + services4/v4l2-gfx/gfx_init.o \ + services4/v4l2-gfx/gfx_io.o \ + services4/v4l2-gfx/gfx_bc.o \ + services4/v4l2-gfx/gfx_tiler.o diff --git a/sgx/services4/v4l2-gfx/Linux.mk b/sgx/services4/v4l2-gfx/Linux.mk new file mode 100755 index 0000000..d75468e --- /dev/null +++ b/sgx/services4/v4l2-gfx/Linux.mk @@ -0,0 +1,5 @@ +modules := v4l2-gfx + +v4l2-gfx_type := kernel_module +v4l2-gfx_target := gfx_vout_mod.ko +v4l2-gfx_makefile := $(THIS_DIR)/Kbuild.mk diff --git a/sgx/services4/v4l2-gfx/gfx_bc.c b/sgx/services4/v4l2-gfx/gfx_bc.c new file mode 100644 index 0000000..3bc1d6f --- /dev/null +++ b/sgx/services4/v4l2-gfx/gfx_bc.c @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#ifndef LINUX +#define LINUX /* Needed by IMG headers */ +#endif +#include "pvrmodule.h" +#include "img_defs.h" +#include "servicesext.h" +#include "kernelbuffer.h" +#include "gfx_bc.h" +#include "v4gfx.h" + +#define BCLOGNM "v4l2-gfx bc: " + +#define BCERR(fmt, arg...) printk(KERN_ERR BCLOGNM fmt, ## arg) + +#define BCLOG(fmt, arg...) \ +do { \ + if (debug >= 1) \ + printk(KERN_INFO BCLOGNM fmt, ## arg); \ +} while (0) + + +struct bc_buffer { + u32 size; + unsigned long *paddrp; /* physical addr. array */ + PVRSRV_SYNC_DATA *pvr_sync_data; +}; + +struct gfx_bc_devinfo { + struct bc_buffer bc_buf[VIDEO_MAX_FRAME]; + int ref; + int num_bufs; + int ref_cnt; + + /* PVR data types */ + IMG_UINT32 pvr_id; + BUFFER_INFO pvr_bcinfo; + PVRSRV_BC_SRV2BUFFER_KMJTABLE pvr_s2b_jt; +}; + +static struct gfx_bc_devinfo *g_devices[DEVICE_COUNT]; +static PVRSRV_BC_BUFFER2SRV_KMJTABLE pvr_b2s_jt; /* Jump table from driver to SGX */ +static int bc_initialized = -1; + +/* + * Service to Buffer Device API - this section covers the entry points from + * the SGX kernel services to this driver + */ +static PVRSRV_ERROR s2b_open_bc_device(IMG_UINT32 ui32DeviceID, + IMG_HANDLE *hdevicep) +{ + struct gfx_bc_devinfo *devinfo; + int idx; + + /* Search for device in g_devices and return it */ + for (idx = 0; idx < DEVICE_COUNT; idx++) { + devinfo = g_devices[idx]; + if (devinfo->pvr_id == ui32DeviceID) { + *hdevicep = (IMG_HANDLE)devinfo; + return PVRSRV_OK; + } + } + + /* Didn't find the device id in g_devices */ + BCERR("Open of bc device %d failed, not found in g_devices\n", + ui32DeviceID); + return -EINVAL; +} + +static PVRSRV_ERROR s2b_close_bc_device(IMG_UINT32 ui32DeviceID, + IMG_HANDLE hdevice) +{ + PVR_UNREFERENCED_PARAMETER(hdevice); + return PVRSRV_OK; +} + +static PVRSRV_ERROR s2b_get_bc_buffer(IMG_HANDLE hdevice, + IMG_UINT32 bufno, + PVRSRV_SYNC_DATA *pvr_sync_data, + IMG_HANDLE *hbufferp) +{ + struct gfx_bc_devinfo *devinfo; + BCLOG("+%s\n", __func__); + + if (!hdevice || !hbufferp) + return PVRSRV_ERROR_INVALID_PARAMS; + + devinfo = (struct gfx_bc_devinfo *) hdevice; + + if (bufno < devinfo->pvr_bcinfo.ui32BufferCount) { + devinfo->bc_buf[bufno].pvr_sync_data = pvr_sync_data; + *hbufferp = (IMG_HANDLE) &devinfo->bc_buf[bufno]; + + } else { + return PVRSRV_ERROR_INVALID_PARAMS; + } + + return PVRSRV_OK; +} + +static PVRSRV_ERROR s2b_get_bc_info(IMG_HANDLE hdevice, BUFFER_INFO *bcinfop) +{ + struct gfx_bc_devinfo *devinfo = NULL; + int rv = 0; + + if (!hdevice || !bcinfop) { + rv = PVRSRV_ERROR_INVALID_PARAMS; + } else { + devinfo = (struct gfx_bc_devinfo *) hdevice; + *bcinfop = devinfo->pvr_bcinfo; + + BCLOG("ui32BufferCount =%d", + (int)devinfo->pvr_bcinfo.ui32BufferCount); + BCLOG("pixelformat =%d", + (int)devinfo->pvr_bcinfo.pixelformat); + BCLOG("ui32Width =%d", + (int)devinfo->pvr_bcinfo.ui32Width); + BCLOG("ui32Height =%d", + (int)devinfo->pvr_bcinfo.ui32Height); + BCLOG("ui32ByteStride =%d", + (int)devinfo->pvr_bcinfo.ui32ByteStride); + BCLOG("ui32BufferDeviceID =%d", + (int)devinfo->pvr_bcinfo.ui32BufferDeviceID); + BCLOG("ui32Flags = %d", + (int)devinfo->pvr_bcinfo.ui32Flags); + + } + BCLOG("-%s %d (0x%x)\n", __func__, rv, (int)devinfo); + return PVRSRV_OK; +} + +static PVRSRV_ERROR s2b_get_buffer_addr(IMG_HANDLE hdevice, + IMG_HANDLE hbuffer, + IMG_SYS_PHYADDR **sysaddrpp, + IMG_UINT32 *sizebytesp, + IMG_VOID **cpuvaddrpp, + IMG_HANDLE *osmapinfop, + IMG_BOOL *iscontiguousp, + IMG_UINT32 *pui32TilingStride) +{ + struct bc_buffer *bc_buf; + PVRSRV_ERROR rv = PVRSRV_OK; + BCLOG("+%s\n", __func__); + + if (!hdevice || !hbuffer || !sysaddrpp || !sizebytesp) + return PVRSRV_ERROR_INVALID_PARAMS; + + bc_buf = (struct bc_buffer *)hbuffer; + *cpuvaddrpp = NULL; + *sizebytesp = bc_buf->size; + + if (bc_buf->paddrp) { + *iscontiguousp = IMG_FALSE; + *sysaddrpp = (IMG_SYS_PHYADDR *)bc_buf->paddrp; + *osmapinfop = IMG_NULL; + *pui32TilingStride = 0; + + BCLOG("+%s paddrp[0] 0x%x, vaddr = 0x%x, sizebytes = %d", + __func__, (int)bc_buf->paddrp[0], + (int)*cpuvaddrpp, (int)*sizebytesp); + + } else { + rv = PVRSRV_ERROR_NOT_SUPPORTED; + } + return rv; +} + +/* + * Rest of the functions + */ +static PVRSRV_PIXEL_FORMAT v4l2_to_pvr_pixfmt(u32 v4l2pixelfmt) +{ + PVRSRV_PIXEL_FORMAT pvr_fmt; + + switch (v4l2pixelfmt) { + case V4L2_PIX_FMT_RGB565: + pvr_fmt = PVRSRV_PIXEL_FORMAT_RGB565; + break; + case V4L2_PIX_FMT_RGB32: + pvr_fmt = PVRSRV_PIXEL_FORMAT_RGB888; + break; + case V4L2_PIX_FMT_YUYV: + pvr_fmt = PVRSRV_PIXEL_FORMAT_FOURCC_ORG_YUYV; + break; + case V4L2_PIX_FMT_UYVY: + pvr_fmt = PVRSRV_PIXEL_FORMAT_FOURCC_ORG_UYVY; + break; + case V4L2_PIX_FMT_NV12: + pvr_fmt = PVRSRV_PIXEL_FORMAT_NV12; + break; + default: + pvr_fmt = PVRSRV_PIXEL_FORMAT_UNKNOWN; + } + return pvr_fmt; +} + +static int gfx_bc_release_device_resources(int idx) +{ + struct gfx_bc_devinfo *devinfo; + + devinfo = g_devices[idx]; + if (devinfo == NULL) + return -ENOENT; + + if (!devinfo->num_bufs) + return 0; + + devinfo->num_bufs = 0; + devinfo->pvr_bcinfo.pixelformat = PVRSRV_PIXEL_FORMAT_UNKNOWN; + devinfo->pvr_bcinfo.ui32Width = 0; + devinfo->pvr_bcinfo.ui32Height = 0; + devinfo->pvr_bcinfo.ui32ByteStride = 0; + devinfo->pvr_bcinfo.ui32BufferDeviceID = -1; + devinfo->pvr_bcinfo.ui32Flags = 0; + devinfo->pvr_bcinfo.ui32BufferCount = 0; + + return 0; +} + +static int gfx_bc_register(int idx) +{ + struct gfx_bc_devinfo *devinfo; + int rv = 0; + BCLOG("+%s\n", __func__); + + devinfo = g_devices[idx]; + + if (devinfo) { + devinfo->ref_cnt++; + BCLOG("%s device already registered\n", __func__); + rv = devinfo->pvr_bcinfo.ui32BufferDeviceID; + goto end; + } + + devinfo = (struct gfx_bc_devinfo *) + kzalloc(sizeof(*devinfo), GFP_KERNEL); + if (!devinfo) { + rv = -ENOMEM; + goto end; + } + BCLOG("%s devinfo idx=%d addr=0x%x\n", __func__, idx, (int)devinfo); + + devinfo->pvr_bcinfo.pixelformat = PVRSRV_PIXEL_FORMAT_UNKNOWN; + devinfo->pvr_bcinfo.ui32Width = 0; + devinfo->pvr_bcinfo.ui32Height = 0; + devinfo->pvr_bcinfo.ui32ByteStride = 0; + devinfo->pvr_bcinfo.ui32Flags = 0; + devinfo->pvr_bcinfo.ui32BufferCount = devinfo->num_bufs; + + devinfo->pvr_s2b_jt.ui32TableSize = + sizeof(PVRSRV_BC_SRV2BUFFER_KMJTABLE); + devinfo->pvr_s2b_jt.pfnOpenBCDevice = s2b_open_bc_device; + devinfo->pvr_s2b_jt.pfnCloseBCDevice = s2b_close_bc_device; + devinfo->pvr_s2b_jt.pfnGetBCBuffer = s2b_get_bc_buffer; + devinfo->pvr_s2b_jt.pfnGetBCInfo = s2b_get_bc_info; + devinfo->pvr_s2b_jt.pfnGetBufferAddr = s2b_get_buffer_addr; + + if (pvr_b2s_jt.pfnPVRSRVRegisterBCDevice(&devinfo->pvr_s2b_jt, + &devinfo->pvr_id) != PVRSRV_OK) { + BCLOG("RegisterBCDevice failed\n"); + rv = -EIO; + goto end; + } + + devinfo->pvr_bcinfo.ui32BufferDeviceID = VOUT_DEVICENODE_SUFFIX + idx; + + devinfo->ref_cnt++; + g_devices[idx] = devinfo; + rv = devinfo->pvr_bcinfo.ui32BufferDeviceID; +end: + BCLOG("-%s [%d]\n", __func__, rv); + return rv; +} + +static int gfx_bc_unregister(int idx) +{ + int rv = 0; + struct gfx_bc_devinfo *devinfo; + + devinfo = g_devices[idx]; + if (devinfo == NULL) { + rv = -ENODEV; + goto end; + } + + devinfo->ref_cnt--; + + if (devinfo->ref_cnt) { + rv = -EAGAIN; + goto end; + } + + if (pvr_b2s_jt.pfnPVRSRVRemoveBCDevice(devinfo->pvr_id) != PVRSRV_OK) { + rv = -EIO; + goto end; + } + + kfree(devinfo); + g_devices[idx] = NULL; + +end: + return rv; +} + +#define FIELDCOPY(dst, src, field) { (dst)->field = (src)->field; } + +#define BC_BUF_PARAMS_COPY(dst, src) { \ + FIELDCOPY(dst, src, count); \ + FIELDCOPY(dst, src, width); \ + FIELDCOPY(dst, src, height); \ + FIELDCOPY(dst, src, pixel_fmt); \ + FIELDCOPY(dst, src, stride); \ + FIELDCOPY(dst, src, size); \ + } + +static void gfx_bc_params2_to_common(struct bc_buf_params2 *p, + struct bc_buf_params_common *pc) +{ + BC_BUF_PARAMS_COPY(pc, p); +} + +/* + * Validate the bc_buf_params and get the PVR pixel format + * + * We shouldn't need to do any further validation of the V4L2 pixelformat + * properties as this should have been taken care of in the appropriate V4L2 + * ioctl handlers. + */ +static int gfx_bc_validateparams( + int idx, + struct bc_buf_params_common *p, + struct gfx_bc_devinfo **devinfop, + PVRSRV_PIXEL_FORMAT *pvr_pix_fmtp) +{ + struct gfx_bc_devinfo *devinfo; + int rv = 0; + + devinfo = g_devices[idx]; + if (devinfo == NULL) { + BCLOG("%s: no such device %d", __func__, idx); + rv = -ENODEV; + } + + /* validate a series of params */ + if (p->count <= 0) { + BCLOG("%s: invalid count", __func__); + rv = -EINVAL; + } + + *pvr_pix_fmtp = v4l2_to_pvr_pixfmt(p->pixel_fmt); + if (*pvr_pix_fmtp == PVRSRV_PIXEL_FORMAT_UNKNOWN) { + BCLOG("%s: invalid pixel format", __func__); + rv = -EINVAL; + } + + *devinfop = rv != 0 ? NULL : devinfo; + return rv; +} + +/* + * API for the V4L2 component + */ +int bc_init(int idx) +{ + int i, rv; + + BCLOG("+%s\n", __func__); + + if (bc_initialized == -1) { + + for (i = 0; i < DEVICE_COUNT; i++) + g_devices[i] = NULL; + + if (!PVRGetBufferClassJTable(&pvr_b2s_jt)) { + BCERR("no jump table to SGX APIs\n"); + rv = -EIO; + goto end; + } + bc_initialized = 1; + } + + rv = gfx_bc_register(idx); + if (rv < 0) { + BCERR("can't register BC service\n"); + goto end; + } + +end: + BCLOG("-%s [%d]\n", __func__, rv); + return rv; +} + +void bc_cleanup(int idx) +{ + if (gfx_bc_release_device_resources(idx) != 0) + BCERR("can't b/c device resources: %d\n", idx); + if (gfx_bc_unregister(idx) != 0) + BCERR("can't un-register BC service\n"); +} + +int bc_setup_complete(int idx, struct bc_buf_params2 *p) +{ + /* Fn called after successful bc_setup() so id should be valid */ + struct gfx_bc_devinfo *devinfo = g_devices[idx]; + if (p->count != devinfo->num_bufs) { + BCLOG("+%s: Count doesn't match\n", __func__); + return -ENODEV; + } + return 0; +} + +int bc_setup_buffer(int idx, struct bc_buf_params2 *p, unsigned long *paddrp) +{ + int i; + /* Fn called after successful bc_setup() so idx should be valid */ + struct gfx_bc_devinfo *devinfo = g_devices[idx]; + i = devinfo->num_bufs; + if (unlikely(i >= VIDEO_MAX_FRAME)) + return -ENOENT; + + devinfo->num_bufs++; + devinfo->pvr_bcinfo.ui32BufferCount = devinfo->num_bufs; + + memset(&devinfo->bc_buf[i], 0, sizeof(devinfo->bc_buf[i])); + devinfo->bc_buf[i].paddrp = paddrp; + devinfo->bc_buf[i].size = p->size; + devinfo->bc_buf[i].pvr_sync_data = IMG_NULL; + return 0; +} + +int bc_setup(int idx, struct bc_buf_params2 *p) +{ + struct gfx_bc_devinfo *devinfo; + int rv = 0; + PVRSRV_PIXEL_FORMAT pvr_pix_fmt; + struct bc_buf_params_common pc; + + BCLOG("+%s\n", __func__); + + gfx_bc_params2_to_common(p, &pc); + rv = gfx_bc_validateparams(idx, &pc, &devinfo, &pvr_pix_fmt); + if (rv != 0) + goto end; + + p->stride = 4096; /* Tiler stride */ + p->size = p->height * p->stride; + if (p->pixel_fmt == V4L2_PIX_FMT_NV12) + p->size += (p->height / 2) * p->stride; /* UV size */ + + devinfo->num_bufs = 0; /* See bc_setup_buffer */ + + devinfo->pvr_bcinfo.pixelformat = pvr_pix_fmt; + devinfo->pvr_bcinfo.ui32Width = p->width; + devinfo->pvr_bcinfo.ui32Height = p->height; + devinfo->pvr_bcinfo.ui32ByteStride = p->stride; + /* I'm not 100% sure these flags are right but here goes */ + devinfo->pvr_bcinfo.ui32Flags = + PVRSRV_BC_FLAGS_YUVCSC_FULL_RANGE | + PVRSRV_BC_FLAGS_YUVCSC_BT601; + + BCLOG("buffers: count=%d, w=%d, h=%d, stride=%d, sz=%d fmt=%d\n", + p->count, p->width, p->height, p->stride, p->size, pvr_pix_fmt); +end: + BCLOG("-%s [%d]\n", __func__, rv); + return rv; +} + +/* + * The caller of this API will ensure that the arguments are valid + */ +int bc_sync_status(int idx, int bufidx) +{ + struct gfx_bc_devinfo *devinfo = g_devices[idx]; + int ui32ReadOpsPending, ui32ReadOpsComplete; + + ui32ReadOpsPending = + devinfo->bc_buf[bufidx].pvr_sync_data->ui32ReadOpsPending; + ui32ReadOpsComplete = + devinfo->bc_buf[bufidx].pvr_sync_data->ui32ReadOpsComplete; + + return ui32ReadOpsComplete == ui32ReadOpsPending ? 1 : 0; +} + diff --git a/sgx/services4/v4l2-gfx/gfx_bc.h b/sgx/services4/v4l2-gfx/gfx_bc.h new file mode 100644 index 0000000..4c2d9d8 --- /dev/null +++ b/sgx/services4/v4l2-gfx/gfx_bc.h @@ -0,0 +1,76 @@ +/********************************************************************** + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + ******************************************************************************/ + +#ifndef __V4L2_GFX_BC_H__ +#define __V4L2_GFX_BC_H__ + +#include <media/v4l2-dev.h> + +struct bc_buf_params_common { + int count; /*number of buffers */ + int width; /*buffer width in pixel, multiple of 32 */ + int height; /*buffer height in pixel */ + u32 pixel_fmt; /* V4L2 buffer pixel format */ + int stride; + int size; +}; + +struct bc_buf_params { + int count; /*number of buffers (in) */ + int width; /*buffer width in pixel, multiple of 32 (in) */ + int height; /*buffer height in pixel (in) */ + u32 pixel_fmt; /* V4L2 buffer pixel format (in) */ + int stride; /*(out) */ + int size; /*(out */ +}; + +struct bc_buf_params2 { + int count; /*number of buffers (in) */ + int width; /*buffer width in pixel, multiple of 32 (in) */ + int height; /*buffer height in pixel (in) */ + u32 pixel_fmt; /* V4L2 buffer pixel format (in) */ + int stride; /*(in) */ + int size; /*(out */ +}; +extern int bc_init(int idx); +extern void bc_cleanup(int idx); + +/* bc_setup + * + * This API will validate the buffer parameters in order to setup a + * buffer class device. Buffers should be added with subsequent calls to + * bc_setup_buffer() + */ +extern int bc_setup(int idx, struct bc_buf_params2 *p); + +/* bc_setup_buffer + * + * Only called after a successful bc_setup(), add a physical buffer reference + * to this device + */ +extern int bc_setup_buffer(int idx, struct bc_buf_params2 *p, + unsigned long *paddr); + +/* bc_setup_complete + * + * Called after all physical buffers have been added to the device + */ +extern int bc_setup_complete(int idx, struct bc_buf_params2 *p); + +/* bc_sync_status + * + * Return the synchronization status of this devices buffer + * + * Return values: + * 0 SGX still has pending operations on the buffer + * 1 SGX done with the buffer + */ +extern int bc_sync_status(int idx, int bufidx); +#endif diff --git a/sgx/services4/v4l2-gfx/gfx_init.c b/sgx/services4/v4l2-gfx/gfx_init.c new file mode 100644 index 0000000..8783d31 --- /dev/null +++ b/sgx/services4/v4l2-gfx/gfx_init.c @@ -0,0 +1,305 @@ +/* + * drivers/media/video/omap/v4gfx.c + * + * Copyright (C) 2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#ifndef LINUX +#define LINUX /* Needed by IMG headers */ +#endif + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/vmalloc.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/version.h> + +#include <linux/omap_v4l2_gfx.h> /* private ioctls */ +#include <media/v4l2-ioctl.h> +#include <img_defs.h> + +#include "v4gfx.h" +#include "gfx_bc.h" + +MODULE_AUTHOR("Texas Instruments."); +MODULE_DESCRIPTION("OMAP V4L2 GFX driver"); +MODULE_LICENSE("GPL"); + +static struct gbl_v4gfx *gbl_dev[DEVICE_COUNT]; + +int debug; /* is used outside this compilation unit too */ +module_param(debug, int, 0644); + +/* + * If bypass is set then buffer streaming operations will be bypassed. This + * enables us to check what the raw performance of stack above the V4L2 + * driver is + */ +static int bypass; +module_param(bypass, int, 0644); + + +static int bypass_vidioc_qbuf( + struct file *file, void *fh, struct v4l2_buffer *buf) { return 0; } + +static int bypass_vidioc_dqbuf( + struct file *file, void *fh, struct v4l2_buffer *buf) { return 0; } + +static int bypass_vidioc_streamon( + struct file *file, void *fh, enum v4l2_buf_type i) { return 0; } + +static int bypass_vidioc_streamoff( + struct file *file, void *fh, enum v4l2_buf_type i) { return 0; } + +static long bypass_vidioc_default( + struct file *file, void *fh, int cmd, void *arg) +{ + struct v4l2_gfx_buf_params *parms = (struct v4l2_gfx_buf_params *)arg; + int rv = 0; + + switch (cmd) { + case V4L2_GFX_IOC_CONSUMER: + break; + case V4L2_GFX_IOC_ACQ: + /* In bypass mode default the first buffer */ + parms->bufid = 0; + break; + case V4L2_GFX_IOC_REL: + break; + default: + rv = -EINVAL; + } + return rv; +} + +/* + * If the module is put in bypass mode the following ioctls + * are effectively nops + */ +static void v4gfx_enable_bypass(void) +{ + v4gfx_ioctl_ops.vidioc_qbuf = bypass_vidioc_qbuf; + v4gfx_ioctl_ops.vidioc_dqbuf = bypass_vidioc_dqbuf; + v4gfx_ioctl_ops.vidioc_streamon = bypass_vidioc_streamon; + v4gfx_ioctl_ops.vidioc_streamoff = bypass_vidioc_streamoff; + v4gfx_ioctl_ops.vidioc_default = bypass_vidioc_default; +} + +static void v4gfx_cleanup_device(struct v4gfx_device *vout) +{ + struct video_device *vfd; + + if (!vout) + return; + vfd = vout->vfd; + + if (vfd) { + if (vfd->minor == -1) { + /* + * The device was never registered, so release the + * video_device struct directly. + */ + video_device_release(vfd); + } else { + /* + * The unregister function will release the video_device + * struct as well as unregistering it. + */ + video_unregister_device(vfd); + } + } + + v4gfx_tiler_buffer_free(vout, vout->buffer_allocated, 0); + kfree(vout); +} + +static int driver_remove(struct platform_device *pdev) +{ + int i; + PVR_UNREFERENCED_PARAMETER(pdev); + + for (i = 0; i < DEVICE_COUNT; i++) { + v4l2_device_unregister(&gbl_dev[i]->v4l2_dev); + v4gfx_cleanup_device(gbl_dev[i]->vout); + kfree(gbl_dev[i]); + } + return 0; +} + +static int driver_probe(struct platform_device *pdev) +{ + printk(KERN_INFO "Probing: " VOUT_NAME); + return 0; +} + +static int v4gfx_create_instance(struct v4gfx_device **voutp, int idx) +{ + int r = 0; + struct v4gfx_device *vout = NULL; + struct video_device *vfd = NULL; + + vout = kzalloc(sizeof(struct v4gfx_device), GFP_KERNEL); + if (vout == NULL) { + r = -ENOMEM; + goto end; + } + mutex_init(&vout->lock); + spin_lock_init(&vout->vbq_lock); + /* TODO set this to an invalid value, need to change unit test though */ + vout->bpp = RGB565_BPP; + vout->gbl_dev = gbl_dev[idx]; + vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + + init_timer(&vout->acquire_timer); + vout->acquire_timer.function = v4gfx_acquire_timer; + vout->acquire_timer.data = (unsigned long)vout; + + init_waitqueue_head(&vout->sync_done); + init_waitqueue_head(&vout->consumer_wait); + + vfd = vout->vfd = video_device_alloc(); + if (!vfd) + goto end; + + strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name)); + vfd->vfl_type = VFL_TYPE_GRABBER; + vfd->release = video_device_release; + vfd->ioctl_ops = &v4gfx_ioctl_ops; + vfd->fops = &v4gfx_fops; + vfd->minor = -1; + vfd->debug = debug; + + r = video_register_device(vfd, VFL_TYPE_GRABBER, + VOUT_DEVICENODE_SUFFIX+idx); + if (r < 0) + goto end; + + video_set_drvdata(vfd, vout); + + *voutp = vout; + printk(KERN_INFO VOUT_NAME ":video device registered\n"); + return 0; +end: + + if (vfd) + video_device_release(vfd); + + kfree(vout); /* safe with null vout */ + + return r; +} + +static void v4gfx_delete_instance( + struct v4l2_device *v4l2_dev, struct v4gfx_device *vout) +{ + v4l2_info(v4l2_dev, "unregistering /dev/video%d\n", vout->vfd->num); + video_unregister_device(vout->vfd); + v4gfx_buffer_array_free(vout, vout->buffer_allocated); + kfree(vout); + return; +} + +static struct platform_driver v4gfx_driver = { + .driver = { + .name = VOUT_NAME, + }, + .probe = driver_probe, + .remove = driver_remove, +}; + +static int module_init_v4gfx(void) +{ + int i, rv; + bool v4l2_dev_registered[DEVICE_COUNT]; + bool bc_dev_registered[DEVICE_COUNT]; + + for (i = 0; i < DEVICE_COUNT; i++) { + gbl_dev[i] = NULL; + v4l2_dev_registered[i] = false; + bc_dev_registered[i] = false; + } + + if (bypass) { + printk(KERN_INFO VOUT_NAME ":Enable bypass mode\n"); + v4gfx_enable_bypass(); + } + + rv = platform_driver_register(&v4gfx_driver); + if (rv != 0) { + printk(KERN_ERR VOUT_NAME ":platform_driver_register failed\n"); + goto end; + } + + for (i = 0; i < DEVICE_COUNT; i++) { + gbl_dev[i] = kzalloc(sizeof(struct gbl_v4gfx), GFP_KERNEL); + if (gbl_dev[i] == NULL) { + rv = -ENOMEM; + goto end; + } + + snprintf(gbl_dev[i]->v4l2_dev.name, + sizeof(gbl_dev[i]->v4l2_dev.name), + "%s-%03d", VOUT_NAME, VOUT_DEVICENODE_SUFFIX+i); + + rv = v4l2_device_register(NULL, &gbl_dev[i]->v4l2_dev); + if (rv != 0) { + printk(KERN_ERR VOUT_NAME\ + ":v4l2_device_register failed\n"); + goto end; + } + v4l2_dev_registered[i] = true; + + rv = v4gfx_create_instance(&gbl_dev[i]->vout, i); + if (rv != 0) + goto end; + + rv = bc_init(i); + if (rv < 0) + goto end; + gbl_dev[i]->vout->deviceidx = i; + bc_dev_registered[i] = true; + rv = 0; + } + + printk(KERN_INFO VOUT_NAME ":OMAP V4L2 GFX driver loaded ok\n"); + return rv; +end: + printk(KERN_INFO VOUT_NAME ":Error %d loading OMAP V4L2 GFX driver\n", + rv); + + for (i = 0; i < DEVICE_COUNT; i++) { + + if (bc_dev_registered[i]) + bc_cleanup(i); + if (v4l2_dev_registered[i]) + v4l2_device_unregister(&gbl_dev[i]->v4l2_dev); + kfree(gbl_dev[i]); /* gbl_dev[i] can be null */ + } + + return rv; +} + +static void module_exit_v4gfx(void) +{ + int i; + + for (i = 0; i < DEVICE_COUNT; i++) { + bc_cleanup(i); + v4gfx_delete_instance(&gbl_dev[i]->v4l2_dev, gbl_dev[i]->vout); + v4l2_device_unregister(&gbl_dev[i]->v4l2_dev); + kfree(gbl_dev[i]); + } + + platform_driver_unregister(&v4gfx_driver); +} + +module_init(module_init_v4gfx); +module_exit(module_exit_v4gfx); diff --git a/sgx/services4/v4l2-gfx/gfx_io.c b/sgx/services4/v4l2-gfx/gfx_io.c new file mode 100644 index 0000000..f4af44b --- /dev/null +++ b/sgx/services4/v4l2-gfx/gfx_io.c @@ -0,0 +1,1331 @@ +/* + * drivers/media/video/omap/v4gfx.c + * + * Copyright (C) 2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> +#include <linux/kdev_t.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/videodev2.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/omap_v4l2_gfx.h> /* private ioctls */ + +#include <media/videobuf-dma-contig.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> + +#include "v4gfx.h" +#include "gfx_bc.h" + +#define V4GFX_WAIT_DEQUE 1 /* Poll buffer sync status during dq */ +#define V4GFX_WAIT_UNLOCK 2 /* Poll buffer sync status from render loop */ +/* + * V4GFX_WAITMETHOD is used to select between how we wait for SGX to release + * buffers sent to it. + */ +/* #define V4GFX_WAITMETHOD V4GFX_WAIT_DEQUE */ +#define V4GFX_WAITMETHOD V4GFX_WAIT_UNLOCK + +#define VID_MAX_WIDTH 2048 /* Largest width */ +#define VID_MAX_HEIGHT 2048 /* Largest height */ +#define VID_MIN_WIDTH 0 +#define VID_MIN_HEIGHT 0 +#define V4GFX_FRAME_UNLOCK_TIMEOUT 16 /* ms */ + + +/* + * This will enable dumping of the mappings obtain + */ +#ifdef V4L2GFX_DUMPMMAP +#define DUMPMMAP(msg, k, vma, m, pos, p) \ + printk(KERN_NOTICE \ + "%s: vm_start+%d = 0x%lx, dma->vmalloc+%d = 0x%lx, w=0x%x\n", \ + msg, k, vma->vm_start + k, m, (pos + m), p); +#else +#define DUMPMMAP(msg, k, vma, m, pos, p) +#endif + +static struct videobuf_queue_ops video_vbq_ops; + +static u32 v4gfx_calc_buffer_size( + int bpp, u32 width, u32 height, u32 pixelformat); +static u32 v4gfx_calc_stride(int bpp, u32 width); + +/* + * List of image formats supported by the SGX buffer-class api + */ +static const struct v4l2_fmtdesc gfx_bc_formats[] = { + { + /* Note: V4L2 defines RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 + * + * OMAP video pipelines interpret RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 + * + * GFX ?? TODO + */ + .description = "RGB565, le", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + { + .description = "RGB32, le", + .pixelformat = V4L2_PIX_FMT_RGB32, + }, + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + { + .description = "UYVY, packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + { + .description = "NV12 - YUV420 format", + .pixelformat = V4L2_PIX_FMT_NV12, + }, +}; + +#define NUM_OUTPUT_FORMATS (ARRAY_SIZE(gfx_bc_formats)) + +int v4gfx_try_format(struct v4l2_pix_format *pix) +{ + int ifmt, bpp = 0; + + pix->height = + clamp(pix->height, (u32)VID_MIN_HEIGHT, (u32)VID_MAX_HEIGHT); + pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH); + + for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) { + if (pix->pixelformat == gfx_bc_formats[ifmt].pixelformat) + break; + } + + if (ifmt >= NUM_OUTPUT_FORMATS) + ifmt = 0; /* Default V4L2_PIX_FMT_RGB565 */ + pix->pixelformat = gfx_bc_formats[ifmt].pixelformat; + + pix->field = V4L2_FIELD_ANY; + pix->priv = 0; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_JPEG; + bpp = YUYV_BPP; + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB565_BPP; + break; + case V4L2_PIX_FMT_RGB24: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB24_BPP; + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + pix->colorspace = V4L2_COLORSPACE_SRGB; + bpp = RGB32_BPP; + break; + case V4L2_PIX_FMT_NV12: + pix->colorspace = V4L2_COLORSPACE_JPEG; + bpp = 1; /* 12bits per pixel, 1 byte for Y */ + break; + } + + pix->bytesperline = v4gfx_calc_stride(bpp, pix->width); + pix->sizeimage = v4gfx_calc_buffer_size(bpp, pix->width, pix->height, + pix->pixelformat); + + if (V4L2_PIX_FMT_NV12 == pix->pixelformat) + pix->sizeimage += pix->sizeimage >> 1; + + return bpp; +} + +void v4gfx_acquire_timer(unsigned long arg) +{ + struct v4gfx_device *vout = (struct v4gfx_device *)arg; + + set_bit(1, &vout->acquire_timedout); +} + +#if V4GFX_WAITMETHOD == V4GFX_WAIT_DEQUE +static struct videobuf_buffer *v4gfx_get_next_syncframe( + struct v4gfx_device *vout) +{ + struct videobuf_buffer *buf; + mutex_lock(&vout->lock); + if (list_empty(&vout->sync_queue)) { + mutex_unlock(&vout->lock); + return NULL; + } + buf = list_entry(vout->sync_queue.next, struct videobuf_buffer, queue); + mutex_unlock(&vout->lock); + return buf; +} + +static int v4gfx_wait_on_pending(struct v4gfx_device *vout, int bufidx) +{ + int dqable = 0; + int iteration = 0; + + do { + dqable = bc_sync_status(vout->deviceidx, bufidx); + if (!dqable) { + /* printk("w-on %d [%d]\n", bufidx, iteration); */ + if (iteration++ < V4GFX_FRAME_UNLOCK_TIMEOUT) { + msleep(1); /* milliseconds */ + } else { + /*printk("t-o %d\n", bufidx); */ + break; /* Timed out */ + } + } +/* + else { + printk("dq-o %d\n", bufidx); + } + */ + } while (!dqable); + + return dqable; +} + +static void v4gfx_done_syncframe(struct v4gfx_device *vout, + struct videobuf_buffer *sync_frame) +{ + struct timeval timevalue = {0}; + unsigned long flags; + mutex_lock(&vout->lock); + spin_lock_irqsave(&vout->vbq_lock, flags); + + list_del(&sync_frame->queue); + + do_gettimeofday(&timevalue); + sync_frame->ts = timevalue; + sync_frame->state = VIDEOBUF_DONE; + wake_up_interruptible(&sync_frame->done); + spin_unlock_irqrestore(&vout->vbq_lock, flags); + mutex_unlock(&vout->lock); +} +#endif /* V4GFX_WAIT_DEQUE */ + + +static u32 v4gfx_calc_stride(int bpp, u32 width) +{ + return PAGE_ALIGN(width * bpp); +} + +static u32 v4gfx_calc_buffer_size( + int bpp, u32 width, u32 height, u32 pixelformat) +{ + int stride; + stride = v4gfx_calc_stride(bpp, width); + + /* i is the block-width - either 4K or 8K, depending upon input width*/ + /* for NV12 format, buffer is height + height / 2*/ + if (V4L2_PIX_FMT_NV12 == pixelformat) + return height * 3/2 * stride; + else + return height * stride; +} + +void v4gfx_buffer_array_free(struct v4gfx_device *vout, int cnt) +{ + /* Fn should be robust and callable with args in a dubious state */ + int i; + if (!vout || !cnt) + return; + if (vout->buf_phys_addr_array) { + for (i = 0; i < cnt; i++) + kfree(vout->buf_phys_addr_array[i]); + kfree(vout->buf_phys_addr_array); + vout->buf_phys_addr_array = NULL; + } +} + +/* + * Allocate a buffer array for all the requested buffers + * If there is an allocation failure the function will clean up after itself + */ +static int v4gfx_buffer_array_realloc(struct v4gfx_device *vout, + int oldcnt, int newcnt) +{ + int i; + + if (vout->buf_phys_addr_array) + v4gfx_buffer_array_free(vout, oldcnt); + + vout->buf_phys_addr_array = + kzalloc(sizeof(unsigned long *) * newcnt, GFP_KERNEL); + if (!vout->buf_phys_addr_array) + return -ENOMEM; + + /* 2048 is the max image height, 2 = (2048 * 4) / CPU_PAGE_SIZE */ + for (i = 0; i < newcnt; i++) { + vout->buf_phys_addr_array[i] = + kmalloc(sizeof(unsigned long) * 2048 * 2, GFP_KERNEL); + if (!vout->buf_phys_addr_array[i]) { + v4gfx_buffer_array_free(vout, newcnt); + return -ENOMEM; + } + } + return 0; +} + +static void v4gfx_buffer_array_fill( + struct v4gfx_device *vout, + int bufno, + unsigned long tiler_paddr_in, + unsigned long tiler_paddr_uv_in) +{ + int buf_phys_idx = 0; + int m = 0, i; + int cpu_pgwidth; + int tiler_increment; + + v4gfx_tiler_image_incr(vout, &cpu_pgwidth, &tiler_increment); + + for (i = 0; i < vout->pix.height; i++) { + unsigned long pg, pgend, tiler_paddr; + + tiler_paddr = tiler_paddr_in+m; + pg = tiler_paddr; + pgend = pg + cpu_pgwidth; + do { + GFXLOGA(2, "%d %d: = %lx\n", bufno, buf_phys_idx, + (long)pg); + vout->buf_phys_addr_array[bufno][buf_phys_idx] = pg; + pg += 4096; + buf_phys_idx++; + } while (pg < pgend); + + m += tiler_increment; + } + + if (V4L2_PIX_FMT_NV12 == vout->pix.pixelformat) { + m = 0; + v4gfx_tiler_image_incr_uv(vout, &tiler_increment); + + /* UV buffer is height / 2 */ + for (i = 0; i < vout->pix.height / 2; i++) { + unsigned long pg; + + pg = tiler_paddr_uv_in+m; + vout->buf_phys_addr_array[bufno][buf_phys_idx] = pg; + m += tiler_increment; + buf_phys_idx++; + } + + GFXLOGA(1, "nv12 uv: 0x%lx\n", tiler_paddr_uv_in); + m += tiler_increment; + } +} + +static int v4gfx_frame_lock(struct v4gfx_device *vout, int *bufid) +{ + struct videobuf_buffer *oldbuf = NULL; +#if V4GFX_WAITMETHOD == V4GFX_WAIT_UNLOCK + struct timeval timevalue = {0}; +#else /* V4GFX_WAIT_DEQUE */ + int oldbufid = -1; +#endif + unsigned long flags; + int rv = 0; + + mutex_lock(&vout->lock); + spin_lock_irqsave(&vout->vbq_lock, flags); + if (!vout->streaming || !vout->cur_frm) { + GFXLOG(1, V4L2DEV(vout), + "%s: ERROR: device not streaming yet\n", __func__); + rv = -EAGAIN; + goto unlock; + } + + /* vout->cur_frm must be set if streaming */ + + if (vout->cur_frm == vout->locked_frm) { + /* + * If this frame has been locked before we will + * attempt to get the next buffer in the dma queue. + * If there is a next buffer, mark the locked + * buffer as done and then promote the next buffer + * to the current buffer whilst locking it in the + * process. + */ + if (list_empty(&vout->dma_queue)) { + *bufid = vout->cur_frm->i; + /* + * We can't do anything else here, it will be upto + * the consumer application to decide whether it wants + * to re-render the texture which depends on what the + * app is doing. + */ + goto unlock; + } + + /* Deactivate the cur_frm */ + oldbuf = vout->cur_frm; + + vout->cur_frm = list_entry(vout->dma_queue.next, + struct videobuf_buffer, queue); + + list_del(&vout->cur_frm->queue); + + vout->cur_frm->state = VIDEOBUF_ACTIVE; + + GFXLOG(2, V4L2DEV(vout), "Active frame %d\n", vout->cur_frm->i); + + vout->locked_frm = vout->cur_frm; + +#if V4GFX_WAITMETHOD == V4GFX_WAIT_UNLOCK + /* + * Mark the previous current buffer done and release it for + * dequeue + */ + do_gettimeofday(&timevalue); + oldbuf->ts = timevalue; + oldbuf->state = VIDEOBUF_DONE; + wake_up_interruptible(&oldbuf->done); +#else /* V4GFX_WAIT_DEQUE */ + oldbufid = oldbuf->i; + list_add_tail(&oldbuf->queue, &vout->sync_queue); + wake_up_interruptible(&vout->sync_done); +#endif + + } else { + /* First time we've tried to lock this frame */ + vout->locked_frm = vout->cur_frm; + /* We be marked for dequeue next time */ + } + *bufid = vout->locked_frm->i; +unlock: + spin_unlock_irqrestore(&vout->vbq_lock, flags); + mutex_unlock(&vout->lock); + +#if V4GFX_WAITMETHOD == V4GFX_WAIT_DEQUE +/* + if (oldbufid != -1) + printk("sync_queue + %d\n", oldbufid); + */ +#endif + return rv; +} + +static int v4gfx_frame_unlock(struct v4gfx_device *vout, int bufidx) +{ + struct videobuf_buffer *vbuf; + int rv = 0; +#if V4GFX_WAITMETHOD == V4GFX_WAIT_UNLOCK + int iteration = 0; +#endif + + mutex_lock(&vout->lock); + vbuf = vout->locked_frm; + if (!vbuf) { + GFXLOG(1, V4L2DEV(vout), + "%s: ERROR: trying to unlock a non-existent frame\n", + __func__); + rv = -EINVAL; + } else if (vbuf->i != bufidx) { + GFXLOG(1, V4L2DEV(vout), + "%s: ERROR: trying to unlock wrong frame %d %d\n", + __func__, vbuf->i, bufidx); + rv = -EINVAL; + } + mutex_unlock(&vout->lock); + +#if V4GFX_WAITMETHOD == V4GFX_WAIT_UNLOCK + if (rv != 0) + goto end; + + do { + /* + * Interrogate the buffer class synch data buffer to see if SGX + * is done with this buffer + */ + rv = bc_sync_status(vout->deviceidx, bufidx); + if (rv == 0) { + if (iteration++ < V4GFX_FRAME_UNLOCK_TIMEOUT) + msleep(1); /* milliseconds */ + } + } while (rv == 0); + + if (iteration >= V4GFX_FRAME_UNLOCK_TIMEOUT) { + printk("%s: INFO: timed out\n", __func__); + rv = -ETIMEDOUT; + } else + rv = 0; +end: +#endif /* V4GFX_WAIT_UNLOCK */ + return rv; +} + +/* + * Buffer setup function is called by videobuf layer when REQBUF ioctl is + * called. This is used to setup buffers and return size and count of + * buffers allocated. After the call to this buffer, videobuf layer will + * setup buffer queue depending on the size and count of buffers + */ +static int vbq_ops_buf_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct v4gfx_device *vout = q->priv_data; + int rv = 0; + GFXLOG(1, V4L2DEV(vout), "+%s\n", __func__); + + if (!vout || (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type)) { + rv = -EINVAL; goto end; + } + + *size = vout->buffer_size = v4gfx_calc_buffer_size( + vout->bpp, + vout->pix.width, + vout->pix.height, + vout->pix.pixelformat); + + GFXLOG(1, V4L2DEV(vout), "height=%d, size=%d\n", + vout->pix.height, *size); + + if (v4gfx_tiler_buffer_setup(vout, count, 0, &vout->pix)) { + rv = -ENOMEM; goto end; + } + +end: + GFXLOG(1, V4L2DEV(vout), "Exiting %s\n", __func__); + return rv; +} + +/* + * This function will be called when VIDIOC_QBUF ioctl is called. + * It prepare buffers before give out for the display. This function + * user space virtual address into physical address if userptr memory + * exchange mechanism is used. + */ +static int vbq_ops_buf_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct v4gfx_device *vout = q->priv_data; + + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vout->pix.width; + vb->height = vout->pix.height; + vb->size = vb->width * vb->height * vout->bpp; + vb->field = field; + + } + vb->state = VIDEOBUF_PREPARED; + + return 0; +} + +/* + * Buffer queue function will be called from the videobuf layer when _QBUF + * ioctl is called. It is used to enqueue buffer, which is ready to be + * displayed. + */ +static void vbq_ops_buf_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct v4gfx_device *vout = q->priv_data; + + list_add_tail(&vb->queue, &vout->dma_queue); + vb->state = VIDEOBUF_QUEUED; +} + +/* + * Buffer release function is called from videobuf layer to release buffer + * which are already allocated + */ +static void vbq_ops_buf_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct v4gfx_device *vout = q->priv_data; + + vb->state = VIDEOBUF_NEEDS_INIT; + + if (V4L2_MEMORY_MMAP != vout->memory) + return; +} + +/* + * File operations + */ +static void v4gfx_vm_open(struct vm_area_struct *vma) +{ + struct v4gfx_device *vout = vma->vm_private_data; + + GFXLOG(1, V4L2DEV(vout), + "vm_open [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end); + vout->mmap_count++; +} + +static void v4gfx_vm_close(struct vm_area_struct *vma) +{ + struct v4gfx_device *vout = vma->vm_private_data; + + GFXLOG(1, V4L2DEV(vout), + "vm_close [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end); + + vout->mmap_count--; +} + +static struct vm_operations_struct v4gfx_vm_ops = { + .open = v4gfx_vm_open, + .close = v4gfx_vm_close, +}; + +static int vidfop_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct v4gfx_device *vout = file->private_data; + struct videobuf_queue *q = &vout->vbq; + int i; + void *pos; + int j = 0, k = 0, m = 0, p = 0, m_increment = 0; + + GFXLOG(1, V4L2DEV(vout), "Entering %s\n", __func__); + + /* look for the buffer to map */ + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + if (V4L2_MEMORY_MMAP != q->bufs[i]->memory) + continue; + if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT)) + break; + } + + if (VIDEO_MAX_FRAME == i) { + GFXLOG(1, V4L2DEV(vout), + "offset invalid [offset=0x%lx]\n", + (vma->vm_pgoff << PAGE_SHIFT)); + return -EINVAL; + } + q->bufs[i]->baddr = vma->vm_start; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_ops = &v4gfx_vm_ops; + vma->vm_private_data = (void *) vout; + pos = (void *)vout->buf_phy_addr[i]; + + /* get line width */ + v4gfx_tiler_image_incr(vout, &p, &m_increment); + + for (j = 0; j < vout->pix.height; j++) { + /* map each page of the line */ + DUMPMMAP("Y buffer", k, vma, m, pos, p); + + vma->vm_pgoff = ((unsigned long)pos + m) >> PAGE_SHIFT; + + if (remap_pfn_range(vma, vma->vm_start + k, + ((unsigned long)pos + m) >> PAGE_SHIFT, + p, vma->vm_page_prot)) + return -EAGAIN; + k += p; + m += m_increment; + } + m = 0; + + /* UV Buffer in case of NV12 format */ + if (V4L2_PIX_FMT_NV12 == vout->pix.pixelformat) { + pos = (void *)vout->buf_phy_uv_addr[i]; + + v4gfx_tiler_image_incr_uv(vout, &m_increment); + + /* UV buffer is height / 2 */ + for (j = 0; j < vout->pix.height / 2; j++) { + /* map each page of the line */ + DUMPMMAP("UV buffer", k, vma, m, pos, p); + + vma->vm_pgoff = ((unsigned long)pos + m) >> PAGE_SHIFT; + + if (remap_pfn_range(vma, vma->vm_start + k, + ((unsigned long)pos + m) >> PAGE_SHIFT, + p, vma->vm_page_prot)) + return -EAGAIN; + k += p; + m += m_increment; + } + } + + vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ + vout->mmap_count++; + GFXLOG(1, V4L2DEV(vout), "Exiting %s\n", __func__); + return 0; +} + +static int vidfop_release(struct file *file) +{ + struct v4gfx_device *vout = file->private_data; + struct videobuf_queue *q; + unsigned int r = 0; + + GFXLOG(1, V4L2DEV(vout), "Entering %s\n", __func__); + GFXLOG(1, V4L2DEV(vout), + "current process id/pid is %d\n", current->pid); + + if (!vout) + goto end; + + vout->opened = vout->opened ? vout->opened - 1 : 0; + if (vout->opened) { + r = 0; + goto end; + } + + clear_bit(1, &vout->producer_ready); + + q = &vout->vbq; + + if (vout->streaming) { + del_timer_sync(&vout->acquire_timer); + clear_bit(1, &vout->acquire_timedout); + + vout->streaming = false; + videobuf_streamoff(q); + videobuf_queue_cancel(q); + } + + if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) + videobuf_mmap_free(q); + vout->mmap_count = 0; + + /* Free buffers */ + if (vout->buffer_allocated) { + v4gfx_tiler_buffer_free(vout, vout->buffer_allocated, 0); + vout->buffer_allocated = 0; + } + + memset(&vout->crop, 0, sizeof(vout->crop)); + memset(&vout->pix, 0, sizeof(vout->pix)); + + file->private_data = NULL; + +end: + GFXLOG(1, V4L2DEV(vout), "Exiting %s\n", __func__); + return r; +} + +static int vidfop_open(struct file *file) +{ + struct v4gfx_device *vout = NULL; + struct videobuf_queue *q; + int rv = 0; + + vout = video_drvdata(file); + if (vout == NULL) { + rv = -ENODEV; + goto end; + } + + GFXLOG(1, V4L2DEV(vout), "Entering %s : %x\n", __func__, (int)vout); + GFXLOG(1, V4L2DEV(vout), "current pid is %d\n", current->pid); + + vout->opened += 1; + file->private_data = vout; + + if (vout->opened > 1) { + GFXLOG(1, V4L2DEV(vout), "Another opening....\n"); + goto end; + } + + clear_bit(1, &vout->producer_ready); + + q = &vout->vbq; + video_vbq_ops.buf_setup = vbq_ops_buf_setup; + video_vbq_ops.buf_prepare = vbq_ops_buf_prepare; + video_vbq_ops.buf_release = vbq_ops_buf_release; + video_vbq_ops.buf_queue = vbq_ops_buf_queue; + + videobuf_queue_dma_contig_init(q, &video_vbq_ops, q->dev, + &vout->vbq_lock, vout->type, V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), vout); + +end: + GFXLOG(1, V4L2DEV(vout), "Exiting %s :%d\n", __func__, rv); + return rv; +} + +/* V4L2 ioctls */ +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct v4gfx_device *vout = fh; + GFXLOG(1, V4L2DEV(vout), "Entering %s\n", __func__); + + strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver)); + strlcpy(cap->card, vout->vfd->name, sizeof(cap->card)); + cap->bus_info[0] = '\0'; + cap->version = VOUT_VERSION; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT; + return 0; +} + +static int vidioc_log_status(struct file *file, void *fh) +{ + /* struct v4gfx_device *vout = fh; */ + printk(KERN_INFO "\n"); + printk(KERN_INFO "============== START LOG STATUS ================\n"); + printk(KERN_INFO "=============== END LOG STATUS =================\n"); + printk(KERN_INFO "\n"); + return 0; +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + struct v4gfx_device *vout = fh; + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + int rv = 0; + + GFXLOG(1, V4L2DEV(vout), "+%s\n", __func__); + + fmt->index = index; + fmt->type = type; + if (index >= NUM_OUTPUT_FORMATS) { + rv = -EINVAL; + goto end; + } + + fmt->flags = gfx_bc_formats[index].flags; + strlcpy(fmt->description, gfx_bc_formats[index].description, + sizeof(fmt->description)); + fmt->pixelformat = gfx_bc_formats[index].pixelformat; +end: + GFXLOG(1, V4L2DEV(vout), "-%s [%d]\n", __func__, rv); + return rv; +} + +static int vidioc_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct v4gfx_device *vout = fh; + GFXLOG(1, V4L2DEV(vout), "+%s\n", __func__); + + f->fmt.pix = vout->pix; + + GFXLOG(1, V4L2DEV(vout), "-%s [%d]\n", __func__, 0); + return 0; + +} + +/* + * VIDIOC_TRY_FMT ioctl is equivalent to VIDIOC_S_FMT with one + * exception: it does not change driver state. It can also be called at any + * time, never returning EBUSY. + */ +static int vidioc_try_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + int r; + struct v4gfx_device *vout = fh; + GFXLOG(1, V4L2DEV(vout), "+%s\n", __func__); + + r = v4gfx_try_format(&f->fmt.pix); + + GFXLOG(1, V4L2DEV(vout), "-%s [%d]\n", __func__, r); + return (r >= 0) ? 0 : r; +} + +static int vidioc_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct v4gfx_device *vout = fh; + int rv = 0; + int bpp; + + GFXLOG(1, V4L2DEV(vout), "+%s\n", __func__); + + mutex_lock(&vout->lock); + if (vout->streaming) { + rv = -EBUSY; + goto end; + } + + bpp = v4gfx_try_format(&f->fmt.pix); + if (bpp <= 0) { + rv = bpp; + goto end; + } + + /* try & set the new output format */ + vout->bpp = bpp; + vout->pix = f->fmt.pix; + +end: + mutex_unlock(&vout->lock); + GFXLOG(1, V4L2DEV(vout), "-%s [%d]\n", __func__, rv); + return rv; +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + struct bc_buf_params2 bc_params; + struct v4gfx_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + unsigned int i; + int rv = 0; + + GFXLOG(1, V4L2DEV(vout), "+%s\n", __func__); + + if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || + (req->count < 0) || + (req->memory != V4L2_MEMORY_MMAP) + ) { + rv = -EINVAL; goto end; + } + + + mutex_lock(&vout->lock); + /* Cannot be requested when streaming is on */ + if (vout->streaming) { + mutex_unlock(&vout->lock); + rv = -EBUSY; goto end; + } + + /* + * TODO A count value of zero frees all buffers, after aborting or + * finishing any DMA in progress, an implicit VIDIOC_STREAMOFF. + */ + + /* If buffers are already allocated free them */ + if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) { + if (vout->mmap_count) { + mutex_unlock(&vout->lock); + rv = -EBUSY; goto end; + } + + v4gfx_tiler_buffer_free(vout, vout->buffer_allocated, 0); + vout->buffer_allocated = 0; + + videobuf_mmap_free(q); + } + + bc_params.count = req->count; + bc_params.width = vout->pix.width; + bc_params.height = vout->pix.height; + bc_params.pixel_fmt = vout->pix.pixelformat; +/* bc_params.stride = vout->pix.bytesperline; */ + rv = bc_setup(vout->deviceidx, &bc_params); + if (rv < 0) { + GFXLOG(1, V4L2DEV(vout), + "+%s bc_setup() failed %d\n", __func__, rv); + mutex_unlock(&vout->lock); + goto end; + } + + /* + * Note that the actual buffer allocation is done in + * vbq_ops_buf_setup + */ + rv = videobuf_reqbufs(q, req); + if (rv < 0) { + mutex_unlock(&vout->lock); + goto end; + } + + INIT_LIST_HEAD(&vout->dma_queue); + INIT_LIST_HEAD(&vout->sync_queue); + + /* + * The realloc will free the old array and allocate a new one + */ + rv = v4gfx_buffer_array_realloc(vout, vout->buffer_allocated, + req->count); + if (rv < 0) { + mutex_unlock(&vout->lock); + goto end; + } + + vout->memory = req->memory; + vout->buffer_allocated = req->count; + + for (i = 0; i < req->count; i++) { + + v4gfx_buffer_array_fill(vout, i, + vout->buf_phy_addr[i], + V4L2_PIX_FMT_NV12 == vout->pix.pixelformat ? + vout->buf_phy_uv_addr[i] : 0); + + bc_setup_buffer(vout->deviceidx, &bc_params, + vout->buf_phys_addr_array[i]); + } + bc_setup_complete(vout->deviceidx, &bc_params); + + mutex_unlock(&vout->lock); +end: + GFXLOG(1, V4L2DEV(vout), "-%s [%d]\n", __func__, rv); + return rv; +} + +static int vidioc_querybuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + struct v4gfx_device *vout = fh; + int rv; + + GFXLOG(1, V4L2DEV(vout), "+%s\n", __func__); + + rv = videobuf_querybuf(&vout->vbq, b); + + GFXLOG(1, V4L2DEV(vout), "-%s [%d]\n", __func__, rv); + return rv; +} + +static int vidioc_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buf) +{ + struct v4gfx_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + int rv = 0; + + GFXLOG(1, V4L2DEV(vout), "qbuf buf: %d\n", buf->index); + + if ((V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) || + (buf->index >= vout->buffer_allocated) || + (q->bufs[buf->index]->memory != buf->memory)) { + return -EINVAL; + } + if (V4L2_MEMORY_USERPTR == buf->memory) { + if ((buf->length < vout->pix.sizeimage) || + (0 == buf->m.userptr)) { + return -EINVAL; + } + } + + rv = videobuf_qbuf(q, buf); + + mutex_lock(&vout->lock); + if (vout->streaming && vout->acquire_timeout_ms) { + del_timer(&vout->acquire_timer); + mod_timer(&vout->acquire_timer, + jiffies + msecs_to_jiffies(vout->acquire_timeout_ms)); + } + mutex_unlock(&vout->lock); + + GFXLOG(2, V4L2DEV(vout), "-%s [%d]\n", __func__, rv); + return rv; +} + +static int vidioc_dqbuf(struct file *file, void *fh, + struct v4l2_buffer *buf) +{ + struct v4gfx_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + int rv = 0; + int nonblocking = file->f_flags & O_NONBLOCK ? 1 : 0; + + GFXLOG(2, V4L2DEV(vout), "dqbuf buf: %x (%d)\n", + (int)buf, nonblocking); + + mutex_lock(&vout->lock); + if (!vout->streaming) { + mutex_unlock(&vout->lock); + return -EINVAL; + } + + mutex_unlock(&vout->lock); + +#if V4GFX_WAITMETHOD == V4GFX_WAIT_DEQUE +{ + struct videobuf_buffer *sync_frame = NULL; + + wait_event_interruptible(vout->sync_done, + !list_empty(&vout->sync_queue)); + + sync_frame = v4gfx_get_next_syncframe(vout); + + if (sync_frame) { + (void)v4gfx_wait_on_pending(vout, sync_frame->i); + v4gfx_done_syncframe(vout, sync_frame); + } else { + /* Can be from an interrupted task */ + printk(KERN_INFO "No sync frame\n"); + } +} +#endif + + rv = videobuf_dqbuf(q, buf, nonblocking); + + GFXLOG(2, V4L2DEV(vout), "-%s [%d]\n", __func__, rv); + return rv; +} + +static int vidioc_streamon(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct v4gfx_device *vout = fh; + struct videobuf_queue *q = &vout->vbq; + int rv = 0; + GFXLOG(1, V4L2DEV(vout), "+%s\n", __func__); + + mutex_lock(&vout->lock); + + if (vout->streaming) { + rv = -EBUSY; + goto end_unlock; + } + + vout->cur_frm = NULL; + vout->locked_frm = NULL; + + rv = videobuf_streamon(q); + if (rv < 0) + goto end_unlock; + + if (list_empty(&vout->dma_queue)) { + rv = -EIO; + goto end_unlock; + } + + vout->streaming = true; + + /* Activate the next current buffer */ + vout->cur_frm = + list_entry(vout->dma_queue.next, struct videobuf_buffer, queue); + list_del(&vout->cur_frm->queue); + vout->cur_frm->state = VIDEOBUF_ACTIVE; + + set_bit(1, &vout->producer_ready); + wake_up_interruptible(&vout->consumer_wait); + +end_unlock: + mutex_unlock(&vout->lock); + GFXLOG(1, V4L2DEV(vout), "-%s [%d]\n", __func__, rv); + + return rv; +} + +static int vidioc_streamoff(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct v4gfx_device *vout = fh; + int rv = 0; + + mutex_lock(&vout->lock); + if (!vout->streaming) { + rv = -EINVAL; + goto end; + } + + del_timer_sync(&vout->acquire_timer); + clear_bit(1, &vout->acquire_timedout); + + clear_bit(1, &vout->producer_ready); + + vout->streaming = false; + + INIT_LIST_HEAD(&vout->dma_queue); + INIT_LIST_HEAD(&vout->sync_queue); + + videobuf_streamoff(&vout->vbq); + videobuf_queue_cancel(&vout->vbq); +end: + mutex_unlock(&vout->lock); + GFXLOG(1, V4L2DEV(vout), "-%s [%d]\n", __func__, rv); + return rv; +} + +static int vidioc_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + struct v4gfx_device *vout = fh; + struct v4l2_pix_format *pix = &vout->pix; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + /* Width and height are always even */ + cropcap->bounds.width = pix->width & ~1; + cropcap->bounds.height = pix->height & ~1; + cropcap->pixelaspect.numerator = 1; + cropcap->pixelaspect.denominator = 1; + return 0; +} + +static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct v4gfx_device *vout = fh; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + crop->c = vout->crop; + GFXLOG(1, V4L2DEV(vout), "g_crop w:%d,h:%d\n", + crop->c.width, crop->c.height); + return 0; +} + +static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct v4gfx_device *vout = fh; + GFXLOG(1, V4L2DEV(vout), "Entering %s\n", __func__); + vout->crop = crop->c; + return 0; +} + +static long vidioc_default(struct file *file, void *fh, int cmd, void *arg) +{ + int rv = 0; + struct v4gfx_device *vout = fh; + GFXLOG(1, V4L2DEV(vout), "Entering %s (c=0x%x)\n", __func__, cmd); + + switch (cmd) { + case V4L2_GFX_IOC_CONSUMER: + { + struct v4l2_gfx_consumer_params *parms = + (struct v4l2_gfx_consumer_params *)arg; + if (parms->type != V4L2_GFX_CONSUMER_WAITSTREAM) + return -EINVAL; + + clear_bit(1, &vout->acquire_timedout); + + rv = wait_event_interruptible(vout->consumer_wait, + test_bit(1, &vout->producer_ready)); + mutex_lock(&vout->lock); + if (rv == -ERESTARTSYS) { + /* + * This condition is hit when the user process + * generates a signal, when we return this value the + * process will continue to block on the ioctl + */ + GFXLOG(1, V4L2DEV(vout), "Woke by signal: %d\n", + ERESTARTSYS); + } else { + vout->acquire_timeout_ms = parms->acquire_timeout_ms; + } + mutex_unlock(&vout->lock); + break; + + } + case V4L2_GFX_IOC_INFO: + { + struct v4l2_gfx_info_params *parms = + (struct v4l2_gfx_info_params *)arg; + parms->opencnt = vout->opened; + break; + } + case V4L2_GFX_IOC_PRODUCER: + { + struct v4l2_gfx_producer_params *parms = + (struct v4l2_gfx_producer_params *)arg; + vout->producer_flags = parms->flags; + if (!(vout->producer_flags & V4L2_GFX_PRODUCER_MASK_OPEN)) { + /* + * We decrement the count here because the Android + * mediaserver threads won't close the V4L2 device + */ + if (vout->opened) + vout->opened--; + } + break; + } + case V4L2_GFX_IOC_ACQ: + { + struct v4l2_gfx_buf_params *parms = + (struct v4l2_gfx_buf_params *)arg; + int bufid = -1; + int timedout; + rv = v4gfx_frame_lock(vout, &bufid); + if (!rv) { + parms->bufid = bufid; + parms->crop_top = vout->crop.top; + parms->crop_left = vout->crop.left; + parms->crop_width = vout->crop.width; + parms->crop_height = vout->crop.height; + GFXLOG(3, V4L2DEV(vout), "%d:%d:%d:%d:%d\n", + parms->bufid , + parms->crop_top , + parms->crop_left , + parms->crop_width , + parms->crop_height); + } + timedout = test_and_clear_bit(1, &vout->acquire_timedout); + if (timedout) { + GFXLOG(1, V4L2DEV(vout), "ACQ Timed out\n"); + rv = -ETIMEDOUT; + } + mutex_lock(&vout->lock); + if (!vout->streaming) { + GFXLOG(1, V4L2DEV(vout), "ACQ stream off\n"); + rv = -ENODEV; + } + mutex_unlock(&vout->lock); + break; + } + case V4L2_GFX_IOC_REL: + { + struct v4l2_gfx_buf_params *parms = + (struct v4l2_gfx_buf_params *)arg; + int bufid = parms->bufid; + rv = v4gfx_frame_unlock(vout, bufid); + break; + } + default: + rv = -EINVAL; + } + GFXLOG(1, V4L2DEV(vout), "Leaving %s (%d)\n", __func__, rv); + return rv; +} + +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) +{ + struct v4gfx_device *vout = fh; + GFXLOG(1, V4L2DEV(vout), "%s: %d\n", __func__, a->id); + return 0; +} + +struct v4l2_ioctl_ops v4gfx_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_log_status = vidioc_log_status, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_default = vidioc_default, + .vidioc_s_ctrl = vidioc_s_ctrl, +}; + +const struct v4l2_file_operations v4gfx_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + .mmap = vidfop_mmap, + .open = vidfop_open, + .release = vidfop_release, +}; + diff --git a/sgx/services4/v4l2-gfx/gfx_tiler.c b/sgx/services4/v4l2-gfx/gfx_tiler.c new file mode 100755 index 0000000..1e77983 --- /dev/null +++ b/sgx/services4/v4l2-gfx/gfx_tiler.c @@ -0,0 +1,152 @@ +/* + * drivers/media/video/omap/gfx_tiler.c + * + * Copyright (C) 2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "v4gfx.h" +#include "gfx_bc.h" + +#ifdef CONFIG_TILER_OMAP +#include <mach/tiler.h> +#define TILER_ALLOCATE_V4L2 +#endif + +void v4gfx_tiler_buffer_free(struct v4gfx_device *vout, unsigned int count, + unsigned int startindex) +{ + int i; + + if (startindex < 0) + startindex = 0; + if (startindex + count > VIDEO_MAX_FRAME) + count = VIDEO_MAX_FRAME - startindex; + + for (i = startindex; i < startindex + count; i++) { + if (vout->buf_phy_addr_alloced[i]) + tiler_free(vout->buf_phy_addr_alloced[i]); + if (vout->buf_phy_uv_addr_alloced[i]) + tiler_free(vout->buf_phy_uv_addr_alloced[i]); + vout->buf_phy_addr[i] = 0; + vout->buf_phy_addr_alloced[i] = 0; + vout->buf_phy_uv_addr[i] = 0; + vout->buf_phy_uv_addr_alloced[i] = 0; + } +} + +/* Allocate the buffers for TILER space. Ideally, the buffers will be ONLY + in tiler space, with different rotated views available by just a convert. + */ +int v4gfx_tiler_buffer_setup(struct v4gfx_device *vout, + unsigned int *count, unsigned int startindex, + struct v4l2_pix_format *pix) +{ + /* startindex is always passed as 0, possibly tidy up? */ + int i, aligned = 1, bpp; + enum tiler_fmt fmt; + int rv = 0; + + /* normalize buffers to allocate so we stay within bounds */ + int start = (startindex < 0) ? 0 : startindex; + int n_alloc = (start + *count > VIDEO_MAX_FRAME) ? + VIDEO_MAX_FRAME - start : *count; + + GFXLOG(1, V4L2DEV(vout), "+%s\n", __func__); + bpp = v4gfx_try_format(pix); + if (bpp <= 0) { + rv = bpp; /* error condition */ + goto end; + } + + GFXLOG(1, V4L2DEV(vout), "tiler buffer alloc: " + "count = %d, start = %d :\n", *count, startindex); + + /* special allocation scheme for NV12 format */ + if (V4L2_PIX_FMT_NV12 == pix->pixelformat) { + + tiler_alloc_packed_nv12(&n_alloc, ALIGN(pix->width, 128), + pix->height, + (void **) vout->buf_phy_addr + start, + (void **) vout->buf_phy_uv_addr + start, + (void **) vout->buf_phy_addr_alloced + start, + (void **) vout->buf_phy_uv_addr_alloced + start, + aligned); + + } else { + /* Only bpp of 1, 2, and 4 is supported by tiler */ + fmt = (bpp == 1 ? TILFMT_8BIT : + bpp == 2 ? TILFMT_16BIT : + bpp == 4 ? TILFMT_32BIT : TILFMT_INVALID); + if (fmt == TILFMT_INVALID) { + rv = -ENOMEM; + goto end; + } + + tiler_alloc_packed(&n_alloc, fmt, ALIGN(pix->width, 128 / bpp), + pix->height, + (void **) vout->buf_phy_addr + start, + (void **) vout->buf_phy_addr_alloced + start, + aligned); + } + + GFXLOG(1, V4L2DEV(vout), + "allocated %d buffers\n", n_alloc); + + if (n_alloc < *count) { + if (n_alloc && (startindex == -1 || + V4L2_MEMORY_MMAP != vout->memory)) { + /* TODO: check this condition's logic */ + v4gfx_tiler_buffer_free(vout, n_alloc, start); + *count = 0; + rv = -ENOMEM; + goto end; + } + } + + for (i = start; i < start + n_alloc; i++) { + GFXLOG(1, V4L2DEV(vout), + "y=%08lx (%d) uv=%08lx (%d)\n", + vout->buf_phy_addr[i], + vout->buf_phy_addr_alloced[i] ? 1 : 0, + vout->buf_phy_uv_addr[i], + vout->buf_phy_uv_addr_alloced[i] ? 1 : 0); + } + + *count = n_alloc; +end: + GFXLOG(1, V4L2DEV(vout), "-%s [%d]\n", __func__, rv); + return rv; +} + +void v4gfx_tiler_image_incr(struct v4gfx_device *vout, int *cpu_pgwidth, + int *tiler_increment) +{ + /* for NV12, Y buffer is 1bpp*/ + if (V4L2_PIX_FMT_NV12 == vout->pix.pixelformat) { + *cpu_pgwidth = + (vout->pix.width + TILER_PAGE - 1) & ~(TILER_PAGE - 1); + *tiler_increment = 64 * TILER_WIDTH; + } else { + *cpu_pgwidth = (vout->pix.width * vout->bpp + TILER_PAGE - 1) & + ~(TILER_PAGE - 1); + if (vout->bpp > 1) + *tiler_increment = 2 * 64 * TILER_WIDTH; + else + *tiler_increment = 64 * TILER_WIDTH; + } +} + +void v4gfx_tiler_image_incr_uv(struct v4gfx_device *vout, int *tiler_increment) +{ + if (vout->pix.pixelformat == V4L2_PIX_FMT_NV12) + *tiler_increment = 2 * 64 * TILER_WIDTH; + /* Otherwise do nothing */ +} diff --git a/sgx/services4/v4l2-gfx/v4gfx.h b/sgx/services4/v4l2-gfx/v4gfx.h new file mode 100644 index 0000000..6257270 --- /dev/null +++ b/sgx/services4/v4l2-gfx/v4gfx.h @@ -0,0 +1,181 @@ +/* + * drivers/media/video/omapgfx/v4gfx.h + * + * Copyright (C) 2010 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __V4L2_GFX_H__ +#define __V4L2_GFX_H__ + +#include <linux/version.h> +#include <media/videobuf-core.h> +#include <media/v4l2-device.h> +#include <asm/atomic.h> + +/* + * First device node will be: /dev/video<VOUT_DEVICENODE_SUFFIX> + * See also /sys/devices/virtual/video4linux/<node>/name which will be + * whatever the value of VOUT_NAME is + */ +#define VOUT_DEVICENODE_SUFFIX 100 + +/* Maximum number of parallel video streams supported */ +#define DEVICE_COUNT 6 + +struct gbl_v4gfx { + struct mutex mtx; + int state; + struct v4l2_device v4l2_dev; + struct v4gfx_device *vout; +}; + +/* per-device data structure */ +struct v4gfx_device { + + struct video_device *vfd; + + struct gbl_v4gfx *gbl_dev; + + int bpp; /* bytes per pixel */ + + enum v4l2_buf_type type; + + struct v4l2_pix_format pix; + + struct v4l2_rect crop; + + enum v4l2_memory memory; /* how memory is managed for the device */ + + /* we don't allow to change image fmt/size once buffer has + * been allocated + */ + int buffer_allocated; /* count of buffers allocated */ + + /* allow to reuse previously allocated buffer which is big enough */ + int buffer_size; + + unsigned long buf_phy_addr[VIDEO_MAX_FRAME]; + + unsigned long buf_phy_uv_addr[VIDEO_MAX_FRAME]; /* NV12 support*/ + + /* keep which buffers we actually allocated (via tiler) */ + unsigned long buf_phy_uv_addr_alloced[VIDEO_MAX_FRAME]; + + unsigned long buf_phy_addr_alloced[VIDEO_MAX_FRAME]; + + /* + For each V4L2 buffer requested we will have an array of page addresses + to give through the buffer class API + */ + unsigned long **buf_phys_addr_array; + + int mmap_count; + + int opened; /* inc/dec on open/close of the device */ + + bool streaming; /* is streaming is in progress? */ + + struct mutex lock; /* protect shared data structures in ioctl */ + + struct videobuf_buffer *cur_frm; + + struct videobuf_buffer *locked_frm; + + struct videobuf_queue vbq; + + /* + * Buffers added by QBUF from the producer application + */ + struct list_head dma_queue; + + /* + * Buffers marked as done with by the consumer application but could + * still be being used by the GPU. DQBUF will examine this queue + * for available buffers. + */ + struct list_head sync_queue; + + wait_queue_head_t sync_done; + + unsigned long producer_ready; + + wait_queue_head_t consumer_wait; + + /* + * If acquire_timeout_ms is non-zero the acquire_timer will be reset + * when buffers are queued. If the timer expires ETIMEOUT will be + * returned via the V4L2_GFX_IOC_ACQ ioctl. + */ + struct timer_list acquire_timer; + + unsigned int acquire_timeout_ms; + + unsigned long acquire_timedout; + + spinlock_t vbq_lock; /* spinlock for videobuf queues */ + + unsigned int producer_flags; + + int deviceidx; /* Device index for buffer class */ +}; + +extern int debug; + +#define GFXLOG(level, dev, fmt, arg...) \ +do { \ + if (debug >= level) \ + printk(KERN_INFO "%s: " fmt, (dev)->name , ## arg); \ +} while (0) + +#define GFXLOGA(level, fmt, arg...) \ +do { \ + if (debug >= level) \ + printk(KERN_INFO "v4l2-gfx: " fmt, ## arg); \ +} while (0) + +/* + * Convert local handle to v4l2_dev, currently only a global dev is supported + */ +#define V4L2DEV(vout) (&vout->gbl_dev->v4l2_dev) + +/* tiler */ +void v4gfx_tiler_buffer_free( + struct v4gfx_device *vout, unsigned int count, + unsigned int startindex); + +int v4gfx_tiler_buffer_setup(struct v4gfx_device *vout, + unsigned int *count, unsigned int startindex, + struct v4l2_pix_format *pix); + +void v4gfx_tiler_image_incr(struct v4gfx_device *vout, + int *cpu_pgwidth, int *tiler_increment); + +void v4gfx_tiler_image_incr_uv(struct v4gfx_device *vout, int *tiler_increment); + +/* v4gfx */ +int v4gfx_try_format(struct v4l2_pix_format *pix); +void v4gfx_buffer_array_free(struct v4gfx_device *vout, int cnt); +extern struct v4l2_ioctl_ops v4gfx_ioctl_ops; +extern const struct v4l2_file_operations v4gfx_fops; +extern void v4gfx_acquire_timer(unsigned long arg); + +/* Other stuff */ +#define YUYV_BPP 2 +#define RGB565_BPP 2 +#define RGB24_BPP 3 +#define RGB32_BPP 4 + +#define VOUT_NAME "v4gfx" + +/* configuration macros */ +#define VOUT_MAJOR_VERSION 0 +#define VOUT_MINOR_VERSION 0 +#define VOUT_RELEASE 0 +#define VOUT_VERSION \ + KERNEL_VERSION(VOUT_MAJOR_VERSION, VOUT_MINOR_VERSION, VOUT_RELEASE) + +#endif /* ifndef __V4L2_GFX_H__ */ |