blob: d6320c8d6c4020bb1e841e26e093ccce1227166b [file] [log] [blame]
/*
* drivers/amlogic/media/osd_ext/osd_fb.c
*
* Copyright (C) 2017 Amlogic, 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
/* Linux Headers */
#include <linux/version.h>
#include <linux/compat.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/spinlock.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/file.h>
#include <linux/fdtable.h>
#include <linux/console.h>
#include <linux/slab.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
#include <linux/uaccess.h>
/* Amlogic Headers */
#include <linux/amlogic/media/vout/vinfo.h>
#include <linux/amlogic/media/vout/vout_notify.h>
/* Local Headers */
#include <osd/osd.h>
#include <osd/osd_log.h>
#include <osd/osd_sync.h>
#include <osd/osd_io.h>
#include "osd_hw.h"
#include "osd_fb.h"
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
#include <linux/amlogic/pm.h>
static struct early_suspend early_suspend;
static int early_suspend_flag;
#endif
#ifdef CONFIG_SCREEN_ON_EARLY
static int early_resume_flag;
#endif
int int_viu2_vsync = -ENXIO;
static struct osd_fb_dev_s *gp_fbdev_list[OSD_COUNT] = {};
static struct reserved_mem fb_rmem;
static phys_addr_t fb_rmem_paddr[2];
static void __iomem *fb_rmem_vaddr[2];
static u32 fb_rmem_size[2];
static DEFINE_MUTEX(dbg_mutex);
static void osddev_ext_setup(struct osd_fb_dev_s *fbdev)
{
mutex_lock(&fbdev->lock);
osd_ext_setup(&fbdev->osd_ctl,
fbdev->fb_info->var.xoffset,
fbdev->fb_info->var.yoffset,
fbdev->fb_info->var.xres,
fbdev->fb_info->var.yres,
fbdev->fb_info->var.xres_virtual,
fbdev->fb_info->var.yres_virtual,
fbdev->osd_ctl.disp_start_x,
fbdev->osd_ctl.disp_start_y,
fbdev->osd_ctl.disp_end_x,
fbdev->osd_ctl.disp_end_y,
fbdev->fb_mem_paddr,
fbdev->color,
fbdev->fb_info->node - 2);
mutex_unlock(&fbdev->lock);
}
static void osddev_ext_update_disp_axis(struct osd_fb_dev_s *fbdev,
int mode_change)
{
osd_ext_update_disp_axis_hw(fbdev->fb_info->node - 2,
fbdev->osd_ctl.disp_start_x,
fbdev->osd_ctl.disp_end_x,
fbdev->osd_ctl.disp_start_y,
fbdev->osd_ctl.disp_end_y,
fbdev->fb_info->var.xoffset,
fbdev->fb_info->var.yoffset,
mode_change);
}
static int osddev_ext_setcolreg(unsigned int regno, u16 red,
u16 green, u16 blue,
u16 transp, struct osd_fb_dev_s *fbdev)
{
struct fb_info *info = fbdev->fb_info;
if ((fbdev->color->color_index == COLOR_INDEX_02_PAL4) ||
(fbdev->color->color_index == COLOR_INDEX_04_PAL16) ||
(fbdev->color->color_index == COLOR_INDEX_08_PAL256)) {
mutex_lock(&fbdev->lock);
osd_ext_setpal_hw(fbdev->fb_info->node - 2,
regno, red, green, blue, transp);
mutex_lock(&fbdev->lock);
}
if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
u32 v, r, g, b, a;
if (regno >= 16)
return 1;
r = red >> (16 - info->var.red.length);
g = green >> (16 - info->var.green.length);
b = blue >> (16 - info->var.blue.length);
a = transp >> (16 - info->var.transp.length);
v = (r << info->var.red.offset) |
(g << info->var.green.offset) |
(b << info->var.blue.offset) |
(a << info->var.transp.offset);
((u32 *)(info->pseudo_palette))[regno] = v;
}
return 0;
}
static const struct color_bit_define_s *
_find_color_format(struct fb_var_screeninfo *var)
{
u32 upper_margin, lower_margin, i, level;
const struct color_bit_define_s *found = NULL;
const struct color_bit_define_s *color = NULL;
level = (var->bits_per_pixel - 1) / 8;
switch (level) {
case 0:
upper_margin = COLOR_INDEX_08_PAL256;
lower_margin = COLOR_INDEX_02_PAL4;
break;
case 1:
upper_margin = COLOR_INDEX_16_565;
lower_margin = COLOR_INDEX_16_655;
break;
case 2:
upper_margin = COLOR_INDEX_24_RGB;
lower_margin = COLOR_INDEX_24_6666_A;
break;
case 3:
upper_margin = COLOR_INDEX_32_ARGB;
lower_margin = COLOR_INDEX_32_BGRA;
break;
case 4:
upper_margin = COLOR_INDEX_YUV_422;
lower_margin = COLOR_INDEX_YUV_422;
break;
default:
return NULL;
}
/*
* if not provide color component length
* then we find the first depth match.
*/
if ((var->red.length == 0) || (var->green.length == 0)
|| (var->blue.length == 0) ||
var->bits_per_pixel != (var->red.length + var->green.length +
var->blue.length + var->transp.length)) {
osd_log_dbg(MODULE_BASE, "not provide color length, use default color\n");
found = &default_color_format_array[upper_margin];
} else {
for (i = upper_margin; i >= lower_margin; i--) {
color = &default_color_format_array[i];
if ((color->red_length == var->red.length) &&
(color->green_length == var->green.length) &&
(color->blue_length == var->blue.length) &&
(color->transp_length == var->transp.length) &&
(color->transp_offset == var->transp.offset) &&
(color->green_offset == var->green.offset) &&
(color->blue_offset == var->blue.offset) &&
(color->red_offset == var->red.offset)) {
found = color;
break;
}
color--;
}
}
return found;
}
static void __init _fbdev_set_default(struct osd_fb_dev_s *fbdev, int index)
{
/* setup default value */
fbdev->fb_info->var = fb_def_var[index];
fbdev->fb_info->fix = fb_def_fix;
fbdev->color = _find_color_format(&fbdev->fb_info->var);
}
static int osd_ext_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct fb_fix_screeninfo *fix;
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)info->par;
const struct color_bit_define_s *color_format_pt;
fix = &info->fix;
color_format_pt = _find_color_format(var);
if (color_format_pt == NULL || color_format_pt->color_index == 0)
return -EFAULT;
osd_log_dbg(MODULE_BASE, "select color format :index %d, bpp %d\n",
color_format_pt->color_index,
color_format_pt->bpp);
fbdev->color = color_format_pt;
var->red.offset = color_format_pt->red_offset;
var->red.length = color_format_pt->red_length;
var->red.msb_right = color_format_pt->red_msb_right;
var->green.offset = color_format_pt->green_offset;
var->green.length = color_format_pt->green_length;
var->green.msb_right = color_format_pt->green_msb_right;
var->blue.offset = color_format_pt->blue_offset;
var->blue.length = color_format_pt->blue_length;
var->blue.msb_right = color_format_pt->blue_msb_right;
var->transp.offset = color_format_pt->transp_offset;
var->transp.length = color_format_pt->transp_length;
var->transp.msb_right = color_format_pt->transp_msb_right;
var->bits_per_pixel = color_format_pt->bpp;
osd_log_dbg(MODULE_BASE, "rgba(L/O):%d/%d-%d/%d-%d/%d-%d/%d\n",
var->red.length, var->red.offset,
var->green.length, var->green.offset,
var->blue.length, var->blue.offset,
var->transp.length, var->transp.offset);
fix->visual = color_format_pt->color_type;
/* adjust memory length. */
fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
fbdev->fb_len) {
osd_log_err("no enough memory for %d*%d*%d\n", var->xres,
var->yres, var->bits_per_pixel);
return -ENOMEM;
}
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;
var->left_margin = var->right_margin = 0;
var->upper_margin = var->lower_margin = 0;
if (var->xres + var->xoffset > var->xres_virtual)
var->xoffset = var->xres_virtual - var->xres;
if (var->yres + var->yoffset > var->yres_virtual)
var->yoffset = var->yres_virtual - var->yres;
return 0;
}
static int osd_ext_set_par(struct fb_info *info)
{
const struct vinfo_s *vinfo = NULL;
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)info->par;
struct osd_ctl_s *osd_ext_ctrl = &fbdev->osd_ctl;
u32 virt_end_x, virt_end_y;
#ifdef CONFIG_AMLOGIC_VOUT2
vinfo = get_current_vinfo2();
if (!vinfo) {
osd_log_err("current vinfo NULL\n");
return -1;
}
#endif
if (vinfo == NULL)
return -1;
virt_end_x = osd_ext_ctrl->disp_start_x + info->var.xres;
virt_end_y = osd_ext_ctrl->disp_start_y + info->var.yres;
if (virt_end_x > vinfo->width)
osd_ext_ctrl->disp_end_x = vinfo->width - 1;
else
osd_ext_ctrl->disp_end_x = virt_end_x - 1;
if (virt_end_y > vinfo->height)
osd_ext_ctrl->disp_end_y = vinfo->height - 1;
else
osd_ext_ctrl->disp_end_y = virt_end_y - 1;
osddev_ext_setup((struct osd_fb_dev_s *)info->par);
return 0;
}
static int osd_ext_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info)
{
return osddev_ext_setcolreg(regno, red, green, blue,
transp, (struct osd_fb_dev_s *)info->par);
}
static int osd_ext_setcmap(struct fb_cmap *cmap, struct fb_info *info)
{
int count, index, r;
u16 *red, *green, *blue, *transp;
u16 trans = 0xffff;
red = cmap->red;
green = cmap->green;
blue = cmap->blue;
transp = cmap->transp;
index = cmap->start;
for (count = 0; count < cmap->len; count++) {
if (transp)
trans = *transp++;
r = osddev_ext_setcolreg(index++, *red++, *green++, *blue++,
trans, (struct osd_fb_dev_s *)info->par);
if (r != 0)
return r;
}
return 0;
}
static int osd_ext_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)info->par;
void __user *argp = (void __user *)arg;
u32 src_colorkey;/* 16 bit or 24 bit */
u32 srckey_enable;
u32 gbl_alpha;
u32 osd_ext_order;
s32 osd_ext_axis[4] = {0};
s32 osd_ext_dst_axis[4] = {0};
u32 block_windows[8] = {0};
u32 block_mode;
unsigned long ret;
struct fb_sync_request_s sync_request;
switch (cmd) {
case FBIOPUT_OSD_SRCKEY_ENABLE:
ret = copy_from_user(&srckey_enable, argp, sizeof(u32));
break;
case FBIOPUT_OSD_SRCCOLORKEY:
ret = copy_from_user(&src_colorkey, argp, sizeof(u32));
break;
case FBIOPUT_OSD_SET_GBL_ALPHA:
ret = copy_from_user(&gbl_alpha, argp, sizeof(u32));
break;
case FBIOPUT_OSD_SCALE_AXIS:
ret = copy_from_user(&osd_ext_axis, argp, 4 * sizeof(s32));
break;
case FBIOPUT_OSD_SYNC_ADD:
ret = copy_from_user(&sync_request, argp,
sizeof(struct fb_sync_request_s));
break;
case FBIO_WAITFORVSYNC:
case FBIOGET_OSD_SCALE_AXIS:
case FBIOPUT_OSD_ORDER:
case FBIOGET_OSD_ORDER:
case FBIOGET_OSD_GET_GBL_ALPHA:
case FBIOPUT_OSD_2X_SCALE:
case FBIOPUT_OSD_ENABLE_3D_MODE:
case FBIOPUT_OSD_FREE_SCALE_ENABLE:
case FBIOPUT_OSD_FREE_SCALE_MODE:
case FBIOPUT_OSD_FREE_SCALE_WIDTH:
case FBIOPUT_OSD_FREE_SCALE_HEIGHT:
case FBIOGET_OSD_BLOCK_WINDOWS:
case FBIOGET_OSD_BLOCK_MODE:
case FBIOGET_OSD_FREE_SCALE_AXIS:
case FBIOGET_OSD_WINDOW_AXIS:
case FBIOPUT_OSD_ROTATE_ON:
case FBIOPUT_OSD_ROTATE_ANGLE:
break;
case FBIOPUT_OSD_BLOCK_MODE:
ret = copy_from_user(&block_mode, argp, sizeof(u32));
break;
case FBIOPUT_OSD_BLOCK_WINDOWS:
ret = copy_from_user(&block_windows, argp, 8 * sizeof(u32));
break;
case FBIOPUT_OSD_FREE_SCALE_AXIS:
ret = copy_from_user(&osd_ext_axis, argp, 4 * sizeof(s32));
break;
case FBIOPUT_OSD_WINDOW_AXIS:
ret = copy_from_user(&osd_ext_dst_axis, argp, 4 * sizeof(s32));
break;
default:
osd_log_err("command not supported\n ");
return -1;
}
mutex_lock(&fbdev->lock);
switch (cmd) {
case FBIOPUT_OSD_ORDER:
osd_ext_set_order_hw(info->node - 2, arg);
break;
case FBIOGET_OSD_ORDER:
osd_ext_order = osd_ext_get_order_hw(info->node - 2);
ret = copy_to_user(argp, &osd_ext_order, sizeof(u32));
break;
case FBIOPUT_OSD_FREE_SCALE_ENABLE:
osd_ext_set_free_scale_enable_hw(info->node - 2, arg);
break;
case FBIOPUT_OSD_FREE_SCALE_MODE:
osd_ext_set_free_scale_mode_hw(info->node - 2, arg);
break;
case FBIOPUT_OSD_ENABLE_3D_MODE:
osd_ext_enable_3d_mode_hw(info->node - 2, arg);
break;
case FBIOPUT_OSD_2X_SCALE:
/*
* higher 16 bit h_scale_enable,
* lower 16 bit v_scale_enable
*/
osd_ext_set_2x_scale_hw(info->node - 2, arg & 0xffff0000 ?
1 : 0, arg & 0xffff ? 1 : 0);
break;
case FBIOPUT_OSD_ROTATE_ON:
break;
case FBIOPUT_OSD_ROTATE_ANGLE:
break;
case FBIOPUT_OSD_SRCCOLORKEY:
switch (fbdev->color->color_index) {
case COLOR_INDEX_16_655:
case COLOR_INDEX_16_844:
case COLOR_INDEX_16_565:
case COLOR_INDEX_24_888_B:
case COLOR_INDEX_24_RGB:
case COLOR_INDEX_YUV_422:
osd_log_dbg(MODULE_BASE,
"set osd color key 0x%x\n", src_colorkey);
fbdev->color_key = src_colorkey;
osd_ext_set_color_key_hw(info->node - 2,
fbdev->color->color_index, src_colorkey);
break;
default:
break;
}
break;
case FBIOPUT_OSD_SRCKEY_ENABLE:
switch (fbdev->color->color_index) {
case COLOR_INDEX_16_655:
case COLOR_INDEX_16_844:
case COLOR_INDEX_16_565:
case COLOR_INDEX_24_888_B:
case COLOR_INDEX_24_RGB:
case COLOR_INDEX_YUV_422:
osd_log_dbg(MODULE_BASE, "set osd color key %s\n",
srckey_enable ? "enable" : "disable");
if (srckey_enable != 0) {
fbdev->enable_key_flag |= KEYCOLOR_FLAG_TARGET;
if (!(fbdev->enable_key_flag &
KEYCOLOR_FLAG_ONHOLD)) {
fbdev->enable_key_flag |=
KEYCOLOR_FLAG_CURRENT;
osd_ext_srckey_enable_hw(info->node - 2, 1);
}
} else {
osd_ext_srckey_enable_hw(info->node - 2, 0);
fbdev->enable_key_flag &=
~(KEYCOLOR_FLAG_TARGET |
KEYCOLOR_FLAG_CURRENT);
}
break;
default:
break;
}
break;
case FBIOPUT_OSD_SET_GBL_ALPHA:
osd_ext_set_gbl_alpha_hw(info->node - 2, gbl_alpha);
break;
case FBIOGET_OSD_GET_GBL_ALPHA:
gbl_alpha = osd_ext_get_gbl_alpha_hw(info->node - 2);
ret = copy_to_user(argp, &gbl_alpha, sizeof(u32));
break;
case FBIOGET_OSD_SCALE_AXIS:
osd_ext_get_scale_axis_hw(info->node - 2,
&osd_ext_axis[0], &osd_ext_axis[1],
&osd_ext_axis[2], &osd_ext_axis[3]);
ret = copy_to_user(argp, &osd_ext_axis, 4 * sizeof(s32));
break;
case FBIOPUT_OSD_SCALE_AXIS:
osd_ext_set_scale_axis_hw(info->node - 2,
osd_ext_axis[0], osd_ext_axis[1],
osd_ext_axis[2], osd_ext_axis[3]);
break;
case FBIOGET_OSD_BLOCK_WINDOWS:
osd_ext_get_block_windows_hw(info->node - 2, block_windows);
ret = copy_to_user(argp, &block_windows, 8 * sizeof(u32));
break;
case FBIOPUT_OSD_BLOCK_WINDOWS:
osd_ext_set_block_windows_hw(info->node - 2, block_windows);
break;
case FBIOPUT_OSD_BLOCK_MODE:
osd_ext_set_block_mode_hw(info->node - 2, block_mode);
break;
case FBIOGET_OSD_BLOCK_MODE:
osd_ext_get_block_mode_hw(info->node - 2, &block_mode);
ret = copy_to_user(argp, &block_mode, sizeof(u32));
break;
case FBIOGET_OSD_FREE_SCALE_AXIS:
osd_ext_get_free_scale_axis_hw(info->node - 2,
&osd_ext_axis[0], &osd_ext_axis[1],
&osd_ext_axis[2], &osd_ext_axis[3]);
ret = copy_to_user(argp, &osd_ext_axis, 4 * sizeof(s32));
break;
case FBIOGET_OSD_WINDOW_AXIS:
osd_ext_get_window_axis_hw(info->node - 2,
&osd_ext_dst_axis[0], &osd_ext_dst_axis[1],
&osd_ext_dst_axis[2], &osd_ext_dst_axis[3]);
ret = copy_to_user(argp, &osd_ext_dst_axis, 4 * sizeof(s32));
break;
case FBIOPUT_OSD_FREE_SCALE_AXIS:
osd_ext_set_free_scale_axis_hw(info->node - 2,
osd_ext_axis[0], osd_ext_axis[1],
osd_ext_axis[2], osd_ext_axis[3]);
break;
case FBIOPUT_OSD_WINDOW_AXIS:
osd_ext_set_window_axis_hw(info->node - 2,
osd_ext_dst_axis[0], osd_ext_dst_axis[1],
osd_ext_dst_axis[2], osd_ext_dst_axis[3]);
break;
case FBIOPUT_OSD_SYNC_ADD:
sync_request.out_fen_fd = osd_ext_sync_request(info->node - 2,
info->var.yres,
sync_request.xoffset,
sync_request.yoffset,
sync_request.in_fen_fd);
ret = copy_to_user(argp, &sync_request,
sizeof(struct fb_sync_request_s));
if (sync_request.out_fen_fd < 0) /* fence create fail. */
ret = -1;
break;
case FBIO_WAITFORVSYNC:
osd_ext_wait_vsync_event();
ret = copy_to_user(argp, &ret, sizeof(u32));
break;
default:
break;
}
mutex_unlock(&fbdev->lock);
return 0;
}
#ifdef CONFIG_COMPAT
static int osd_ext_compat_ioctl(struct fb_info *info,
unsigned int cmd, unsigned long arg)
{
unsigned long ret;
arg = (unsigned long)compat_ptr(arg);
ret = osd_ext_ioctl(info, cmd, arg);
return ret;
}
#endif
static int osd_ext_open(struct fb_info *info, int arg)
{
return 0;
}
static int
osd_ext_blank(int blank_mode, struct fb_info *info)
{
osd_ext_enable_hw(info->node - 2, (blank_mode != 0) ? 0 : 1);
return 0;
}
static int osd_ext_pan_display(struct fb_var_screeninfo *var,
struct fb_info *fbi)
{
osd_ext_pan_display_hw(fbi->node - 2, var->xoffset, var->yoffset);
osd_log_info("osd_ext_pan_display:=>osd%d\n", fbi->node);
return 0;
}
static int osd_ext_sync(struct fb_info *info)
{
return 0;
}
/* fb_ops structures */
static struct fb_ops osd_ext_ops = {
.owner = THIS_MODULE,
.fb_check_var = osd_ext_check_var,
.fb_set_par = osd_ext_set_par,
.fb_setcolreg = osd_ext_setcolreg,
.fb_setcmap = osd_ext_setcmap,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
#ifdef CONFIG_FB_SOFT_CURSOR
.fb_cursor = soft_cursor,
#elif defined(CONFIG_AMLOGIC_MEDIA_FB_OSD2_CURSOR)
.fb_cursor = NULL,
#endif
.fb_ioctl = osd_ext_ioctl,
#ifdef CONFIG_COMPAT
.fb_compat_ioctl = osd_ext_compat_ioctl,
#endif
.fb_open = osd_ext_open,
.fb_blank = osd_ext_blank,
.fb_pan_display = osd_ext_pan_display,
.fb_sync = osd_ext_sync,
};
static void set_default_display_axis(struct fb_var_screeninfo *var,
struct osd_ctl_s *osd_ext_ctrl, const struct vinfo_s *vinfo)
{
u32 virt_end_x = osd_ext_ctrl->disp_start_x + var->xres;
u32 virt_end_y = osd_ext_ctrl->disp_start_y + var->yres;
if (virt_end_x > vinfo->width)
osd_ext_ctrl->disp_end_x = vinfo->width - 1; /* screen axis */
else
osd_ext_ctrl->disp_end_x = virt_end_x - 1; /* screen axis */
if (virt_end_y > vinfo->height)
osd_ext_ctrl->disp_end_y = vinfo->height - 1;
else
osd_ext_ctrl->disp_end_y = virt_end_y - 1; /* screen axis */
}
int osd_ext_notify_callback(struct notifier_block *block, unsigned long cmd,
void *para)
{
const struct vinfo_s *vinfo = NULL;
struct osd_fb_dev_s *fb_dev;
int i, blank;
struct disp_rect_s *disp_rect;
#ifdef CONFIG_AMLOGIC_VOUT2
vinfo = get_current_vinfo2();
osd_log_info("tv_server:vmode=%s\n", vinfo->name);
#endif
if (vinfo == NULL)
return -1;
if (vinfo->mode == VMODE_INIT_NULL)
return 1;
switch (cmd) {
case VOUT_EVENT_MODE_CHANGE:
osd_log_info("recevie change mode message\n");
for (i = 0; i < OSD_COUNT; i++) {
fb_dev = gp_fbdev_list[i];
if (fb_dev == NULL)
continue;
set_default_display_axis(&fb_dev->fb_info->var,
&fb_dev->osd_ctl, vinfo);
console_lock();
osddev_ext_update_disp_axis(fb_dev, 1);
console_unlock();
}
break;
case VOUT_EVENT_OSD_BLANK:
blank = *(int *)para;
for (i = 0; i < OSD_COUNT; i++) {
fb_dev = gp_fbdev_list[i];
if (fb_dev == NULL)
continue;
console_lock();
osd_ext_blank(blank, fb_dev->fb_info);
console_unlock();
}
break;
case VOUT_EVENT_OSD_DISP_AXIS:
disp_rect = (struct disp_rect_s *)para;
for (i = 0; i < OSD_COUNT; i++) {
if (!disp_rect)
break;
fb_dev = gp_fbdev_list[i];
/*
* if (fb_dev->preblend_enable)
* break;
*/
fb_dev->osd_ctl.disp_start_x = disp_rect->x;
fb_dev->osd_ctl.disp_start_y = disp_rect->y;
osd_log_info("set disp axis: x:%d y:%d w:%d h:%d\n",
disp_rect->x, disp_rect->y,
disp_rect->w, disp_rect->h);
if (disp_rect->x + disp_rect->w > vinfo->width)
fb_dev->osd_ctl.disp_end_x = vinfo->width - 1;
else
fb_dev->osd_ctl.disp_end_x =
fb_dev->osd_ctl.disp_start_x +
disp_rect->w - 1;
if (disp_rect->y + disp_rect->h > vinfo->height)
fb_dev->osd_ctl.disp_end_y = vinfo->height - 1;
else
fb_dev->osd_ctl.disp_end_y =
fb_dev->osd_ctl.disp_start_y +
disp_rect->h - 1;
disp_rect++;
osd_log_info("new disp axis: x0:%d y0:%d x1:%d y1:%d\n",
fb_dev->osd_ctl.disp_start_x,
fb_dev->osd_ctl.disp_start_y,
fb_dev->osd_ctl.disp_end_x,
fb_dev->osd_ctl.disp_end_y);
console_lock();
osddev_ext_update_disp_axis(fb_dev, 0);
console_unlock();
}
break;
}
return 0;
}
static struct notifier_block osd_ext_notifier_nb = {
.notifier_call = osd_ext_notify_callback,
};
static ssize_t store_enable_3d(struct device *device,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
fbdev->enable_3d = res;
osd_ext_enable_3d_mode_hw(fb_info->node - 2, fbdev->enable_3d);
return count;
}
static ssize_t show_enable_3d(struct device *device,
struct device_attribute *attr, char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
return snprintf(buf, PAGE_SIZE, "3d_enable:[0x%x]\n", fbdev->enable_3d);
}
static ssize_t store_color_key(struct device *device,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 16, &res);
switch (fbdev->color->color_index) {
case COLOR_INDEX_16_655:
case COLOR_INDEX_16_844:
case COLOR_INDEX_16_565:
case COLOR_INDEX_24_888_B:
case COLOR_INDEX_24_RGB:
case COLOR_INDEX_YUV_422:
fbdev->color_key = res;
osd_ext_set_color_key_hw(fb_info->node - 2,
fbdev->color->color_index, fbdev->color_key);
break;
default:
break;
}
return count;
}
static ssize_t show_color_key(struct device *device,
struct device_attribute *attr, char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
return snprintf(buf, PAGE_SIZE, "0x%x\n", fbdev->color_key);
}
static ssize_t store_enable_key(struct device *device,
struct device_attribute *attr, const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
mutex_lock(&fbdev->lock);
if (res != 0) {
fbdev->enable_key_flag |= KEYCOLOR_FLAG_TARGET;
if (!(fbdev->enable_key_flag & KEYCOLOR_FLAG_ONHOLD)) {
osd_ext_srckey_enable_hw(fb_info->node - 2, 1);
fbdev->enable_key_flag |= KEYCOLOR_FLAG_CURRENT;
}
} else {
fbdev->enable_key_flag &=
~(KEYCOLOR_FLAG_TARGET | KEYCOLOR_FLAG_CURRENT);
osd_ext_srckey_enable_hw(fb_info->node - 2, 0);
}
mutex_unlock(&fbdev->lock);
return count;
}
static ssize_t show_enable_key(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
return snprintf(buf, PAGE_SIZE,
(fbdev->enable_key_flag & KEYCOLOR_FLAG_TARGET) ?
"1\n" : "0\n");
}
static ssize_t store_enable_key_onhold(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
mutex_lock(&fbdev->lock);
if (res != 0) {
/* hold all the calls to enable color key */
fbdev->enable_key_flag |= KEYCOLOR_FLAG_ONHOLD;
fbdev->enable_key_flag &= ~KEYCOLOR_FLAG_CURRENT;
osd_ext_srckey_enable_hw(fb_info->node - 2, 0);
} else {
fbdev->enable_key_flag &= ~KEYCOLOR_FLAG_ONHOLD;
/*
* if target and current mistach
* then recover the pending key settings
*/
if (fbdev->enable_key_flag & KEYCOLOR_FLAG_TARGET) {
if ((fbdev->enable_key_flag &
KEYCOLOR_FLAG_CURRENT) == 0) {
fbdev->enable_key_flag |= KEYCOLOR_FLAG_CURRENT;
osd_ext_srckey_enable_hw(fb_info->node - 2, 1);
}
} else {
if (fbdev->enable_key_flag & KEYCOLOR_FLAG_CURRENT) {
fbdev->enable_key_flag &=
~KEYCOLOR_FLAG_CURRENT;
osd_ext_srckey_enable_hw(fb_info->node - 2, 0);
}
}
}
mutex_unlock(&fbdev->lock);
return count;
}
static ssize_t show_enable_key_onhold(struct device *device,
struct device_attribute *attr, char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
return snprintf(buf, PAGE_SIZE,
(fbdev->enable_key_flag & KEYCOLOR_FLAG_ONHOLD) ?
"1\n" : "0\n");
}
static int parse_para(const char *para, int para_num, int *result)
{
char *token = NULL;
char *params, *params_base;
int *out = result;
int len = 0, count = 0;
int res = 0;
int ret = 0;
if (!para)
return 0;
params = kstrdup(para, GFP_KERNEL);
params_base = params;
token = params;
len = strlen(token);
do {
token = strsep(&params, " ");
while (token && (isspace(*token)
|| !isgraph(*token)) && len) {
token++;
len--;
}
if (len == 0)
break;
ret = kstrtoint(token, 0, &res);
if (ret < 0)
break;
len = strlen(token);
*out++ = res;
count++;
} while ((token) && (count < para_num) && (len > 0));
kfree(params_base);
return count;
}
static ssize_t show_free_scale_axis(struct device *device,
struct device_attribute *attr, char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
int x, y, w, h;
osd_ext_get_free_scale_axis_hw(fb_info->node - 2, &x, &y, &w, &h);
return snprintf(buf, PAGE_SIZE, "%d %d %d %d\n", x, y, w, h);
}
static ssize_t store_free_scale_axis(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
int parsed[4];
if (likely(parse_para(buf, 4, parsed) == 4))
osd_ext_set_free_scale_axis_hw(fb_info->node - 2,
parsed[0], parsed[1], parsed[2], parsed[3]);
else
osd_log_err("set free scale axis error\n");
return count;
}
static ssize_t show_scale_axis(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
int x0, y0, x1, y1;
osd_ext_get_scale_axis_hw(fb_info->node - 2, &x0, &y0, &x1, &y1);
return snprintf(buf, PAGE_SIZE, "%d %d %d %d\n", x0, y0, x1, y1);
}
static ssize_t store_scale_axis(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
int parsed[4];
if (likely(parse_para(buf, 4, parsed) == 4))
osd_ext_set_scale_axis_hw(fb_info->node - 2,
parsed[0], parsed[1], parsed[2], parsed[3]);
else
osd_log_err("set scale axis error\n");
return count;
}
static ssize_t show_scale_width(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
unsigned int free_scale_width = 0;
osd_ext_get_free_scale_width_hw(fb_info->node - 2, &free_scale_width);
return snprintf(buf, PAGE_SIZE, "free_scale_width:[0x%x]\n",
free_scale_width);
}
static ssize_t show_scale_height(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
unsigned int free_scale_height = 0;
osd_ext_get_free_scale_height_hw(fb_info->node - 2, &free_scale_height);
return snprintf(buf, PAGE_SIZE, "free_scale_height:[0x%x]\n",
free_scale_height);
}
static ssize_t store_free_scale(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
unsigned int free_scale_enable = 0;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
free_scale_enable = res;
osd_ext_set_free_scale_enable_hw(fb_info->node - 2, free_scale_enable);
return count;
}
static ssize_t show_free_scale(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
unsigned int free_scale_enable = 0;
osd_ext_get_free_scale_enable_hw(fb_info->node - 2, &free_scale_enable);
return snprintf(buf, PAGE_SIZE, "free_scale_enable:[0x%x]\n",
free_scale_enable);
}
static ssize_t show_scale(struct device *device, struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
return snprintf(buf, PAGE_SIZE, "scale:[0x%x]\n", fbdev->scale);
}
static ssize_t store_scale(struct device *device, struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
fbdev->scale = res;
osd_ext_set_2x_scale_hw(fb_info->node - 2,
fbdev->scale & 0xffff0000 ? 1 : 0,
fbdev->scale & 0xffff ? 1 : 0);
return count;
}
static ssize_t show_debug(struct device *device, struct device_attribute *attr,
char *buf)
{
char help[] = "Usage:\n"
" echo val > debug ; show osd pan/display/scale value\n"
" echo reg > debug ; Show osd register value\n";
return snprintf(buf, sizeof(help), "%s", help);
}
static ssize_t store_debug(struct device *device, struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
int debug_flag = 0;
if (!buf)
return count;
if (strncmp(buf, "val", 3) == 0)
debug_flag = 1;
else if (strncmp(buf, "reg", 3) == 0)
debug_flag = 2;
osd_ext_set_debug_hw(fb_info->node - 2, debug_flag);
return count;
}
static ssize_t store_order(struct device *device, struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
fbdev->order = res;
osd_ext_set_order_hw(fb_info->node - 2, fbdev->order);
return count;
}
static ssize_t show_order(struct device *device, struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct osd_fb_dev_s *fbdev = (struct osd_fb_dev_s *)fb_info->par;
fbdev->order = osd_ext_get_order_hw(fb_info->node - 2);
return snprintf(buf, PAGE_SIZE, "order:[0x%x]\n", fbdev->order);
}
static ssize_t show_block_windows(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
u32 wins[8];
osd_ext_get_block_windows_hw(fb_info->node - 2, wins);
return snprintf(buf, PAGE_SIZE,
"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
wins[0], wins[1], wins[2], wins[3],
wins[4], wins[5], wins[6], wins[7]);
}
static ssize_t store_block_windows(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
int parsed[8];
if (likely(parse_para(buf, 8, parsed) == 8))
osd_ext_set_block_windows_hw(fb_info->node - 2, parsed);
else
osd_log_err("set block windows error\n");
return count;
}
static ssize_t show_block_mode(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
u32 mode;
osd_ext_get_block_mode_hw(fb_info->node - 2, &mode);
return snprintf(buf, PAGE_SIZE, "0x%x\n", mode);
}
static ssize_t store_block_mode(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
u32 mode;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
mode = res;
osd_ext_set_block_mode_hw(fb_info->node - 2, mode);
return count;
}
static ssize_t store_freescale_mode(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
unsigned int free_scale_mode = 0;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
free_scale_mode = res;
osd_ext_set_free_scale_mode_hw(fb_info->node - 2, free_scale_mode);
return count;
}
static ssize_t show_freescale_mode(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
unsigned int free_scale_mode = 0;
osd_ext_get_free_scale_mode_hw(fb_info->node - 2, &free_scale_mode);
return snprintf(buf, PAGE_SIZE, "free_scale_mode:%s\n",
free_scale_mode ? "new" : "default");
}
static ssize_t show_window_axis(struct device *device,
struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
int x0, y0, x1, y1;
osd_ext_get_window_axis_hw(fb_info->node - 2, &x0, &y0, &x1, &y1);
return snprintf(buf, PAGE_SIZE,
"window axis is [%d %d %d %d]\n",
x0, y0, x1, y1);
}
static ssize_t store_window_axis(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
s32 parsed[4];
if (likely(parse_para(buf, 4, parsed) == 4))
osd_ext_set_window_axis_hw(fb_info->node - 2,
parsed[0], parsed[1], parsed[2], parsed[3]);
else
osd_log_err("set window axis error\n");
return count;
}
static ssize_t show_clone(struct device *device, struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
u32 clone;
osd_ext_get_clone_hw(fb_info->node - 2, &clone);
return snprintf(buf, PAGE_SIZE, "osd_ext->clone: [0x%x]\n", clone);
}
static ssize_t store_clone(struct device *device, struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
u32 clone;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
clone = res;
osd_ext_set_clone_hw(fb_info->node - 2, clone);
return count;
}
static ssize_t show_angle(struct device *device, struct device_attribute *attr,
char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
u32 angle;
osd_ext_get_angle_hw(fb_info->node - 2, &angle);
return snprintf(buf, PAGE_SIZE, "osd_ext->angle: [0x%x]\n", angle);
}
static ssize_t store_angle(struct device *device, struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
u32 angle;
int res = 0;
int ret = 0;
ret = kstrtoint(buf, 0, &res);
angle = res;
osd_ext_set_angle_hw(fb_info->node - 2, angle);
return count;
}
static struct device_attribute osd_ext_attrs[] = {
__ATTR(scale, 0664,
show_scale, store_scale),
__ATTR(order, 0664,
show_order, store_order),
__ATTR(enable_3d, 0644,
show_enable_3d, store_enable_3d),
__ATTR(free_scale, 0664,
show_free_scale, store_free_scale),
__ATTR(scale_axis, 0644,
show_scale_axis, store_scale_axis),
__ATTR(scale_width, 0644,
show_scale_width, NULL),
__ATTR(scale_height, 0644,
show_scale_height, NULL),
__ATTR(color_key, 0644,
show_color_key, store_color_key),
__ATTR(enable_key, 0644,
show_enable_key, store_enable_key),
__ATTR(enable_key_onhold, 0644,
show_enable_key_onhold, store_enable_key_onhold),
__ATTR(block_windows, 0644,
show_block_windows, store_block_windows),
__ATTR(block_mode, 0644,
show_block_mode, store_block_mode),
__ATTR(free_scale_axis, 0644,
show_free_scale_axis, store_free_scale_axis),
__ATTR(debug, 0644,
show_debug, store_debug),
__ATTR(window_axis, 0644,
show_window_axis, store_window_axis),
__ATTR(freescale_mode, 0644,
show_freescale_mode, store_freescale_mode),
__ATTR(clone, 0664,
show_clone, store_clone),
__ATTR(angle, 0644,
show_angle, store_angle),
};
#ifdef CONFIG_PM
static int osd_ext_suspend(struct platform_device *pdev, pm_message_t state)
{
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
if (early_suspend_flag)
return 0;
#endif
osd_ext_suspend_hw();
return 0;
}
static int osd_ext_resume(struct platform_device *dev)
{
#ifdef CONFIG_SCREEN_ON_EARLY
if (early_resume_flag) {
early_resume_flag = 0;
return 0;
}
#endif
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
if (early_suspend_flag)
return 0;
#endif
osd_ext_resume_hw();
return 0;
}
#endif
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
static void osd_ext_early_suspend(struct early_suspend *h)
{
if (early_suspend_flag)
return;
osd_ext_suspend_hw();
early_suspend_flag = 1;
}
static void osd_ext_late_resume(struct early_suspend *h)
{
if (!early_suspend_flag)
return;
early_suspend_flag = 0;
osd_ext_resume_hw();
}
#endif
#ifdef CONFIG_SCREEN_ON_EARLY
void osd_ext_resume_early(void)
{
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
osd_ext_resume_hw();
early_suspend_flag = 0;
#endif
early_resume_flag = 1;
}
EXPORT_SYMBOL(osd_ext_resume_early);
#endif
/* static struct resource memobj; */
static int
osd_ext_probe(struct platform_device *pdev)
{
int ret = 0;
struct fb_info *fbi = NULL;
const struct vinfo_s *vinfo = NULL;
struct fb_var_screeninfo *var;
struct fb_fix_screeninfo *fix;
/* struct resource *mem; */
int index, Bpp;
int i;
struct osd_fb_dev_s *fbdev = NULL;
u32 memsize[2];
int osd_ext_memory = 0;
/* 0:don't need osd_ext memory,1:need osd_ext memory */
#ifdef CONFIG_AMLOGIC_VOUT2
/* register vout2 client */
vout2_register_client(&osd_ext_notifier_nb);
#endif
/* get interrupt resource */
int_viu2_vsync = platform_get_irq_byname(pdev, "viu2-vsync");
if (int_viu2_vsync == -ENXIO) {
osd_log_err("cannot get viu2 irq resource\n");
goto failed1;
} else
osd_log_info("viu2 vysnc irq: %d\n", int_viu2_vsync);
ret = osd_io_remap();
if (!ret) {
osd_log_err("osd_io_remap failed\n");
goto failed1;
}
/* get buffer size from dt */
ret = of_property_read_u32_array(pdev->dev.of_node,
"mem_size", memsize, 2);
if (ret) {
osd_log_err("not found mem_size from dt\n");
osd_ext_memory = 0;
} else {
osd_log_dbg(MODULE_BASE,
"mem_size: 0x%x, 0x%x\n", memsize[0], memsize[1]);
fb_rmem_size[0] = memsize[0];
fb_rmem_paddr[0] = fb_rmem.base;
if ((OSD_COUNT == 2) && ((memsize[0] + memsize[1]) <=
fb_rmem.size)) {
fb_rmem_size[1] = memsize[1];
fb_rmem_paddr[1] = fb_rmem.base + memsize[0];
}
osd_ext_memory = 1;
}
/* init reserved memory */
ret = of_reserved_mem_device_init(&pdev->dev);
if (ret != 0) {
osd_log_info("not found reserved memory\n");
osd_ext_memory = 0;
}
#ifdef CONFIG_AMLOGIC_VOUT2
set_current_vmode2(VMODE_INIT_NULL);
#endif
osd_ext_init_hw(0);
#ifdef CONFIG_AMLOGIC_VOUT2
vinfo = get_current_vinfo2();
osd_log_info("%s vinfo:%p\n", __func__, vinfo);
#endif
for (index = 0; index < OSD_COUNT; index++) {
fbi = framebuffer_alloc(sizeof(struct osd_fb_dev_s),
&pdev->dev);
if (!fbi) {
ret = -ENOMEM;
goto failed1;
}
fbdev = (struct osd_fb_dev_s *)fbi->par;
fbdev->fb_info = fbi;
fbdev->dev = pdev;
mutex_init(&fbdev->lock);
var = &fbi->var;
fix = &fbi->fix;
gp_fbdev_list[index] = fbdev;
if (osd_ext_memory) {
fbdev->fb_len = fb_rmem_size[index];
fbdev->fb_mem_paddr = fb_rmem_paddr[index];
fbdev->fb_mem_vaddr = fb_rmem_vaddr[index];
if (!fbdev->fb_mem_vaddr) {
osd_log_err("failed to ioremap frame buffer\n");
ret = -ENOMEM;
goto failed1;
}
} else
fbdev->fb_mem_paddr = 0;
osd_log_info("Frame buffer memory assigned at");
osd_log_info(" phy:0x%08x, vir:0x%p, size=%dK\n",
fbdev->fb_mem_paddr,
fbdev->fb_mem_vaddr,
fbdev->fb_len >> 10);
if (index == DEV_OSD0) {
ret = of_property_read_u32_array(pdev->dev.of_node,
"display_size_default",
&var_screeninfo[0], 5);
if (ret)
osd_log_info("not found default size\n");
else {
int bpp = var_screeninfo[4];
fb_def_var[index].xres = var_screeninfo[0];
fb_def_var[index].yres = var_screeninfo[1];
fb_def_var[index].xres_virtual =
var_screeninfo[2];
fb_def_var[index].yres_virtual =
var_screeninfo[3];
/* logo always use double buffer */
fb_def_var[index].bits_per_pixel = bpp;
osd_log_info("init fbdev bpp is :%d\n",
fb_def_var[index].bits_per_pixel);
if (fb_def_var[index].bits_per_pixel > 32)
fb_def_var[index].bits_per_pixel = 32;
}
}
_fbdev_set_default(fbdev, index);
if (fbdev->color == NULL) {
osd_log_err("fbdev->color NULL\n");
ret = -ENOENT;
goto failed1;
}
if (osd_ext_memory) {
Bpp = (fbdev->color->color_index > 8 ?
(fbdev->color->color_index > 16 ?
(fbdev->color->color_index > 24 ?
4 : 3) : 2) : 1);
fix->line_length = var->xres_virtual * Bpp;
fix->smem_start = fbdev->fb_mem_paddr;
fix->smem_len = fbdev->fb_len;
}
if (fb_alloc_cmap(&fbi->cmap, 16, 0) != 0) {
osd_log_err("unable to allocate color map memory\n");
ret = -ENOMEM;
goto failed2;
}
fbi->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
if (!(fbi->pseudo_palette)) {
osd_log_err("unable to allocate pseudo palette mem\n");
ret = -ENOMEM;
goto failed2;
}
memset(fbi->pseudo_palette, 0, sizeof(u32) * 16);
fbi->fbops = &osd_ext_ops;
if (osd_ext_memory) {
fbi->screen_base = (char __iomem *)fbdev->fb_mem_vaddr;
fbi->screen_size = fix->smem_len;
}
if (vinfo)
set_default_display_axis(&fbdev->fb_info->var,
&fbdev->osd_ctl, vinfo);
osd_ext_check_var(var, fbi);
register_framebuffer(fbi);
osddev_ext_setup(fbdev);
for (i = 0; i < ARRAY_SIZE(osd_ext_attrs); i++)
ret = device_create_file(fbi->dev, &osd_ext_attrs[i]);
}
index = 0;
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING;
early_suspend.suspend = osd_ext_early_suspend;
early_suspend.resume = osd_ext_late_resume;
register_early_suspend(&early_suspend);
#endif
osd_log_info("osd-ext probe ok\n");
return 0;
failed2:
fb_dealloc_cmap(&fbi->cmap);
failed1:
osd_log_err("osd-ext module probe failed.\n");
return ret;
}
static int
osd_ext_remove(struct platform_device *pdev)
{
struct fb_info *fbi;
int i = 0;
osd_log_info("osd_ext_remove.\n");
if (!pdev)
return -ENODEV;
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
unregister_early_suspend(&early_suspend);
#endif
vout_unregister_client(&osd_ext_notifier_nb);
for (i = 0; i < OSD_COUNT; i++) {
int j;
if (gp_fbdev_list[i]) {
struct osd_fb_dev_s *fbdev = gp_fbdev_list[i];
fbi = fbdev->fb_info;
for (j = 0; j < ARRAY_SIZE(osd_ext_attrs); j++)
device_remove_file(fbi->dev, &osd_ext_attrs[j]);
iounmap(fbdev->fb_mem_vaddr);
kfree(fbi->pseudo_palette);
fb_dealloc_cmap(&fbi->cmap);
unregister_framebuffer(fbi);
framebuffer_release(fbi);
}
}
return 0;
}
/* Process kernel command line parameters */
static int __init
osd_ext_setup_attribute(char *options)
{
char *this_opt = NULL;
int r = 0;
unsigned long res;
if (!options || !*options)
goto exit;
while (!r && (this_opt = strsep(&options, ",")) != NULL) {
if (!strncmp(this_opt, "xres:", 5)) {
r = kstrtol(this_opt + 5, 0, &res);
fb_def_var[0].xres = fb_def_var[0].xres_virtual = res;
} else if (!strncmp(this_opt, "yres:", 5)) {
r = kstrtol(this_opt + 5, 0, &res);
fb_def_var[0].yres = fb_def_var[0].yres_virtual = res;
} else {
osd_log_err("invalid option\n");
r = -1;
}
}
exit:
return r;
}
static int rmem_fb_device_init(struct reserved_mem *rmem, struct device *dev)
{
int i = 0;
for (i = 0; i < OSD_COUNT; i++) {
if ((fb_rmem_paddr[i] > 0) && (fb_rmem_size[i] > 0)) {
fb_rmem_vaddr[i] = ioremap_wc(fb_rmem_paddr[i],
fb_rmem_size[i]);
if (!fb_rmem_vaddr[i])
osd_log_err("fb [%d] ioremap error", i);
}
}
return 0;
}
static const struct reserved_mem_ops rmem_fb_ops = {
.device_init = rmem_fb_device_init,
};
static int __init rmem_fb_setup(struct reserved_mem *rmem)
{
phys_addr_t align = PAGE_SIZE;
phys_addr_t mask = align - 1;
if ((rmem->base & mask) || (rmem->size & mask)) {
pr_err("Reserved memory: incorrect alignment of region\n");
return -EINVAL;
}
fb_rmem.base = rmem->base;
fb_rmem.size = rmem->size;
rmem->ops = &rmem_fb_ops;
osd_log_info("Reserved memory: created fb at 0x%p, size %ld MiB\n",
(void *)rmem->base, (unsigned long)rmem->size / SZ_1M);
return 0;
}
RESERVEDMEM_OF_DECLARE(fb, "amlogic, fb-ext-memory", rmem_fb_setup);
/****************************************/
static const struct of_device_id meson_fbext_dt_match[] = {
{.compatible = "amlogic, meson-fb-ext",},
{},
};
static struct platform_driver
osd_ext_driver = {
.probe = osd_ext_probe,
.remove = osd_ext_remove,
#ifdef CONFIG_PM
.suspend = osd_ext_suspend,
.resume = osd_ext_resume,
#endif
.driver = {
.name = "meson-fb-ext",
.of_match_table = meson_fbext_dt_match,
}
};
static int __init osd_ext_init_module(void)
{
char *option;
osd_log_info("%s\n", __func__);
if (fb_get_options("meson-fb-ext", &option))
return -ENODEV;
osd_ext_setup_attribute(option);
if (platform_driver_register(&osd_ext_driver)) {
osd_log_err("failed to register osd ext driver\n");
return -ENODEV;
}
return 0;
}
static void __exit osd_ext_exit_module(void)
{
osd_log_info("%s\n", __func__);
platform_driver_unregister(&osd_ext_driver);
}
/****************************************/
module_init(osd_ext_init_module);
module_exit(osd_ext_exit_module);
MODULE_AUTHOR("Platform-BJ <platform.bj@amlogic.com>");
MODULE_DESCRIPTION("OSD EXT Module");
MODULE_LICENSE("GPL");