1130 lines
28 KiB
C
1130 lines
28 KiB
C
/*
|
|
* v4l2_common.c
|
|
*
|
|
* Copyright (C) 2012 by Digi International Inc.
|
|
* 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 as published by
|
|
* the Free Software Foundation.
|
|
*
|
|
* Description: V4L2 common library.
|
|
*
|
|
* V4L2 API @ http://v4l2spec.bytesex.org/
|
|
*
|
|
*/
|
|
|
|
#include "v4l2_defs.h"
|
|
|
|
#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \
|
|
((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)
|
|
#define IPU_MAX_CH 32
|
|
#define _MAKE_ALT_CHAN(ch) (ch | (IPU_MAX_CH << 24))
|
|
#define NO_DMA 0x3F
|
|
/*!
|
|
* Enumeration of IPU logical channels. An IPU logical channel is defined as a
|
|
* combination of an input (memory to IPU), output (IPU to memory), and/or
|
|
* secondary input IDMA channels and in some cases an Image Converter task.
|
|
* Some channels consist of only an input or output.
|
|
*/
|
|
typedef enum {
|
|
CHAN_NONE = -1,
|
|
MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48),
|
|
MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49),
|
|
MEM_ROT_PP_MEM = _MAKE_CHAN(3, 47, NO_DMA, NO_DMA, 50),
|
|
|
|
MEM_PRP_ENC_MEM = _MAKE_CHAN(4, 12, 14, 17, 20),
|
|
MEM_PRP_VF_MEM = _MAKE_CHAN(5, 12, 14, 17, 21),
|
|
MEM_PP_MEM = _MAKE_CHAN(6, 11, 15, 18, 22),
|
|
|
|
MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA),
|
|
MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA),
|
|
MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA),
|
|
MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA),
|
|
|
|
MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA),
|
|
MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA),
|
|
MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0),
|
|
MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0),
|
|
|
|
DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
|
|
DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
|
|
|
|
CSI_MEM0 = _MAKE_CHAN(15, NO_DMA, NO_DMA, NO_DMA, 0),
|
|
CSI_MEM1 = _MAKE_CHAN(16, NO_DMA, NO_DMA, NO_DMA, 1),
|
|
CSI_MEM2 = _MAKE_CHAN(17, NO_DMA, NO_DMA, NO_DMA, 2),
|
|
CSI_MEM3 = _MAKE_CHAN(18, NO_DMA, NO_DMA, NO_DMA, 3),
|
|
|
|
CSI_MEM = CSI_MEM0,
|
|
|
|
CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20),
|
|
CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21),
|
|
|
|
/* for vdi mem->vdi->ic->mem , add graphics plane and alpha*/
|
|
MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21),
|
|
MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21),
|
|
MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21),
|
|
|
|
/* for vdi mem->vdi->mem */
|
|
MEM_VDI_MEM_P = _MAKE_CHAN(24, 8, NO_DMA, NO_DMA, 5),
|
|
MEM_VDI_MEM = _MAKE_CHAN(25, 9, NO_DMA, NO_DMA, 5),
|
|
MEM_VDI_MEM_N = _MAKE_CHAN(26, 10, NO_DMA, NO_DMA, 5),
|
|
|
|
/* fake channel for vdoa to link with IPU */
|
|
MEM_VDOA_MEM = _MAKE_CHAN(27, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
|
|
|
|
MEM_PP_ADC = CHAN_NONE,
|
|
ADC_SYS2 = CHAN_NONE,
|
|
|
|
} ipu_channel_t;
|
|
|
|
#define BRIGHTNESS_MAX_VALUE 0xFF
|
|
#define BRIGTHNESS_MIN_VALUE 0
|
|
#define SATURATION_MAX_VALUE 0xFF
|
|
#define SATURATION_MIN_VALUE 0
|
|
#define GLOBAL_ALPHA_MAX_VALUE 0xFF
|
|
/* This is confusing, because they are inverted */
|
|
#define LOCAL_ALPHA_MAX_VALUE 0x0
|
|
#define LOCAL_ALPHA_MIN_VALUE 0xFF
|
|
|
|
/* Returns the open file descriptor if the fb is an MXC fb overlay,
|
|
* error otherwise. */
|
|
static int v4l2_mxc_is_overlay(char *fb_device)
|
|
{
|
|
int fd, ret;
|
|
struct fb_fix_screeninfo fb_fix;
|
|
|
|
fd = ret = -1;
|
|
|
|
if ((fd = open(fb_device, O_RDWR)) < 0) {
|
|
log("Unable to open frame buffer 0\n");
|
|
return ret;
|
|
}
|
|
|
|
if ((ret = ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix)) < 0) {
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
if (!strcmp(fb_fix.id, "DISP3 FG"))
|
|
return fd;
|
|
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
/* Return overlay background framebuffer device path, error otherwise */
|
|
int v4l2_get_overlay_bg(char *fb_device)
|
|
{
|
|
static const char *fb_path = "/dev/fb";
|
|
int ipu_ch, i;
|
|
int fd = -1;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
sprintf(fb_device, "%s%d", fb_path, i);
|
|
if ((fd = open(fb_device, O_RDWR)) < 0) {
|
|
log("Unable to open frame buffer %s\n", fb_device);
|
|
goto error;
|
|
}
|
|
if (ioctl(fd, MXCFB_GET_FB_IPU_CHAN, &ipu_ch) < 0) {
|
|
log("ioctl MXCFB_GET_FB_IPU_CHAN error\n");
|
|
close(fd);
|
|
goto error;
|
|
}
|
|
close(fd);
|
|
if (ipu_ch == MEM_BG_SYNC) {
|
|
log("Overlay background frame buffer device: %s\n", fb_device);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
/* Enables or disables the V4L2 previewing */
|
|
int v4l2_overlay_control(int fd_overlay, int start)
|
|
{
|
|
/* Start overlay */
|
|
return ioctl(fd_overlay, VIDIOC_OVERLAY, &start);
|
|
}
|
|
|
|
/* Returns a string representation of some v4L2 pixel formats, or its
|
|
* numerical code string otherwise. */
|
|
char *v4l2_fmt_str(int pixelformat)
|
|
{
|
|
static char tmp[64] = "";
|
|
|
|
switch (pixelformat) {
|
|
case IPU_PIX_FMT_RGB332:
|
|
return "RGB332";
|
|
case IPU_PIX_FMT_RGB555:
|
|
return "RGB555";
|
|
case IPU_PIX_FMT_RGB565:
|
|
return "RGB565";
|
|
case IPU_PIX_FMT_RGB666:
|
|
return "RGB666";
|
|
case IPU_PIX_FMT_RGB24:
|
|
return "RGB24";
|
|
case IPU_PIX_FMT_BGR32:
|
|
return "BGR32";
|
|
case IPU_PIX_FMT_BGRA32:
|
|
return "BGRA32";
|
|
case IPU_PIX_FMT_RGB32:
|
|
return "RGB32";
|
|
case IPU_PIX_FMT_RGBA32:
|
|
return "RGBA32";
|
|
case IPU_PIX_FMT_ABGR32:
|
|
return "ABGR32";
|
|
case IPU_PIX_FMT_BGR24:
|
|
return "BGR24";
|
|
case IPU_PIX_FMT_YUYV:
|
|
return "YUYV";
|
|
case IPU_PIX_FMT_UYVY:
|
|
return "UYVY";
|
|
case IPU_PIX_FMT_YVYU:
|
|
return "YVYU";
|
|
case IPU_PIX_FMT_VYUY:
|
|
return "VYUY";
|
|
case IPU_PIX_FMT_Y41P:
|
|
return "Y41P";
|
|
case IPU_PIX_FMT_YUV444:
|
|
return "YUV444";
|
|
case IPU_PIX_FMT_VYU444:
|
|
return "VYU444";
|
|
case IPU_PIX_FMT_NV12:
|
|
return "NV12";
|
|
// Missing formats
|
|
default:
|
|
sprintf(tmp, "Undecoded format: %x\n", pixelformat);
|
|
return tmp;
|
|
}
|
|
}
|
|
|
|
/* Returns true if the specified V4L2 capability is supported by the
|
|
* device, false or error otherwise.*/
|
|
int v4l2_is_capability_supported(int fd, int cap)
|
|
{
|
|
struct v4l2_capability capability;
|
|
int ret = 0;
|
|
|
|
/* Check the device capabilities */
|
|
memset(&capability, 0, sizeof(capability));
|
|
ret = ioctl(fd, VIDIOC_QUERYCAP, &capability);
|
|
if (ret < 0) {
|
|
if (errno == -EINVAL)
|
|
log("Not a V4L2 device.\n");
|
|
return ret;
|
|
}
|
|
|
|
if (capability.capabilities & cap) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Returns true if the specified V4L2 video standard is supported by the
|
|
* device, false or error otherwise.*/
|
|
int v4l2_is_video_std_supported(int fd, int standard)
|
|
{
|
|
v4l2_std_id std_id;
|
|
int ret = 0;
|
|
|
|
/* Check video standard */
|
|
memset(&std_id, 0, sizeof(std_id));
|
|
if ((ret = ioctl(fd, VIDIOC_G_STD, &std_id)) < 0) {
|
|
log("VIDIOC_G_STD failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (std_id & standard)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Returns true if the specified V4L2 capture pixel format is supported by the
|
|
* device, false or error otherwise.*/
|
|
int v4l2_is_format_supported(int fd, int format)
|
|
{
|
|
struct v4l2_fmtdesc fmtdesc;
|
|
int ret = 0;
|
|
|
|
/* Check whether the requested format is supported by the device */
|
|
memset(&fmtdesc, 0, sizeof(fmtdesc));
|
|
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
do {
|
|
ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
|
|
fmtdesc.index++;
|
|
if (fmtdesc.pixelformat == format)
|
|
break;
|
|
} while (ret >= 0);
|
|
|
|
if (ret == 0)
|
|
return TRUE;
|
|
|
|
if (ret < 0 && errno != EINVAL)
|
|
log("VIDIOC_ENUM_FMT failed with %d\n", errno);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check whether a specified framerate is configured in the device */
|
|
int v4l2_check_frame_rate(int fd, int framerate)
|
|
{
|
|
struct v4l2_streamparm streamparm;
|
|
|
|
memset(&streamparm, 0, sizeof(streamparm));
|
|
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
streamparm.parm.capture.timeperframe.numerator = 0;
|
|
streamparm.parm.capture.timeperframe.denominator = 0;
|
|
if (ioctl(fd, VIDIOC_G_PARM, &streamparm) < 0) {
|
|
log("get frame rate failed\n");
|
|
return FALSE;
|
|
}
|
|
log("Frame rate is %d\n", streamparm.parm.capture.timeperframe.denominator);
|
|
|
|
if (streamparm.parm.capture.timeperframe.denominator == framerate)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set the specified framerate on the V4L2 device */
|
|
int v4l2_set_frame_rate(int fd, int framerate)
|
|
{
|
|
struct v4l2_streamparm streamparm;
|
|
|
|
memset(&streamparm, 0, sizeof(streamparm));
|
|
|
|
/* Set stream parameters */
|
|
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
streamparm.parm.capture.timeperframe.numerator = 1;
|
|
streamparm.parm.capture.timeperframe.denominator = framerate;
|
|
|
|
return ioctl(fd, VIDIOC_S_PARM, &streamparm);
|
|
}
|
|
|
|
/* Set the specified capture mode on the V4L2 device */
|
|
int v4l2_set_capture_mode(int fd, int capturemode)
|
|
{
|
|
struct v4l2_streamparm streamparm;
|
|
|
|
memset(&streamparm, 0, sizeof(streamparm));
|
|
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
if (ioctl(fd, VIDIOC_G_PARM, &streamparm) < 0) {
|
|
log("get frame rate failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set capture mode */
|
|
streamparm.parm.capture.capturemode = capturemode;
|
|
|
|
return ioctl(fd, VIDIOC_S_PARM, &streamparm);
|
|
}
|
|
|
|
/* Return true if the output specified by name is supported by the device,
|
|
* false or error otherwise */
|
|
int v4l2_check_output(int fd, char *name)
|
|
{
|
|
struct v4l2_output output;
|
|
int ret = 0;
|
|
|
|
/* Check what are the available video outputs */
|
|
memset(&output, 0, sizeof(output));
|
|
|
|
do {
|
|
ret = ioctl(fd, VIDIOC_ENUMOUTPUT, &output);
|
|
output.index++;
|
|
if (ret >= 0)
|
|
log("Supported output %s\n", output.name);
|
|
if (!strcmp((char *)output.name, name))
|
|
return TRUE;
|
|
} while (ret >= 0);
|
|
|
|
if (ret != 0 && errno != EINVAL) {
|
|
log("VIDIOC_ENUMOUTPUT failed with %d\n", errno);
|
|
return ret;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set the current video output */
|
|
int v4l2_set_output(int fd, struct fb_fix_screeninfo *fb_fix)
|
|
{
|
|
struct v4l2_output output;
|
|
int ret = 0;
|
|
|
|
memset(&output, 0, sizeof(output));
|
|
while ((ret = ioctl(fd, VIDIOC_ENUMOUTPUT, &output)) >= 0) {
|
|
if (!strcmp((char *)output.name, fb_fix->id)) {
|
|
if ((ret = ioctl(fd, VIDIOC_S_OUTPUT, &(output.index))) < 0) {
|
|
log("Set output failed with %d\n", ret);
|
|
}
|
|
break;
|
|
}
|
|
output.index++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Sets a specified pixel format and scale to the V4L2 device overlay */
|
|
int v4l2_set_format_capture(int fd, int pixelformat, int height, int width)
|
|
{
|
|
struct v4l2_format format;
|
|
int ret = 0;
|
|
|
|
/* Get the data format */
|
|
if ((ret = ioctl(fd, VIDIOC_G_FMT, &format)) < 0) {
|
|
log("Get format failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
format.fmt.pix.width = width;
|
|
format.fmt.pix.height = height;
|
|
format.fmt.pix.pixelformat = pixelformat;
|
|
|
|
if ((ret = ioctl(fd, VIDIOC_S_FMT, &format)) < 0) {
|
|
log("Set format failed with %d\n", ret);
|
|
}
|
|
log("Pixel format set to %s\n", v4l2_fmt_str(pixelformat));
|
|
log("Sensor cropping dimensions set to H %d W %d\n", height, width);
|
|
return ret;
|
|
}
|
|
|
|
/* Sets a specified pixel format and scale to the V4L2 device overlay */
|
|
int v4l2_set_format_overlay(int fd, int top, int left, int height, int width)
|
|
{
|
|
struct v4l2_format format;
|
|
int ret = 0;
|
|
|
|
/* Get the data format */
|
|
format.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
|
|
if ((ret = ioctl(fd, VIDIOC_G_FMT, &format)) < 0) {
|
|
log("Get format failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
format.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
|
|
format.fmt.win.w.top = top;
|
|
format.fmt.win.w.left = left;
|
|
format.fmt.win.w.width = width;
|
|
format.fmt.win.w.height = height;
|
|
|
|
if ((ret = ioctl(fd, VIDIOC_S_FMT, &format)) < 0) {
|
|
log("Set format failed with %d\n", ret);
|
|
}
|
|
log("Overlay dimensions set to H %d W %d L %d T %d\n", height, width, left, top);
|
|
return ret;
|
|
}
|
|
|
|
int v4l2_fb_blank(int fd)
|
|
{
|
|
return (ioctl(fd, FBIOBLANK, FB_BLANK_NORMAL));
|
|
}
|
|
|
|
int v4l2_fb_unblank(int fd)
|
|
{
|
|
return (ioctl(fd, FBIOBLANK, FB_BLANK_UNBLANK));
|
|
}
|
|
|
|
static int v4l2_fb_put_vinfo(int fd, struct fb_var_screeninfo *fb_var)
|
|
{
|
|
return (ioctl(fd, FBIOPUT_VSCREENINFO, fb_var));
|
|
}
|
|
|
|
static int v4l2_fb_get_vinfo(int fd, struct fb_var_screeninfo *fb_var)
|
|
{
|
|
return (ioctl(fd, FBIOGET_VSCREENINFO, fb_var));
|
|
}
|
|
|
|
static int v4l2_fb_get_finfo(int fd, struct fb_fix_screeninfo *fb_fix)
|
|
{
|
|
return (ioctl(fd, FBIOGET_FSCREENINFO, fb_fix));
|
|
}
|
|
|
|
/* Retrieves fixed and variable framebuffer information */
|
|
int v4l2_fb_get_info(char *fb_device, struct fb_fix_screeninfo *fb_fix,
|
|
struct fb_var_screeninfo *fb_var)
|
|
{
|
|
int ret = 0;
|
|
int fd = 0;
|
|
|
|
/* Open framebuffer device */
|
|
if ((fd = open(fb_device, O_RDWR)) < 0) {
|
|
log("Unable to open frame buffer %s\n", fb_device);
|
|
return -EIO;
|
|
}
|
|
|
|
log("Opened %s\n", fb_device);
|
|
|
|
/* Obtain fixed and variable screen info for framebuffer */
|
|
if ((ret = v4l2_fb_get_vinfo(fd, fb_var)) < 0)
|
|
goto out;
|
|
|
|
if ((ret = v4l2_fb_get_finfo(fd, fb_fix)) < 0)
|
|
goto out;
|
|
|
|
out:
|
|
return fd;
|
|
}
|
|
|
|
/* Identifies the available outputs for a framebuffer device.
|
|
*
|
|
* MXC hardcoded ID strings are:
|
|
* DISP3 BG: For background framebuffer.
|
|
* DISP3 BG - DI1: For secondary background framebuffer.
|
|
* DISP3 FG: For overlay (foreground) framebuffer.
|
|
*/
|
|
void v4l2_ident_outputs(int fd)
|
|
{
|
|
struct v4l2_output output;
|
|
int ret = 0;
|
|
|
|
/* Identify available outputs */
|
|
memset(&output, 0, sizeof(output));
|
|
do {
|
|
ret = ioctl(fd, VIDIOC_ENUMOUTPUT, &output);
|
|
output.index++;
|
|
log("Supported output %s\n", output.name);
|
|
} while (ret >= 0);
|
|
}
|
|
|
|
/* Returns an opened file descriptor for the MXC overlay framebuffer device. */
|
|
int v4l2_mxc_find_overlay(struct fb_fix_screeninfo *fb_fix, int *fb_fg_fd)
|
|
{
|
|
/* Look for overlay (foreground) framebuffer */
|
|
if (!strcmp(fb_fix->id, "DISP3 BG")) {
|
|
/* Overlay can be /dev/fb1 or /dev/fb2 */
|
|
if ((*fb_fg_fd = v4l2_mxc_is_overlay("/dev/fb1")) < 0) {
|
|
*fb_fg_fd = v4l2_mxc_is_overlay("/dev/fb2");
|
|
log("Overlay framebuffer is /dev/fb2\n");
|
|
} else {
|
|
log("Overlay framebuffer is /dev/fb1\n");
|
|
}
|
|
|
|
if (*fb_fg_fd < 0) {
|
|
log("No overlay framebuffer found.\n");
|
|
return -1;
|
|
}
|
|
} else if (!strcmp(fb_fix->id, "DISP3 BG - DI1")) {
|
|
/* Overlay must be /dev/fb2 */
|
|
if ((*fb_fg_fd = v4l2_mxc_is_overlay("/dev/fb2")) < 0) {
|
|
log("No overlay framebuffer found.\n");
|
|
return -1;
|
|
} else {
|
|
log("Overlay framebuffer is /dev/fb2\n");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Sets the V4L2 device overlay framebuffer format, dimensions and type */
|
|
int v4l2_overlay_set_framebuffer(int fd, struct fb_fix_screeninfo *fb_fix,
|
|
struct fb_var_screeninfo *fb_var, int non_destructive)
|
|
{
|
|
struct v4l2_framebuffer framebuffer;
|
|
int ret = 0;
|
|
|
|
memset(&framebuffer, 0, sizeof(framebuffer));
|
|
|
|
framebuffer.fmt.width = fb_var->xres;
|
|
framebuffer.fmt.height = fb_var->yres;
|
|
|
|
if (fb_var->bits_per_pixel == 32) {
|
|
framebuffer.fmt.pixelformat = IPU_PIX_FMT_BGR32;
|
|
framebuffer.fmt.bytesperline = 4 * framebuffer.fmt.width;
|
|
log("Framebuffer: BGR32\n");
|
|
} else if (fb_var->bits_per_pixel == 24) {
|
|
framebuffer.fmt.pixelformat = IPU_PIX_FMT_BGR24;
|
|
framebuffer.fmt.bytesperline = 3 * framebuffer.fmt.width;
|
|
log("Framebuffer: BGR24\n");
|
|
} else if (fb_var->bits_per_pixel == 16) {
|
|
framebuffer.fmt.pixelformat = IPU_PIX_FMT_RGB565;
|
|
framebuffer.fmt.bytesperline = 2 * framebuffer.fmt.width;
|
|
log("Framebuffer: RGB565\n");
|
|
}
|
|
|
|
/* Draw over the framebuffer, destructive overlay */
|
|
if (!non_destructive) {
|
|
framebuffer.flags = V4L2_FBUF_FLAG_PRIMARY;
|
|
/* Physical base address of the framebuffer, that is the address of the
|
|
* pixel in the top left corner of the framebuffer. Only relevant
|
|
* for destructive video overlay. */
|
|
framebuffer.base = (void *)fb_fix->smem_start
|
|
+ fb_fix->line_length * fb_var->yoffset;
|
|
} else {
|
|
framebuffer.flags = V4L2_FBUF_FLAG_OVERLAY;
|
|
}
|
|
|
|
if ((ret = ioctl(fd, VIDIOC_S_FBUF, &framebuffer)) < 0) {
|
|
log("set framebuffer failed with %d\n", errno);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Reset V4L2 device cropping rectangle to its default. */
|
|
int v4l2_reset_cropping_rectangle(int fd)
|
|
{
|
|
struct v4l2_crop crop;
|
|
struct v4l2_cropcap cropcap;
|
|
int ret = 0;
|
|
|
|
memset(&cropcap, 0, sizeof(cropcap));
|
|
memset(&crop, 0, sizeof(crop));
|
|
|
|
cropcap.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
|
|
if ((ret = ioctl(fd, VIDIOC_CROPCAP, &cropcap)) < 0) {
|
|
log("VIDIOC_CROPCAP failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
crop.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
|
|
crop.c = cropcap.defrect;
|
|
/* Ignore if cropping is not supported (EINVAL). */
|
|
if (((ret = ioctl(fd, VIDIOC_S_CROP, &crop)) < 0)
|
|
&& errno != EINVAL) {
|
|
log("VIDIOC_S_CROP failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int v4l2_get_cropping_limits(int fd, struct v4l2_rect *limits)
|
|
{
|
|
int retval;
|
|
struct v4l2_cropcap cropcap;
|
|
|
|
cropcap.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
|
|
if ((retval = ioctl(fd, VIDIOC_CROPCAP, &cropcap)) < 0) {
|
|
log("VIDIOC_CROPCAP failed: %d", retval);
|
|
return retval;
|
|
}
|
|
|
|
memcpy(limits, &cropcap.bounds, sizeof(cropcap.bounds));
|
|
return 0;
|
|
}
|
|
|
|
int v4l2_crop_input(int fd, struct v4l2_rect *crop_rectangle)
|
|
{
|
|
int retval;
|
|
struct v4l2_crop crop;
|
|
struct v4l2_rect crop_bounds;
|
|
|
|
/* Sanity check */
|
|
retval = v4l2_get_cropping_limits(fd, &crop_bounds);
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
if (crop_rectangle->left + crop_rectangle->width > crop_bounds.left + crop_bounds.width ||
|
|
crop_rectangle->top + crop_rectangle->height > crop_bounds.top + crop_bounds.height ||
|
|
crop_rectangle->width < 8 || crop_rectangle->height < 8) {
|
|
log("Invalid input\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
crop.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
|
|
memcpy(&crop.c, crop_rectangle, sizeof(crop.c));
|
|
|
|
retval = ioctl(fd, VIDIOC_S_CROP, &crop);
|
|
if (retval < 0) {
|
|
log("VIDIOC_S_CROP failed: %d", retval);
|
|
return retval;
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int v4l2_area_zoom(int fd, struct v4l2_rect *crop_rectangle)
|
|
{
|
|
int retval;
|
|
struct v4l2_rect crop_bounds;
|
|
|
|
retval = v4l2_overlay_control(fd, 0);
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
retval = v4l2_crop_input(fd, crop_rectangle);
|
|
if (retval < 0) {
|
|
if (retval == -EINVAL) {
|
|
v4l2_get_cropping_limits(fd, &crop_bounds);
|
|
printf("\nInvalid rectangle!\n");
|
|
printf("Input bounds are:\tTop: %d\tLeft: %d\tWidth: %d\tHeight: %d",
|
|
crop_bounds.top, crop_bounds.left, crop_bounds.width,
|
|
crop_bounds.height);
|
|
printf("\nRequested rectangle: \tTop: %d\tLeft: %d"
|
|
"\tWidth: %d\tHeight: %d",
|
|
crop_rectangle->top, crop_rectangle->left,
|
|
crop_rectangle->width, crop_rectangle->height);
|
|
printf("\nValid rectangles are those which:\n"
|
|
" Height >= 8\n"
|
|
" Width >= 8\n"
|
|
" Top + Height < Bound_Top + Bound_Height\n"
|
|
" Left + Width < Bound_Left + Bound_Width\n\n");
|
|
} else {
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
retval = v4l2_overlay_control(fd, 1);
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int v4l2_zoom(int fd, int zoom_percentage)
|
|
{
|
|
int retval;
|
|
float zoom;
|
|
struct v4l2_rect crop_rectangle;
|
|
struct v4l2_rect crop_bounds;
|
|
|
|
/* Get cropping capabilities */
|
|
retval = v4l2_get_cropping_limits(fd, &crop_bounds);
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
/* Calculate new values */
|
|
zoom = (float)zoom_percentage / 100;
|
|
crop_rectangle.height = crop_bounds.height / zoom;
|
|
crop_rectangle.width = crop_bounds.width / zoom;
|
|
crop_rectangle.left = (crop_bounds.width - crop_rectangle.width) / 2;
|
|
crop_rectangle.top = (crop_bounds.height - crop_rectangle.height) / 2;
|
|
|
|
return v4l2_area_zoom(fd, &crop_rectangle);
|
|
}
|
|
|
|
int v4l2_set_control(int fd, int id, int value)
|
|
{
|
|
int retval;
|
|
struct v4l2_control control;
|
|
|
|
control.id = id;
|
|
control.value = value;
|
|
|
|
retval = ioctl(fd, VIDIOC_S_CTRL, &control);
|
|
if (retval < 0) {
|
|
log("VIDIOC_S_CTRL failed. Id: %d\tValue: %d\n", id, value);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_get_control(int fd, int id, int *value)
|
|
{
|
|
int retval;
|
|
struct v4l2_control control;
|
|
|
|
control.id = id;
|
|
control.value = 0;
|
|
retval = ioctl(fd, VIDIOC_G_CTRL, &control);
|
|
if (retval < 0) {
|
|
log("VIDIOC_G_CTRL failed. Id: %d\tValue: %d\n", id, *value);
|
|
} else {
|
|
*value = control.value;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_set_rotate(int fd, int rotate)
|
|
{
|
|
/* This is not supported by V4L2, it's done by the IPUv3 driver */
|
|
return v4l2_set_control(fd, V4L2_CID_MXC_VF_ROT, rotate);
|
|
}
|
|
|
|
int v4l2_rotate(int fd, int rotate)
|
|
{
|
|
int retval;
|
|
|
|
/* Stop overlay */
|
|
retval = v4l2_overlay_control(fd, 0);
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
/* Set new rotation value */
|
|
retval = v4l2_set_rotate(fd, rotate);
|
|
if (retval < 0 && errno != EINVAL)
|
|
return retval;
|
|
|
|
/* Start overlay */
|
|
return v4l2_overlay_control(fd, 1);
|
|
}
|
|
|
|
int v4l2_set_brightness(int fd, int percentage)
|
|
{
|
|
int retval;
|
|
int brightness = percentage * BRIGHTNESS_MAX_VALUE / 100;
|
|
|
|
retval = v4l2_set_control(fd, V4L2_CID_BRIGHTNESS, brightness);
|
|
if (retval < 0) {
|
|
log("Set control V4L2_CID_BRIGHTNESS failed. Error: %d\n", retval);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_get_brightness(int fd, int *percentage)
|
|
{
|
|
int retval;
|
|
int brightness = 0;
|
|
|
|
retval = v4l2_get_control(fd, V4L2_CID_BRIGHTNESS, &brightness);
|
|
if (retval < 0) {
|
|
log("Get control V4L2_CID_BRIGHTNESS failed. Error: %s\n", strerror(errno));
|
|
} else {
|
|
/* (+ 0.5 * BRIGHTNESS_MAX_VALUE) to round integer not using libm */
|
|
*percentage = (brightness * 100 + 0.5 * BRIGHTNESS_MAX_VALUE) / BRIGHTNESS_MAX_VALUE;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_set_saturation(int fd, int percentage)
|
|
{
|
|
int retval;
|
|
int saturation = 0;
|
|
|
|
/* Valid values are 0, 25, 37, 50, 75, 100 and 150 (hardware limitation) */
|
|
if (percentage < 0 || percentage > 150) {
|
|
log("Invalid parameter\n");
|
|
saturation = 50;
|
|
} else if (percentage == 0) {
|
|
saturation = 0;
|
|
} else if (percentage <= 25) {
|
|
saturation = 25;
|
|
} else if (percentage <= 37) {
|
|
saturation = 37;
|
|
} else if (percentage <= 50) {
|
|
saturation = 50;
|
|
} else if (percentage <= 75) {
|
|
saturation = 75;
|
|
} else if (percentage <= 100) {
|
|
saturation = 100;
|
|
} else if (percentage <= 150) {
|
|
saturation = 150;
|
|
}
|
|
|
|
retval = v4l2_set_control(fd, V4L2_CID_SATURATION, saturation);
|
|
if (retval < 0) {
|
|
log("Set control V4L2_CID_SATURATION failed. Error: %s\n", strerror(errno));
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_get_saturation(int fd, int *percentage)
|
|
{
|
|
int retval;
|
|
int saturation = 0;
|
|
|
|
retval = v4l2_get_control(fd, V4L2_CID_SATURATION, &saturation);
|
|
if (retval < 0) {
|
|
log("Get control V4L2_CID_SATURATION failed. Error: %s\n", strerror(errno));
|
|
} else {
|
|
*percentage = saturation;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_set_red_balance(int fd, int red)
|
|
{
|
|
int retval;
|
|
|
|
/* Sanity check */
|
|
if (red > 0xFF) {
|
|
log("Invalid parameter\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
retval = v4l2_set_control(fd, V4L2_CID_RED_BALANCE, red);
|
|
if (retval < 0) {
|
|
log("Set control V4L2_CID_RED_BALANCE failed. Error: %s\n", strerror(errno));
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_set_blue_balance(int fd, int blue)
|
|
{
|
|
int retval;
|
|
|
|
/* Sanity check */
|
|
if (blue > 0xFF) {
|
|
log("Invalid parameter\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* Set blue value */
|
|
retval = v4l2_set_control(fd, V4L2_CID_BLUE_BALANCE, blue);
|
|
if (retval < 0) {
|
|
log("Set control V4L2_CID_BLUE_BALANCE failed. Error: %d\n", retval);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_set_black_level(int fd, int black)
|
|
{
|
|
int retval;
|
|
|
|
/* Sanity check */
|
|
if (black > 0xFF) {
|
|
log("Invalid parameter\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* Set black value */
|
|
retval = v4l2_set_control(fd, V4L2_CID_BLACK_LEVEL, black);
|
|
if (retval < 0) {
|
|
log("Set control V4L2_CID_BLACK_LEVEL failed. Error: %d\n", retval);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_get_blue_balance(int fd, int *blue)
|
|
{
|
|
int retval;
|
|
|
|
/* Sanity check */
|
|
if (blue == NULL) {
|
|
log("Invalid parameter\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
retval = v4l2_get_control(fd, V4L2_CID_BLUE_BALANCE, blue);
|
|
if (retval < 0) {
|
|
log("Get control V4L2_CID_BLUE_BALANCE failed. Error: %d\n", retval);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_get_red_balance(int fd, int *red)
|
|
{
|
|
int retval;
|
|
|
|
/* Sanity check */
|
|
if (red == NULL) {
|
|
log("Invalid parameter\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
retval = v4l2_get_control(fd, V4L2_CID_RED_BALANCE, red);
|
|
if (retval < 0) {
|
|
log("Get control V4L2_CID_RED_BALANCE failed. Error: %d\n", retval);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_get_black_level(int fd, int *black)
|
|
{
|
|
int retval;
|
|
|
|
/* Sanity check */
|
|
if (black == NULL) {
|
|
log("Invalid parameter\n");
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
retval = v4l2_get_control(fd, V4L2_CID_BLACK_LEVEL, black);
|
|
if (retval < 0) {
|
|
log("Get control V4L2_CID_BLACK_LEVEL failed. Error: %d\n", retval);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int v4l2_global_alpha_set(int fd, int enable, int percentage)
|
|
{
|
|
int retval;
|
|
struct mxcfb_gbl_alpha global_alpha;
|
|
|
|
global_alpha.enable = enable;
|
|
global_alpha.alpha = percentage * GLOBAL_ALPHA_MAX_VALUE / 100;
|
|
|
|
retval = ioctl(fd, MXCFB_SET_GBL_ALPHA, &global_alpha);
|
|
if (retval < 0)
|
|
log("Set global alpha failed MXCFB_SET_GBL_ALPHA. Error: %s\n",
|
|
strerror(errno));
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* Set overlay background framebuffer through sysfs */
|
|
int v4l2_sysfs_set_overlay_bg(char *fb_device)
|
|
{
|
|
int ret = 0;
|
|
FILE *fp = NULL;
|
|
|
|
/* +5 to remove '/dev/' */
|
|
if (!strncmp(fb_device + 5, "fb0", 3)) {
|
|
fp = fopen(SYSFS_FSL_DISP_PROPERTY_FB0, "w");
|
|
} else if (!strncmp(fb_device + 5, "fb1", 3)) {
|
|
fp = fopen(SYSFS_FSL_DISP_PROPERTY_FB1, "w");
|
|
}
|
|
|
|
if (fp) {
|
|
fprintf(fp, "%s", "2-layer-fb-bg");
|
|
fclose(fp);
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void fill_alpha_buffer(char *alpha_buf, int left, int top, int right,
|
|
int bottom, char alpha_val, int alpha_fb_w)
|
|
{
|
|
char *pPointAlphaValue;
|
|
int x, y;
|
|
|
|
for (y = top; y < bottom; y++) {
|
|
for (x = left; x < right; x++) {
|
|
pPointAlphaValue = (char *)(alpha_buf + alpha_fb_w * y + x);
|
|
*pPointAlphaValue = alpha_val;
|
|
}
|
|
}
|
|
}
|
|
|
|
void fill_alpha_rect(char *alpha_buf, struct v4l2_rect *rect, char alpha_val, int alpha_fb_w)
|
|
{
|
|
char *pPointAlphaValue;
|
|
int x, y;
|
|
|
|
for (y = rect->top; y < rect->height; y++) {
|
|
for (x = rect->left; x < rect->width; x++) {
|
|
pPointAlphaValue = (char *)(alpha_buf + alpha_fb_w * y + x);
|
|
*pPointAlphaValue = alpha_val;
|
|
}
|
|
}
|
|
}
|
|
|
|
int v4l2_local_alpha_set(ARGUMENTS * args, int fd_fb_fg,
|
|
struct v4l2_rect *alpha_rectangle, int alpha_percentage)
|
|
{
|
|
struct fb_var_screeninfo fb_fg_var;
|
|
struct mxcfb_loc_alpha l_alpha;
|
|
struct v4l2_rect aux_rect; /* Used for setting the default alpha */
|
|
unsigned long loc_alpha_phy_addr0;
|
|
unsigned long loc_alpha_phy_addr1;
|
|
char *alpha_buf0 = NULL;
|
|
char *alpha_buf1 = NULL;
|
|
int alpha_buf_size;
|
|
int alpha_value = (100 - alpha_percentage) * LOCAL_ALPHA_MIN_VALUE / 100;
|
|
|
|
/* Sanity check */
|
|
if (alpha_rectangle->left + alpha_rectangle->width > args->options.width ||
|
|
alpha_rectangle->top + alpha_rectangle->height > args->options.height) {
|
|
log("Invalid alpha_rectangle\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Switch off overlay */
|
|
v4l2_overlay_control(args->fd_in, 0);
|
|
|
|
/* Fetch current fb var info */
|
|
if (v4l2_fb_get_vinfo(fd_fb_fg, &fb_fg_var) < 0) {
|
|
log("Failed get varinfo %s\n", strerror(errno));
|
|
close(fd_fb_fg);
|
|
return -1;
|
|
}
|
|
|
|
/* Set dimensions */
|
|
fb_fg_var.xres = args->options.width;
|
|
fb_fg_var.yres = args->options.height;
|
|
fb_fg_var.xres_virtual = args->options.width;
|
|
fb_fg_var.yres_virtual = args->options.height * 2;
|
|
|
|
if (v4l2_fb_put_vinfo(fd_fb_fg, &fb_fg_var) < 0) {
|
|
log("Failed get varinfo %s\n", strerror(errno));
|
|
close(fd_fb_fg);
|
|
return -1;
|
|
}
|
|
|
|
/* Assure we have the correct var info */
|
|
if (v4l2_fb_get_vinfo(fd_fb_fg, &fb_fg_var) < 0) {
|
|
log("Failed get varinfo %s\n", strerror(errno));
|
|
close(fd_fb_fg);
|
|
return -1;
|
|
}
|
|
|
|
/* Enable local alpha */
|
|
memset(&l_alpha, 0, sizeof(struct mxcfb_loc_alpha));
|
|
l_alpha.enable = 1;
|
|
l_alpha.alpha_phy_addr0 = 0;
|
|
l_alpha.alpha_phy_addr1 = 0;
|
|
l_alpha.alpha_in_pixel = 0;
|
|
|
|
if (ioctl(fd_fb_fg, MXCFB_SET_LOC_ALPHA, &l_alpha) < 0) {
|
|
log("Set local alpha failed: %s\n", strerror(errno));
|
|
close(fd_fb_fg);
|
|
return -1;
|
|
}
|
|
|
|
/* mmap the memory */
|
|
loc_alpha_phy_addr0 = (unsigned long)(l_alpha.alpha_phy_addr0);
|
|
loc_alpha_phy_addr1 = (unsigned long)(l_alpha.alpha_phy_addr1);
|
|
|
|
alpha_buf_size = fb_fg_var.xres * fb_fg_var.yres;
|
|
|
|
alpha_buf0 = (char *)mmap(0, alpha_buf_size, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, fd_fb_fg, loc_alpha_phy_addr0);
|
|
if ((int)alpha_buf0 == -1) {
|
|
log("\nError: failed to map alpha buffer 0 to memory: %s\n", strerror(errno));
|
|
close(fd_fb_fg);
|
|
return -1;
|
|
}
|
|
|
|
alpha_buf1 = (char *)mmap(0, alpha_buf_size, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, fd_fb_fg, loc_alpha_phy_addr1);
|
|
if ((int)alpha_buf1 == -1) {
|
|
log("\nError: failed to map alpha buffer 1 to memory: %s\n", strerror(errno));
|
|
munmap((void *)alpha_buf0, alpha_buf_size);
|
|
close(fd_fb_fg);
|
|
return -1;
|
|
}
|
|
|
|
/* Switch on overlay */
|
|
v4l2_overlay_control(args->fd_in, 1);
|
|
|
|
/* Initialize the rectangle */
|
|
aux_rect.top = 0;
|
|
aux_rect.left = 0;
|
|
aux_rect.width = args->options.width;
|
|
aux_rect.height = args->options.height;
|
|
|
|
fill_alpha_rect(alpha_buf0, &aux_rect, 0xFF, args->options.width);
|
|
|
|
if (ioctl(fd_fb_fg, MXCFB_SET_LOC_ALP_BUF, &loc_alpha_phy_addr0) < 0) {
|
|
printf("Set local alpha buf failed\n");
|
|
close(fd_fb_fg);
|
|
return -1;
|
|
}
|
|
|
|
/* Fill and display the rectangle */
|
|
fill_alpha_rect(alpha_buf0, alpha_rectangle, alpha_value, args->options.width);
|
|
|
|
if (ioctl(fd_fb_fg, MXCFB_SET_LOC_ALP_BUF, &loc_alpha_phy_addr0) < 0) {
|
|
printf("Set local alpha buf failed\n");
|
|
close(fd_fb_fg);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|