xref: /rk3399_rockchip-uboot/drivers/video/rk_eink/rk_eink_display.c (revision 77bac292f4ebd0ec3e4e2e49c2af5551cbc57f2d)
193a7515aSWenping Zhang /*
293a7515aSWenping Zhang  * (C) Copyright 2020 Rockchip Electronics Co., Ltd
393a7515aSWenping Zhang  *
493a7515aSWenping Zhang  * SPDX-License-Identifier:     GPL-2.0+
593a7515aSWenping Zhang  * Author: Wenping Zhang <wenping.zhang@rock-chips.com>
693a7515aSWenping Zhang  */
793a7515aSWenping Zhang #include <common.h>
893a7515aSWenping Zhang #include <dm.h>
993a7515aSWenping Zhang #include <stdio.h>
1093a7515aSWenping Zhang #include <errno.h>
1193a7515aSWenping Zhang #include <mapmem.h>
1293a7515aSWenping Zhang #include <stdlib.h>
1393a7515aSWenping Zhang #include <asm/arch/vendor.h>
1493a7515aSWenping Zhang #include <dm/of_access.h>
1593a7515aSWenping Zhang #include <dm/uclass.h>
1693a7515aSWenping Zhang #include <dm/uclass-id.h>
1793a7515aSWenping Zhang #include <boot_rkimg.h>
1893a7515aSWenping Zhang #include <rk_eink.h>
193f41a942SWeixin Zhou #include <backlight.h>
2093a7515aSWenping Zhang #include "rk_ebc.h"
2193a7515aSWenping Zhang #include "epdlut/epd_lut.h"
2293a7515aSWenping Zhang 
2393a7515aSWenping Zhang #define PART_WAVEFORM		"waveform"
2493a7515aSWenping Zhang #define EINK_LOGO_PART_MAGIC	"RKEL"
2593a7515aSWenping Zhang #define EINK_LOGO_IMAGE_MAGIC	"GR04"
2693a7515aSWenping Zhang /*
2793a7515aSWenping Zhang  * grayscale logo partition format:
2893a7515aSWenping Zhang  * block0:
2993a7515aSWenping Zhang  * struct logo_part_header part_header;
3093a7515aSWenping Zhang  * struct grayscale_header logo1_header;
3193a7515aSWenping Zhang  * struct grayscale_header logo2_header;
3293a7515aSWenping Zhang  * struct grayscale_header logo3_header;
3393a7515aSWenping Zhang  * struct grayscale_header logo4_header;
3493a7515aSWenping Zhang  * ....
3593a7515aSWenping Zhang  *
3693a7515aSWenping Zhang  * block 1:
3793a7515aSWenping Zhang  * logo1_image
3893a7515aSWenping Zhang  *
3993a7515aSWenping Zhang  * .....
4093a7515aSWenping Zhang  * block m:
4193a7515aSWenping Zhang  * logo2_image
4293a7515aSWenping Zhang  *
4393a7515aSWenping Zhang  * ........
4493a7515aSWenping Zhang  * block n:
4593a7515aSWenping Zhang  * logo3_image
4693a7515aSWenping Zhang  *
4793a7515aSWenping Zhang  * ........
4893a7515aSWenping Zhang  * block i:
4993a7515aSWenping Zhang  * logoi_image
5093a7515aSWenping Zhang  */
5193a7515aSWenping Zhang 
5293a7515aSWenping Zhang //logo partition Header, 64byte
5393a7515aSWenping Zhang struct logo_part_header {
5493a7515aSWenping Zhang 	char magic[4]; /* must be "RKEL" */
5593a7515aSWenping Zhang 	u32  totoal_size;
5693a7515aSWenping Zhang 	u32  screen_width;
5793a7515aSWenping Zhang 	u32  screen_height;
5893a7515aSWenping Zhang 	u32  logo_count;
5993a7515aSWenping Zhang 	u8   version[4];
6093a7515aSWenping Zhang 	u32  rsv[10];
6193a7515aSWenping Zhang } __packed;
6293a7515aSWenping Zhang 
6393a7515aSWenping Zhang // logo image header,32 byte
6493a7515aSWenping Zhang struct grayscale_header {
6593a7515aSWenping Zhang 	char magic[4]; /* must be "GR04" */
6693a7515aSWenping Zhang 	u16 x;
6793a7515aSWenping Zhang 	u16 y;
6893a7515aSWenping Zhang 	u16 w;
6993a7515aSWenping Zhang 	u16 h;
7093a7515aSWenping Zhang 	u32 logo_type;
7193a7515aSWenping Zhang 	u32 data_offset; /* image offset in byte */
7293a7515aSWenping Zhang 	u32 data_size; /* image size in byte */
7393a7515aSWenping Zhang 	u32 rsv[2];
7493a7515aSWenping Zhang } __packed;
7593a7515aSWenping Zhang 
7693a7515aSWenping Zhang /*
7793a7515aSWenping Zhang  * The start address of logo image in logo.img must be aligned in 512 bytes,
7893a7515aSWenping Zhang  * so the header size must be times of 512 bytes. Here we fix the size to 512
7993a7515aSWenping Zhang  * bytes, so the count of logo image can only support up to 14.
8093a7515aSWenping Zhang  */
8193a7515aSWenping Zhang struct logo_info {
8293a7515aSWenping Zhang 	struct logo_part_header part_hdr;
8393a7515aSWenping Zhang 	struct grayscale_header img_hdr[14];
8493a7515aSWenping Zhang } __packed;
8593a7515aSWenping Zhang 
8693a7515aSWenping Zhang struct rockchip_eink_display_priv {
8793a7515aSWenping Zhang 	struct udevice *dev;
8893a7515aSWenping Zhang 	struct udevice *ebc_tcon_dev;
8993a7515aSWenping Zhang 	struct udevice *ebc_pwr_dev;
9093a7515aSWenping Zhang 	int vcom;
913f41a942SWeixin Zhou 	struct udevice *backlight;
9293a7515aSWenping Zhang };
9393a7515aSWenping Zhang 
9493a7515aSWenping Zhang enum {
9593a7515aSWenping Zhang 	EBC_PWR_DOWN = 0,
9693a7515aSWenping Zhang 	EBC_PWR_ON = 1,
9793a7515aSWenping Zhang };
9893a7515aSWenping Zhang 
9993a7515aSWenping Zhang #define EINK_VCOM_ID		17
10093a7515aSWenping Zhang #define EINK_VCOM_MAX		64
10193a7515aSWenping Zhang #define VCOM_DEFAULT_VALUE	1650
10293a7515aSWenping Zhang 
10393a7515aSWenping Zhang static struct logo_info eink_logo_info;
10493a7515aSWenping Zhang static struct udevice *eink_dev;
10593a7515aSWenping Zhang static volatile int last_logo_type = -1;
10693a7515aSWenping Zhang static int read_vcom_from_vendor(void)
10793a7515aSWenping Zhang {
10893a7515aSWenping Zhang 	int ret = 0;
10993a7515aSWenping Zhang 	char vcom_str[EINK_VCOM_MAX] = {0};
11093a7515aSWenping Zhang 	char vcom_args[EINK_VCOM_MAX] = {0};
11193a7515aSWenping Zhang 
11293a7515aSWenping Zhang 	/* Read vcom value from vendor storage part */
11393a7515aSWenping Zhang 	ret = vendor_storage_read(EINK_VCOM_ID, vcom_str, (EINK_VCOM_MAX - 1));
11493a7515aSWenping Zhang 	if (ret > 0) {
115cd44409eSZorro Liu 		snprintf(vcom_args, strlen(vcom_str) + 15, "ebc_pmic.vcom=%s", vcom_str);
11693a7515aSWenping Zhang 		printf("eink update bootargs: %s\n", vcom_args);
11793a7515aSWenping Zhang 		env_update("bootargs", vcom_args);
11893a7515aSWenping Zhang 	} else {
11993a7515aSWenping Zhang 		return ret;
12093a7515aSWenping Zhang 	}
12193a7515aSWenping Zhang 
12293a7515aSWenping Zhang 	return atoi(vcom_str);
12393a7515aSWenping Zhang }
12493a7515aSWenping Zhang 
12593a7515aSWenping Zhang static int read_waveform(struct udevice *dev)
12693a7515aSWenping Zhang {
12793a7515aSWenping Zhang 	int cnt, start, ret;
12893a7515aSWenping Zhang 	disk_partition_t part;
12993a7515aSWenping Zhang 	struct blk_desc *dev_desc;
13093a7515aSWenping Zhang 	struct ebc_panel *plat = dev_get_platdata(dev);
13193a7515aSWenping Zhang 
13293a7515aSWenping Zhang 	dev_desc = rockchip_get_bootdev();
13393a7515aSWenping Zhang 	if (!dev_desc) {
13493a7515aSWenping Zhang 		printf("%s: Could not find device\n", __func__);
13593a7515aSWenping Zhang 		return -EIO;
13693a7515aSWenping Zhang 	}
13793a7515aSWenping Zhang 	if (part_get_info_by_name(dev_desc, PART_WAVEFORM, &part) < 0) {
13893a7515aSWenping Zhang 		printf("Get waveform partition failed\n");
13993a7515aSWenping Zhang 		return -ENODEV;
14093a7515aSWenping Zhang 	}
14193a7515aSWenping Zhang 	cnt = plat->lut_pbuf_size / RK_BLK_SIZE;
14293a7515aSWenping Zhang 	start = part.start;
14393a7515aSWenping Zhang 	ret = blk_dread(dev_desc, start, cnt, (void *)plat->lut_pbuf);
14493a7515aSWenping Zhang 	if (ret != cnt)
14593a7515aSWenping Zhang 		printf("Try to read %d blocks failed, only read %d\n",
14693a7515aSWenping Zhang 		       cnt, ret);
14793a7515aSWenping Zhang 
14893a7515aSWenping Zhang 	flush_dcache_range((ulong)plat->lut_pbuf,
14993a7515aSWenping Zhang 			   ALIGN((ulong)plat->lut_pbuf + cnt,
15093a7515aSWenping Zhang 				 CONFIG_SYS_CACHELINE_SIZE));
15193a7515aSWenping Zhang 	ret = epd_lut_from_mem_init(plat->lut_pbuf, &plat->lut_ops);
15293a7515aSWenping Zhang 	if (ret < 0) {
15393a7515aSWenping Zhang 		printf("lut init failed\n");
15493a7515aSWenping Zhang 		return -EINVAL;
15593a7515aSWenping Zhang 	}
15693a7515aSWenping Zhang 
15793a7515aSWenping Zhang 	return 0;
15893a7515aSWenping Zhang }
15993a7515aSWenping Zhang 
16093a7515aSWenping Zhang static u32 aligned_image_size_4k(struct udevice *dev)
16193a7515aSWenping Zhang {
16293a7515aSWenping Zhang 	struct ebc_panel *plat = dev_get_platdata(dev);
1639876686dSWenping Zhang 	u32 w = plat->vir_width;
1649876686dSWenping Zhang 	u32 h = plat->vir_height;
16593a7515aSWenping Zhang 
16693a7515aSWenping Zhang 	return ALIGN((w * h) >> 1, 4096);
16793a7515aSWenping Zhang }
16893a7515aSWenping Zhang 
16993a7515aSWenping Zhang /*
17093a7515aSWenping Zhang  * This driver load the grayscale image from flash,
17193a7515aSWenping Zhang  * and put it in the reserve memory which define in dts:
17293a7515aSWenping Zhang  * display_reserved: framebuffer@10000000 {
17393a7515aSWenping Zhang  *	reg = <0x0 0x10000000 0x0 0x2000000>;
17493a7515aSWenping Zhang  *	no-map;
17593a7515aSWenping Zhang  * };
17693a7515aSWenping Zhang  * Every image logo size must be aligned in 4K, make sure
17793a7515aSWenping Zhang  * kernel can use it rightly, the buffer of LOGO image is
17893a7515aSWenping Zhang  * put in order of below map:
179e79e2085SWenping Zhang  *  |---reset logo        ---|
18093a7515aSWenping Zhang  *  |---uboot logo        ---|
18193a7515aSWenping Zhang  *  |---kernel logo       ---|
18293a7515aSWenping Zhang  *  |---charge_0 logo   ---|
18393a7515aSWenping Zhang  *  |---charge_1 logo   ---|
18493a7515aSWenping Zhang  *  |---charge_2 logo   ---|
18593a7515aSWenping Zhang  *  |---charge_3 logo   ---|
18693a7515aSWenping Zhang  *  |---charge_4 logo   ---|
18793a7515aSWenping Zhang  *  |---charge_5 logo   ---|
18893a7515aSWenping Zhang  *  |---battery low logo---|
1894f14f98fSWenping Zhang  *  |---temp un-mirror buffer--|
19093a7515aSWenping Zhang  */
19193a7515aSWenping Zhang static int get_addr_by_type(struct udevice *dev, u32 logo_type)
19293a7515aSWenping Zhang {
19393a7515aSWenping Zhang 	u32 offset, indx, img_size;
19493a7515aSWenping Zhang 	struct ebc_panel *plat = dev_get_platdata(dev);
19593a7515aSWenping Zhang 
19693a7515aSWenping Zhang 	if (plat->disp_pbuf_size == 0 || !plat->disp_pbuf) {
19793a7515aSWenping Zhang 		printf("invalid display buffer, please check dts\n");
19893a7515aSWenping Zhang 		return -EINVAL;
19993a7515aSWenping Zhang 	}
200e79e2085SWenping Zhang 	indx = ffs(logo_type);
20193a7515aSWenping Zhang 	img_size = aligned_image_size_4k(dev);
20293a7515aSWenping Zhang 	offset = img_size * indx;
20393a7515aSWenping Zhang 	if (offset + img_size > plat->disp_pbuf_size) {
20493a7515aSWenping Zhang 		printf("reserve display memory size is not enough\n");
20593a7515aSWenping Zhang 		return -EINVAL;
20693a7515aSWenping Zhang 	}
20793a7515aSWenping Zhang 
20893a7515aSWenping Zhang 	switch (logo_type) {
209e79e2085SWenping Zhang 	case EINK_LOGO_RESET:
21093a7515aSWenping Zhang 	case EINK_LOGO_UBOOT:
21193a7515aSWenping Zhang 	case EINK_LOGO_KERNEL:
21293a7515aSWenping Zhang 	case EINK_LOGO_CHARGING_0:
21393a7515aSWenping Zhang 	case EINK_LOGO_CHARGING_1:
21493a7515aSWenping Zhang 	case EINK_LOGO_CHARGING_2:
21593a7515aSWenping Zhang 	case EINK_LOGO_CHARGING_3:
21693a7515aSWenping Zhang 	case EINK_LOGO_CHARGING_4:
21793a7515aSWenping Zhang 	case EINK_LOGO_CHARGING_5:
21893a7515aSWenping Zhang 	case EINK_LOGO_CHARGING_LOWPOWER:
219*77bac292SZorro Liu 	case EINK_LOGO_POWEROFF:
2204f14f98fSWenping Zhang 	/*
2214f14f98fSWenping Zhang 	 * The MIRROR_TEMP_BUF is used to save the
2224f14f98fSWenping Zhang 	 * non-mirror image data.
2234f14f98fSWenping Zhang 	 */
2244f14f98fSWenping Zhang 	case EINK_LOGO_UNMIRROR_TEMP_BUF:
22593a7515aSWenping Zhang 		return (plat->disp_pbuf + offset);
22693a7515aSWenping Zhang 	default:
22793a7515aSWenping Zhang 		printf("invalid logo type[%d]\n", logo_type);
22893a7515aSWenping Zhang 	}
22993a7515aSWenping Zhang 
23093a7515aSWenping Zhang 	return -EINVAL;
23193a7515aSWenping Zhang }
23293a7515aSWenping Zhang 
23393a7515aSWenping Zhang static int read_header(struct blk_desc *dev_desc,
23493a7515aSWenping Zhang 		       disk_partition_t *part,
23593a7515aSWenping Zhang 		       struct logo_info *header)
23693a7515aSWenping Zhang {
23793a7515aSWenping Zhang 	int i;
23893a7515aSWenping Zhang 	struct logo_part_header *part_hdr = &header->part_hdr;
23993a7515aSWenping Zhang 
24093a7515aSWenping Zhang 	if (blk_dread(dev_desc, part->start, 1, header) != 1)
24193a7515aSWenping Zhang 		return -EIO;
24293a7515aSWenping Zhang 
24393a7515aSWenping Zhang 	if (memcmp(part_hdr->magic, EINK_LOGO_PART_MAGIC, 4)) {
24493a7515aSWenping Zhang 		printf("partition header is invalid\n");
24593a7515aSWenping Zhang 		return -EINVAL;
24693a7515aSWenping Zhang 	}
24793a7515aSWenping Zhang 	if (part_hdr->logo_count == 0) {
24893a7515aSWenping Zhang 		printf("the count of logo image is 0\n");
24993a7515aSWenping Zhang 		return -EINVAL;
25093a7515aSWenping Zhang 	}
25193a7515aSWenping Zhang 	for (i = 0; i < part_hdr->logo_count; i++) {
25293a7515aSWenping Zhang 		struct grayscale_header *img_hdr = &header->img_hdr[i];
25393a7515aSWenping Zhang 
25493a7515aSWenping Zhang 		if (memcmp(img_hdr->magic, EINK_LOGO_IMAGE_MAGIC, 4)) {
25593a7515aSWenping Zhang 			printf("image[%d] header '%s' is invalid\n", i,
25693a7515aSWenping Zhang 			       img_hdr->magic);
25793a7515aSWenping Zhang 			return -EINVAL;
25893a7515aSWenping Zhang 		}
25993a7515aSWenping Zhang 	}
26093a7515aSWenping Zhang 
26193a7515aSWenping Zhang 	return 0;
26293a7515aSWenping Zhang }
26393a7515aSWenping Zhang 
26493a7515aSWenping Zhang static int read_grayscale(struct blk_desc *dev_desc,
26593a7515aSWenping Zhang 			  disk_partition_t *part, u32 offset,
26693a7515aSWenping Zhang 			  u32 size, void *buf)
26793a7515aSWenping Zhang {
26893a7515aSWenping Zhang 	u32 blk_start, blk_offset, blk_count;
26993a7515aSWenping Zhang 
27093a7515aSWenping Zhang 	blk_offset = DIV_ROUND_UP(offset, dev_desc->blksz);
27193a7515aSWenping Zhang 	blk_start = part->start + blk_offset;
27293a7515aSWenping Zhang 	blk_count = DIV_ROUND_UP(size, dev_desc->blksz);
27393a7515aSWenping Zhang 
27493a7515aSWenping Zhang 	debug("blk_offset=%d, blk_start=%d,blk_count=%d,out buf=%p\n",
27593a7515aSWenping Zhang 	      blk_offset, blk_start, blk_count, buf);
27693a7515aSWenping Zhang 	if (blk_dread(dev_desc, blk_start, blk_count, buf) != blk_count) {
27793a7515aSWenping Zhang 		printf("read grayscale data failed\n");
27893a7515aSWenping Zhang 		return -EIO;
27993a7515aSWenping Zhang 	}
28093a7515aSWenping Zhang 
28193a7515aSWenping Zhang 	return 0;
28293a7515aSWenping Zhang }
28393a7515aSWenping Zhang 
2844f14f98fSWenping Zhang static int image_mirror(u8 *in_buf, u8 *out_buf, u16 w, u16 h)
2854f14f98fSWenping Zhang {
2864f14f98fSWenping Zhang 	int i;
2874f14f98fSWenping Zhang 
2884f14f98fSWenping Zhang 	if (!in_buf || !out_buf) {
2894f14f98fSWenping Zhang 		printf("mirror in buffer or out buffer is NULL\n");
2904f14f98fSWenping Zhang 		return -EINVAL;
2914f14f98fSWenping Zhang 	}
2924f14f98fSWenping Zhang 
2934f14f98fSWenping Zhang 	for (i = 0; i < h; i++) {
2944f14f98fSWenping Zhang 		u16 column_len = w / 2;
2954f14f98fSWenping Zhang 		u8 *column_in = in_buf + i * column_len;
2964f14f98fSWenping Zhang 		u8 *column_out = out_buf + (h - i - 1) * column_len;
2974f14f98fSWenping Zhang 
2984f14f98fSWenping Zhang 		memcpy(column_out, column_in, column_len);
2994f14f98fSWenping Zhang 	}
3004f14f98fSWenping Zhang 
3014f14f98fSWenping Zhang 	return 0;
3024f14f98fSWenping Zhang }
3034f14f98fSWenping Zhang 
30493a7515aSWenping Zhang /*
30593a7515aSWenping Zhang  * The eink kernel driver need last frame to do part refresh,
30693a7515aSWenping Zhang  * so we need to transfer two images to kernel, which is kernel
30793a7515aSWenping Zhang  * logo and the logo displayed in uboot.
30893a7515aSWenping Zhang  *
30993a7515aSWenping Zhang  * this function use logo type bitmap to indicate several logo.
31093a7515aSWenping Zhang  * u32 needed_logo: we only load needed logo image into ram, such as
31193a7515aSWenping Zhang  *                   uboot logo + kernel logo or charger logo + kernel
31293a7515aSWenping Zhang  *                   logo
313074c7ac4SWenping Zhang  * u32 *loaded_logo: because the needed logo may not exist in logo.img,
314074c7ac4SWenping Zhang  *                  store the really loaded logo in para loaded_logo.
31593a7515aSWenping Zhang  */
31693a7515aSWenping Zhang static int read_needed_logo_from_partition(struct udevice *dev,
31793a7515aSWenping Zhang 					   u32 needed_logo,
31893a7515aSWenping Zhang 					   u32 *loaded_logo)
31993a7515aSWenping Zhang {
32093a7515aSWenping Zhang 	int ret, i;
32193a7515aSWenping Zhang 	disk_partition_t part;
32293a7515aSWenping Zhang 	struct blk_desc *dev_desc;
32393a7515aSWenping Zhang 	struct logo_info *hdr = &eink_logo_info;
32493a7515aSWenping Zhang 	struct logo_part_header *part_hdr = &hdr->part_hdr;
32593a7515aSWenping Zhang 	struct ebc_panel *panel = dev_get_platdata(dev);
32639939100SWenping Zhang 	u32 logo = needed_logo & (~(*loaded_logo));
32793a7515aSWenping Zhang 
32839939100SWenping Zhang 	if (!logo) {
329074c7ac4SWenping Zhang 		printf("logo[0x%x] is already loaded, just return!\n",
330074c7ac4SWenping Zhang 		       needed_logo);
331074c7ac4SWenping Zhang 		return 0;
332074c7ac4SWenping Zhang 	}
33393a7515aSWenping Zhang 	dev_desc = rockchip_get_bootdev();
33493a7515aSWenping Zhang 	if (!dev_desc) {
33593a7515aSWenping Zhang 		printf("%s: Could not find device\n", __func__);
33693a7515aSWenping Zhang 		return -EIO;
33793a7515aSWenping Zhang 	}
33893a7515aSWenping Zhang 
33993a7515aSWenping Zhang 	if (part_get_info_by_name(dev_desc, PART_LOGO, &part) < 0)
34093a7515aSWenping Zhang 		return -ENODEV;
34193a7515aSWenping Zhang 
34293a7515aSWenping Zhang 	ret = read_header(dev_desc, &part, hdr);
34393a7515aSWenping Zhang 	if (ret < 0) {
34493a7515aSWenping Zhang 		printf("eink logo read header failed,ret = %d\n", ret);
34593a7515aSWenping Zhang 		return -EINVAL;
34693a7515aSWenping Zhang 	}
3479876686dSWenping Zhang 	if (part_hdr->screen_width != panel->vir_width ||
3489876686dSWenping Zhang 	    part_hdr->screen_height != panel->vir_height){
34993a7515aSWenping Zhang 		printf("logo size(%dx%d) is not same as screen size(%dx%d)\n",
35093a7515aSWenping Zhang 		       part_hdr->screen_width, part_hdr->screen_height,
3519876686dSWenping Zhang 			panel->vir_width, panel->vir_height);
35293a7515aSWenping Zhang 		return -EINVAL;
35393a7515aSWenping Zhang 	}
35493a7515aSWenping Zhang 
35593a7515aSWenping Zhang 	for (i = 0; i < part_hdr->logo_count; i++) {
35693a7515aSWenping Zhang 		struct grayscale_header *img_hdr = &hdr->img_hdr[i];
35793a7515aSWenping Zhang 		int pic_buf;
35893a7515aSWenping Zhang 		u32 offset = img_hdr->data_offset;
35993a7515aSWenping Zhang 		u32 size = img_hdr->data_size;
36093a7515aSWenping Zhang 		u32 logo_type = img_hdr->logo_type;
36193a7515aSWenping Zhang 
36293a7515aSWenping Zhang 		debug("offset=0x%x, size=%d,logo_type=%d,w=%d,h=%d\n",
36393a7515aSWenping Zhang 		      offset, size, logo_type, img_hdr->w, img_hdr->h);
36493a7515aSWenping Zhang 
36539939100SWenping Zhang 		if (logo & logo_type) {
36693a7515aSWenping Zhang 			pic_buf = get_addr_by_type(dev, logo_type);
36793a7515aSWenping Zhang 
36893a7515aSWenping Zhang 			if (pic_buf <= 0) {
36993a7515aSWenping Zhang 				printf("Get buffer failed for image %d\n",
37093a7515aSWenping Zhang 				       img_hdr->logo_type);
37193a7515aSWenping Zhang 				return -EIO;
37293a7515aSWenping Zhang 			}
37393a7515aSWenping Zhang 			if (!IS_ALIGNED((ulong)pic_buf,
37493a7515aSWenping Zhang 					ARCH_DMA_MINALIGN)) {
37593a7515aSWenping Zhang 				printf("disp buffer is not dma aligned\n");
37693a7515aSWenping Zhang 				return -EINVAL;
37793a7515aSWenping Zhang 			}
3784f14f98fSWenping Zhang 			/*
3794f14f98fSWenping Zhang 			 * kernel logo is transmitted to kernel to display, and
3804f14f98fSWenping Zhang 			 * kernel will do the mirror operation, so skip kernel
3814f14f98fSWenping Zhang 			 * logo here.
3824f14f98fSWenping Zhang 			 */
38339939100SWenping Zhang 			if (panel->mirror && logo_type != EINK_LOGO_KERNEL) {
3844f14f98fSWenping Zhang 				u32 w = panel->vir_width;
3854f14f98fSWenping Zhang 				u32 h = panel->vir_height;
3864f14f98fSWenping Zhang 				u32 mirror_buf = 0;
3874f14f98fSWenping Zhang 
3884f14f98fSWenping Zhang 				mirror_buf = get_addr_by_type(dev,
3894f14f98fSWenping Zhang 							      EINK_LOGO_UNMIRROR_TEMP_BUF);
3904f14f98fSWenping Zhang 				if (mirror_buf <= 0) {
3914f14f98fSWenping Zhang 					printf("get mirror buffer failed\n");
3924f14f98fSWenping Zhang 					return -EIO;
3934f14f98fSWenping Zhang 				}
3944f14f98fSWenping Zhang 				read_grayscale(dev_desc, &part, offset, size,
3954f14f98fSWenping Zhang 					       (void *)((ulong)mirror_buf));
3964f14f98fSWenping Zhang 				image_mirror((u8 *)((ulong)mirror_buf),
3974f14f98fSWenping Zhang 					     (u8 *)((ulong)pic_buf), w, h);
3984f14f98fSWenping Zhang 			} else {
39993a7515aSWenping Zhang 				read_grayscale(dev_desc, &part, offset, size,
40093a7515aSWenping Zhang 					       (void *)((ulong)pic_buf));
4014f14f98fSWenping Zhang 			}
40293a7515aSWenping Zhang 			flush_dcache_range((ulong)pic_buf,
40393a7515aSWenping Zhang 					   ALIGN((ulong)pic_buf + size,
40493a7515aSWenping Zhang 						 CONFIG_SYS_CACHELINE_SIZE));
40593a7515aSWenping Zhang 			*loaded_logo |= logo_type;
40639939100SWenping Zhang 
40739939100SWenping Zhang 			logo &= ~logo_type;
40839939100SWenping Zhang 			if (!logo)
40939939100SWenping Zhang 				break;
41093a7515aSWenping Zhang 		}
41193a7515aSWenping Zhang 	}
41293a7515aSWenping Zhang 
41393a7515aSWenping Zhang 	return 0;
41493a7515aSWenping Zhang }
41593a7515aSWenping Zhang 
41693a7515aSWenping Zhang static int ebc_power_set(struct udevice *dev, int is_on)
41793a7515aSWenping Zhang {
41893a7515aSWenping Zhang 	int ret;
41993a7515aSWenping Zhang 	struct rockchip_eink_display_priv *priv = dev_get_priv(dev);
42093a7515aSWenping Zhang 	struct ebc_panel *panel = dev_get_platdata(dev);
42193a7515aSWenping Zhang 	struct udevice *ebc_tcon_dev = priv->ebc_tcon_dev;
42293a7515aSWenping Zhang 	struct rk_ebc_tcon_ops *ebc_tcon_ops = ebc_tcon_get_ops(ebc_tcon_dev);
42393a7515aSWenping Zhang 	struct udevice *ebc_pwr_dev = priv->ebc_pwr_dev;
42493a7515aSWenping Zhang 	struct rk_ebc_pwr_ops *pwr_ops = ebc_pwr_get_ops(ebc_pwr_dev);
42593a7515aSWenping Zhang 
42693a7515aSWenping Zhang 	if (is_on) {
42793a7515aSWenping Zhang 		ret = ebc_tcon_ops->enable(ebc_tcon_dev, panel);
42893a7515aSWenping Zhang 		if (ret) {
42993a7515aSWenping Zhang 			printf("%s, ebc tcon enabled failed\n", __func__);
43093a7515aSWenping Zhang 			return -1;
43193a7515aSWenping Zhang 		}
43293a7515aSWenping Zhang 		ret = pwr_ops->power_on(ebc_pwr_dev);
43393a7515aSWenping Zhang 		if (ret) {
43493a7515aSWenping Zhang 			printf("%s, power on failed\n", __func__);
43593a7515aSWenping Zhang 			return -1;
43693a7515aSWenping Zhang 		}
43793a7515aSWenping Zhang 	} else {
43893a7515aSWenping Zhang 		ret = pwr_ops->power_down(ebc_pwr_dev);
43993a7515aSWenping Zhang 		if (ret) {
44093a7515aSWenping Zhang 			printf("%s, power_down failed\n", __func__);
44193a7515aSWenping Zhang 			return -1;
44293a7515aSWenping Zhang 		}
44393a7515aSWenping Zhang 		ret = ebc_tcon_ops->disable(ebc_tcon_dev);
44493a7515aSWenping Zhang 		if (ret) {
44593a7515aSWenping Zhang 			printf("%s, ebc tcon disable failed\n", __func__);
44693a7515aSWenping Zhang 			return -1;
44793a7515aSWenping Zhang 		}
44893a7515aSWenping Zhang 	}
44993a7515aSWenping Zhang 	return 0;
45093a7515aSWenping Zhang }
45193a7515aSWenping Zhang 
45293a7515aSWenping Zhang static int eink_display(struct udevice *dev, u32 pre_img_buf,
45393a7515aSWenping Zhang 			u32 cur_img_buf, u32 lut_type, int update_mode)
45493a7515aSWenping Zhang {
45593a7515aSWenping Zhang 	u32 temperature, frame_num;
45693a7515aSWenping Zhang 	struct rockchip_eink_display_priv *priv = dev_get_priv(dev);
45793a7515aSWenping Zhang 	struct ebc_panel *plat = dev_get_platdata(dev);
45893a7515aSWenping Zhang 	struct epd_lut_ops *lut_ops = &plat->lut_ops;
45993a7515aSWenping Zhang 	struct udevice *ebc_pwr_dev = priv->ebc_pwr_dev;
46093a7515aSWenping Zhang 	struct rk_ebc_pwr_ops *pwr_ops = ebc_pwr_get_ops(ebc_pwr_dev);
46193a7515aSWenping Zhang 	struct udevice *ebc_tcon_dev = priv->ebc_tcon_dev;
46293a7515aSWenping Zhang 	struct rk_ebc_tcon_ops *ebc_tcon_ops = ebc_tcon_get_ops(ebc_tcon_dev);
46393a7515aSWenping Zhang 
46493a7515aSWenping Zhang 	pwr_ops->temp_get(ebc_pwr_dev, &temperature);
46593a7515aSWenping Zhang 	if (temperature <= 0 || temperature > 50) {
46693a7515aSWenping Zhang 		printf("temperature = %d, out of range0~50 ,use 25\n",
46793a7515aSWenping Zhang 		       temperature);
46893a7515aSWenping Zhang 		temperature = 25;
46993a7515aSWenping Zhang 	}
47093a7515aSWenping Zhang 
47193a7515aSWenping Zhang 	if (!lut_ops->lut_get) {
47293a7515aSWenping Zhang 		printf("get lut ops failed\n");
47393a7515aSWenping Zhang 		return -EIO;
47493a7515aSWenping Zhang 	}
47593a7515aSWenping Zhang 	lut_ops->lut_get(&plat->lut_data, lut_type, temperature);
47693a7515aSWenping Zhang 	frame_num = plat->lut_data.frame_num;
47793a7515aSWenping Zhang 	debug("lut_type=%d, frame num=%d, temp=%d\n", lut_type,
47893a7515aSWenping Zhang 	      frame_num, temperature);
47993a7515aSWenping Zhang 
48039939100SWenping Zhang 	ebc_tcon_ops->wait_for_last_frame_complete(ebc_tcon_dev);
48193a7515aSWenping Zhang 	ebc_tcon_ops->lut_data_set(ebc_tcon_dev, plat->lut_data.data,
48293a7515aSWenping Zhang 				   frame_num, 0);
48393a7515aSWenping Zhang 	ebc_tcon_ops->dsp_mode_set(ebc_tcon_dev, update_mode,
48493a7515aSWenping Zhang 				   LUT_MODE, !THREE_WIN_MODE, !EINK_MODE);
48593a7515aSWenping Zhang 	ebc_tcon_ops->image_addr_set(ebc_tcon_dev, pre_img_buf, cur_img_buf);
48693a7515aSWenping Zhang 	ebc_tcon_ops->frame_start(ebc_tcon_dev, frame_num);
48793a7515aSWenping Zhang 	return 0;
48893a7515aSWenping Zhang }
48993a7515aSWenping Zhang 
49093a7515aSWenping Zhang static int rk_eink_display_init(void)
49193a7515aSWenping Zhang {
49293a7515aSWenping Zhang 	int ret;
49393a7515aSWenping Zhang 	struct uclass *uc;
49493a7515aSWenping Zhang 	struct udevice *dev;
49593a7515aSWenping Zhang 
49693a7515aSWenping Zhang 	if (eink_dev) {
49793a7515aSWenping Zhang 		printf("ebc-dev is already initialized!\n");
49893a7515aSWenping Zhang 		return 0;
49993a7515aSWenping Zhang 	}
50093a7515aSWenping Zhang 
50193a7515aSWenping Zhang 	ret = uclass_get(UCLASS_EINK_DISPLAY, &uc);
50293a7515aSWenping Zhang 	if (ret) {
50393a7515aSWenping Zhang 		printf("can't find uclass eink\n");
50493a7515aSWenping Zhang 		return -ENODEV;
50593a7515aSWenping Zhang 	}
50693a7515aSWenping Zhang 	for (uclass_first_device(UCLASS_EINK_DISPLAY, &dev);
50793a7515aSWenping Zhang 	     dev; uclass_next_device(&dev))
50893a7515aSWenping Zhang 		;
50993a7515aSWenping Zhang 
51093a7515aSWenping Zhang 	if (eink_dev) {
51193a7515aSWenping Zhang 		printf("ebc-dev is probed success!\n");
51293a7515aSWenping Zhang 		return 0;
51393a7515aSWenping Zhang 	}
51493a7515aSWenping Zhang 	printf("Can't find ebc-dev\n");
51593a7515aSWenping Zhang 	return -ENODEV;
51693a7515aSWenping Zhang }
51793a7515aSWenping Zhang 
51893a7515aSWenping Zhang /*
51993a7515aSWenping Zhang  * Eink display need current and previous image buffer, We assume
52093a7515aSWenping Zhang  * every type of logo has only one image, so just tell this function
52193a7515aSWenping Zhang  * last logo type and current logo type, it will find the right images.
52293a7515aSWenping Zhang  * last_logo_type: -1 means it's first displaying.
52393a7515aSWenping Zhang  */
52493a7515aSWenping Zhang static int rockchip_eink_show_logo(int cur_logo_type, int update_mode)
52593a7515aSWenping Zhang {
52693a7515aSWenping Zhang 	int ret = 0;
52793a7515aSWenping Zhang 	u32 logo_addr;
52893a7515aSWenping Zhang 	u32 last_logo_addr;
52993a7515aSWenping Zhang 	struct ebc_panel *plat;
53093a7515aSWenping Zhang 	struct udevice *dev;
53139939100SWenping Zhang 	static u32 loaded_logo = 0;
5323f41a942SWeixin Zhou 	struct rockchip_eink_display_priv *priv;
53393a7515aSWenping Zhang 
53493a7515aSWenping Zhang 	if (!eink_dev) {
535449de1d3SWenping Zhang 		static bool first_init = true;
536449de1d3SWenping Zhang 
537449de1d3SWenping Zhang 		if (first_init) {
538449de1d3SWenping Zhang 			first_init = false;
53993a7515aSWenping Zhang 			ret = rk_eink_display_init();
54093a7515aSWenping Zhang 			if (ret) {
541449de1d3SWenping Zhang 				printf("Get ebc dev failed, check dts\n");
542449de1d3SWenping Zhang 				return -ENODEV;
543449de1d3SWenping Zhang 			}
544449de1d3SWenping Zhang 		} else {
54593a7515aSWenping Zhang 			return -ENODEV;
54693a7515aSWenping Zhang 		}
54793a7515aSWenping Zhang 	}
54893a7515aSWenping Zhang 	dev = eink_dev;
54993a7515aSWenping Zhang 
55093a7515aSWenping Zhang 	/*Don't need to update display*/
55193a7515aSWenping Zhang 	if (last_logo_type == cur_logo_type) {
55293a7515aSWenping Zhang 		debug("Same as last picture, Don't need to display\n");
55393a7515aSWenping Zhang 		return 0;
55493a7515aSWenping Zhang 	}
55593a7515aSWenping Zhang 
55693a7515aSWenping Zhang 	plat = dev_get_platdata(dev);
5573f41a942SWeixin Zhou 	priv = dev_get_priv(dev);
55893a7515aSWenping Zhang 
55939939100SWenping Zhang 	/*
56039939100SWenping Zhang 	 * The last_logo_type is -1 means it's first displaying
56139939100SWenping Zhang 	 */
56239939100SWenping Zhang 	if (last_logo_type == -1) {
56393a7515aSWenping Zhang 		ret = ebc_power_set(dev, EBC_PWR_ON);
56493a7515aSWenping Zhang 		if (ret) {
56593a7515aSWenping Zhang 			printf("Eink power on failed\n");
56693a7515aSWenping Zhang 			return -1;
56793a7515aSWenping Zhang 		}
56839939100SWenping Zhang 
5699876686dSWenping Zhang 		int size = (plat->vir_width * plat->vir_height) >> 1;
57093a7515aSWenping Zhang 
571e79e2085SWenping Zhang 		logo_addr = get_addr_by_type(dev, EINK_LOGO_RESET);
572e79e2085SWenping Zhang 		memset((u32 *)(u64)logo_addr, 0xff, size);
57323f3ce8dSWenping Zhang 		flush_dcache_range((ulong)logo_addr,
57423f3ce8dSWenping Zhang 				   ALIGN((ulong)logo_addr + size,
57523f3ce8dSWenping Zhang 					 CONFIG_SYS_CACHELINE_SIZE));
576e79e2085SWenping Zhang 		eink_display(dev, logo_addr, logo_addr,
57739939100SWenping Zhang 			     WF_TYPE_RESET, EINK_LOGO_RESET);
57893a7515aSWenping Zhang 		last_logo_type = 0;
579e79e2085SWenping Zhang 		last_logo_addr = logo_addr;
58093a7515aSWenping Zhang 	} else {
58193a7515aSWenping Zhang 		last_logo_addr = get_addr_by_type(dev, last_logo_type);
58293a7515aSWenping Zhang 		if (last_logo_addr < 0) {
58393a7515aSWenping Zhang 			printf("Invalid last logo addr, exit!\n");
58493a7515aSWenping Zhang 			goto out;
58593a7515aSWenping Zhang 		}
586*77bac292SZorro Liu 	}
587*77bac292SZorro Liu 	ret = read_needed_logo_from_partition(dev, cur_logo_type,
588*77bac292SZorro Liu 					      &loaded_logo);
589*77bac292SZorro Liu 	if (ret || !(loaded_logo & cur_logo_type)) {
590*77bac292SZorro Liu 		printf("read logo[0x%x] failed, loaded_logo=0x%x\n",
591*77bac292SZorro Liu 		       cur_logo_type, loaded_logo);
592*77bac292SZorro Liu 		ret = -EIO;
593*77bac292SZorro Liu 		goto out;
594*77bac292SZorro Liu 	}
595*77bac292SZorro Liu 	logo_addr = get_addr_by_type(dev, cur_logo_type);
596*77bac292SZorro Liu 	debug("logo_addr=%x, logo_type=%d\n", logo_addr, cur_logo_type);
597*77bac292SZorro Liu 	if (logo_addr <= 0) {
598*77bac292SZorro Liu 		printf("get logo buffer failed\n");
599*77bac292SZorro Liu 		ret = -EIO;
600*77bac292SZorro Liu 		goto out;
601*77bac292SZorro Liu 	}
60239939100SWenping Zhang 
603*77bac292SZorro Liu 	eink_display(dev, last_logo_addr, logo_addr, WF_TYPE_GC16, update_mode);
604*77bac292SZorro Liu 
605*77bac292SZorro Liu 	if (priv->backlight)
606*77bac292SZorro Liu 		backlight_enable(priv->backlight);
607*77bac292SZorro Liu 
608*77bac292SZorro Liu 	last_logo_type = cur_logo_type;
609*77bac292SZorro Liu 
610*77bac292SZorro Liu 	if (cur_logo_type == EINK_LOGO_POWEROFF) {
61139939100SWenping Zhang 		struct udevice *ebc_tcon_dev = priv->ebc_tcon_dev;
61239939100SWenping Zhang 		struct rk_ebc_tcon_ops *ebc_tcon_ops;
61339939100SWenping Zhang 
61493a7515aSWenping Zhang 		last_logo_type = -1;
61539939100SWenping Zhang 		/*
61639939100SWenping Zhang 		 * For normal logo display, waiting for the last frame
61739939100SWenping Zhang 		 * completion before start a new frame, except one
61839939100SWenping Zhang 		 * situation which charging logo display finished,
61939939100SWenping Zhang 		 * because device will rebooting or shutdown after
62039939100SWenping Zhang 		 * charging logo is competed.
62139939100SWenping Zhang 		 *
62239939100SWenping Zhang 		 * We should take care of the power sequence,
62339939100SWenping Zhang 		 * because ebc can't power off if last frame
62439939100SWenping Zhang 		 * data is still sending, so keep the ebc power
62539939100SWenping Zhang 		 * during u-boot phase and shutdown the
62639939100SWenping Zhang 		 * power only if uboot charging is finished.
62739939100SWenping Zhang 		 */
62839939100SWenping Zhang 		ebc_tcon_ops = ebc_tcon_get_ops(ebc_tcon_dev);
62939939100SWenping Zhang 		ebc_tcon_ops->wait_for_last_frame_complete(ebc_tcon_dev);
63039939100SWenping Zhang 		debug("charging logo displaying is complete\n");
63139939100SWenping Zhang 		/*
63239939100SWenping Zhang 		 *shutdown ebc after charging logo display is complete
63339939100SWenping Zhang 		 */
63439939100SWenping Zhang 		ret = ebc_power_set(dev, EBC_PWR_DOWN);
63539939100SWenping Zhang 		if (ret)
63639939100SWenping Zhang 			printf("Eink power down failed\n");
63793a7515aSWenping Zhang 		goto out;
63893a7515aSWenping Zhang 	}
63993a7515aSWenping Zhang 
64093a7515aSWenping Zhang 	/*
64193a7515aSWenping Zhang 	 * System will boot up to kernel only when the
64293a7515aSWenping Zhang 	 * logo is uboot logo
64393a7515aSWenping Zhang 	 */
64493a7515aSWenping Zhang 	if (cur_logo_type == EINK_LOGO_UBOOT) {
64593a7515aSWenping Zhang 		char logo_args[64] = {0};
6464f14f98fSWenping Zhang 		u32 uboot_logo_buf;
64793a7515aSWenping Zhang 
6484f14f98fSWenping Zhang 		if (plat->mirror)
6494f14f98fSWenping Zhang 			uboot_logo_buf = get_addr_by_type(dev,
6504f14f98fSWenping Zhang 							  EINK_LOGO_UNMIRROR_TEMP_BUF);
6514f14f98fSWenping Zhang 		else
6524f14f98fSWenping Zhang 			uboot_logo_buf = logo_addr;
6534f14f98fSWenping Zhang 		printf("Transmit uboot logo addr(0x%x) to kernel\n",
6544f14f98fSWenping Zhang 		       uboot_logo_buf);
6554f14f98fSWenping Zhang 		sprintf(logo_args, "ulogo_addr=0x%x", uboot_logo_buf);
65693a7515aSWenping Zhang 		env_update("bootargs", logo_args);
65793a7515aSWenping Zhang 		ret = read_needed_logo_from_partition(dev, EINK_LOGO_KERNEL,
658074c7ac4SWenping Zhang 						      &loaded_logo);
659074c7ac4SWenping Zhang 		if (ret || !(loaded_logo & EINK_LOGO_KERNEL)) {
66093a7515aSWenping Zhang 			printf("No invalid kernel logo in logo.img\n");
66193a7515aSWenping Zhang 		} else {
66293a7515aSWenping Zhang 			int klogo_addr = get_addr_by_type(dev,
66393a7515aSWenping Zhang 							  EINK_LOGO_KERNEL);
66493a7515aSWenping Zhang 
66593a7515aSWenping Zhang 			if (klogo_addr <= 0) {
66693a7515aSWenping Zhang 				printf("get kernel logo buffer failed\n");
66793a7515aSWenping Zhang 				ret = -EIO;
66893a7515aSWenping Zhang 				goto out;
66993a7515aSWenping Zhang 			}
67093a7515aSWenping Zhang 			printf("Transmit kernel logo addr(0x%x) to kernel\n",
67193a7515aSWenping Zhang 			       klogo_addr);
67293a7515aSWenping Zhang 			sprintf(logo_args, "klogo_addr=0x%x", klogo_addr);
67393a7515aSWenping Zhang 			env_update("bootargs", logo_args);
67493a7515aSWenping Zhang 		}
67593a7515aSWenping Zhang 	}
67693a7515aSWenping Zhang 
67793a7515aSWenping Zhang out:
67893a7515aSWenping Zhang 	return ret;
67993a7515aSWenping Zhang }
68093a7515aSWenping Zhang 
68193a7515aSWenping Zhang int rockchip_eink_show_uboot_logo(void)
68293a7515aSWenping Zhang {
68393a7515aSWenping Zhang 	return rockchip_eink_show_logo(EINK_LOGO_UBOOT, EINK_UPDATE_DIFF);
68493a7515aSWenping Zhang }
68593a7515aSWenping Zhang 
68693a7515aSWenping Zhang int rockchip_eink_show_charge_logo(int logo_type)
68793a7515aSWenping Zhang {
68893a7515aSWenping Zhang 	return rockchip_eink_show_logo(logo_type, EINK_UPDATE_DIFF);
68993a7515aSWenping Zhang }
69093a7515aSWenping Zhang 
69193a7515aSWenping Zhang static int rockchip_eink_display_probe(struct udevice *dev)
69293a7515aSWenping Zhang {
69393a7515aSWenping Zhang 	int ret, vcom;
69493a7515aSWenping Zhang 	struct rockchip_eink_display_priv *priv = dev_get_priv(dev);
69593a7515aSWenping Zhang 
69693a7515aSWenping Zhang 	/* Before relocation we don't need to do anything */
69793a7515aSWenping Zhang 	if (!(gd->flags & GD_FLG_RELOC))
69893a7515aSWenping Zhang 		return 0;
69993a7515aSWenping Zhang 
70093a7515aSWenping Zhang 	ret = uclass_get_device_by_phandle(UCLASS_EBC, dev,
70193a7515aSWenping Zhang 					   "ebc_tcon",
70293a7515aSWenping Zhang 					   &priv->ebc_tcon_dev);
703449de1d3SWenping Zhang 	if (ret) {
70493a7515aSWenping Zhang 		dev_err(dev, "Cannot get ebc_tcon: %d\n", ret);
70593a7515aSWenping Zhang 		return ret;
70693a7515aSWenping Zhang 	}
70793a7515aSWenping Zhang 
70893a7515aSWenping Zhang 	ret = uclass_get_device_by_phandle(UCLASS_I2C_GENERIC, dev,
70993a7515aSWenping Zhang 					   "pmic",
71093a7515aSWenping Zhang 					   &priv->ebc_pwr_dev);
711449de1d3SWenping Zhang 	if (ret) {
71293a7515aSWenping Zhang 		dev_err(dev, "Cannot get pmic: %d\n", ret);
71393a7515aSWenping Zhang 		return ret;
71493a7515aSWenping Zhang 	}
71593a7515aSWenping Zhang 
7163f41a942SWeixin Zhou 	ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
7173f41a942SWeixin Zhou 					   "backlight", &priv->backlight);
7183f41a942SWeixin Zhou 	if (ret && ret != -ENOENT) {
7193f41a942SWeixin Zhou 		printf("%s: Cannot get backlight: %d\n", __func__, ret);
7203f41a942SWeixin Zhou 	}
7213f41a942SWeixin Zhou 
72293a7515aSWenping Zhang 	vcom = read_vcom_from_vendor();
72393a7515aSWenping Zhang 	if (vcom <= 0) {
72493a7515aSWenping Zhang 		printf("read vcom from vendor failed, use default vcom\n");
72593a7515aSWenping Zhang 		priv->vcom = VCOM_DEFAULT_VALUE;
72693a7515aSWenping Zhang 	} else {
72793a7515aSWenping Zhang 		priv->vcom = vcom;
72893a7515aSWenping Zhang 	}
72993a7515aSWenping Zhang 
73093a7515aSWenping Zhang 	if (priv->ebc_pwr_dev) {
73193a7515aSWenping Zhang 		struct rk_ebc_pwr_ops *pwr_ops;
73293a7515aSWenping Zhang 
73393a7515aSWenping Zhang 		pwr_ops = ebc_pwr_get_ops(priv->ebc_pwr_dev);
73493a7515aSWenping Zhang 		ret = pwr_ops->vcom_set(priv->ebc_pwr_dev, priv->vcom);
73593a7515aSWenping Zhang 		if (ret) {
73693a7515aSWenping Zhang 			printf("%s, vcom_set failed\n", __func__);
73793a7515aSWenping Zhang 			return -EIO;
73893a7515aSWenping Zhang 		}
73993a7515aSWenping Zhang 	}
74093a7515aSWenping Zhang 
74193a7515aSWenping Zhang 	// read lut to ram, and get lut ops
74293a7515aSWenping Zhang 	ret = read_waveform(dev);
74393a7515aSWenping Zhang 	if (ret < 0) {
74493a7515aSWenping Zhang 		printf("read wavform failed\n");
74593a7515aSWenping Zhang 		return -EIO;
74693a7515aSWenping Zhang 	}
74793a7515aSWenping Zhang 
74893a7515aSWenping Zhang 	eink_dev = dev;
7493f41a942SWeixin Zhou 
75093a7515aSWenping Zhang 	return 0;
75193a7515aSWenping Zhang }
75293a7515aSWenping Zhang 
75393a7515aSWenping Zhang static int rockchip_eink_display_ofdata_to_platdata(struct udevice *dev)
75493a7515aSWenping Zhang {
75593a7515aSWenping Zhang 	fdt_size_t size;
75693a7515aSWenping Zhang 	fdt_addr_t tmp_addr;
75793a7515aSWenping Zhang 	struct device_node *disp_mem;
75893a7515aSWenping Zhang 	struct device_node *waveform_mem;
75993a7515aSWenping Zhang 	struct ebc_panel *plat = dev_get_platdata(dev);
76093a7515aSWenping Zhang 
76193a7515aSWenping Zhang 	plat->width = dev_read_u32_default(dev, "panel,width", 0);
76293a7515aSWenping Zhang 	plat->height = dev_read_u32_default(dev, "panel,height", 0);
7639876686dSWenping Zhang 	plat->vir_width = dev_read_u32_default(dev, "panel,vir_width", plat->width);
7649876686dSWenping Zhang 	plat->vir_height = dev_read_u32_default(dev, "panel,vir_height", plat->height);
76593a7515aSWenping Zhang 	plat->sdck = dev_read_u32_default(dev, "panel,sdck", 0);
76693a7515aSWenping Zhang 	plat->lsl = dev_read_u32_default(dev, "panel,lsl", 0);
76793a7515aSWenping Zhang 	plat->lbl = dev_read_u32_default(dev, "panel,lbl", 0);
76893a7515aSWenping Zhang 	plat->ldl = dev_read_u32_default(dev, "panel,ldl", 0);
76993a7515aSWenping Zhang 	plat->lel = dev_read_u32_default(dev, "panel,lel", 0);
77093a7515aSWenping Zhang 	plat->gdck_sta = dev_read_u32_default(dev, "panel,gdck-sta", 0);
77193a7515aSWenping Zhang 	plat->lgonl = dev_read_u32_default(dev, "panel,lgonl", 0);
77293a7515aSWenping Zhang 	plat->fsl = dev_read_u32_default(dev, "panel,fsl", 0);
77393a7515aSWenping Zhang 	plat->fbl = dev_read_u32_default(dev, "panel,fbl", 0);
77493a7515aSWenping Zhang 	plat->fdl = dev_read_u32_default(dev, "panel,fdl", 0);
77593a7515aSWenping Zhang 	plat->fel = dev_read_u32_default(dev, "panel,fel", 0);
77693a7515aSWenping Zhang 	plat->panel_16bit = dev_read_u32_default(dev, "panel,panel_16bit", 0);
77793a7515aSWenping Zhang 	plat->panel_color = dev_read_u32_default(dev, "panel,panel_color", 0);
77893a7515aSWenping Zhang 	plat->mirror = dev_read_u32_default(dev, "panel,mirror", 0);
77993a7515aSWenping Zhang 	plat->width_mm = dev_read_u32_default(dev, "panel,width-mm", 0);
78093a7515aSWenping Zhang 	plat->height_mm = dev_read_u32_default(dev, "panel,height-mm", 0);
78193a7515aSWenping Zhang 
78293a7515aSWenping Zhang 	disp_mem = of_parse_phandle(ofnode_to_np(dev_ofnode(dev)),
78393a7515aSWenping Zhang 				    "memory-region", 0);
78493a7515aSWenping Zhang 	if (!disp_mem) {
78593a7515aSWenping Zhang 		dev_err(dev, "Cannot get memory-region from dts\n");
78693a7515aSWenping Zhang 		return -ENODEV;
78793a7515aSWenping Zhang 	}
78893a7515aSWenping Zhang 	tmp_addr = ofnode_get_addr_size(np_to_ofnode(disp_mem), "reg", &size);
78993a7515aSWenping Zhang 	if (tmp_addr == FDT_ADDR_T_NONE) {
79093a7515aSWenping Zhang 		printf("get display memory address failed\n");
79193a7515aSWenping Zhang 		return -ENODEV;
79293a7515aSWenping Zhang 	}
79393a7515aSWenping Zhang 
79493a7515aSWenping Zhang 	plat->disp_pbuf = (u64)map_sysmem(tmp_addr, 0);
79593a7515aSWenping Zhang 	plat->disp_pbuf_size = size;
79693a7515aSWenping Zhang 	debug("display mem=0x%x, size=%x\n", plat->disp_pbuf,
79793a7515aSWenping Zhang 	      plat->disp_pbuf_size);
79893a7515aSWenping Zhang 	waveform_mem = of_parse_phandle(ofnode_to_np(dev_ofnode(dev)),
79993a7515aSWenping Zhang 					"waveform-region", 0);
80093a7515aSWenping Zhang 	if (!waveform_mem) {
80193a7515aSWenping Zhang 		printf("Cannot get waveform-region from dts\n");
80293a7515aSWenping Zhang 		return -ENODEV;
80393a7515aSWenping Zhang 	}
80493a7515aSWenping Zhang 	tmp_addr = ofnode_get_addr_size(np_to_ofnode(waveform_mem),
80593a7515aSWenping Zhang 					"reg", &size);
80693a7515aSWenping Zhang 	if (tmp_addr == FDT_ADDR_T_NONE) {
80793a7515aSWenping Zhang 		printf("get waveform memory address failed\n");
80893a7515aSWenping Zhang 		return -ENODEV;
80993a7515aSWenping Zhang 	}
81093a7515aSWenping Zhang 
81193a7515aSWenping Zhang 	plat->lut_pbuf = map_sysmem(tmp_addr, 0);
81293a7515aSWenping Zhang 	plat->lut_pbuf_size = size;
81393a7515aSWenping Zhang 	debug("lut mem=0x%p, size=%x\n", plat->lut_pbuf, plat->lut_pbuf_size);
81493a7515aSWenping Zhang 	return 0;
81593a7515aSWenping Zhang }
81693a7515aSWenping Zhang 
81793a7515aSWenping Zhang static const struct udevice_id rockchip_eink_display_ids[] = {
81893a7515aSWenping Zhang 	{ .compatible = "rockchip,ebc-dev", },
81993a7515aSWenping Zhang 	{}
82093a7515aSWenping Zhang };
82193a7515aSWenping Zhang 
82293a7515aSWenping Zhang U_BOOT_DRIVER(rk_eink_display) = {
82393a7515aSWenping Zhang 	.name = "rockchip_eink_display",
82493a7515aSWenping Zhang 	.id = UCLASS_EINK_DISPLAY,
82593a7515aSWenping Zhang 	.of_match = rockchip_eink_display_ids,
82693a7515aSWenping Zhang 	.ofdata_to_platdata = rockchip_eink_display_ofdata_to_platdata,
82793a7515aSWenping Zhang 	.probe = rockchip_eink_display_probe,
82893a7515aSWenping Zhang 	.priv_auto_alloc_size = sizeof(struct rockchip_eink_display_priv),
82993a7515aSWenping Zhang 	.platdata_auto_alloc_size = sizeof(struct ebc_panel),
83093a7515aSWenping Zhang };
83193a7515aSWenping Zhang 
83293a7515aSWenping Zhang UCLASS_DRIVER(rk_eink) = {
83393a7515aSWenping Zhang 	.id	= UCLASS_EINK_DISPLAY,
83493a7515aSWenping Zhang 	.name	= "rk_eink",
83593a7515aSWenping Zhang };
83693a7515aSWenping Zhang 
837