/* * 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; }