/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ /* * drivers/display/osd/osd_fb.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ /* System Headers */ #include #include #include #include #include #include #include #include /* Local Headers */ #include #include #include #include /* 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) { void *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 #if defined(CONFIG_AML_MINUI) if (in_fastboot_mode == 1) { osd_logi("in fastboot mode, load default fb_addr parameters \n"); } else #endif { dt_addr = (void*)env_strtoul("dtb_mem_addr", 10); 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; } u32 osd_canvas_align(u32 x) { if (osd_hw.osd_ver >= OSD_HIGH_ONE) return (((x) + 63) & ~63); else return (((x) + 31) & ~31); } 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; char *dual_2_single = NULL; get_osd_version(); vout_init(); /*if dual logo platform only display panel */ dual_2_single = getenv("dual2single"); if (dual_2_single && strcmp(dual_2_single, "true") == 0) { setenv_ulong("fb_width", env_strtoul("display_width", 10)); setenv_ulong("fb_height", env_strtoul("display_height", 10)); } 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; unsigned long width, height; unsigned long pheight; unsigned long pwidth; unsigned colors, bpix, bmp_bpix; uint lcd_line_length; int osd_index = -1; int bmp_line_bytes; int bmp_line_align_offset; 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. */ bmp_line_bytes = width * bmp_bpix / 8; bmp_line_bytes = (bmp_line_bytes & 0x3) ? ((bmp_line_bytes & ~0x3) + 4) : (bmp_line_bytes); bmp_line_align_offset = bmp_line_bytes - width * bmp_bpix / 8; 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, bmp_line_bytes=%d, fb_gdev.fb_width=%d, fb_gdev.fb_height=%d \n", fb, bmap, width, height, lcd_line_length, bmp_line_bytes, 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 += bmp_line_align_offset; 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 += bmp_line_align_offset; 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 += bmp_line_align_offset; 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 += bmp_line_align_offset; 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 += bmp_line_align_offset; 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); } osd_logd2("osd%d axis(%d %d %d %d)\n", osd_index, axis[0], axis[1], axis[2], axis[3]); #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; 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(hist_env_key[i], 16); break; case 1: hist_spl_val[i/4][family_id] = env_strtoul(hist_env_key[i], 16); break; case 2: hist_spl_pix_cnt[i/4][family_id] = env_strtoul(hist_env_key[i], 16); break; case 3: hist_cheoma_sum[i/4][family_id] = env_strtoul(hist_env_key[i], 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; get_osd_version(); 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); } }