blob: ae8fff19313d286d00beaad28cdc14bf3153267f [file] [log] [blame] [edit]
/*
* 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>
/* 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"
#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;
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];
#ifdef CONFIG_OSD_SCALE_ENABLE
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;
#else
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;
#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;
char *str = NULL;
str = getenv(name);
if (str)
ret = simple_strtoul(str, NULL, base);
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;
#endif
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;
}
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 (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, "/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);
}
}
}
#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;
}
void *video_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;;
char *layer_str;
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);
layer_str = getenv("display_layer");
/* 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 NULL;
}
if (strcmp(layer_str, "osd0") == 0)
osd_layer_init(fb_gdev, OSD1);
else if (strcmp(layer_str, "osd1") == 0) {
if (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_AXG) {
osd_loge("AXG not support osd2\n");
return NULL;
}
osd_layer_init(fb_gdev, OSD2);
}
else {
osd_loge("display_layer(%s) invalid\n", layer_str);
return NULL;
}
return (void *)&fb_gdev;
}
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);
}
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;
#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;
char *layer_str = NULL;
int osd_index = -1;
layer_str = getenv("display_layer");
if (strcmp(layer_str, "osd0") == 0)
osd_index = 0;
else if (strcmp(layer_str, "osd1") == 0)
osd_index = 1;
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;
}
width = le32_to_cpu(bmp->header.width);
height = le32_to_cpu(bmp->header.height);
bmp_bpix = le16_to_cpu(bmp->header.bit_count);
colors = 1 << bmp_bpix;
uchar *buffer_rgb = NULL;
bpix = NBITS(info->vl_bpix);
if ((x == -1) && (y == -1)) {
if ((width > pwidth) || (height > pheight)) {
x = 0;
y = 0;
} else {
x = (pwidth - width) / 2;
y = (pheight - height) / 2;
}
}
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);
/*
* 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);
#ifdef CONFIG_SPLASH_SCREEN_ALIGN
if (x == BMP_ALIGN_CENTER)
x = max(0, (pwidth - width) / 2);
else if (x < 0)
x = max(0, pwidth - width + x + 1);
if (y == BMP_ALIGN_CENTER)
y = max(0, (info->vl_row - height) / 2);
else if (y < 0)
y = max(0, info->vl_row - height + y + 1);
#endif /* CONFIG_SPLASH_SCREEN_ALIGN */
if ((x + width) > pwidth)
width = pwidth - x;
if ((y + height) > pheight)
height = pheight - y;
osd_enable_hw(osd_index, 1);
bmap = (uchar *)bmp + le32_to_cpu(bmp->header.data_offset);
fb = (uchar *)(info->vd_base +
(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\n",
fb, bmap, width, height, lcd_line_length, padded_line);
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!");
}
}
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;
#if 0
flush_cache((unsigned long)info->vd_base,
info->vl_col * info->vl_row * info->vl_bpix / 8);
#else
flush_cache((unsigned long)info->vd_base,
pheight * pwidth * info->vl_bpix / 8);
#endif
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);
layer_str = getenv("display_layer");
vout_get_current_axis(axis);
if (strcmp(layer_str, "osd0") == 0)
osd_index = 0;
else if (strcmp(layer_str, "osd1") == 0)
osd_index = 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);
#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 (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_G12A)
osd_update_blend(&disp_data);
#endif
osd_enable_hw(osd_index, 1);
return (1);
}
#endif