blob: bb6a2d114120a5a34cfa7eede784153084fa5349 [file] [log] [blame]
/*
* drivers/osd/osd_fb.c
*
* Copyright (C) 2015 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.
*
*/
/* System Headers */
#include <common.h>
#include <video_fb.h>
#include <stdio_dev.h>
#include <malloc.h>
#include <bmp_layout.h>
#include <asm/cpu_id.h>
#include <asm/arch/cpu.h>
#include <asm/arch/timer.h>
/* Local Headers */
#include <amlogic/fb.h>
#include <amlogic/color.h>
#include <amlogic/vinfo.h>
#include <amlogic/vout.h>
/* Local Headers */
#include "osd.h"
#include "osd_log.h"
#include "osd_hw.h"
#include "osd_fb.h"
#define INVALID_BPP_ITEM {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
static const struct color_bit_define_s default_color_format_array[] = {
INVALID_BPP_ITEM,
INVALID_BPP_ITEM,
{
COLOR_INDEX_02_PAL4, 0, 0,
0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0,
FB_VISUAL_PSEUDOCOLOR, 2,
},
INVALID_BPP_ITEM,
{
COLOR_INDEX_04_PAL16, 0, 1,
0, 4, 0, 0, 4, 0, 0, 4, 0, 0, 0, 0,
FB_VISUAL_PSEUDOCOLOR, 4,
},
INVALID_BPP_ITEM,
INVALID_BPP_ITEM,
INVALID_BPP_ITEM,
{
COLOR_INDEX_08_PAL256, 0, 2,
0, 8, 0, 0, 8, 0, 0, 8, 0, 0, 0, 0,
FB_VISUAL_PSEUDOCOLOR, 8,
},
/*16 bit color*/
{
COLOR_INDEX_16_655, 0, 4,
10, 6, 0, 5, 5, 0, 0, 5, 0, 0, 0, 0,
FB_VISUAL_TRUECOLOR, 16
},
{
COLOR_INDEX_16_844, 1, 4,
8, 8, 0, 4, 4, 0, 0, 4, 0, 0, 0, 0,
FB_VISUAL_TRUECOLOR, 16
},
{
COLOR_INDEX_16_6442, 2, 4,
10, 6, 0, 6, 4, 0, 2, 4, 0, 0, 2, 0,
FB_VISUAL_TRUECOLOR, 16
},
{
COLOR_INDEX_16_4444_R, 3, 4,
12, 4, 0, 8, 4, 0, 4, 4, 0, 0, 4, 0,
FB_VISUAL_TRUECOLOR, 16
},
{
COLOR_INDEX_16_4642_R, 7, 4,
12, 4, 0, 6, 6, 0, 2, 4, 0, 0, 2, 0,
FB_VISUAL_TRUECOLOR, 16
},
{
COLOR_INDEX_16_1555_A, 6, 4,
10, 5, 0, 5, 5, 0, 0, 5, 0, 15, 1, 0,
FB_VISUAL_TRUECOLOR, 16
},
{
COLOR_INDEX_16_4444_A, 5, 4,
8, 4, 0, 4, 4, 0, 0, 4, 0, 12, 4, 0,
FB_VISUAL_TRUECOLOR, 16
},
{
COLOR_INDEX_16_565, 4, 4,
11, 5, 0, 5, 6, 0, 0, 5, 0, 0, 0, 0,
FB_VISUAL_TRUECOLOR, 16
},
/*24 bit color*/
INVALID_BPP_ITEM,
INVALID_BPP_ITEM,
{
COLOR_INDEX_24_6666_A, 4, 7,
12, 6, 0, 6, 6, 0, 0, 6, 0, 18, 6, 0,
FB_VISUAL_TRUECOLOR, 24
},
{
COLOR_INDEX_24_6666_R, 3, 7,
18, 6, 0, 12, 6, 0, 6, 6, 0, 0, 6, 0,
FB_VISUAL_TRUECOLOR, 24
},
{
COLOR_INDEX_24_8565, 2, 7,
11, 5, 0, 5, 6, 0, 0, 5, 0, 16, 8, 0,
FB_VISUAL_TRUECOLOR, 24
},
{
COLOR_INDEX_24_5658, 1, 7,
19, 5, 0, 13, 6, 0, 8, 5, 0, 0, 8, 0,
FB_VISUAL_TRUECOLOR, 24
},
{
COLOR_INDEX_24_888_B, 5, 7,
0, 8, 0, 8, 8, 0, 16, 8, 0, 0, 0, 0,
FB_VISUAL_TRUECOLOR, 24
},
{
COLOR_INDEX_24_RGB, 0, 7,
16, 8, 0, 8, 8, 0, 0, 8, 0, 0, 0, 0,
FB_VISUAL_TRUECOLOR, 24
},
/*32 bit color*/
INVALID_BPP_ITEM,
INVALID_BPP_ITEM,
INVALID_BPP_ITEM,
INVALID_BPP_ITEM,
{
COLOR_INDEX_32_BGRA, 3, 5,
8, 8, 0, 16, 8, 0, 24, 8, 0, 0, 8, 0,
FB_VISUAL_TRUECOLOR, 32
},
{
COLOR_INDEX_32_ABGR, 2, 5,
0, 8, 0, 8, 8, 0, 16, 8, 0, 24, 8, 0,
FB_VISUAL_TRUECOLOR, 32
},
{
COLOR_INDEX_32_RGBA, 0, 5,
24, 8, 0, 16, 8, 0, 8, 8, 0, 0, 8, 0,
FB_VISUAL_TRUECOLOR, 32
},
{
COLOR_INDEX_32_ARGB, 1, 5,
16, 8, 0, 8, 8, 0, 0, 8, 0, 24, 8, 0,
FB_VISUAL_TRUECOLOR, 32
},
/*YUV color*/
{COLOR_INDEX_YUV_422, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16},
};
GraphicDevice fb_gdev;
struct hw_para_s osd_hw;
typedef struct pic_info_t {
unsigned int mode;
unsigned int type;
unsigned int pic_width;
unsigned int pic_height;
unsigned int bpp;
ulong pic_image;
} pic_info_t;
static pic_info_t g_pic_info;
static int img_video_init = 0;
#if defined(CONFIG_AML_MINUI)
extern int in_fastboot_mode;
#endif
static void osd_layer_init(GraphicDevice *gdev, int layer)
{
u32 index = layer;
u32 xoffset = 0;
u32 yoffset = 0;
u32 xres = 0;
u32 yres = 0;
u32 xres_virtual = 0;
u32 yres_virtual = 0;
u32 disp_start_x = 0;
u32 disp_start_y = 0;
u32 disp_end_x = 0;
u32 disp_end_y = 0;
u32 fbmem = gdev->frameAdrs;
const struct color_bit_define_s *color =
&default_color_format_array[gdev->gdfIndex];
if (index >= VIU2_OSD1) {
xres = gdev->winSizeX;
yres = gdev->winSizeY;
xres_virtual = gdev->winSizeX;
yres_virtual = gdev->winSizeY * 2;
disp_end_x = gdev->winSizeX - 1;
disp_end_y = gdev->winSizeY - 1;
} else {
xres = gdev->fb_width;
yres = gdev->fb_height;
xres_virtual = gdev->fb_width;
yres_virtual = gdev->fb_height * 2;
disp_end_x = gdev->fb_width - 1;
disp_end_y = gdev->fb_height - 1;
}
#ifdef CONFIG_AML_MESON_G12A
if (index >= VIU2_OSD1)
osd_init_hw_viu2();
else
#endif
osd_init_hw();
osd_setup_hw(index,
xoffset,
yoffset,
xres,
yres,
xres_virtual ,
yres_virtual,
disp_start_x,
disp_start_y,
disp_end_x,
disp_end_y,
fbmem,
color);
}
static unsigned long env_strtoul(const char *name, int base)
{
unsigned long ret = 0;
ret = getenv_ulong(name, base, 0);
if (base == 16)
osd_logd("%s: 0x%lx\n", name, ret);
else if (base == 10)
osd_logd("%s: %ld\n", name, ret);
return ret;
}
static int get_dts_node(char *dt_addr, char *dtb_node)
{
#ifdef CONFIG_OF_LIBFDT
int parent_offset = 0;
char *propdata = NULL;
parent_offset = fdt_path_offset(dt_addr, dtb_node);
if (parent_offset < 0) {
/* not found */
return -1;
} else {
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "status", NULL);
if (propdata == NULL) {
osd_logi("not find status, default to disabled\n");
return -1;
} else {
if (strncmp(propdata, "okay", 2)) {
osd_logi("status disabled\n");
return -1;
}
}
}
return parent_offset;
#else
return -1;
#endif
}
unsigned long get_fb_addr(void)
{
char *dt_addr = NULL;
unsigned long fb_addr = 0;
static int initrd_set = 0;
char str_fb_addr[32];
char fdt_node[32];
#ifdef CONFIG_OF_LIBFDT
int parent_offset = 0;
char *propdata = NULL;
#endif
fb_addr = env_strtoul("fb_addr", 16);
#ifdef CONFIG_OF_LIBFDT
#ifdef CONFIG_DTB_MEM_ADDR
dt_addr = (char *)CONFIG_DTB_MEM_ADDR;
#else
dt_addr = (char *)0x01000000;
#endif
#if defined(CONFIG_AML_MINUI)
if (in_fastboot_mode == 1) {
osd_logi("in fastboot mode, load default fb_addr parameters \n");
} else
#endif
{
if (fdt_check_header(dt_addr) < 0) {
osd_logi("check dts: %s, load default fb_addr parameters\n",
fdt_strerror(fdt_check_header(dt_addr)));
} else {
strcpy(fdt_node, "/meson-fb");
osd_logi("load fb addr from dts:%s\n", fdt_node);
parent_offset = get_dts_node(dt_addr, fdt_node);
if (parent_offset < 0) {
strcpy(fdt_node, "/fb");
osd_logi("load fb addr from dts:%s\n", fdt_node);
parent_offset = get_dts_node(dt_addr, fdt_node);
if (parent_offset < 0) {
strcpy(fdt_node, "/drm-vpu");
osd_logi("load fb addr from dts:%s\n", fdt_node);
parent_offset = get_dts_node(dt_addr, fdt_node);
if (parent_offset < 0) {
osd_logi("not find node: %s\n",fdt_strerror(parent_offset));
osd_logi("use default fb_addr parameters\n");
} else {
/* check fb_addr */
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "logo_addr", NULL);
if (propdata == NULL) {
osd_logi("failed to get fb addr for logo\n");
osd_logi("use default fb_addr parameters\n");
} else {
fb_addr = simple_strtoul(propdata, NULL, 16);
}
}
} else {
/* check fb_addr */
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "logo_addr", NULL);
if (propdata == NULL) {
osd_logi("failed to get fb addr for logo\n");
osd_logi("use default fb_addr parameters\n");
} else {
fb_addr = simple_strtoul(propdata, NULL, 16);
}
}
} else {
/* check fb_addr */
propdata = (char *)fdt_getprop(dt_addr, parent_offset, "logo_addr", NULL);
if (propdata == NULL) {
osd_logi("failed to get fb addr for logo\n");
osd_logi("use default fb_addr parameters\n");
} else {
fb_addr = simple_strtoul(propdata, NULL, 16);
}
}
}
}
#endif
if ((!initrd_set) && (get_cpu_id().family_id >= MESON_CPU_MAJOR_ID_AXG)) {
sprintf(str_fb_addr,"%lx",fb_addr);
setenv("initrd_high", str_fb_addr);
initrd_set = 1;
osd_logi("set initrd_high: 0x%s\n", str_fb_addr);
}
osd_logi("fb_addr for logo: 0x%lx\n", fb_addr);
return fb_addr;
}
static void get_osd_version(void)
{
u32 family_id = get_cpu_id().family_id;
if (family_id == MESON_CPU_MAJOR_ID_AXG)
osd_hw.osd_ver = OSD_SIMPLE;
else if ((family_id == MESON_CPU_MAJOR_ID_G12A) ||
(family_id == MESON_CPU_MAJOR_ID_G12B) ||
(family_id >= MESON_CPU_MAJOR_ID_SM1))
osd_hw.osd_ver = OSD_HIGH_ONE;
else
osd_hw.osd_ver = OSD_NORMAL;
}
int get_osd_layer(void)
{
char *layer_str;
int osd_index = -1;
layer_str = getenv("display_layer");
if (strcmp(layer_str, "osd0") == 0)
osd_index = OSD1;
else if (strcmp(layer_str, "osd1") == 0)
osd_index = OSD2;
else if (strcmp(layer_str, "viu2_osd0") == 0)
osd_index = VIU2_OSD1;
else
osd_loge("%s, error found\n", __func__);
return osd_index;
}
static void *osd_hw_init(void)
{
int osd_index = -1;
u32 color_index = 0;
color_index = env_strtoul("display_color_index", 10);
if ((color_index < ARRAY_SIZE(default_color_format_array))
&& (default_color_format_array[color_index].color_index !=
COLOR_INDEX_NULL))
fb_gdev.gdfIndex = color_index;
else {
osd_loge("color_index %d invalid\n", color_index);
return NULL;
}
osd_index = get_osd_layer();
if (osd_index < 0) {
osd_loge("osd_hw_init: invalid osd_index\n");
return NULL;
}
if (osd_index == OSD1)
osd_layer_init(&fb_gdev, OSD1);
else if (osd_index == OSD2) {
if (osd_hw.osd_ver == OSD_SIMPLE) {
osd_loge("AXG not support osd2\n");
return NULL;
}
osd_layer_init(&fb_gdev, OSD2);
} else if (osd_index == VIU2_OSD1) {
if (osd_hw.osd_ver == OSD_SIMPLE) {
osd_loge("AXG not support viu2 osd0\n");
return NULL;
}
osd_layer_init(&fb_gdev, VIU2_OSD1);
} else {
osd_loge("display_layer(%d) invalid\n", osd_index);
return NULL;
}
return (void *)&fb_gdev;
}
void *video_hw_init(int display_mode)
{
u32 fb_addr = 0;
u32 display_width = 0;
u32 display_height = 0;
u32 display_bpp = 0;
u32 fg = 0;
u32 bg = 0;
u32 fb_width = 0;
u32 fb_height = 0;
get_osd_version();
vout_init();
fb_addr = get_fb_addr();
switch (display_mode) {
case MIDDLE_MODE:
case RECT_MODE:
#ifdef CONFIG_OSD_SCALE_ENABLE
fb_width = env_strtoul("fb_width", 10);
fb_height = env_strtoul("fb_height", 10);
#endif
display_bpp = env_strtoul("display_bpp", 10);
break;
case FULL_SCREEN_MODE:
fb_width = g_pic_info.pic_width;
fb_height = g_pic_info.pic_height;
display_bpp = g_pic_info.bpp;
break;
}
display_width = env_strtoul("display_width", 10);
display_height = env_strtoul("display_height", 10);
fg = env_strtoul("display_color_fg", 10);
bg = env_strtoul("display_color_bg", 10);
/* fill in Graphic Device */
fb_gdev.frameAdrs = fb_addr;
fb_gdev.fb_width = fb_width;
fb_gdev.fb_height = fb_height;
fb_gdev.winSizeX = display_width;
fb_gdev.winSizeY = display_height;
fb_gdev.gdfBytesPP = display_bpp / 8;
fb_gdev.fg = fg;
fb_gdev.bg = bg;
fb_gdev.mode = display_mode;
return osd_hw_init();
}
int rle8_decode(uchar *ptr, bmp_image_t *bmap_rle8, ulong width_bmp, ulong height_bmp) {
uchar a;
uchar cnt, runlen;
int i;
int decode;
int pixels;
uchar *pic;
int limit;
a = 0xFF;
decode = 1;
pixels = 0;
limit = width_bmp * height_bmp;
pic = (uchar *)bmap_rle8 + le32_to_cpu(bmap_rle8->header.data_offset);
while (decode) {
switch (pic[0]) {
case 0:
switch (pic[1]) {
case 0:
/* end of row */
pic += 2;
continue;
case 1:
/* end of bmp */
decode = 0;
break;
case 2:
/* 00 02 mode */
pic += 4;
break;
default:
/* 00 (03~FF) mode */
cnt = pic[1];
runlen = cnt;
pixels += cnt;
if (pixels > limit)
{
osd_loge("Error: Too much encoded pixel data, validate your bitmap\n");
decode = 0;
return -1;
}
pic += 2;
for (i = 0; i < cnt; i++) {
*ptr = bmap_rle8->color_table[*pic].blue;
ptr += 1;
*ptr = bmap_rle8->color_table[*pic].green;
ptr += 1;
*ptr = bmap_rle8->color_table[*pic].red;
ptr += 1;
*ptr = a;
ptr += 1;
pic += 1;
}
if (runlen & 1)
pic += 1; /* 0 padding if length is odd */
break;
}
break;
default:
/* normal mode */
cnt = pic[0];
runlen = cnt;
pixels += cnt;
if (pixels > limit) {
osd_loge("Error: Too much encoded pixel data, validate your bitmap\n");
return -1;
}
pic += 1;
for (i = 0; i < cnt; i++) {
*ptr = bmap_rle8->color_table[*pic].blue;
ptr += 1;
*ptr = bmap_rle8->color_table[*pic].green;
ptr += 1;
*ptr = bmap_rle8->color_table[*pic].red;
ptr += 1;
*ptr = a;
ptr += 1;
}
pic += 1;
break;
}
}
return (0);
}
static int parse_bmp_info(ulong bmp_image)
{
bmp_image_t *bmp = (bmp_image_t *)bmp_image;
if (!((bmp->header.signature[0] == 'B') &&
(bmp->header.signature[1] == 'M'))) {
osd_loge("no valid bmp image at 0x%lx\n", bmp_image);
return 1;
}
g_pic_info.pic_width = le32_to_cpu(bmp->header.width);
g_pic_info.pic_height = le32_to_cpu(bmp->header.height);
g_pic_info.bpp = le16_to_cpu(bmp->header.bit_count);
return 0;
}
int video_display_bitmap(ulong bmp_image, int x, int y)
{
struct vinfo_s *info = NULL;
#if defined CONFIG_AML_VOUT
info = vout_get_current_vinfo();
#endif
// ushort *cmap_base = NULL;
unsigned long byte_width;
ushort i, j;
uchar *fb;
bmp_image_t *bmp = (bmp_image_t *)bmp_image;
uchar *bmap;
ushort padded_line;
unsigned long width, height;
unsigned long pheight;
unsigned long pwidth;
unsigned colors, bpix, bmp_bpix;
uint lcd_line_length;
int osd_index = -1;
osd_index = get_osd_layer();
if (osd_index < 0) {
osd_loge("video_display_bitmap: invalid osd_index\n");
return (-1);
}
/* viu1 has scaler, viu2 has no scaler */
if (osd_index >= VIU2_OSD1) {
pwidth = info->width;
pheight = info->height;
} else {
pheight = fb_gdev.fb_height;
pwidth = fb_gdev.fb_width;
}
lcd_line_length = CANVAS_ALIGNED((pwidth * NBITS(info->vl_bpix)) / 8);
if (fb_gdev.mode != FULL_SCREEN_MODE)
if (parse_bmp_info(bmp_image))
return -1;
width = g_pic_info.pic_width;
height = g_pic_info.pic_height;
bmp_bpix = g_pic_info.bpp;
colors = 1 << bmp_bpix;
uchar *buffer_rgb = NULL;
bpix = NBITS(info->vl_bpix);
if ((bpix != 1) && (bpix != 8) && (bpix != 16) && (bpix != 24) &&
(bpix != 32)) {
osd_loge("%d bit/pixel mode1, but BMP has %d bit/pixel\n",
bpix, bmp_bpix);
return 1;
}
/* We support displaying 8bpp BMPs on 16bpp LCDs */
/*if (bpix != bmp_bpix && (bmp_bpix != 8 || bpix != 16)) {
osd_loge("%d bit/pixel mode2, but BMP has %d bit/pixel\n",
bpix,
le16_to_cpu(bmp->header.bit_count));
return 1;
}*/
osd_logd("Display-bmp: %d x %d with %d colors\n",
(int)width, (int)height, (int)colors);
if ((x == -1) && (y == -1)) {
/* use MIDDLE_MODE */
if ((width > pwidth) || (height > pheight)) {
x = 0;
y = 0;
} else {
x = (pwidth - width) / 2;
y = (pheight - height) / 2;
}
} else {
switch (fb_gdev.mode) {
case MIDDLE_MODE:
if ((width > pwidth) || (height > pheight)) {
x = 0;
y = 0;
} else {
x = (pwidth - width) / 2;
y = (pheight - height) / 2;
}
break;
case RECT_MODE:
break;
case FULL_SCREEN_MODE:
x = 0;
y = 0;
break;
}
}
/*
* BMP format for Monochrome assumes that the state of a
* pixel is described on a per Bit basis, not per Byte.
* So, in case of Monochrome BMP we should align widths
* on a byte boundary and convert them from Bit to Byte
* units.
* Probably, PXA250 and MPC823 process 1bpp BMP images in
* their own ways, so make the converting to be MCC200
* specific.
*/
padded_line = (width & 0x3) ? ((width & ~0x3) + 4) : (width);
if ((x + width) > pwidth)
width = pwidth - x;
if ((y + height) > pheight)
height = pheight - y;
bmap = (uchar *)bmp + le32_to_cpu(bmp->header.data_offset);
fb = (uchar *)(osd_hw.fb_gem[osd_index].addr +
(y + height - 1) * lcd_line_length + x * fb_gdev.gdfBytesPP);
osd_logd("fb=0x%p; bmap=0x%p, width=%ld, height= %ld, lcd_line_length=%d, padded_line=%d, fb_gdev.fb_width=%d, fb_gdev.fb_height=%d \n",
fb, bmap, width, height, lcd_line_length, padded_line,fb_gdev.fb_width,fb_gdev.fb_height);
if (bmp_bpix == 8) {
/* decode of RLE8 */
buffer_rgb = (uchar *)malloc(height * width * 4 * sizeof(uchar) + 1);
if (buffer_rgb == NULL) {
printf("Error:fail to malloc the memory!");
return (-1);
}
}
uchar *ptr_rgb = buffer_rgb;
switch (bmp_bpix) {
case 8:
if (bpix != 16)
byte_width = width;
else
byte_width = width * 2;
rle8_decode(ptr_rgb, bmp, width, height);
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
if (bpix != 16) {
*(fb++) = *buffer_rgb;
buffer_rgb += 1;
*(fb++) = *buffer_rgb;
buffer_rgb += 1;
*(fb++) = *buffer_rgb;
buffer_rgb += 1;
*(fb++) = *buffer_rgb;
buffer_rgb += 1;
}
// else {
// *(uint16_t *)fb = cmap_base[*buffer_rgb++];
// fb += sizeof(uint16_t) / sizeof(*fb);
// }
}
buffer_rgb += (padded_line - width);
fb -= (byte_width * 4 + lcd_line_length);
}
buffer_rgb -= width*height*4;
free(buffer_rgb);
break;
case 16:
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
}
bmap += (padded_line - width) * 2;
fb -= (width * 2 + lcd_line_length);
}
break;
case 24:
if (bpix == 32) {
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = 0xff;
}
bmap += (padded_line - width);
fb -= (width * 4 + lcd_line_length);
}
} else {
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
}
bmap += (padded_line - width);
fb -= (width * 3 + lcd_line_length);
}
}
break;
case 32:
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
}
bmap += (padded_line - width);
fb -= (width * 4 + lcd_line_length);
}
break;
default:
osd_loge("error: gdev.bpp %d, but bmp.bpp %d\n", fb_gdev.gdfBytesPP, bmp_bpix);
return (-1);
}
buffer_rgb = NULL;
ptr_rgb = NULL;
flush_cache((unsigned long)osd_hw.fb_gem[osd_index].addr,
pheight * CANVAS_ALIGNED(pwidth * info->vl_bpix / 8));
return (0);
}
int video_display_raw(ulong raw_image, int x, int y)
{
struct vinfo_s *info = NULL;
#if defined CONFIG_AML_VOUT
info = vout_get_current_vinfo();
#endif
ushort i, j;
uchar *fb;
uchar *bmap = (uchar *)raw_image;
ushort padded_line;
unsigned long width, height;
#ifdef CONFIG_OSD_SCALE_ENABLE
unsigned long pheight = fb_gdev.fb_height;
unsigned long pwidth = fb_gdev.fb_width;
#else
unsigned long pheight = info->width;
unsigned long pwidth = info->height;
#endif
unsigned colors, bpix, bmp_bpix;
int lcd_line_length = (pwidth * NBITS(info->vl_bpix)) / 8;
int osd_index = -1;
osd_index = get_osd_layer();
if (osd_index < 0) {
osd_loge("video_display_raw: invalid osd_index\n");
return (-1);
}
width = g_pic_info.pic_width;
height = g_pic_info.pic_height;
bmp_bpix = g_pic_info.bpp;
colors = 1 << bmp_bpix;
bpix = NBITS(info->vl_bpix);
if ((bpix != 1) && (bpix != 8) && (bpix != 16) && (bpix != 24) &&
(bpix != 32)) {
osd_loge("%d bit/pixel mode1, but BMP has %d bit/pixel\n",
bpix, bmp_bpix);
return 1;
}
/* We support displaying 8bpp BMPs on 16bpp LCDs */
/*if (bpix != bmp_bpix && (bmp_bpix != 8 || bpix != 16)) {
osd_loge("%d bit/pixel mode2, but BMP has %d bit/pixel\n",
bpix,
le16_to_cpu(bmp->header.bit_count));
return 1;
}*/
osd_logd("Display-bmp: %d x %d with %d colors\n",
(int)width, (int)height, (int)colors);
if ((x == -1) && (y == -1)) {
/* use MIDDLE_MODE */
if ((width > pwidth) || (height > pheight)) {
x = 0;
y = 0;
} else {
x = (pwidth - width) / 2;
y = (pheight - height) / 2;
}
} else {
switch (fb_gdev.mode) {
case MIDDLE_MODE:
if ((width > pwidth) || (height > pheight)) {
x = 0;
y = 0;
} else {
x = (pwidth - width) / 2;
y = (pheight - height) / 2;
}
break;
case RECT_MODE:
break;
case FULL_SCREEN_MODE:
x = 0;
y = 0;
break;
}
}
/*
* BMP format for Monochrome assumes that the state of a
* pixel is described on a per Bit basis, not per Byte.
* So, in case of Monochrome BMP we should align widths
* on a byte boundary and convert them from Bit to Byte
* units.
* Probably, PXA250 and MPC823 process 1bpp BMP images in
* their own ways, so make the converting to be MCC200
* specific.
*/
padded_line = (width & 0x3) ? ((width & ~0x3) + 4) : (width);
if ((x + width) > pwidth)
width = pwidth - x;
if ((y + height) > pheight)
height = pheight - y;
osd_enable_hw(osd_index, 1);
fb = (uchar *)(info->vd_base + y * lcd_line_length + x * fb_gdev.gdfBytesPP);
osd_logd("fb=0x%p; bmap=0x%p, width=%ld, height= %ld, lcd_line_length=%d, padded_line=%d, fb_gdev.fb_width=%d, fb_gdev.fb_height=%d \n",
fb, bmap, width, height, lcd_line_length, padded_line,fb_gdev.fb_width,fb_gdev.fb_height);
if (FULL_SCREEN_MODE == fb_gdev.mode) {
memcpy(fb, bmap, height * width * bmp_bpix / 8);
} else {
switch (bmp_bpix) {
case 16:
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
}
bmap += (padded_line - width) * 2;
fb += (lcd_line_length - width * 2);
}
break;
case 24:
if (bpix == 32) {
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = 0xff;
}
bmap += (padded_line - width) * 4;
fb += (lcd_line_length - width * 4);
}
} else {
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
}
bmap += (padded_line - width) * 3;
fb += (lcd_line_length - width * 3);
}
}
break;
case 32:
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
*(fb++) = *(bmap++);
}
bmap += (padded_line - width) * 4;
fb += (lcd_line_length - width * 4);
}
break;
default:
osd_loge("error: gdev.bpp %d, but raw.bpp %d\n", fb_gdev.gdfBytesPP, bmp_bpix);
return (-1);
}
}
flush_cache((unsigned long)info->vd_base,
pheight * pwidth * info->vl_bpix / 8);
return (0);
}
#ifdef CONFIG_OSD_SCALE_ENABLE
int video_scale_bitmap(void)
{
char *layer_str = NULL;
int osd_index = -1;
int axis[4] = {};
#ifdef CONFIG_AML_MESON_G12A
struct pandata_s disp_data;
#endif
osd_logd2("video_scale_bitmap src w=%d, h=%d, dst w=%d, dst h=%d\n",
fb_gdev.fb_width, fb_gdev.fb_height, fb_gdev.winSizeX, fb_gdev.winSizeY);
vout_get_current_axis(axis);
layer_str = getenv("display_layer");
if (strcmp(layer_str, "osd0") == 0)
osd_index = OSD1;
else if (strcmp(layer_str, "osd1") == 0)
osd_index = OSD2;
else if (strcmp(layer_str, "viu2_osd0") == 0) {
osd_index = VIU2_OSD1;
goto no_scale;
} else {
osd_logd2("video_scale_bitmap: invalid display_layer\n");
return (-1);
}
#ifdef CONFIG_OSD_SUPERSCALE_ENABLE
if ((fb_gdev.fb_width * 2 != fb_gdev.winSizeX) ||
(fb_gdev.fb_height * 2 != fb_gdev.winSizeY)) {
osd_enable_hw(osd_index, 1);
return (-1);
}
osd_set_free_scale_mode_hw(osd_index, 2);
#else
osd_set_free_scale_mode_hw(osd_index, 1);
#endif
osd_set_free_scale_axis_hw(osd_index, 0, 0, fb_gdev.fb_width - 1,
fb_gdev.fb_height - 1);
osd_set_window_axis_hw(osd_index, axis[0], axis[1], axis[0] + axis[2] - 1,
axis[1] + axis[3] - 1);
osd_set_free_scale_enable_hw(osd_index, 0x10001);
no_scale:
#ifdef CONFIG_AML_MESON_G12A
disp_data.x_start = axis[0];
disp_data.y_start = axis[1];
disp_data.x_end = axis[0] + axis[2] - 1;
disp_data.y_end = axis[1] + axis[3] - 1;
if (osd_hw.osd_ver == OSD_HIGH_ONE && osd_index < VIU2_OSD1)
osd_update_blend(&disp_data);
#endif
osd_enable_hw(osd_index, 1);
return (1);
}
#endif
/*
MIDDLE_MODE,RECT_MODE:
fixed framebuffer size get from uboot env.
bmp_image can be 0.
FULL_SCREEN_MODE:
usd bmp size as framebuffer size;
must set bmp_image;
*/
void img_mode_set(u32 display_mode)
{
fb_gdev.mode = display_mode;
}
void img_addr_set(ulong pic_image)
{
g_pic_info.pic_image = pic_image;
}
void img_type_set(u32 type)
{
g_pic_info.type = type;
}
void img_raw_size_set(u32 raw_width, u32 raw_height, u32 raw_bpp)
{
g_pic_info.pic_width = raw_width;
g_pic_info.pic_height = raw_height;
g_pic_info.bpp = raw_bpp;
}
static int img_raw_init(void)
{
unsigned int display_mode;
if (img_video_init)
return 0;
display_mode = fb_gdev.mode;
#if 0
if (display_mode == FULL_SCREEN_MODE) {
g_pic_info.pic_width = env_strtoul("pic_width", 10);
g_pic_info.pic_height = env_strtoul("pic_height", 10);
g_pic_info.bpp = env_strtoul("pic_bpp", 10);
}
#endif
if (NULL == video_hw_init(display_mode)) {
printf("Initialize video device failed!\n");
return -1;
}
osd_logd2("raw_width=%d, raw_height=%d, raw_bpp=%d\n", g_pic_info.pic_width, g_pic_info.pic_height, g_pic_info.bpp);
img_video_init = 1;
return 0;
}
static int img_bmp_init(void)
{
unsigned int display_mode;
if (img_video_init)
return 0;
display_mode = fb_gdev.mode;
if (display_mode == FULL_SCREEN_MODE) {
if (g_pic_info.pic_image != 0)
if (parse_bmp_info(g_pic_info.pic_image))
return -1;
}
if (NULL == video_hw_init(display_mode)) {
printf("Initialize video device failed!\n");
return -1;
}
img_video_init = 1;
return 0;
}
int img_osd_init(void)
{
int ret = -1;
if (g_pic_info.type == BMP_PIC)
ret = img_bmp_init();
else if(g_pic_info.type == RAW_PIC)
ret = img_raw_init();
return ret;
}
int img_bmp_display(ulong bmp_image, int x, int y)
{
return video_display_bitmap(bmp_image, x, y);
}
int img_raw_display(ulong raw_image, int x, int y)
{
return video_display_raw(raw_image, x, y);
}
int img_display(ulong bmp_image, int x, int y)
{
int ret = -1;
if (g_pic_info.type == BMP_PIC)
ret = img_bmp_display(bmp_image, x, y);
else if(g_pic_info.type == RAW_PIC)
ret = img_raw_display(bmp_image, x, y);
return ret;
}
int img_scale(void)
{
int ret = -1;
if (!img_video_init) {
printf("fastboot osd not enabled!\n");
}
#ifdef CONFIG_OSD_SCALE_ENABLE
ret = video_scale_bitmap();
#endif
return ret;
}
int img_osd_clear(void)
{
if (!img_video_init) {
printf("Please enable osd device first!\n");
return 1;
}
#ifdef CONFIG_OSD_SCALE_ENABLE
memset((void *)(long long)(fb_gdev.frameAdrs), 0,
(fb_gdev.fb_width * fb_gdev.fb_height)*fb_gdev.gdfBytesPP);
flush_cache(fb_gdev.frameAdrs,
((fb_gdev.fb_width * fb_gdev.fb_height)*fb_gdev.gdfBytesPP));
#else
memset((void *)(long long)(fb_gdev.frameAdrs), 0,
(fb_gdev.winSizeX * fb_gdev.winSizeY)*fb_gdev.gdfBytesPP);
flush_cache(fb_gdev.frameAdrs,
((fb_gdev.winSizeX * fb_gdev.winSizeY)*fb_gdev.gdfBytesPP));
#endif
return 0;
}
void img_osd_uninit(void)
{
img_video_init = 0;
}
static int _osd_hw_init(void)
{
u32 fb_addr = 0;
u32 display_width = 0;
u32 display_height = 0;
u32 display_bpp = 0;
u32 color_index = 0;
u32 fg = 0;
u32 bg = 0;
u32 fb_width = 0;
u32 fb_height = 0;;
get_osd_version();
vout_init();
fb_addr = get_fb_addr();
#ifdef CONFIG_OSD_SCALE_ENABLE
fb_width = env_strtoul("fb_width", 10);
fb_height = env_strtoul("fb_height", 10);
#endif
display_width = env_strtoul("display_width", 10);
display_height = env_strtoul("display_height", 10);
display_bpp = env_strtoul("display_bpp", 10);
color_index = env_strtoul("display_color_index", 10);
fg = env_strtoul("display_color_fg", 10);
bg = env_strtoul("display_color_bg", 10);
/* fill in Graphic Device */
fb_gdev.frameAdrs = fb_addr;
fb_gdev.fb_width = fb_width;
fb_gdev.fb_height = fb_height;
fb_gdev.winSizeX = display_width;
fb_gdev.winSizeY = display_height;
fb_gdev.gdfBytesPP = display_bpp / 8;
fb_gdev.fg = fg;
fb_gdev.bg = bg;
if ((color_index < ARRAY_SIZE(default_color_format_array))
&& (default_color_format_array[color_index].color_index !=
COLOR_INDEX_NULL))
fb_gdev.gdfIndex = color_index;
else {
osd_loge("color_index %d invalid\n", color_index);
return -1;
}
return 0;
}
static int osd_hw_init_by_index(u32 osd_index)
{
if (_osd_hw_init() < 0)
return -1;
if (osd_index == OSD1)
osd_layer_init(&fb_gdev, OSD1);
else if ( osd_index == OSD2) {
if (osd_hw.osd_ver == OSD_SIMPLE) {
osd_loge("AXG not support osd2\n");
return -1;
}
osd_layer_init(&fb_gdev, OSD2);
}
else {
osd_loge("display_layer(%d) invalid\n", osd_index);
return -1;
}
return 0;
}
static int video_display_osd(u32 osd_index)
{
struct vinfo_s *info = NULL;
#if defined CONFIG_AML_VOUT
info = vout_get_current_vinfo();
#endif
ushort i, j;
uchar *fb;
unsigned long width, height;
#ifdef CONFIG_OSD_SCALE_ENABLE
unsigned long pheight = fb_gdev.fb_height;
unsigned long pwidth = fb_gdev.fb_width;
#else
unsigned long pheight = info->width;
unsigned long pwidth = info->height;
#endif
int bpp;
if (fb_gdev.gdfBytesPP != 2) {
osd_loge("%d bit/pixel mode1, not support now\n",
fb_gdev.gdfBytesPP);
return -1;
}
bpp = fb_gdev.gdfBytesPP;
width = fb_gdev.fb_width;
height = fb_gdev.fb_height;
osd_set_free_scale_enable_hw(osd_index, 0x0); // disable free_scale
osd_enable_hw(osd_index, 1);
fb = (uchar *)(info->vd_base);
osd_logd("fb=0x%p; width=%ld, height= %ld, bpp=%d\n",
fb, width, height, bpp);
for (j = 0; j < height; j++) {
for (i = 0; i < width; i++ ) {
*(fb++) = 0x0;
*(fb++) = 0xf8;
}
}
flush_cache((unsigned long)info->vd_base, pheight * pwidth * bpp);
return (0);
}
u32 hist_max_min[3][100], hist_spl_val[3][100],
hist_spl_pix_cnt[3][100], hist_cheoma_sum[3][100];
void hist_set_golden_data(void)
{
u32 i = 0;
u32 family_id = get_cpu_id().family_id;
char *str = NULL;
char *hist_env_key[12] =
{"hist_max_min_osd0","hist_spl_val_osd0","hist_spl_pix_cnt_osd0","hist_cheoma_sum_osd0",
"hist_max_min_osd1","hist_spl_val_osd1","hist_spl_pix_cnt_osd1","hist_cheoma_sum_osd1",
"hist_max_min_osd2","hist_spl_val_osd2","hist_spl_pix_cnt_osd2","hist_cheoma_sum_osd2"};
// GXL
hist_max_min[OSD1][MESON_CPU_MAJOR_ID_GXL] = 0x3d3d;
hist_spl_val[OSD1][MESON_CPU_MAJOR_ID_GXL] = 0xc4dad1;
hist_spl_pix_cnt[OSD1][MESON_CPU_MAJOR_ID_GXL] = 0x33a25;
hist_cheoma_sum[OSD1][MESON_CPU_MAJOR_ID_GXL] = 0xd4fd8a;
hist_max_min[OSD2][MESON_CPU_MAJOR_ID_GXL] = 0x4f4f;
hist_spl_val[OSD2][MESON_CPU_MAJOR_ID_GXL] = 0xfef16b;
hist_spl_pix_cnt[OSD2][MESON_CPU_MAJOR_ID_GXL] = 0x33a25;
hist_cheoma_sum[OSD2][MESON_CPU_MAJOR_ID_GXL] = 0xe85a68;
// TXL
hist_max_min[OSD1][MESON_CPU_MAJOR_ID_TXL] = 0x3d3d;
hist_spl_val[OSD1][MESON_CPU_MAJOR_ID_TXL] = 0x78a1400;
hist_spl_pix_cnt[OSD1][MESON_CPU_MAJOR_ID_TXL] = 0x1fa400;
hist_cheoma_sum[OSD1][MESON_CPU_MAJOR_ID_TXL] = 0x8284800;
hist_max_min[OSD2][MESON_CPU_MAJOR_ID_TXL] = 0x4f4f;
hist_spl_val[OSD2][MESON_CPU_MAJOR_ID_TXL] = 0x9c39c00;
hist_spl_pix_cnt[OSD2][MESON_CPU_MAJOR_ID_TXL] = 0x1fa400;
hist_cheoma_sum[OSD2][MESON_CPU_MAJOR_ID_TXL] = 0x8e62000;
// G12A
hist_max_min[OSD1][MESON_CPU_MAJOR_ID_G12A]
= hist_max_min[OSD2][MESON_CPU_MAJOR_ID_G12A]
= 0x3d3d;
hist_spl_val[OSD1][MESON_CPU_MAJOR_ID_G12A]
= hist_spl_val[OSD2][MESON_CPU_MAJOR_ID_G12A]
= 0xc4dcf6;
hist_spl_pix_cnt[OSD1][MESON_CPU_MAJOR_ID_G12A]
= hist_spl_pix_cnt[OSD2][MESON_CPU_MAJOR_ID_G12A]
= 0x33a2e;
hist_cheoma_sum[OSD1][MESON_CPU_MAJOR_ID_G12A]
= hist_cheoma_sum[OSD2][MESON_CPU_MAJOR_ID_G12A]
= 0xd4ffdc;
for (i = 0; i < 12; i++) {
str = getenv(hist_env_key[i]);
if (str) {
switch (i%4) {
case 0:
hist_max_min[i/4][family_id] = env_strtoul(str, 16);
break;
case 1:
hist_spl_val[i/4][family_id] = env_strtoul(str, 16);
break;
case 2:
hist_spl_pix_cnt[i/4][family_id] = env_strtoul(str, 16);
break;
case 3:
hist_cheoma_sum[i/4][family_id] = env_strtoul(str, 16);
break;
}
}
}
}
int osd_rma_test(u32 osd_index)
{
u32 i = osd_index, osd_max = 1;
u32 hist_result[4];
u32 family_id = get_cpu_id().family_id;
if (osd_hw.osd_ver == OSD_SIMPLE) {
osd_max = 0;
} else if (osd_hw.osd_ver == OSD_HIGH_ONE) {
osd_max = 1; // osd3 not supported now
}
if (osd_index > osd_max) {
osd_loge("=== osd%d is not supported, osd_max is %d ===\n", osd_index, osd_max);
return (-1);
}
hist_set_golden_data();
osd_logi("=== osd_rma_test for osd%d ===\n", i);
osd_hw_init_by_index(i);
if (-1 == video_display_osd(i)) {
return (-1);
}
osd_hist_enable(osd_index);
_udelay(50000);
osd_get_hist_stat(hist_result);
_udelay(50000);
osd_get_hist_stat(hist_result);
if ((hist_result[0] == hist_max_min[osd_index][family_id]) && (hist_result[1] == hist_spl_val[osd_index][family_id]) &&
(hist_result[2] == hist_spl_pix_cnt[osd_index][family_id]) && (hist_result[3] == hist_cheoma_sum[osd_index][family_id])) {
osd_logi("=== osd%d, osd_rma_test pass. ===\n", osd_index);
return (0);
} else {
osd_loge("osd hist stat result:0x%x, 0x%x, 0x%x, 0x%x\n",
hist_result[0], hist_result[1], hist_result[2], hist_result[3]);
osd_loge("osd hist golden data:0x%x, 0x%x, 0x%x, 0x%x\n",
hist_max_min[osd_index][family_id], hist_spl_val[osd_index][family_id],
hist_spl_pix_cnt[osd_index][family_id], hist_cheoma_sum[osd_index][family_id]);
osd_loge("=== osd%d, osd_rma_test failed. ===\n", osd_index);
return (-1);
}
}