blob: 0f593f8fe54286ed2d4c687f905822375e814a60 [file] [log] [blame]
/*
* Copyright (c) 2013-2014, Freescale Semiconductor, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <linux/fb.h>
#include <linux/mxcfb.h>
#include "gstimxcommon.h"
#include "displays.h"
#include "gstsutils.h"
#include "gstimxv4l2.h"
GST_DEBUG_CATEGORY_EXTERN (overlay_sink_debug);
#define GST_CAT_DEFAULT overlay_sink_debug
#define KEY_DEVICE "device"
#define KEY_FMT "fmt"
#define KEY_WIDTH "width"
#define KEY_HEIGHT "height"
#define KEY_COLOR_KEY "color_key"
#define KEY_ALPHA "alpha"
#define DISPLAY_NUM_BUFFERS (3)
#define PAGE_SHIFT 12
#ifndef PAGE_SIZE
#define PAGE_SIZE (1 << PAGE_SHIFT)
#endif
#define ROUNDUP2PAGESIZE(x) (((x) + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1))
// handle for v4l2 display
typedef struct {
gchar *name;
gchar *device;
gint fmt;
gint w;
gint h;
guint alpha;
gboolean enable_color_key;
guint color_key;
gpointer v4l2handle;
PhyMemBlock memblk[DISPLAY_NUM_BUFFERS];
gint first_request;
gint clear_cnt;
} DisplayHandle;
static guint string_to_fmt (char *value)
{
guint fmt, a, b, c, d;
a = value[0];
b = value[1];
c = value[2];
d = value[3];
fmt = (((a) << 0) | ((b) << 8) | ((c) << 16) | ((d) << 24));
return fmt;
}
static guint display_fmt_to_v4l2_fmt (guint display_fmt)
{
guint fmt = 0;
if (display_fmt == GST_MAKE_FOURCC('R', 'G', 'B', 'P'))
fmt = V4L2_PIX_FMT_RGB565;
else if (display_fmt == GST_MAKE_FOURCC('R', 'G', 'B', 'x'))
fmt = V4L2_PIX_FMT_RGB32;
return fmt;
}
gint scan_displays(gpointer **phandle, gint *pcount)
{
GstsutilsEntry *entry = NULL;
gint group_count;
gint i;
gint count = 0;
if (HAS_IPU())
entry = gstsutils_init_entry ("/usr/share/imx_6q_display_config");
else if (HAS_PXP())
entry = gstsutils_init_entry ("/usr/share/imx_6sx_display_config");
else {
GST_ERROR ("Not supported platform.");
return -1;
}
if(entry == NULL) {
GST_ERROR ("scan display configuration file failed.");
return -1;
}
group_count = gstsutils_get_group_count (entry);
GST_DEBUG ("osink config group count (%d)", group_count);
for (i=1; i<=group_count; i++) {
GstsutilsGroup *group;
DisplayHandle *hdisplay;
gchar *name = NULL;
gchar *device;
gchar *fmt, *w, *h, *color_key, *alpha;
color_key = w = h = NULL;
if(!gstsutils_get_group_by_index(entry, i, &group)) {
GST_ERROR ("gstsutils_get_group_by_index failed.\n");
continue;
}
name = gstsutils_get_group_name (group);
if (!name)
name = "unknown";
if(!gstsutils_get_value_by_key(group, KEY_DEVICE, &device)) {
GST_ERROR ("can find display %s device.", name);
continue;
}
if(!gstsutils_get_value_by_key(group, KEY_FMT, &fmt)) {
fmt = g_strdup("RGBP"); //default rgb565
}
if(!gstsutils_get_value_by_key(group, KEY_WIDTH, &w)) {
GST_DEBUG ("No width configured for display %s.", name);
}
if(!gstsutils_get_value_by_key(group, KEY_HEIGHT, &h)) {
GST_DEBUG ("No height configured for display %s.", name);
}
if(!gstsutils_get_value_by_key(group, KEY_ALPHA, &alpha))
alpha = g_strdup("0");
if(!gstsutils_get_value_by_key(group, KEY_COLOR_KEY, &color_key)) {
/* do nothing */
}
hdisplay = g_slice_alloc (sizeof(DisplayHandle));
if (!hdisplay) {
GST_ERROR ("allocate DisplayHandle for display %s failed.", name);
gstsutils_deinit_entry (entry);
return -1;
}
memset (hdisplay, 0, sizeof (DisplayHandle));
hdisplay->name = name;
hdisplay->device = device;
hdisplay->fmt = string_to_fmt (fmt);
hdisplay->alpha = atoi (alpha);
g_free(fmt);
g_free(alpha);
if (color_key) {
hdisplay->enable_color_key = TRUE;
hdisplay->color_key = atoi (color_key);
g_free(color_key);
} else {
hdisplay->enable_color_key = FALSE;
}
if (!w || !h) {
gst_imx_v4l2_get_display_resolution (hdisplay->device, &hdisplay->w, &hdisplay->h);
}
else {
hdisplay->w = atoi (w);
hdisplay->h = atoi (h);
g_free(w);
g_free(h);
}
GST_DEBUG ("display(%s), device(%s), fmt(%d), res(%dx%d), ckey(%x), alpha(%d)",
hdisplay->name, hdisplay->device, hdisplay->fmt, hdisplay->w, hdisplay->h,
hdisplay->color_key, hdisplay->alpha);
phandle[count] = (gpointer)hdisplay;
count ++;
if (count >= MAX_DISPLAY)
break;
}
gstsutils_deinit_entry (entry);
*pcount = count;
return 0;
}
void free_display (gpointer display)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
if (hdisplay) {
if (hdisplay->name)
g_free (hdisplay->name);
if (hdisplay->device)
g_free (hdisplay->device);
g_slice_free1 (sizeof(DisplayHandle), hdisplay);
}
}
gchar *get_display_name(gpointer display)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
return hdisplay->name;
}
gint get_display_format(gpointer display)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
return hdisplay->fmt;
}
gint get_display_res(gpointer display, gint *width, gint *height, gint *stride)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
*width = hdisplay->w;
*height = hdisplay->h;
*stride = 0;
return 0;
}
// v4l2 display implementation
gint init_display (gpointer display)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
IMXV4l2Rect rect;
hdisplay->v4l2handle = gst_imx_v4l2_open_device (hdisplay->device, V4L2_BUF_TYPE_VIDEO_OUTPUT);
if (!hdisplay->v4l2handle) {
GST_ERROR ("gst_imx_v4l2_open_device %s failed.", hdisplay->device);
return -1;
}
rect.left = rect.top = 0;
rect.width = hdisplay->w;
rect.height = hdisplay->h;
hdisplay->clear_cnt = 0;
guint fmt = display_fmt_to_v4l2_fmt(hdisplay->fmt);
if (0 == fmt) {
GST_ERROR ("Unsupported display format, check the display config file.");
goto err;
}
if (gst_imx_v4l2out_config_input (hdisplay->v4l2handle, fmt, hdisplay->w, hdisplay->h, &rect) < 0) {
GST_ERROR ("configure v4l2 device %s input failed.", hdisplay->device);
goto err;
}
if (gst_imx_v4l2out_config_output (hdisplay->v4l2handle, &rect, FALSE) < 0) {
GST_ERROR ("configure v4l2 device %s output failed.", hdisplay->device);
goto err;
}
if (gst_imx_v4l2_config_rotate (hdisplay->v4l2handle, 0) < 0) {
GST_ERROR ("configure v4l2 device %s rotate to 0 failed.", hdisplay->device);
goto err;
}
if (gst_imx_v4l2_set_buffer_count (hdisplay->v4l2handle, DISPLAY_NUM_BUFFERS, V4L2_MEMORY_MMAP) < 0) {
GST_ERROR ("configure v4l2 device %s buffer count failed.", hdisplay->device);
goto err;
}
gint i;
for (i=0; i<DISPLAY_NUM_BUFFERS; i++) {
if (gst_imx_v4l2_allocate_buffer (hdisplay->v4l2handle, &hdisplay->memblk[i]) < 0) {
GST_ERROR ("allocate memory from v4l2 device %s failed.", hdisplay->device);
goto err;
} else {
memset (hdisplay->memblk[i].vaddr, 0, hdisplay->memblk[i].size);
}
}
gst_imx_v4l2out_config_alpha (hdisplay->v4l2handle, hdisplay->alpha);
gst_imx_v4l2out_config_color_key (hdisplay->v4l2handle, hdisplay->enable_color_key, hdisplay->color_key);
return 0;
err:
deinit_display (display);
return -1;
}
void deinit_display (gpointer display)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
gint i;
for (i=0; i<DISPLAY_NUM_BUFFERS; i++) {
if (hdisplay->memblk[i].vaddr) {
gst_imx_v4l2_free_buffer (hdisplay->v4l2handle, &hdisplay->memblk[i]);
memset (&hdisplay->memblk[i], 0, sizeof (PhyMemBlock));
}
}
if (hdisplay->v4l2handle) {
gst_imx_v4l2_close_device (hdisplay->v4l2handle);
hdisplay->v4l2handle = NULL;
}
return;
}
gint clear_display (gpointer display)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
hdisplay->clear_cnt = DISPLAY_NUM_BUFFERS;
return 0;
}
gint get_next_display_buffer (gpointer display, SurfaceBuffer *buffer)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
GstVideoFrameFlags flags = GST_VIDEO_FRAME_FLAG_NONE;
PhyMemBlock *memblk = NULL;
gint index;
if (hdisplay->first_request < DISPLAY_NUM_BUFFERS) {
memblk = &hdisplay->memblk[hdisplay->first_request];
hdisplay->first_request ++;
}
else {
if (gst_imx_v4l2_dequeue_v4l2memblk (hdisplay->v4l2handle, &memblk, &flags) < 0) {
GST_ERROR ("get buffer from %s failed.", hdisplay->device);
return -1;
}
}
if (!memblk) {
GST_ERROR ("get display buffer failed.");
return -1;
}
memcpy (&(buffer->mem), memblk, sizeof (PhyMemBlock));
if (hdisplay->clear_cnt > 0) {
memset (buffer->mem.vaddr, 0, buffer->mem.size);
hdisplay->clear_cnt--;
}
//GST_DEBUG ("get display buffer, vaddr (%p) paddr (%p).", buffer->vaddr, buffer->paddr);
return 0;
}
gint flip_display_buffer (gpointer display, SurfaceBuffer *buffer)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
PhyMemBlock *memblk = NULL;
gint i;
//GST_DEBUG ("flip display buffer, vaddr (%p) paddr (%p).", buffer->vaddr, buffer->paddr);
for (i=0; i<DISPLAY_NUM_BUFFERS; i++) {
if (buffer->mem.vaddr == hdisplay->memblk[i].vaddr)
memblk = &hdisplay->memblk[i];
}
if (!memblk) {
GST_ERROR ("invalid display buffer.");
return -1;
}
if (gst_imx_v4l2_queue_v4l2memblk (hdisplay->v4l2handle, memblk, GST_VIDEO_FRAME_FLAG_NONE) < 0) {
GST_ERROR ("flip display buffer failed.");
return -1;
}
return 0;
}
void set_global_alpha(gpointer display, gint alpha)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
if (hdisplay && hdisplay->v4l2handle)
gst_imx_v4l2out_config_alpha (hdisplay->v4l2handle, alpha);
}
void set_color_key(gpointer display, gboolean enable, guint colorkey)
{
DisplayHandle *hdisplay = (DisplayHandle*) display;
if (hdisplay && hdisplay->v4l2handle)
gst_imx_v4l2out_config_color_key (hdisplay->v4l2handle, enable, colorkey);
}