/* * (C) Copyright 2020 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ * Author: Wenping Zhang */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rk_ebc.h" #include "epdlut/epd_lut.h" #define PART_WAVEFORM "waveform" #define EINK_LOGO_PART_MAGIC "RKEL" #define EINK_LOGO_IMAGE_MAGIC "GR04" /* * grayscale logo partition format: * block0: * struct logo_part_header part_header; * struct grayscale_header logo1_header; * struct grayscale_header logo2_header; * struct grayscale_header logo3_header; * struct grayscale_header logo4_header; * .... * * block 1: * logo1_image * * ..... * block m: * logo2_image * * ........ * block n: * logo3_image * * ........ * block i: * logoi_image */ //logo partition Header, 64byte struct logo_part_header { char magic[4]; /* must be "RKEL" */ u32 totoal_size; u32 screen_width; u32 screen_height; u32 logo_count; u8 version[4]; u32 rsv[10]; } __packed; // logo image header,32 byte struct grayscale_header { char magic[4]; /* must be "GR04" */ u16 x; u16 y; u16 w; u16 h; u32 logo_type; u32 data_offset; /* image offset in byte */ u32 data_size; /* image size in byte */ u32 rsv[2]; } __packed; /* * The start address of logo image in logo.img must be aligned in 512 bytes, * so the header size must be times of 512 bytes. Here we fix the size to 512 * bytes, so the count of logo image can only support up to 14. */ struct logo_info { struct logo_part_header part_hdr; struct grayscale_header img_hdr[14]; } __packed; struct rockchip_eink_display_priv { struct udevice *dev; struct udevice *ebc_tcon_dev; struct udevice *ebc_pwr_dev; int vcom; struct udevice *backlight; }; enum { EBC_PWR_DOWN = 0, EBC_PWR_ON = 1, }; #define EINK_VCOM_ID 17 #define EINK_VCOM_MAX 64 #define VCOM_DEFAULT_VALUE 1650 static struct logo_info eink_logo_info; static struct udevice *eink_dev; static volatile int last_logo_type = -1; static int read_vcom_from_vendor(void) { int ret = 0; char vcom_str[EINK_VCOM_MAX] = {0}; char vcom_args[EINK_VCOM_MAX] = {0}; /* Read vcom value from vendor storage part */ ret = vendor_storage_read(EINK_VCOM_ID, vcom_str, (EINK_VCOM_MAX - 1)); if (ret > 0) { snprintf(vcom_args, strlen(vcom_str) + 15, "ebc_pmic.vcom=%s", vcom_str); printf("eink update bootargs: %s\n", vcom_args); env_update("bootargs", vcom_args); } else { return ret; } return atoi(vcom_str); } static int read_waveform(struct udevice *dev) { int cnt, start, ret; disk_partition_t part; struct blk_desc *dev_desc; struct ebc_panel *plat = dev_get_platdata(dev); dev_desc = rockchip_get_bootdev(); if (!dev_desc) { printf("%s: Could not find device\n", __func__); return -EIO; } if (part_get_info_by_name(dev_desc, PART_WAVEFORM, &part) < 0) { printf("Get waveform partition failed\n"); return -ENODEV; } cnt = plat->lut_pbuf_size / RK_BLK_SIZE; start = part.start; ret = blk_dread(dev_desc, start, cnt, (void *)plat->lut_pbuf); if (ret != cnt) printf("Try to read %d blocks failed, only read %d\n", cnt, ret); flush_dcache_range((ulong)plat->lut_pbuf, ALIGN((ulong)plat->lut_pbuf + cnt, CONFIG_SYS_CACHELINE_SIZE)); ret = epd_lut_from_mem_init(plat->lut_pbuf, &plat->lut_ops); if (ret < 0) { printf("lut init failed\n"); return -EINVAL; } return 0; } static u32 aligned_image_size_4k(struct udevice *dev) { struct ebc_panel *plat = dev_get_platdata(dev); u32 w = plat->vir_width; u32 h = plat->vir_height; return ALIGN((w * h) >> 1, 4096); } /* * This driver load the grayscale image from flash, * and put it in the reserve memory which define in dts: * display_reserved: framebuffer@10000000 { * reg = <0x0 0x10000000 0x0 0x2000000>; * no-map; * }; * Every image logo size must be aligned in 4K, make sure * kernel can use it rightly, the buffer of LOGO image is * put in order of below map: * |---reset logo ---| * |---uboot logo ---| * |---kernel logo ---| * |---charge_0 logo ---| * |---charge_1 logo ---| * |---charge_2 logo ---| * |---charge_3 logo ---| * |---charge_4 logo ---| * |---charge_5 logo ---| * |---battery low logo---| * |---temp un-mirror buffer--| */ static int get_addr_by_type(struct udevice *dev, u32 logo_type) { u32 offset, indx, img_size; struct ebc_panel *plat = dev_get_platdata(dev); if (plat->disp_pbuf_size == 0 || !plat->disp_pbuf) { printf("invalid display buffer, please check dts\n"); return -EINVAL; } indx = ffs(logo_type); img_size = aligned_image_size_4k(dev); offset = img_size * indx; if (offset + img_size > plat->disp_pbuf_size) { printf("reserve display memory size is not enough\n"); return -EINVAL; } switch (logo_type) { case EINK_LOGO_RESET: case EINK_LOGO_UBOOT: case EINK_LOGO_KERNEL: case EINK_LOGO_CHARGING_0: case EINK_LOGO_CHARGING_1: case EINK_LOGO_CHARGING_2: case EINK_LOGO_CHARGING_3: case EINK_LOGO_CHARGING_4: case EINK_LOGO_CHARGING_5: case EINK_LOGO_CHARGING_LOWPOWER: case EINK_LOGO_POWEROFF: /* * The MIRROR_TEMP_BUF is used to save the * non-mirror image data. */ case EINK_LOGO_UNMIRROR_TEMP_BUF: return (plat->disp_pbuf + offset); default: printf("invalid logo type[%d]\n", logo_type); } return -EINVAL; } static int read_header(struct blk_desc *dev_desc, disk_partition_t *part, struct logo_info *header) { int i; struct logo_part_header *part_hdr = &header->part_hdr; if (blk_dread(dev_desc, part->start, 1, header) != 1) return -EIO; if (memcmp(part_hdr->magic, EINK_LOGO_PART_MAGIC, 4)) { printf("partition header is invalid\n"); return -EINVAL; } if (part_hdr->logo_count == 0) { printf("the count of logo image is 0\n"); return -EINVAL; } for (i = 0; i < part_hdr->logo_count; i++) { struct grayscale_header *img_hdr = &header->img_hdr[i]; if (memcmp(img_hdr->magic, EINK_LOGO_IMAGE_MAGIC, 4)) { printf("image[%d] header '%s' is invalid\n", i, img_hdr->magic); return -EINVAL; } } return 0; } static int read_grayscale(struct blk_desc *dev_desc, disk_partition_t *part, u32 offset, u32 size, void *buf) { u32 blk_start, blk_offset, blk_count; blk_offset = DIV_ROUND_UP(offset, dev_desc->blksz); blk_start = part->start + blk_offset; blk_count = DIV_ROUND_UP(size, dev_desc->blksz); debug("blk_offset=%d, blk_start=%d,blk_count=%d,out buf=%p\n", blk_offset, blk_start, blk_count, buf); if (blk_dread(dev_desc, blk_start, blk_count, buf) != blk_count) { printf("read grayscale data failed\n"); return -EIO; } return 0; } static int image_rearrange(u8 *in_buf, u8 *out_buf, u16 w, u16 h) { int i, j; u8 in_data; u8 *out_buf_tmp; if (!in_buf || !out_buf) { printf("rearrange in buffer or out buffer is NULL\n"); return -EINVAL; } for (i = 0; i < h; i += 2) { out_buf_tmp = out_buf + (i * w / 2); for (j = 0; j < w / 2; j++) { in_data = *in_buf++; *(out_buf_tmp + j * 2) = in_data & 0x0f; *(out_buf_tmp + j * 2 + 1) = (in_data >> 4) & 0x0f; } for (j = 0; j < w / 2; j++) { in_data = *in_buf++; *(out_buf_tmp + j * 2) |= (in_data << 4) & 0xf0; *(out_buf_tmp + j * 2 + 1) |= in_data & 0xf0; } } return 0; } static int image_mirror(u8 *in_buf, u8 *out_buf, u16 w, u16 h) { int i; if (!in_buf || !out_buf) { printf("mirror in buffer or out buffer is NULL\n"); return -EINVAL; } for (i = 0; i < h; i++) { u16 column_len = w / 2; u8 *column_in = in_buf + i * column_len; u8 *column_out = out_buf + (h - i - 1) * column_len; memcpy(column_out, column_in, column_len); } return 0; } /* * The eink kernel driver need last frame to do part refresh, * so we need to transfer two images to kernel, which is kernel * logo and the logo displayed in uboot. * * this function use logo type bitmap to indicate several logo. * u32 needed_logo: we only load needed logo image into ram, such as * uboot logo + kernel logo or charger logo + kernel * logo * u32 *loaded_logo: because the needed logo may not exist in logo.img, * store the really loaded logo in para loaded_logo. */ static int read_needed_logo_from_partition(struct udevice *dev, u32 needed_logo, u32 *loaded_logo) { int ret, i; disk_partition_t part; struct blk_desc *dev_desc; struct logo_info *hdr = &eink_logo_info; struct logo_part_header *part_hdr = &hdr->part_hdr; struct ebc_panel *panel = dev_get_platdata(dev); u32 logo = needed_logo & (~(*loaded_logo)); if (!logo) { printf("logo[0x%x] is already loaded, just return!\n", needed_logo); return 0; } dev_desc = rockchip_get_bootdev(); if (!dev_desc) { printf("%s: Could not find device\n", __func__); return -EIO; } if (part_get_info_by_name(dev_desc, PART_LOGO, &part) < 0) return -ENODEV; ret = read_header(dev_desc, &part, hdr); if (ret < 0) { printf("eink logo read header failed,ret = %d\n", ret); return -EINVAL; } if (part_hdr->screen_width != panel->vir_width || part_hdr->screen_height != panel->vir_height){ printf("logo size(%dx%d) is not same as screen size(%dx%d)\n", part_hdr->screen_width, part_hdr->screen_height, panel->vir_width, panel->vir_height); return -EINVAL; } for (i = 0; i < part_hdr->logo_count; i++) { struct grayscale_header *img_hdr = &hdr->img_hdr[i]; int pic_buf; u32 offset = img_hdr->data_offset; u32 size = img_hdr->data_size; u32 logo_type = img_hdr->logo_type; debug("offset=0x%x, size=%d,logo_type=%d,w=%d,h=%d\n", offset, size, logo_type, img_hdr->w, img_hdr->h); if (logo & logo_type) { pic_buf = get_addr_by_type(dev, logo_type); if (pic_buf <= 0) { printf("Get buffer failed for image %d\n", img_hdr->logo_type); return -EIO; } if (!IS_ALIGNED((ulong)pic_buf, ARCH_DMA_MINALIGN)) { printf("disp buffer is not dma aligned\n"); return -EINVAL; } /* * kernel logo is transmitted to kernel to display, and * kernel will do the mirror operation, so skip kernel * logo here. */ if (panel->mirror && logo_type != EINK_LOGO_KERNEL) { u32 w = panel->vir_width; u32 h = panel->vir_height; u32 mirror_buf = 0; mirror_buf = get_addr_by_type(dev, EINK_LOGO_UNMIRROR_TEMP_BUF); if (mirror_buf <= 0) { printf("get mirror buffer failed\n"); return -EIO; } read_grayscale(dev_desc, &part, offset, size, (void *)((ulong)mirror_buf)); image_mirror((u8 *)((ulong)mirror_buf), (u8 *)((ulong)pic_buf), w, h); } else if (panel->rearrange && logo_type != EINK_LOGO_KERNEL) { u32 w = panel->vir_width; u32 h = panel->vir_height; u32 rearrange_buf = 0; rearrange_buf = get_addr_by_type(dev, EINK_LOGO_UNMIRROR_TEMP_BUF); if (rearrange_buf <= 0) { printf("get mirror buffer failed\n"); return -EIO; } read_grayscale(dev_desc, &part, offset, size, (void *)((ulong)rearrange_buf)); image_rearrange((u8 *)((ulong)rearrange_buf), (u8 *)((ulong)pic_buf), w, h); } else { read_grayscale(dev_desc, &part, offset, size, (void *)((ulong)pic_buf)); } flush_dcache_range((ulong)pic_buf, ALIGN((ulong)pic_buf + size, CONFIG_SYS_CACHELINE_SIZE)); *loaded_logo |= logo_type; logo &= ~logo_type; if (!logo) break; } } return 0; } static int ebc_power_set(struct udevice *dev, int is_on) { int ret; struct rockchip_eink_display_priv *priv = dev_get_priv(dev); struct ebc_panel *panel = dev_get_platdata(dev); struct udevice *ebc_tcon_dev = priv->ebc_tcon_dev; struct rk_ebc_tcon_ops *ebc_tcon_ops = ebc_tcon_get_ops(ebc_tcon_dev); struct udevice *ebc_pwr_dev = priv->ebc_pwr_dev; struct rk_ebc_pwr_ops *pwr_ops = ebc_pwr_get_ops(ebc_pwr_dev); if (is_on) { ret = pwr_ops->power_on(ebc_pwr_dev); if (ret) { printf("%s, power on failed\n", __func__); return -1; } ret = ebc_tcon_ops->enable(ebc_tcon_dev, panel); if (ret) { printf("%s, ebc tcon enabled failed\n", __func__); return -1; } } else { ret = ebc_tcon_ops->disable(ebc_tcon_dev); if (ret) { printf("%s, ebc tcon disable failed\n", __func__); return -1; } ret = pwr_ops->power_down(ebc_pwr_dev); if (ret) { printf("%s, power_down failed\n", __func__); return -1; } } return 0; } static int eink_display(struct udevice *dev, u32 pre_img_buf, u32 cur_img_buf, u32 lut_type, int update_mode) { u32 temperature, frame_num; struct rockchip_eink_display_priv *priv = dev_get_priv(dev); struct ebc_panel *plat = dev_get_platdata(dev); struct epd_lut_ops *lut_ops = &plat->lut_ops; struct udevice *ebc_pwr_dev = priv->ebc_pwr_dev; struct rk_ebc_pwr_ops *pwr_ops = ebc_pwr_get_ops(ebc_pwr_dev); struct udevice *ebc_tcon_dev = priv->ebc_tcon_dev; struct rk_ebc_tcon_ops *ebc_tcon_ops = ebc_tcon_get_ops(ebc_tcon_dev); pwr_ops->temp_get(ebc_pwr_dev, &temperature); if (temperature <= 0 || temperature > 50) { printf("temperature = %d, out of range0~50 ,use 25\n", temperature); temperature = 25; } if (!lut_ops->lut_get) { printf("get lut ops failed\n"); return -EIO; } lut_ops->lut_get(&plat->lut_data, lut_type, temperature); frame_num = plat->lut_data.frame_num; debug("lut_type=%d, frame num=%d, temp=%d\n", lut_type, frame_num, temperature); ebc_tcon_ops->wait_for_last_frame_complete(ebc_tcon_dev); ebc_tcon_ops->lut_data_set(ebc_tcon_dev, plat->lut_data.data, frame_num, 0); ebc_tcon_ops->dsp_mode_set(ebc_tcon_dev, update_mode, LUT_MODE, !THREE_WIN_MODE, !EINK_MODE); ebc_tcon_ops->image_addr_set(ebc_tcon_dev, pre_img_buf, cur_img_buf); ebc_tcon_ops->frame_start(ebc_tcon_dev, frame_num); return 0; } static int rk_eink_display_init(void) { int ret; struct uclass *uc; struct udevice *dev; if (eink_dev) { printf("ebc-dev is already initialized!\n"); return 0; } ret = uclass_get(UCLASS_EINK_DISPLAY, &uc); if (ret) { printf("can't find uclass eink\n"); return -ENODEV; } for (uclass_first_device(UCLASS_EINK_DISPLAY, &dev); dev; uclass_next_device(&dev)) ; if (eink_dev) { printf("ebc-dev is probed success!\n"); return 0; } printf("Can't find ebc-dev\n"); return -ENODEV; } /* * Eink display need current and previous image buffer, We assume * every type of logo has only one image, so just tell this function * last logo type and current logo type, it will find the right images. * last_logo_type: -1 means it's first displaying. */ static int rockchip_eink_show_logo(int cur_logo_type, int update_mode) { int ret = 0; u32 logo_addr; u32 last_logo_addr; struct ebc_panel *plat; struct udevice *dev; static u32 loaded_logo = 0; struct rockchip_eink_display_priv *priv; if (!eink_dev) { static bool first_init = true; if (first_init) { first_init = false; ret = rk_eink_display_init(); if (ret) { printf("Get ebc dev failed, check dts\n"); return -ENODEV; } } else { return -ENODEV; } } dev = eink_dev; /*Don't need to update display*/ if (last_logo_type == cur_logo_type) { debug("Same as last picture, Don't need to display\n"); return 0; } plat = dev_get_platdata(dev); priv = dev_get_priv(dev); /* * The last_logo_type is -1 means it's first displaying */ if (last_logo_type == -1) { ret = ebc_power_set(dev, EBC_PWR_ON); if (ret) { printf("Eink power on failed\n"); return -1; } int size = (plat->vir_width * plat->vir_height) >> 1; logo_addr = get_addr_by_type(dev, EINK_LOGO_RESET); memset((u32 *)(u64)logo_addr, 0xff, size); flush_dcache_range((ulong)logo_addr, ALIGN((ulong)logo_addr + size, CONFIG_SYS_CACHELINE_SIZE)); eink_display(dev, logo_addr, logo_addr, WF_TYPE_RESET, EINK_LOGO_RESET); last_logo_type = 0; last_logo_addr = logo_addr; } else { last_logo_addr = get_addr_by_type(dev, last_logo_type); if (last_logo_addr < 0) { printf("Invalid last logo addr, exit!\n"); goto out; } } ret = read_needed_logo_from_partition(dev, cur_logo_type, &loaded_logo); if (ret || !(loaded_logo & cur_logo_type)) { printf("read logo[0x%x] failed, loaded_logo=0x%x\n", cur_logo_type, loaded_logo); ret = -EIO; goto out; } logo_addr = get_addr_by_type(dev, cur_logo_type); debug("logo_addr=%x, logo_type=%d\n", logo_addr, cur_logo_type); if (logo_addr <= 0) { printf("get logo buffer failed\n"); ret = -EIO; goto out; } eink_display(dev, last_logo_addr, logo_addr, WF_TYPE_GC16, update_mode); if (priv->backlight) backlight_enable(priv->backlight); last_logo_type = cur_logo_type; if (cur_logo_type == EINK_LOGO_POWEROFF) { struct udevice *ebc_tcon_dev = priv->ebc_tcon_dev; struct rk_ebc_tcon_ops *ebc_tcon_ops; last_logo_type = -1; /* * For normal logo display, waiting for the last frame * completion before start a new frame, except one * situation which charging logo display finished, * because device will rebooting or shutdown after * charging logo is competed. * * We should take care of the power sequence, * because ebc can't power off if last frame * data is still sending, so keep the ebc power * during u-boot phase and shutdown the * power only if uboot charging is finished. */ ebc_tcon_ops = ebc_tcon_get_ops(ebc_tcon_dev); ebc_tcon_ops->wait_for_last_frame_complete(ebc_tcon_dev); debug("charging logo displaying is complete\n"); /* *shutdown ebc after charging logo display is complete */ ret = ebc_power_set(dev, EBC_PWR_DOWN); if (ret) printf("Eink power down failed\n"); goto out; } /* * System will boot up to kernel only when the * logo is uboot logo */ if (cur_logo_type == EINK_LOGO_UBOOT) { char logo_args[64] = {0}; u32 uboot_logo_buf; if (plat->mirror || plat->rearrange) uboot_logo_buf = get_addr_by_type(dev, EINK_LOGO_UNMIRROR_TEMP_BUF); else uboot_logo_buf = logo_addr; printf("Transmit uboot logo addr(0x%x) to kernel\n", uboot_logo_buf); sprintf(logo_args, "ulogo_addr=0x%x", uboot_logo_buf); env_update("bootargs", logo_args); ret = read_needed_logo_from_partition(dev, EINK_LOGO_KERNEL, &loaded_logo); if (ret || !(loaded_logo & EINK_LOGO_KERNEL)) { printf("No invalid kernel logo in logo.img\n"); } else { int klogo_addr = get_addr_by_type(dev, EINK_LOGO_KERNEL); if (klogo_addr <= 0) { printf("get kernel logo buffer failed\n"); ret = -EIO; goto out; } printf("Transmit kernel logo addr(0x%x) to kernel\n", klogo_addr); sprintf(logo_args, "klogo_addr=0x%x", klogo_addr); env_update("bootargs", logo_args); } } out: return ret; } int rockchip_eink_show_uboot_logo(void) { return rockchip_eink_show_logo(EINK_LOGO_UBOOT, EINK_UPDATE_DIFF); } int rockchip_eink_show_charge_logo(int logo_type) { return rockchip_eink_show_logo(logo_type, EINK_UPDATE_DIFF); } static int rockchip_eink_display_probe(struct udevice *dev) { int ret, vcom, size, i; const fdt32_t *list; uint32_t phandle; struct rockchip_eink_display_priv *priv = dev_get_priv(dev); /* Before relocation we don't need to do anything */ if (!(gd->flags & GD_FLG_RELOC)) return 0; ret = uclass_get_device_by_phandle(UCLASS_EBC, dev, "ebc_tcon", &priv->ebc_tcon_dev); if (ret) { dev_err(dev, "Cannot get ebc_tcon: %d\n", ret); return ret; } list = dev_read_prop(dev, "pmic", &size); if (!list) { dev_err(dev, "Cannot get pmic prop\n"); return -EINVAL; } size /= sizeof(*list); for (i = 0; i < size; i++) { phandle = fdt32_to_cpu(*list++); ret = uclass_get_device_by_phandle_id(UCLASS_I2C_GENERIC, phandle, &priv->ebc_pwr_dev); if (!ret) { printf("Eink: use pmic %s\n", priv->ebc_pwr_dev->name); break; } } if (ret) { dev_err(dev, "Cannot get pmic: %d\n", ret); return ret; } ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, "backlight", &priv->backlight); if (ret && ret != -ENOENT) { printf("%s: Cannot get backlight: %d\n", __func__, ret); } vcom = read_vcom_from_vendor(); if (vcom <= 0) { printf("read vcom from vendor failed, use default vcom\n"); priv->vcom = VCOM_DEFAULT_VALUE; } else { priv->vcom = vcom; } if (priv->ebc_pwr_dev) { struct rk_ebc_pwr_ops *pwr_ops; pwr_ops = ebc_pwr_get_ops(priv->ebc_pwr_dev); ret = pwr_ops->vcom_set(priv->ebc_pwr_dev, priv->vcom); if (ret) { printf("%s, vcom_set failed\n", __func__); return -EIO; } } // read lut to ram, and get lut ops ret = read_waveform(dev); if (ret < 0) { printf("read wavform failed\n"); return -EIO; } eink_dev = dev; return 0; } static int rockchip_eink_display_ofdata_to_platdata(struct udevice *dev) { fdt_size_t size; fdt_addr_t tmp_addr; struct device_node *disp_mem; struct device_node *waveform_mem; struct ebc_panel *plat = dev_get_platdata(dev); plat->width = dev_read_u32_default(dev, "panel,width", 0); plat->height = dev_read_u32_default(dev, "panel,height", 0); plat->vir_width = dev_read_u32_default(dev, "panel,vir_width", plat->width); plat->vir_height = dev_read_u32_default(dev, "panel,vir_height", plat->height); plat->sdck = dev_read_u32_default(dev, "panel,sdck", 0); plat->lsl = dev_read_u32_default(dev, "panel,lsl", 0); plat->lbl = dev_read_u32_default(dev, "panel,lbl", 0); plat->ldl = dev_read_u32_default(dev, "panel,ldl", 0); plat->lel = dev_read_u32_default(dev, "panel,lel", 0); plat->gdck_sta = dev_read_u32_default(dev, "panel,gdck-sta", 0); plat->lgonl = dev_read_u32_default(dev, "panel,lgonl", 0); plat->fsl = dev_read_u32_default(dev, "panel,fsl", 0); plat->fbl = dev_read_u32_default(dev, "panel,fbl", 0); plat->fdl = dev_read_u32_default(dev, "panel,fdl", 0); plat->fel = dev_read_u32_default(dev, "panel,fel", 0); plat->panel_16bit = dev_read_u32_default(dev, "panel,panel_16bit", 0); plat->panel_color = dev_read_u32_default(dev, "panel,panel_color", 0); plat->mirror = dev_read_u32_default(dev, "panel,mirror", 0); plat->rearrange = dev_read_u32_default(dev, "panel,rearrange", 0); plat->width_mm = dev_read_u32_default(dev, "panel,width-mm", 0); plat->height_mm = dev_read_u32_default(dev, "panel,height-mm", 0); disp_mem = of_parse_phandle(ofnode_to_np(dev_ofnode(dev)), "memory-region", 0); if (!disp_mem) { dev_err(dev, "Cannot get memory-region from dts\n"); return -ENODEV; } tmp_addr = ofnode_get_addr_size(np_to_ofnode(disp_mem), "reg", &size); if (tmp_addr == FDT_ADDR_T_NONE) { printf("get display memory address failed\n"); return -ENODEV; } plat->disp_pbuf = (u64)map_sysmem(tmp_addr, 0); plat->disp_pbuf_size = size; debug("display mem=0x%x, size=%x\n", plat->disp_pbuf, plat->disp_pbuf_size); waveform_mem = of_parse_phandle(ofnode_to_np(dev_ofnode(dev)), "waveform-region", 0); if (!waveform_mem) { printf("Cannot get waveform-region from dts\n"); return -ENODEV; } tmp_addr = ofnode_get_addr_size(np_to_ofnode(waveform_mem), "reg", &size); if (tmp_addr == FDT_ADDR_T_NONE) { printf("get waveform memory address failed\n"); return -ENODEV; } plat->lut_pbuf = map_sysmem(tmp_addr, 0); plat->lut_pbuf_size = size; debug("lut mem=0x%p, size=%x\n", plat->lut_pbuf, plat->lut_pbuf_size); return 0; } static const struct udevice_id rockchip_eink_display_ids[] = { { .compatible = "rockchip,ebc-dev", }, {} }; U_BOOT_DRIVER(rk_eink_display) = { .name = "rockchip_eink_display", .id = UCLASS_EINK_DISPLAY, .of_match = rockchip_eink_display_ids, .ofdata_to_platdata = rockchip_eink_display_ofdata_to_platdata, .probe = rockchip_eink_display_probe, .priv_auto_alloc_size = sizeof(struct rockchip_eink_display_priv), .platdata_auto_alloc_size = sizeof(struct ebc_panel), }; UCLASS_DRIVER(rk_eink) = { .id = UCLASS_EINK_DISPLAY, .name = "rk_eink", };