linuxOS_D21X/source/uboot-2021.10/drivers/video/artinchip/display/aic_display.c
2024-11-29 16:33:21 +08:00

619 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2021 ArtInChip Technology Co.,Ltd
* Huahui Mai <huahui.mai@artinchip.com>
*/
#include <common.h>
#include <dm.h>
#include <video.h>
#include <video_console.h>
#include <bmp_layout.h>
#include <dm/device_compat.h>
#include <asm/unaligned.h>
#include <artinchip/artinchip_fb.h>
#include <artinchip_ve.h>
#include <asm/arch/boot_param.h>
#include <image.h>
#include <mmc.h>
#include <spi.h>
#include <spi_flash.h>
#include <mtd.h>
#include <fat.h>
#include <cpu_func.h>
#include <dm/uclass-internal.h>
#include <config_parse.h>
#include "aic_com.h"
#define IMAGE_HEADER_SIZE (4 << 10)
#define CONFIG_LOGO_ITB_ADDRESS 0x42400000
#define BOOTCFG_FILE_SIZE 1024
/* font color */
#define FONT_BG_COLOR VID_WHITE
#define FONT_FILL_COLOR VID_BLACK
/* bar background */
#define BAR_BG_R 0x00
#define BAR_BG_G 0x0f
#define BAR_BG_B 0xff
/* bar fill */
#define BAR_FILL_R 0x00
#define BAR_FILL_G 0xff
#define BAR_FILL_B 0x0f
#define WIDTH_SPLIT_NUMERATOR 5
#define WIDTH_SPLIT_DENOMINATOR 6
#define BAR_HEIGHT 35
#define SPLIT_WIDTH(w) \
((w) * WIDTH_SPLIT_NUMERATOR / WIDTH_SPLIT_DENOMINATOR)
void aicfb_draw_text(uint x_frac, uint y, int value)
{
struct udevice *dev;
char str[25];
int i;
if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev)) {
debug("Failed to find video console dev\n");
return;
}
struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
struct udevice *vid = dev->parent;
struct video_priv *vid_priv = dev_get_uclass_priv(vid);
priv->ycur = y;
priv->xcur_frac = x_frac * 256;
vid_priv->colour_bg = vid_console_color(vid_priv, FONT_BG_COLOR);
vid_priv->colour_fg = vid_console_color(vid_priv, FONT_FILL_COLOR);
sprintf(str, "Upgrading %d%%", value);
for (i = 0; str[i] != '\0'; i++)
vidconsole_put_char(dev, str[i]);
video_sync(dev->parent, false);
}
void aicfb_draw_rect(struct udevice *dev,
uint x, uint y, uint width, uint height,
u8 red, u8 green, u8 blue)
{
struct video_priv *priv = dev_get_uclass_priv(dev);
struct aicfb_dt *dt = dev_get_plat(dev);
int pbytes = dt->format->bits_per_pixel / 8;
uchar *fb;
int i, j;
fb = (uchar *)(priv->fb + y * priv->line_length + x * pbytes);
switch (dt->format->format) {
case AIC_FMT_RGB_888:
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = blue;
*(fb++) = green;
*(fb++) = red;
}
fb += priv->line_length - width * pbytes;
}
break;
case AIC_FMT_ARGB_8888:
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(fb++) = blue;
*(fb++) = green;
*(fb++) = red;
*(fb++) = 0xFF;
}
fb += priv->line_length - width * pbytes;
}
break;
case AIC_FMT_RGB_565:
for (i = 0; i < height; ++i) {
for (j = 0; j < width; j++) {
*(uint16_t *)fb = ((red >> 3) << 11)
| ((green >> 2) << 5)
| (blue >> 3);
fb += sizeof(uint16_t) / sizeof(*fb);
}
fb += priv->line_length - width * pbytes;
}
break;
default:
debug("%s: unsupported fb format %d", __func__,
(int)dt->format->format);
break;
};
video_sync(dev, false);
}
int aic_bmp_display(struct udevice *dev, ulong bmp_image)
{
struct aicfb_dt *plat = dev_get_plat(dev);
struct video_uc_plat *uplat = dev_get_uclass_plat(dev);
uchar *fb, *bmap;
struct bmp_image *bmp = (struct bmp_image *)bmp_image;
int width, height, line_length;
unsigned int bpix, bmp_bpix, byte_width, padded_byte;
int i, x, y;
if (!bmp || !(bmp->header.signature[0] == 'B' &&
bmp->header.signature[1] == 'M')) {
dev_err(dev, "no valid bmp image at %lx\n", bmp_image);
return -EINVAL;
}
width = get_unaligned_le32(&bmp->header.width);
height = get_unaligned_le32(&bmp->header.height);
bmp_bpix = get_unaligned_le16(&bmp->header.bit_count);
bpix = plat->format->bits_per_pixel;
if (bpix != bmp_bpix) {
dev_err(dev, "%d bit/pixel mode, but BMP has %d bit/pixel\n",
bpix, bmp_bpix);
return -EINVAL;
}
if (width > plat->width || height > plat->height) {
dev_err(dev, "Video buffer %d x %d y"
" but BMP has %d x %d y\n",
plat->width, plat->height,
width, height);
return -EINVAL;
}
line_length = plat->stride;
x = (plat->width - width) / 2;
y = (plat->height - height) / 2;
byte_width = width * (bmp_bpix / 8);
padded_byte = (byte_width & 0x3 ? 4 - (byte_width & 0x3) : 0);
bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
fb = (uchar *)(uplat->base +
(y + height) * line_length + x * bpix / 8);
for (i = 0; i < height; ++i) {
memcpy(fb, bmap, byte_width);
bmap += byte_width + padded_byte;
fb -= line_length;
}
video_sync(dev, false);
return 0;
}
void draw_progress_bar(int value)
{
struct udevice *dev = NULL;
unsigned int x, y, width, height, ret;
unsigned int text_x_pos, text_y_pos;
ret = uclass_first_device(UCLASS_VIDEO, &dev);
struct video_priv *priv = dev_get_uclass_priv(dev);
width = SPLIT_WIDTH(priv->xsize);
height = BAR_HEIGHT;
x = (priv->xsize - width) / 2;
y = (priv->ysize - height) / 2;
text_x_pos = priv->xsize / 2;
text_y_pos = y + BAR_HEIGHT + 5;
if (value == 0) {
aicfb_draw_rect(dev, x, y, width, height,
BAR_BG_R, BAR_BG_G, BAR_BG_B);
aicfb_draw_text(text_x_pos, text_y_pos, 0);
return;
}
if (value < 100)
width = width * value / 100;
aicfb_draw_rect(dev, x, y, width, height,
BAR_FILL_R, BAR_FILL_G, BAR_FILL_B);
if (value == 100) {
aicfb_draw_text(text_x_pos, text_y_pos, 100);
flush_dcache_range((uintptr_t)&priv->fb, (uintptr_t)(&priv->fb+priv->fb_size));
}
aicfb_draw_text(text_x_pos, text_y_pos, value);
flush_dcache_range((uintptr_t)&priv->fb, (uintptr_t)(&priv->fb+priv->fb_size));
}
static int aic_logo_decode(unsigned char *dst, unsigned int size)
{
if (dst[0] == 0xff || dst[1] == 0xd8) {
pr_debug("Loaded a JPEG logo image\n");
return aic_jpeg_decode(dst, size);
}
if (dst[1] == 'P' || dst[2] == 'N' || dst[3] == 'G') {
pr_debug("Loaded a PNG logo image\n");
return aic_png_decode(dst, size);
}
pr_err("not support logo file format, need a png/jpg image\n");
return 0;
}
static int fit_image_get_node_prop(const void *fit, const char *name,
const void **data, size_t *size)
{
int noffset, ret;
/* Get specific logo image offset */
noffset = fit_image_get_node(fit, name);
if (noffset < 0) {
pr_err("Failed to get %s node\n", name);
return -1;
}
/* Load specific logo image */
ret = fit_image_get_data(fit, noffset, data, size);
if (ret < 0) {
pr_err("Failed to get data\n");
return -1;
}
return 0;
}
static int fat_load_logo(const char *name)
{
char *file_buf = NULL, *logo_itb = NULL;
ulong offset, maxsize = 0;
char imgname[IMG_NAME_MAX_SIZ];
loff_t actread;
size_t data_size;
const void *data;
unsigned char *dst;
struct udevice *dev;
int ret = -1;
file_buf = (char *)malloc(BOOTCFG_FILE_SIZE);
if (!file_buf) {
pr_err("Error, malloc bootcfg.txt buf failed.\n");
return ret;
}
memset((void *)file_buf, 0, BOOTCFG_FILE_SIZE);
/* load bootcfg file */
ret = fat_read_file("bootcfg.txt", (void *)file_buf, 0,
BOOTCFG_FILE_SIZE, &actread);
if (actread == 0 || ret != 0) {
pr_err("Error:read file bootcfg.txt failed!\n");
return ret;
}
/* Get logo part size and offset */
ret = boot_cfg_parse_file(file_buf, actread, "logo", imgname,
IMG_NAME_MAX_SIZ, &offset, &maxsize);
if (ret <= 0) {
pr_err("Parse boot cfg file failed.\n");
return ret;
}
logo_itb = (char *)malloc(maxsize);
if (!logo_itb) {
pr_err("Error, malloc logo itb failed.\n");
return -1;
}
/* Load logo itb */
ret = fat_read_file(imgname, (void *)logo_itb,
offset, maxsize, &actread);
if (actread == 0 || ret != 0) {
printf("Error:read file bootcfg.txt failed!\n");
return ret;
}
ret = fit_image_get_node_prop(logo_itb, name, &data, &data_size);
if (ret)
goto out;
dst = memalign(DECODE_ALIGN, data_size);
if (!dst) {
pr_err("Failed to alloc dst buf\n");
return -1;
}
memcpy(dst, data, data_size);
flush_dcache_range((uintptr_t)dst, (uintptr_t)dst + data_size);
ret = uclass_find_first_device(UCLASS_VIDEO, &dev);
if (ret) {
pr_err("Failed to find aicfb udevice\n");
return ret;
}
aic_logo_decode(dst, data_size);
mdelay(16);
out:
if (file_buf)
free(file_buf);
if (logo_itb)
free(logo_itb);
if (dst)
free(dst);
return ret;
}
static int spinand_load_logo(const char *name)
{
int ret = 0;
#ifdef CONFIG_MTD
unsigned int header_len, first_block, other_block;
unsigned char *dst = NULL, *fit = NULL;
const struct fdt_property *fdt_prop;
int i, offset, next_offset;
struct mtd_info *mtd;
int tag = FDT_PROP;
fdt32_t image_len;
size_t retlen;
fit = memalign(DECODE_ALIGN, IMAGE_HEADER_SIZE);
if (!fit) {
pr_err("Failed to malloc for logo header!\n");
ret = -ENOMEM;
goto out;
}
mtd_probe_devices();
mtd = get_mtd_device_nm("logo");
if (IS_ERR_OR_NULL(mtd)) {
pr_err("MTD partition %s not found, ret %ld\n", "logo",
PTR_ERR(mtd));
return PTR_ERR(mtd);
}
ret = mtd_read(mtd, 0, IMAGE_HEADER_SIZE, &retlen, fit);
if (ret) {
pr_err("Failed to read logo itb header for SPINAND\n");
goto out;
}
offset = fit_image_get_node(fit, name);
if (offset < 0) {
pr_err("Failed to find boot node in logo itb\n");
goto out;
}
/* Find data property */
for (i = 0; i < 4; i++) {
tag = fdt_next_tag(fit, offset, &next_offset);
if (tag == FDT_END)
goto out;
offset = next_offset;
}
fdt_prop = fdt_get_property_by_offset(fit, offset, NULL);
image_len = fdt32_to_cpu(fdt_prop->len);
header_len = (phys_addr_t)fdt_prop - (phys_addr_t)fit
+ sizeof(struct fdt_property);
dst = memalign(DECODE_ALIGN, ALIGN(image_len, IMAGE_HEADER_SIZE));
if (!dst) {
pr_err("Failed to malloc for logo image!\n");
ret = -ENOMEM;
goto out;
}
if (image_len < IMAGE_HEADER_SIZE) {
first_block = image_len;
other_block = 0;
} else {
first_block = IMAGE_HEADER_SIZE - header_len;
other_block = ALIGN(image_len - first_block, IMAGE_HEADER_SIZE);
}
memcpy(dst, fit + header_len, first_block);
flush_dcache_range((uintptr_t)dst, (uintptr_t)dst + first_block);
if (other_block) {
ret = mtd_read(mtd, IMAGE_HEADER_SIZE, other_block, &retlen,
dst + first_block);
if (ret) {
pr_err("Failed to read logo image for SPINAND\n");
goto out;
}
}
aic_logo_decode(dst, image_len);
out:
put_mtd_device(mtd);
if (dst)
free(dst);
if (fit)
free(fit);
#endif
return ret;
}
static int mmc_load_logo(const char *name, int id)
{
#ifdef CONFIG_MMC
struct mmc *mmc = find_mmc_device(id);
struct disk_partition part_info;
struct udevice *dev;
unsigned char *fit, *dst;
size_t data_size;
const void *data;
int ret;
ret = uclass_first_device(UCLASS_VIDEO, &dev);
if (ret) {
pr_err("Failed to find aicfb udevice\n");
return ret;
}
ret = part_get_info_by_name(mmc_get_blk_desc(mmc), "logo", &part_info);
if (ret < 0) {
pr_err("Get logo partition information failed.\n");
return -EINVAL;
}
fit = memalign(DECODE_ALIGN, part_info.blksz * part_info.size);
if (!fit) {
pr_err("Failed to malloc for fit image!\n");
return -ENOMEM;
}
ret = blk_dread(mmc_get_blk_desc(mmc), part_info.start,
part_info.size, fit);
if (ret != part_info.size) {
pr_err("Failed to read logo image from MMC/SD!\n");
ret = -EIO;
goto out;
}
ret = fit_image_get_node_prop(fit, name, &data, &data_size);
if (ret) {
pr_err("Failed to get fit image prop\n");
ret = -EINVAL;
goto out;
}
dst = memalign(DECODE_ALIGN, data_size);
if (!dst) {
pr_err("Failed to malloc dst buffer\n");
ret = -ENOMEM;
goto out;
}
memcpy(dst, data, data_size);
flush_dcache_range((uintptr_t)dst, (uintptr_t)dst + data_size);
aic_logo_decode(dst, data_size);
out:
if (fit)
free(fit);
if (dst)
free(dst);
#endif
return ret;
}
static int bootrom_load_logo(const char *name)
{
const void *fit = (void *)CONFIG_LOGO_ITB_ADDRESS;
const void *data;
size_t size;
char *dst;
int ret;
ret = fit_image_get_node_prop(fit, name, &data, &size);
if (ret)
return -EINVAL;
dst = memalign(DECODE_ALIGN, size);
if (!dst) {
pr_err("Failed to malloc dst buffer\n");
return -EINVAL;
}
memcpy(dst, data, size);
flush_dcache_range((uintptr_t)dst, (uintptr_t)dst + size);
aic_logo_decode(dst, size);
free(dst);
return 0;
}
static int spinor_load_logo(const char *name)
{
#ifdef CONFIG_DM_SPI_FLASH
unsigned int bus = CONFIG_SF_DEFAULT_BUS;
unsigned int cs = CONFIG_SF_DEFAULT_CS;
unsigned char *fit = NULL, *dst = NULL;
struct udevice *new, *bus_dev;
struct spi_flash *flash;
size_t data_size;
const void *data;
int ret;
ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new);
if (ret) {
pr_err("Failed to find a spi device\n");
return -EINVAL;
}
flash = dev_get_uclass_priv(new);
fit = memalign(DECODE_ALIGN, LOGO_MAX_SIZE);
if (!fit) {
printf("Failed to malloc for logo image!\n");
return -ENOMEM;
}
ret = spi_flash_read(flash, CONFIG_LOGO_PART_OFFSET, LOGO_MAX_SIZE, fit);
if (ret) {
printf("Failed to read logo image for SPINOR\n");
ret = -EINVAL;
goto out;
}
ret = fit_image_get_node_prop(fit, name, &data, &data_size);
if (ret) {
printf("Failed to get fit image prop\n");
ret = -EINVAL;
goto out;
}
dst = memalign(DECODE_ALIGN, data_size);
if (!dst) {
printf("Failed to alloc dst buf\n");
ret = -ENOMEM;
goto out;
}
memcpy(dst, data, data_size);
flush_dcache_range((uintptr_t)dst, (uintptr_t)dst + data_size);
aic_logo_decode(dst, data_size);
out:
if (fit)
free(fit);
if (dst)
free(dst);
#endif
return 0;
}
int aic_disp_logo(const char *name, int boot_param)
{
int ret = 0;
switch (boot_param) {
case BD_SDMC0:
ret = mmc_load_logo(name, 0);
break;
case BD_SDMC1:
ret = mmc_load_logo(name, 1);
break;
case BD_SPINAND:
ret = spinand_load_logo(name);
break;
case BD_SPINOR:
ret = spinor_load_logo(name);
break;
case BD_SDFAT32:
ret = fat_load_logo(name);
break;
case BD_BOOTROM:
ret = bootrom_load_logo(name);
break;
default:
pr_err("Do not support boot device id: %d\n", boot_param);
return -EINVAL;
}
return ret;
}