// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2021 ArtInChip Technology Co.,Ltd * Huahui Mai */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }