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>
14f71d410fSChaoyi Chen #include <dm/device-internal.h>
1593a7515aSWenping Zhang #include <dm/of_access.h>
1693a7515aSWenping Zhang #include <dm/uclass.h>
1793a7515aSWenping Zhang #include <dm/uclass-id.h>
1893a7515aSWenping Zhang #include <boot_rkimg.h>
1993a7515aSWenping Zhang #include <rk_eink.h>
203f41a942SWeixin Zhou #include <backlight.h>
21f71d410fSChaoyi Chen #include <power/regulator.h>
22f71d410fSChaoyi Chen #include <thermal.h>
2393a7515aSWenping Zhang #include "rk_ebc.h"
2493a7515aSWenping Zhang #include "epdlut/epd_lut.h"
2593a7515aSWenping Zhang
26f71d410fSChaoyi Chen #if !CONFIG_IS_ENABLED(DM_THERMAL)
thermal_get_temp(struct udevice * dev,int * temp)27f71d410fSChaoyi Chen inline int thermal_get_temp(struct udevice *dev, int *temp)
28f71d410fSChaoyi Chen {
29f71d410fSChaoyi Chen return 0;
30f71d410fSChaoyi Chen }
31f71d410fSChaoyi Chen #endif
32f71d410fSChaoyi Chen
3393a7515aSWenping Zhang #define PART_WAVEFORM "waveform"
3493a7515aSWenping Zhang #define EINK_LOGO_PART_MAGIC "RKEL"
3593a7515aSWenping Zhang #define EINK_LOGO_IMAGE_MAGIC "GR04"
3693a7515aSWenping Zhang /*
3793a7515aSWenping Zhang * grayscale logo partition format:
3893a7515aSWenping Zhang * block0:
3993a7515aSWenping Zhang * struct logo_part_header part_header;
4093a7515aSWenping Zhang * struct grayscale_header logo1_header;
4193a7515aSWenping Zhang * struct grayscale_header logo2_header;
4293a7515aSWenping Zhang * struct grayscale_header logo3_header;
4393a7515aSWenping Zhang * struct grayscale_header logo4_header;
4493a7515aSWenping Zhang * ....
4593a7515aSWenping Zhang *
4693a7515aSWenping Zhang * block 1:
4793a7515aSWenping Zhang * logo1_image
4893a7515aSWenping Zhang *
4993a7515aSWenping Zhang * .....
5093a7515aSWenping Zhang * block m:
5193a7515aSWenping Zhang * logo2_image
5293a7515aSWenping Zhang *
5393a7515aSWenping Zhang * ........
5493a7515aSWenping Zhang * block n:
5593a7515aSWenping Zhang * logo3_image
5693a7515aSWenping Zhang *
5793a7515aSWenping Zhang * ........
5893a7515aSWenping Zhang * block i:
5993a7515aSWenping Zhang * logoi_image
6093a7515aSWenping Zhang */
6193a7515aSWenping Zhang
6293a7515aSWenping Zhang //logo partition Header, 64byte
6393a7515aSWenping Zhang struct logo_part_header {
6493a7515aSWenping Zhang char magic[4]; /* must be "RKEL" */
6593a7515aSWenping Zhang u32 totoal_size;
6693a7515aSWenping Zhang u32 screen_width;
6793a7515aSWenping Zhang u32 screen_height;
6893a7515aSWenping Zhang u32 logo_count;
6993a7515aSWenping Zhang u8 version[4];
7093a7515aSWenping Zhang u32 rsv[10];
7193a7515aSWenping Zhang } __packed;
7293a7515aSWenping Zhang
7393a7515aSWenping Zhang // logo image header,32 byte
7493a7515aSWenping Zhang struct grayscale_header {
7593a7515aSWenping Zhang char magic[4]; /* must be "GR04" */
7693a7515aSWenping Zhang u16 x;
7793a7515aSWenping Zhang u16 y;
7893a7515aSWenping Zhang u16 w;
7993a7515aSWenping Zhang u16 h;
8093a7515aSWenping Zhang u32 logo_type;
8193a7515aSWenping Zhang u32 data_offset; /* image offset in byte */
8293a7515aSWenping Zhang u32 data_size; /* image size in byte */
8393a7515aSWenping Zhang u32 rsv[2];
8493a7515aSWenping Zhang } __packed;
8593a7515aSWenping Zhang
8693a7515aSWenping Zhang /*
8793a7515aSWenping Zhang * The start address of logo image in logo.img must be aligned in 512 bytes,
8893a7515aSWenping Zhang * so the header size must be times of 512 bytes. Here we fix the size to 512
8993a7515aSWenping Zhang * bytes, so the count of logo image can only support up to 14.
9093a7515aSWenping Zhang */
9193a7515aSWenping Zhang struct logo_info {
9293a7515aSWenping Zhang struct logo_part_header part_hdr;
9393a7515aSWenping Zhang struct grayscale_header img_hdr[14];
9493a7515aSWenping Zhang } __packed;
9593a7515aSWenping Zhang
9693a7515aSWenping Zhang struct rockchip_eink_display_priv {
9793a7515aSWenping Zhang struct udevice *dev;
9893a7515aSWenping Zhang struct udevice *ebc_tcon_dev;
9993a7515aSWenping Zhang struct udevice *ebc_pwr_dev;
100f71d410fSChaoyi Chen struct udevice *regulator_dev;
101f71d410fSChaoyi Chen struct udevice *thermal_dev;
10293a7515aSWenping Zhang int vcom;
1033f41a942SWeixin Zhou struct udevice *backlight;
10493a7515aSWenping Zhang };
10593a7515aSWenping Zhang
10693a7515aSWenping Zhang enum {
10793a7515aSWenping Zhang EBC_PWR_DOWN = 0,
10893a7515aSWenping Zhang EBC_PWR_ON = 1,
10993a7515aSWenping Zhang };
11093a7515aSWenping Zhang
11193a7515aSWenping Zhang #define EINK_VCOM_ID 17
11293a7515aSWenping Zhang #define EINK_VCOM_MAX 64
11393a7515aSWenping Zhang #define VCOM_DEFAULT_VALUE 1650
11493a7515aSWenping Zhang
11593a7515aSWenping Zhang static struct logo_info eink_logo_info;
11693a7515aSWenping Zhang static struct udevice *eink_dev;
11793a7515aSWenping Zhang static volatile int last_logo_type = -1;
read_vcom_from_vendor(void)11893a7515aSWenping Zhang static int read_vcom_from_vendor(void)
11993a7515aSWenping Zhang {
12093a7515aSWenping Zhang int ret = 0;
12193a7515aSWenping Zhang char vcom_str[EINK_VCOM_MAX] = {0};
12293a7515aSWenping Zhang char vcom_args[EINK_VCOM_MAX] = {0};
12393a7515aSWenping Zhang
12493a7515aSWenping Zhang /* Read vcom value from vendor storage part */
12593a7515aSWenping Zhang ret = vendor_storage_read(EINK_VCOM_ID, vcom_str, (EINK_VCOM_MAX - 1));
12693a7515aSWenping Zhang if (ret > 0) {
127cd44409eSZorro Liu snprintf(vcom_args, strlen(vcom_str) + 15, "ebc_pmic.vcom=%s", vcom_str);
12893a7515aSWenping Zhang printf("eink update bootargs: %s\n", vcom_args);
12993a7515aSWenping Zhang env_update("bootargs", vcom_args);
13093a7515aSWenping Zhang } else {
13193a7515aSWenping Zhang return ret;
13293a7515aSWenping Zhang }
13393a7515aSWenping Zhang
13493a7515aSWenping Zhang return atoi(vcom_str);
13593a7515aSWenping Zhang }
13693a7515aSWenping Zhang
read_waveform(struct udevice * dev)13793a7515aSWenping Zhang static int read_waveform(struct udevice *dev)
13893a7515aSWenping Zhang {
13993a7515aSWenping Zhang int cnt, start, ret;
14093a7515aSWenping Zhang disk_partition_t part;
14193a7515aSWenping Zhang struct blk_desc *dev_desc;
14293a7515aSWenping Zhang struct ebc_panel *plat = dev_get_platdata(dev);
14393a7515aSWenping Zhang
14493a7515aSWenping Zhang dev_desc = rockchip_get_bootdev();
14593a7515aSWenping Zhang if (!dev_desc) {
14693a7515aSWenping Zhang printf("%s: Could not find device\n", __func__);
14793a7515aSWenping Zhang return -EIO;
14893a7515aSWenping Zhang }
14993a7515aSWenping Zhang if (part_get_info_by_name(dev_desc, PART_WAVEFORM, &part) < 0) {
15093a7515aSWenping Zhang printf("Get waveform partition failed\n");
15193a7515aSWenping Zhang return -ENODEV;
15293a7515aSWenping Zhang }
15393a7515aSWenping Zhang cnt = plat->lut_pbuf_size / RK_BLK_SIZE;
15493a7515aSWenping Zhang start = part.start;
15593a7515aSWenping Zhang ret = blk_dread(dev_desc, start, cnt, (void *)plat->lut_pbuf);
15693a7515aSWenping Zhang if (ret != cnt)
15793a7515aSWenping Zhang printf("Try to read %d blocks failed, only read %d\n",
15893a7515aSWenping Zhang cnt, ret);
15993a7515aSWenping Zhang
16093a7515aSWenping Zhang flush_dcache_range((ulong)plat->lut_pbuf,
16193a7515aSWenping Zhang ALIGN((ulong)plat->lut_pbuf + cnt,
16293a7515aSWenping Zhang CONFIG_SYS_CACHELINE_SIZE));
163*1e54c433SZhibin Huang ret = epd_lut_from_mem_init(plat->lut_pbuf);
16493a7515aSWenping Zhang if (ret < 0) {
16593a7515aSWenping Zhang printf("lut init failed\n");
16693a7515aSWenping Zhang return -EINVAL;
16793a7515aSWenping Zhang }
16893a7515aSWenping Zhang
16993a7515aSWenping Zhang return 0;
17093a7515aSWenping Zhang }
17193a7515aSWenping Zhang
aligned_image_size_4k(struct udevice * dev)17293a7515aSWenping Zhang static u32 aligned_image_size_4k(struct udevice *dev)
17393a7515aSWenping Zhang {
17493a7515aSWenping Zhang struct ebc_panel *plat = dev_get_platdata(dev);
1752512b172SZorro Liu u32 w = plat->width;
1762512b172SZorro Liu u32 h = plat->height;
17793a7515aSWenping Zhang
17893a7515aSWenping Zhang return ALIGN((w * h) >> 1, 4096);
17993a7515aSWenping Zhang }
18093a7515aSWenping Zhang
18193a7515aSWenping Zhang /*
18293a7515aSWenping Zhang * This driver load the grayscale image from flash,
18393a7515aSWenping Zhang * and put it in the reserve memory which define in dts:
18493a7515aSWenping Zhang * display_reserved: framebuffer@10000000 {
18593a7515aSWenping Zhang * reg = <0x0 0x10000000 0x0 0x2000000>;
18693a7515aSWenping Zhang * no-map;
18793a7515aSWenping Zhang * };
18893a7515aSWenping Zhang * Every image logo size must be aligned in 4K, make sure
18993a7515aSWenping Zhang * kernel can use it rightly, the buffer of LOGO image is
19093a7515aSWenping Zhang * put in order of below map:
191e79e2085SWenping Zhang * |---reset logo ---|
19293a7515aSWenping Zhang * |---uboot logo ---|
19393a7515aSWenping Zhang * |---kernel logo ---|
19493a7515aSWenping Zhang * |---charge_0 logo ---|
19593a7515aSWenping Zhang * |---charge_1 logo ---|
19693a7515aSWenping Zhang * |---charge_2 logo ---|
19793a7515aSWenping Zhang * |---charge_3 logo ---|
19893a7515aSWenping Zhang * |---charge_4 logo ---|
19993a7515aSWenping Zhang * |---charge_5 logo ---|
20093a7515aSWenping Zhang * |---battery low logo---|
2014f14f98fSWenping Zhang * |---temp un-mirror buffer--|
20293a7515aSWenping Zhang */
get_addr_by_type(struct udevice * dev,u32 logo_type)20393a7515aSWenping Zhang static int get_addr_by_type(struct udevice *dev, u32 logo_type)
20493a7515aSWenping Zhang {
20593a7515aSWenping Zhang u32 offset, indx, img_size;
20693a7515aSWenping Zhang struct ebc_panel *plat = dev_get_platdata(dev);
20793a7515aSWenping Zhang
20893a7515aSWenping Zhang if (plat->disp_pbuf_size == 0 || !plat->disp_pbuf) {
20993a7515aSWenping Zhang printf("invalid display buffer, please check dts\n");
21093a7515aSWenping Zhang return -EINVAL;
21193a7515aSWenping Zhang }
212e79e2085SWenping Zhang indx = ffs(logo_type);
21393a7515aSWenping Zhang img_size = aligned_image_size_4k(dev);
21493a7515aSWenping Zhang offset = img_size * indx;
21593a7515aSWenping Zhang if (offset + img_size > plat->disp_pbuf_size) {
21693a7515aSWenping Zhang printf("reserve display memory size is not enough\n");
21793a7515aSWenping Zhang return -EINVAL;
21893a7515aSWenping Zhang }
21993a7515aSWenping Zhang
22093a7515aSWenping Zhang switch (logo_type) {
221e79e2085SWenping Zhang case EINK_LOGO_RESET:
22293a7515aSWenping Zhang case EINK_LOGO_UBOOT:
22393a7515aSWenping Zhang case EINK_LOGO_KERNEL:
22493a7515aSWenping Zhang case EINK_LOGO_CHARGING_0:
22593a7515aSWenping Zhang case EINK_LOGO_CHARGING_1:
22693a7515aSWenping Zhang case EINK_LOGO_CHARGING_2:
22793a7515aSWenping Zhang case EINK_LOGO_CHARGING_3:
22893a7515aSWenping Zhang case EINK_LOGO_CHARGING_4:
22993a7515aSWenping Zhang case EINK_LOGO_CHARGING_5:
23093a7515aSWenping Zhang case EINK_LOGO_CHARGING_LOWPOWER:
23177bac292SZorro Liu case EINK_LOGO_POWEROFF:
2324f14f98fSWenping Zhang /*
2334f14f98fSWenping Zhang * The MIRROR_TEMP_BUF is used to save the
2344f14f98fSWenping Zhang * non-mirror image data.
2354f14f98fSWenping Zhang */
2364f14f98fSWenping Zhang case EINK_LOGO_UNMIRROR_TEMP_BUF:
23793a7515aSWenping Zhang return (plat->disp_pbuf + offset);
23893a7515aSWenping Zhang default:
23993a7515aSWenping Zhang printf("invalid logo type[%d]\n", logo_type);
24093a7515aSWenping Zhang }
24193a7515aSWenping Zhang
24293a7515aSWenping Zhang return -EINVAL;
24393a7515aSWenping Zhang }
24493a7515aSWenping Zhang
read_header(struct blk_desc * dev_desc,disk_partition_t * part,struct logo_info * header)24593a7515aSWenping Zhang static int read_header(struct blk_desc *dev_desc,
24693a7515aSWenping Zhang disk_partition_t *part,
24793a7515aSWenping Zhang struct logo_info *header)
24893a7515aSWenping Zhang {
24993a7515aSWenping Zhang int i;
25093a7515aSWenping Zhang struct logo_part_header *part_hdr = &header->part_hdr;
25193a7515aSWenping Zhang
25293a7515aSWenping Zhang if (blk_dread(dev_desc, part->start, 1, header) != 1)
25393a7515aSWenping Zhang return -EIO;
25493a7515aSWenping Zhang
25593a7515aSWenping Zhang if (memcmp(part_hdr->magic, EINK_LOGO_PART_MAGIC, 4)) {
25693a7515aSWenping Zhang printf("partition header is invalid\n");
25793a7515aSWenping Zhang return -EINVAL;
25893a7515aSWenping Zhang }
25993a7515aSWenping Zhang if (part_hdr->logo_count == 0) {
26093a7515aSWenping Zhang printf("the count of logo image is 0\n");
26193a7515aSWenping Zhang return -EINVAL;
26293a7515aSWenping Zhang }
26393a7515aSWenping Zhang for (i = 0; i < part_hdr->logo_count; i++) {
26493a7515aSWenping Zhang struct grayscale_header *img_hdr = &header->img_hdr[i];
26593a7515aSWenping Zhang
26693a7515aSWenping Zhang if (memcmp(img_hdr->magic, EINK_LOGO_IMAGE_MAGIC, 4)) {
26793a7515aSWenping Zhang printf("image[%d] header '%s' is invalid\n", i,
26893a7515aSWenping Zhang img_hdr->magic);
26993a7515aSWenping Zhang return -EINVAL;
27093a7515aSWenping Zhang }
27193a7515aSWenping Zhang }
27293a7515aSWenping Zhang
27393a7515aSWenping Zhang return 0;
27493a7515aSWenping Zhang }
27593a7515aSWenping Zhang
read_grayscale(struct blk_desc * dev_desc,disk_partition_t * part,u32 offset,u32 size,void * buf)27693a7515aSWenping Zhang static int read_grayscale(struct blk_desc *dev_desc,
27793a7515aSWenping Zhang disk_partition_t *part, u32 offset,
27893a7515aSWenping Zhang u32 size, void *buf)
27993a7515aSWenping Zhang {
28093a7515aSWenping Zhang u32 blk_start, blk_offset, blk_count;
28193a7515aSWenping Zhang
28293a7515aSWenping Zhang blk_offset = DIV_ROUND_UP(offset, dev_desc->blksz);
28393a7515aSWenping Zhang blk_start = part->start + blk_offset;
28493a7515aSWenping Zhang blk_count = DIV_ROUND_UP(size, dev_desc->blksz);
28593a7515aSWenping Zhang
28693a7515aSWenping Zhang debug("blk_offset=%d, blk_start=%d,blk_count=%d,out buf=%p\n",
28793a7515aSWenping Zhang blk_offset, blk_start, blk_count, buf);
28893a7515aSWenping Zhang if (blk_dread(dev_desc, blk_start, blk_count, buf) != blk_count) {
28993a7515aSWenping Zhang printf("read grayscale data failed\n");
29093a7515aSWenping Zhang return -EIO;
29193a7515aSWenping Zhang }
29293a7515aSWenping Zhang
29393a7515aSWenping Zhang return 0;
29493a7515aSWenping Zhang }
29593a7515aSWenping Zhang
image_rearrange(u8 * in_buf,u8 * out_buf,u16 w,u16 h)296dfc47936SZorro Liu static int image_rearrange(u8 *in_buf, u8 *out_buf, u16 w, u16 h)
297dfc47936SZorro Liu {
298dfc47936SZorro Liu int i, j;
299dfc47936SZorro Liu u8 in_data;
300dfc47936SZorro Liu u8 *out_buf_tmp;
301dfc47936SZorro Liu
302dfc47936SZorro Liu if (!in_buf || !out_buf) {
303dfc47936SZorro Liu printf("rearrange in buffer or out buffer is NULL\n");
304dfc47936SZorro Liu return -EINVAL;
305dfc47936SZorro Liu }
306dfc47936SZorro Liu
307dfc47936SZorro Liu for (i = 0; i < h; i += 2) {
308dfc47936SZorro Liu out_buf_tmp = out_buf + (i * w / 2);
309dfc47936SZorro Liu for (j = 0; j < w / 2; j++) {
310dfc47936SZorro Liu in_data = *in_buf++;
311dfc47936SZorro Liu *(out_buf_tmp + j * 2) = in_data & 0x0f;
312dfc47936SZorro Liu *(out_buf_tmp + j * 2 + 1) = (in_data >> 4) & 0x0f;
313dfc47936SZorro Liu }
314dfc47936SZorro Liu for (j = 0; j < w / 2; j++) {
315dfc47936SZorro Liu in_data = *in_buf++;
316dfc47936SZorro Liu *(out_buf_tmp + j * 2) |= (in_data << 4) & 0xf0;
317dfc47936SZorro Liu *(out_buf_tmp + j * 2 + 1) |= in_data & 0xf0;
318dfc47936SZorro Liu }
319dfc47936SZorro Liu }
320dfc47936SZorro Liu
321dfc47936SZorro Liu return 0;
322dfc47936SZorro Liu }
323dfc47936SZorro Liu
image_mirror(u8 * in_buf,u8 * out_buf,u16 w,u16 h)3244f14f98fSWenping Zhang static int image_mirror(u8 *in_buf, u8 *out_buf, u16 w, u16 h)
3254f14f98fSWenping Zhang {
3264f14f98fSWenping Zhang int i;
3274f14f98fSWenping Zhang
3284f14f98fSWenping Zhang if (!in_buf || !out_buf) {
3294f14f98fSWenping Zhang printf("mirror in buffer or out buffer is NULL\n");
3304f14f98fSWenping Zhang return -EINVAL;
3314f14f98fSWenping Zhang }
3324f14f98fSWenping Zhang
3334f14f98fSWenping Zhang for (i = 0; i < h; i++) {
3344f14f98fSWenping Zhang u16 column_len = w / 2;
3354f14f98fSWenping Zhang u8 *column_in = in_buf + i * column_len;
3364f14f98fSWenping Zhang u8 *column_out = out_buf + (h - i - 1) * column_len;
3374f14f98fSWenping Zhang
3384f14f98fSWenping Zhang memcpy(column_out, column_in, column_len);
3394f14f98fSWenping Zhang }
3404f14f98fSWenping Zhang
3414f14f98fSWenping Zhang return 0;
3424f14f98fSWenping Zhang }
3434f14f98fSWenping Zhang
34493a7515aSWenping Zhang /*
34593a7515aSWenping Zhang * The eink kernel driver need last frame to do part refresh,
34693a7515aSWenping Zhang * so we need to transfer two images to kernel, which is kernel
34793a7515aSWenping Zhang * logo and the logo displayed in uboot.
34893a7515aSWenping Zhang *
34993a7515aSWenping Zhang * this function use logo type bitmap to indicate several logo.
35093a7515aSWenping Zhang * u32 needed_logo: we only load needed logo image into ram, such as
35193a7515aSWenping Zhang * uboot logo + kernel logo or charger logo + kernel
35293a7515aSWenping Zhang * logo
353074c7ac4SWenping Zhang * u32 *loaded_logo: because the needed logo may not exist in logo.img,
354074c7ac4SWenping Zhang * store the really loaded logo in para loaded_logo.
35593a7515aSWenping Zhang */
read_needed_logo_from_partition(struct udevice * dev,u32 needed_logo,u32 * loaded_logo)35693a7515aSWenping Zhang static int read_needed_logo_from_partition(struct udevice *dev,
35793a7515aSWenping Zhang u32 needed_logo,
35893a7515aSWenping Zhang u32 *loaded_logo)
35993a7515aSWenping Zhang {
36093a7515aSWenping Zhang int ret, i;
36193a7515aSWenping Zhang disk_partition_t part;
36293a7515aSWenping Zhang struct blk_desc *dev_desc;
36393a7515aSWenping Zhang struct logo_info *hdr = &eink_logo_info;
36493a7515aSWenping Zhang struct logo_part_header *part_hdr = &hdr->part_hdr;
36593a7515aSWenping Zhang struct ebc_panel *panel = dev_get_platdata(dev);
36639939100SWenping Zhang u32 logo = needed_logo & (~(*loaded_logo));
36793a7515aSWenping Zhang
36839939100SWenping Zhang if (!logo) {
369074c7ac4SWenping Zhang printf("logo[0x%x] is already loaded, just return!\n",
370074c7ac4SWenping Zhang needed_logo);
371074c7ac4SWenping Zhang return 0;
372074c7ac4SWenping Zhang }
37393a7515aSWenping Zhang dev_desc = rockchip_get_bootdev();
37493a7515aSWenping Zhang if (!dev_desc) {
37593a7515aSWenping Zhang printf("%s: Could not find device\n", __func__);
37693a7515aSWenping Zhang return -EIO;
37793a7515aSWenping Zhang }
37893a7515aSWenping Zhang
37993a7515aSWenping Zhang if (part_get_info_by_name(dev_desc, PART_LOGO, &part) < 0)
38093a7515aSWenping Zhang return -ENODEV;
38193a7515aSWenping Zhang
38293a7515aSWenping Zhang ret = read_header(dev_desc, &part, hdr);
38393a7515aSWenping Zhang if (ret < 0) {
38493a7515aSWenping Zhang printf("eink logo read header failed,ret = %d\n", ret);
38593a7515aSWenping Zhang return -EINVAL;
38693a7515aSWenping Zhang }
3872512b172SZorro Liu if (part_hdr->screen_width != panel->width ||
3882512b172SZorro Liu part_hdr->screen_height != panel->height){
38993a7515aSWenping Zhang printf("logo size(%dx%d) is not same as screen size(%dx%d)\n",
39093a7515aSWenping Zhang part_hdr->screen_width, part_hdr->screen_height,
3912512b172SZorro Liu panel->width, panel->height);
39293a7515aSWenping Zhang return -EINVAL;
39393a7515aSWenping Zhang }
39493a7515aSWenping Zhang
39593a7515aSWenping Zhang for (i = 0; i < part_hdr->logo_count; i++) {
39693a7515aSWenping Zhang struct grayscale_header *img_hdr = &hdr->img_hdr[i];
39793a7515aSWenping Zhang int pic_buf;
39893a7515aSWenping Zhang u32 offset = img_hdr->data_offset;
39993a7515aSWenping Zhang u32 size = img_hdr->data_size;
40093a7515aSWenping Zhang u32 logo_type = img_hdr->logo_type;
40193a7515aSWenping Zhang
40293a7515aSWenping Zhang debug("offset=0x%x, size=%d,logo_type=%d,w=%d,h=%d\n",
40393a7515aSWenping Zhang offset, size, logo_type, img_hdr->w, img_hdr->h);
40493a7515aSWenping Zhang
40539939100SWenping Zhang if (logo & logo_type) {
40693a7515aSWenping Zhang pic_buf = get_addr_by_type(dev, logo_type);
40793a7515aSWenping Zhang
40893a7515aSWenping Zhang if (pic_buf <= 0) {
40993a7515aSWenping Zhang printf("Get buffer failed for image %d\n",
41093a7515aSWenping Zhang img_hdr->logo_type);
41193a7515aSWenping Zhang return -EIO;
41293a7515aSWenping Zhang }
41393a7515aSWenping Zhang if (!IS_ALIGNED((ulong)pic_buf,
41493a7515aSWenping Zhang ARCH_DMA_MINALIGN)) {
41593a7515aSWenping Zhang printf("disp buffer is not dma aligned\n");
41693a7515aSWenping Zhang return -EINVAL;
41793a7515aSWenping Zhang }
4184f14f98fSWenping Zhang /*
4194f14f98fSWenping Zhang * kernel logo is transmitted to kernel to display, and
4204f14f98fSWenping Zhang * kernel will do the mirror operation, so skip kernel
4214f14f98fSWenping Zhang * logo here.
4224f14f98fSWenping Zhang */
42339939100SWenping Zhang if (panel->mirror && logo_type != EINK_LOGO_KERNEL) {
4242512b172SZorro Liu u32 w = panel->width;
4252512b172SZorro Liu u32 h = panel->height;
4264f14f98fSWenping Zhang u32 mirror_buf = 0;
4274f14f98fSWenping Zhang
4284f14f98fSWenping Zhang mirror_buf = get_addr_by_type(dev,
4294f14f98fSWenping Zhang EINK_LOGO_UNMIRROR_TEMP_BUF);
4304f14f98fSWenping Zhang if (mirror_buf <= 0) {
4314f14f98fSWenping Zhang printf("get mirror buffer failed\n");
4324f14f98fSWenping Zhang return -EIO;
4334f14f98fSWenping Zhang }
4344f14f98fSWenping Zhang read_grayscale(dev_desc, &part, offset, size,
4354f14f98fSWenping Zhang (void *)((ulong)mirror_buf));
4364f14f98fSWenping Zhang image_mirror((u8 *)((ulong)mirror_buf),
4374f14f98fSWenping Zhang (u8 *)((ulong)pic_buf), w, h);
438dfc47936SZorro Liu } else if (panel->rearrange && logo_type != EINK_LOGO_KERNEL) {
4392512b172SZorro Liu u32 w = panel->width;
4402512b172SZorro Liu u32 h = panel->height;
441dfc47936SZorro Liu u32 rearrange_buf = 0;
442dfc47936SZorro Liu
443dfc47936SZorro Liu rearrange_buf = get_addr_by_type(dev,
444dfc47936SZorro Liu EINK_LOGO_UNMIRROR_TEMP_BUF);
445dfc47936SZorro Liu if (rearrange_buf <= 0) {
446dfc47936SZorro Liu printf("get mirror buffer failed\n");
447dfc47936SZorro Liu return -EIO;
448dfc47936SZorro Liu }
449dfc47936SZorro Liu read_grayscale(dev_desc, &part, offset, size,
450dfc47936SZorro Liu (void *)((ulong)rearrange_buf));
451dfc47936SZorro Liu image_rearrange((u8 *)((ulong)rearrange_buf),
452dfc47936SZorro Liu (u8 *)((ulong)pic_buf), w, h);
4534f14f98fSWenping Zhang } else {
45493a7515aSWenping Zhang read_grayscale(dev_desc, &part, offset, size,
45593a7515aSWenping Zhang (void *)((ulong)pic_buf));
4564f14f98fSWenping Zhang }
45793a7515aSWenping Zhang flush_dcache_range((ulong)pic_buf,
45893a7515aSWenping Zhang ALIGN((ulong)pic_buf + size,
45993a7515aSWenping Zhang CONFIG_SYS_CACHELINE_SIZE));
46093a7515aSWenping Zhang *loaded_logo |= logo_type;
46139939100SWenping Zhang
46239939100SWenping Zhang logo &= ~logo_type;
46339939100SWenping Zhang if (!logo)
46439939100SWenping Zhang break;
46593a7515aSWenping Zhang }
46693a7515aSWenping Zhang }
46793a7515aSWenping Zhang
46893a7515aSWenping Zhang return 0;
46993a7515aSWenping Zhang }
47093a7515aSWenping Zhang
ebc_power_set(struct udevice * dev,int is_on)47193a7515aSWenping Zhang static int ebc_power_set(struct udevice *dev, int is_on)
47293a7515aSWenping Zhang {
47393a7515aSWenping Zhang int ret;
47493a7515aSWenping Zhang struct rockchip_eink_display_priv *priv = dev_get_priv(dev);
47593a7515aSWenping Zhang struct ebc_panel *panel = dev_get_platdata(dev);
47693a7515aSWenping Zhang struct udevice *ebc_tcon_dev = priv->ebc_tcon_dev;
47793a7515aSWenping Zhang struct rk_ebc_tcon_ops *ebc_tcon_ops = ebc_tcon_get_ops(ebc_tcon_dev);
47893a7515aSWenping Zhang struct udevice *ebc_pwr_dev = priv->ebc_pwr_dev;
479f71d410fSChaoyi Chen struct rk_ebc_pwr_ops *pwr_ops = NULL;
480f71d410fSChaoyi Chen
481f71d410fSChaoyi Chen if (ebc_pwr_dev)
482f71d410fSChaoyi Chen pwr_ops = ebc_pwr_get_ops(ebc_pwr_dev);
48393a7515aSWenping Zhang
48493a7515aSWenping Zhang if (is_on) {
485f71d410fSChaoyi Chen if (pwr_ops)
48693a7515aSWenping Zhang ret = pwr_ops->power_on(ebc_pwr_dev);
487f71d410fSChaoyi Chen else
488f71d410fSChaoyi Chen ret = regulator_set_enable(priv->regulator_dev, true);
48993a7515aSWenping Zhang if (ret) {
49093a7515aSWenping Zhang printf("%s, power on failed\n", __func__);
49193a7515aSWenping Zhang return -1;
49293a7515aSWenping Zhang }
493e425562eSZorro Liu ret = ebc_tcon_ops->enable(ebc_tcon_dev, panel);
49493a7515aSWenping Zhang if (ret) {
495e425562eSZorro Liu printf("%s, ebc tcon enabled failed\n", __func__);
49693a7515aSWenping Zhang return -1;
49793a7515aSWenping Zhang }
498e425562eSZorro Liu } else {
49993a7515aSWenping Zhang ret = ebc_tcon_ops->disable(ebc_tcon_dev);
50093a7515aSWenping Zhang if (ret) {
50193a7515aSWenping Zhang printf("%s, ebc tcon disable failed\n", __func__);
50293a7515aSWenping Zhang return -1;
50393a7515aSWenping Zhang }
504f71d410fSChaoyi Chen
505f71d410fSChaoyi Chen if (pwr_ops)
506e425562eSZorro Liu ret = pwr_ops->power_down(ebc_pwr_dev);
507f71d410fSChaoyi Chen else
508f71d410fSChaoyi Chen ret = regulator_set_enable(priv->regulator_dev, false);
509e425562eSZorro Liu if (ret) {
510e425562eSZorro Liu printf("%s, power_down failed\n", __func__);
511e425562eSZorro Liu return -1;
512e425562eSZorro Liu }
51393a7515aSWenping Zhang }
51493a7515aSWenping Zhang return 0;
51593a7515aSWenping Zhang }
51693a7515aSWenping Zhang
eink_display(struct udevice * dev,u32 pre_img_buf,u32 cur_img_buf,u32 lut_type,int update_mode)51793a7515aSWenping Zhang static int eink_display(struct udevice *dev, u32 pre_img_buf,
51893a7515aSWenping Zhang u32 cur_img_buf, u32 lut_type, int update_mode)
51993a7515aSWenping Zhang {
520f71d410fSChaoyi Chen int temperature;
521f71d410fSChaoyi Chen u32 frame_num;
52293a7515aSWenping Zhang struct rockchip_eink_display_priv *priv = dev_get_priv(dev);
52393a7515aSWenping Zhang struct ebc_panel *plat = dev_get_platdata(dev);
52493a7515aSWenping Zhang struct udevice *ebc_pwr_dev = priv->ebc_pwr_dev;
525f71d410fSChaoyi Chen struct rk_ebc_pwr_ops *pwr_ops = NULL;
52693a7515aSWenping Zhang struct udevice *ebc_tcon_dev = priv->ebc_tcon_dev;
52793a7515aSWenping Zhang struct rk_ebc_tcon_ops *ebc_tcon_ops = ebc_tcon_get_ops(ebc_tcon_dev);
52893a7515aSWenping Zhang
529f71d410fSChaoyi Chen if (ebc_pwr_dev)
530f71d410fSChaoyi Chen pwr_ops = ebc_pwr_get_ops(ebc_pwr_dev);
531f71d410fSChaoyi Chen
532f71d410fSChaoyi Chen if (pwr_ops)
533f71d410fSChaoyi Chen pwr_ops->temp_get(ebc_pwr_dev, (u32 *)(&temperature));
534f71d410fSChaoyi Chen else
535f71d410fSChaoyi Chen thermal_get_temp(priv->thermal_dev, &temperature);
53693a7515aSWenping Zhang if (temperature <= 0 || temperature > 50) {
53793a7515aSWenping Zhang printf("temperature = %d, out of range0~50 ,use 25\n",
53893a7515aSWenping Zhang temperature);
53993a7515aSWenping Zhang temperature = 25;
54093a7515aSWenping Zhang }
54193a7515aSWenping Zhang
542*1e54c433SZhibin Huang if(!plat->lut_data.wf_table[0])
543*1e54c433SZhibin Huang plat->lut_data.wf_table[0] = kzalloc(MAXFRAME * 32 * 32, GFP_KERNEL);
544*1e54c433SZhibin Huang epd_lut_get(&plat->lut_data, lut_type, temperature, WF_4BIT, 0);
545*1e54c433SZhibin Huang kfree(plat->lut_data.wf_table[0]);
546*1e54c433SZhibin Huang plat->lut_data.wf_table[0] = NULL;
547*1e54c433SZhibin Huang
548*1e54c433SZhibin Huang frame_num = plat->lut_data.frame_num & 0xff;
549*1e54c433SZhibin Huang printk("lut_type=%d, frame num=%d, temp=%d\n", lut_type,
55093a7515aSWenping Zhang frame_num, temperature);
55193a7515aSWenping Zhang
55239939100SWenping Zhang ebc_tcon_ops->wait_for_last_frame_complete(ebc_tcon_dev);
55393a7515aSWenping Zhang ebc_tcon_ops->lut_data_set(ebc_tcon_dev, plat->lut_data.data,
55493a7515aSWenping Zhang frame_num, 0);
55593a7515aSWenping Zhang ebc_tcon_ops->dsp_mode_set(ebc_tcon_dev, update_mode,
55693a7515aSWenping Zhang LUT_MODE, !THREE_WIN_MODE, !EINK_MODE);
55793a7515aSWenping Zhang ebc_tcon_ops->image_addr_set(ebc_tcon_dev, pre_img_buf, cur_img_buf);
55893a7515aSWenping Zhang ebc_tcon_ops->frame_start(ebc_tcon_dev, frame_num);
55993a7515aSWenping Zhang return 0;
56093a7515aSWenping Zhang }
56193a7515aSWenping Zhang
rk_eink_display_init(void)56293a7515aSWenping Zhang static int rk_eink_display_init(void)
56393a7515aSWenping Zhang {
56493a7515aSWenping Zhang int ret;
56593a7515aSWenping Zhang struct uclass *uc;
56693a7515aSWenping Zhang struct udevice *dev;
56793a7515aSWenping Zhang
56893a7515aSWenping Zhang if (eink_dev) {
56993a7515aSWenping Zhang printf("ebc-dev is already initialized!\n");
57093a7515aSWenping Zhang return 0;
57193a7515aSWenping Zhang }
57293a7515aSWenping Zhang
57393a7515aSWenping Zhang ret = uclass_get(UCLASS_EINK_DISPLAY, &uc);
57493a7515aSWenping Zhang if (ret) {
57593a7515aSWenping Zhang printf("can't find uclass eink\n");
57693a7515aSWenping Zhang return -ENODEV;
57793a7515aSWenping Zhang }
57893a7515aSWenping Zhang for (uclass_first_device(UCLASS_EINK_DISPLAY, &dev);
57993a7515aSWenping Zhang dev; uclass_next_device(&dev))
58093a7515aSWenping Zhang ;
58193a7515aSWenping Zhang
58293a7515aSWenping Zhang if (eink_dev) {
58393a7515aSWenping Zhang printf("ebc-dev is probed success!\n");
58493a7515aSWenping Zhang return 0;
58593a7515aSWenping Zhang }
58693a7515aSWenping Zhang printf("Can't find ebc-dev\n");
58793a7515aSWenping Zhang return -ENODEV;
58893a7515aSWenping Zhang }
58993a7515aSWenping Zhang
59093a7515aSWenping Zhang /*
59193a7515aSWenping Zhang * Eink display need current and previous image buffer, We assume
59293a7515aSWenping Zhang * every type of logo has only one image, so just tell this function
59393a7515aSWenping Zhang * last logo type and current logo type, it will find the right images.
59493a7515aSWenping Zhang * last_logo_type: -1 means it's first displaying.
59593a7515aSWenping Zhang */
rockchip_eink_show_logo(int cur_logo_type,int update_mode)59693a7515aSWenping Zhang static int rockchip_eink_show_logo(int cur_logo_type, int update_mode)
59793a7515aSWenping Zhang {
59893a7515aSWenping Zhang int ret = 0;
59993a7515aSWenping Zhang u32 logo_addr;
60093a7515aSWenping Zhang u32 last_logo_addr;
60193a7515aSWenping Zhang struct ebc_panel *plat;
60293a7515aSWenping Zhang struct udevice *dev;
60339939100SWenping Zhang static u32 loaded_logo = 0;
6043f41a942SWeixin Zhou struct rockchip_eink_display_priv *priv;
60593a7515aSWenping Zhang
60693a7515aSWenping Zhang if (!eink_dev) {
607449de1d3SWenping Zhang static bool first_init = true;
608449de1d3SWenping Zhang
609449de1d3SWenping Zhang if (first_init) {
610449de1d3SWenping Zhang first_init = false;
61193a7515aSWenping Zhang ret = rk_eink_display_init();
61293a7515aSWenping Zhang if (ret) {
613449de1d3SWenping Zhang printf("Get ebc dev failed, check dts\n");
614449de1d3SWenping Zhang return -ENODEV;
615449de1d3SWenping Zhang }
616449de1d3SWenping Zhang } else {
61793a7515aSWenping Zhang return -ENODEV;
61893a7515aSWenping Zhang }
61993a7515aSWenping Zhang }
62093a7515aSWenping Zhang dev = eink_dev;
62193a7515aSWenping Zhang
62293a7515aSWenping Zhang /*Don't need to update display*/
62393a7515aSWenping Zhang if (last_logo_type == cur_logo_type) {
62493a7515aSWenping Zhang debug("Same as last picture, Don't need to display\n");
62593a7515aSWenping Zhang return 0;
62693a7515aSWenping Zhang }
62793a7515aSWenping Zhang
62893a7515aSWenping Zhang plat = dev_get_platdata(dev);
6293f41a942SWeixin Zhou priv = dev_get_priv(dev);
63093a7515aSWenping Zhang
63139939100SWenping Zhang /*
63239939100SWenping Zhang * The last_logo_type is -1 means it's first displaying
63339939100SWenping Zhang */
63439939100SWenping Zhang if (last_logo_type == -1) {
63593a7515aSWenping Zhang ret = ebc_power_set(dev, EBC_PWR_ON);
63693a7515aSWenping Zhang if (ret) {
63793a7515aSWenping Zhang printf("Eink power on failed\n");
63893a7515aSWenping Zhang return -1;
63993a7515aSWenping Zhang }
64039939100SWenping Zhang
6412512b172SZorro Liu int size = (plat->width * plat->height) >> 1;
64293a7515aSWenping Zhang
643e79e2085SWenping Zhang logo_addr = get_addr_by_type(dev, EINK_LOGO_RESET);
644e79e2085SWenping Zhang memset((u32 *)(u64)logo_addr, 0xff, size);
64523f3ce8dSWenping Zhang flush_dcache_range((ulong)logo_addr,
64623f3ce8dSWenping Zhang ALIGN((ulong)logo_addr + size,
64723f3ce8dSWenping Zhang CONFIG_SYS_CACHELINE_SIZE));
648e79e2085SWenping Zhang eink_display(dev, logo_addr, logo_addr,
64939939100SWenping Zhang WF_TYPE_RESET, EINK_LOGO_RESET);
65093a7515aSWenping Zhang last_logo_type = 0;
651e79e2085SWenping Zhang last_logo_addr = logo_addr;
65293a7515aSWenping Zhang } else {
65393a7515aSWenping Zhang last_logo_addr = get_addr_by_type(dev, last_logo_type);
65493a7515aSWenping Zhang if (last_logo_addr < 0) {
65593a7515aSWenping Zhang printf("Invalid last logo addr, exit!\n");
65693a7515aSWenping Zhang goto out;
65793a7515aSWenping Zhang }
65877bac292SZorro Liu }
65977bac292SZorro Liu ret = read_needed_logo_from_partition(dev, cur_logo_type,
66077bac292SZorro Liu &loaded_logo);
66177bac292SZorro Liu if (ret || !(loaded_logo & cur_logo_type)) {
66277bac292SZorro Liu printf("read logo[0x%x] failed, loaded_logo=0x%x\n",
66377bac292SZorro Liu cur_logo_type, loaded_logo);
66477bac292SZorro Liu ret = -EIO;
66577bac292SZorro Liu goto out;
66677bac292SZorro Liu }
66777bac292SZorro Liu logo_addr = get_addr_by_type(dev, cur_logo_type);
66877bac292SZorro Liu debug("logo_addr=%x, logo_type=%d\n", logo_addr, cur_logo_type);
66977bac292SZorro Liu if (logo_addr <= 0) {
67077bac292SZorro Liu printf("get logo buffer failed\n");
67177bac292SZorro Liu ret = -EIO;
67277bac292SZorro Liu goto out;
67377bac292SZorro Liu }
67439939100SWenping Zhang
67577bac292SZorro Liu eink_display(dev, last_logo_addr, logo_addr, WF_TYPE_GC16, update_mode);
67677bac292SZorro Liu
67777bac292SZorro Liu if (priv->backlight)
67877bac292SZorro Liu backlight_enable(priv->backlight);
67977bac292SZorro Liu
68077bac292SZorro Liu last_logo_type = cur_logo_type;
68177bac292SZorro Liu
68277bac292SZorro Liu if (cur_logo_type == EINK_LOGO_POWEROFF) {
68339939100SWenping Zhang struct udevice *ebc_tcon_dev = priv->ebc_tcon_dev;
68439939100SWenping Zhang struct rk_ebc_tcon_ops *ebc_tcon_ops;
68539939100SWenping Zhang
68693a7515aSWenping Zhang last_logo_type = -1;
68739939100SWenping Zhang /*
68839939100SWenping Zhang * For normal logo display, waiting for the last frame
68939939100SWenping Zhang * completion before start a new frame, except one
69039939100SWenping Zhang * situation which charging logo display finished,
69139939100SWenping Zhang * because device will rebooting or shutdown after
69239939100SWenping Zhang * charging logo is competed.
69339939100SWenping Zhang *
69439939100SWenping Zhang * We should take care of the power sequence,
69539939100SWenping Zhang * because ebc can't power off if last frame
69639939100SWenping Zhang * data is still sending, so keep the ebc power
69739939100SWenping Zhang * during u-boot phase and shutdown the
69839939100SWenping Zhang * power only if uboot charging is finished.
69939939100SWenping Zhang */
70039939100SWenping Zhang ebc_tcon_ops = ebc_tcon_get_ops(ebc_tcon_dev);
70139939100SWenping Zhang ebc_tcon_ops->wait_for_last_frame_complete(ebc_tcon_dev);
70239939100SWenping Zhang debug("charging logo displaying is complete\n");
70339939100SWenping Zhang /*
70439939100SWenping Zhang *shutdown ebc after charging logo display is complete
70539939100SWenping Zhang */
70639939100SWenping Zhang ret = ebc_power_set(dev, EBC_PWR_DOWN);
70739939100SWenping Zhang if (ret)
70839939100SWenping Zhang printf("Eink power down failed\n");
70993a7515aSWenping Zhang goto out;
71093a7515aSWenping Zhang }
71193a7515aSWenping Zhang
71293a7515aSWenping Zhang /*
71393a7515aSWenping Zhang * System will boot up to kernel only when the
71493a7515aSWenping Zhang * logo is uboot logo
71593a7515aSWenping Zhang */
71693a7515aSWenping Zhang if (cur_logo_type == EINK_LOGO_UBOOT) {
71793a7515aSWenping Zhang char logo_args[64] = {0};
7184f14f98fSWenping Zhang u32 uboot_logo_buf;
71993a7515aSWenping Zhang
720dfc47936SZorro Liu if (plat->mirror || plat->rearrange)
7214f14f98fSWenping Zhang uboot_logo_buf = get_addr_by_type(dev,
7224f14f98fSWenping Zhang EINK_LOGO_UNMIRROR_TEMP_BUF);
7234f14f98fSWenping Zhang else
7244f14f98fSWenping Zhang uboot_logo_buf = logo_addr;
7254f14f98fSWenping Zhang printf("Transmit uboot logo addr(0x%x) to kernel\n",
7264f14f98fSWenping Zhang uboot_logo_buf);
7274f14f98fSWenping Zhang sprintf(logo_args, "ulogo_addr=0x%x", uboot_logo_buf);
72893a7515aSWenping Zhang env_update("bootargs", logo_args);
72993a7515aSWenping Zhang ret = read_needed_logo_from_partition(dev, EINK_LOGO_KERNEL,
730074c7ac4SWenping Zhang &loaded_logo);
731074c7ac4SWenping Zhang if (ret || !(loaded_logo & EINK_LOGO_KERNEL)) {
73293a7515aSWenping Zhang printf("No invalid kernel logo in logo.img\n");
73393a7515aSWenping Zhang } else {
73493a7515aSWenping Zhang int klogo_addr = get_addr_by_type(dev,
73593a7515aSWenping Zhang EINK_LOGO_KERNEL);
73693a7515aSWenping Zhang
73793a7515aSWenping Zhang if (klogo_addr <= 0) {
73893a7515aSWenping Zhang printf("get kernel logo buffer failed\n");
73993a7515aSWenping Zhang ret = -EIO;
74093a7515aSWenping Zhang goto out;
74193a7515aSWenping Zhang }
74293a7515aSWenping Zhang printf("Transmit kernel logo addr(0x%x) to kernel\n",
74393a7515aSWenping Zhang klogo_addr);
74493a7515aSWenping Zhang sprintf(logo_args, "klogo_addr=0x%x", klogo_addr);
74593a7515aSWenping Zhang env_update("bootargs", logo_args);
74693a7515aSWenping Zhang }
74793a7515aSWenping Zhang }
74893a7515aSWenping Zhang
74993a7515aSWenping Zhang out:
75093a7515aSWenping Zhang return ret;
75193a7515aSWenping Zhang }
75293a7515aSWenping Zhang
rockchip_eink_show_uboot_logo(void)75393a7515aSWenping Zhang int rockchip_eink_show_uboot_logo(void)
75493a7515aSWenping Zhang {
75593a7515aSWenping Zhang return rockchip_eink_show_logo(EINK_LOGO_UBOOT, EINK_UPDATE_DIFF);
75693a7515aSWenping Zhang }
75793a7515aSWenping Zhang
rockchip_eink_show_charge_logo(int logo_type)75893a7515aSWenping Zhang int rockchip_eink_show_charge_logo(int logo_type)
75993a7515aSWenping Zhang {
76093a7515aSWenping Zhang return rockchip_eink_show_logo(logo_type, EINK_UPDATE_DIFF);
76193a7515aSWenping Zhang }
76293a7515aSWenping Zhang
rockchip_eink_display_probe(struct udevice * dev)76393a7515aSWenping Zhang static int rockchip_eink_display_probe(struct udevice *dev)
76493a7515aSWenping Zhang {
765f71d410fSChaoyi Chen struct rockchip_eink_display_priv *priv = dev_get_priv(dev);
766976b84eaSChaoyi Chen struct dm_regulator_uclass_platdata *uc_pdata;
767f71d410fSChaoyi Chen struct rk_ebc_pwr_ops *pwr_ops = NULL;
768f71d410fSChaoyi Chen struct udevice *child, *pmic_dev;
769f71d410fSChaoyi Chen int ret, vcom, size, i, uclass_id;
770f71d410fSChaoyi Chen bool find_pmic = false;
77181ad4508SZorro Liu const fdt32_t *list;
77281ad4508SZorro Liu uint32_t phandle;
77393a7515aSWenping Zhang
77493a7515aSWenping Zhang /* Before relocation we don't need to do anything */
77593a7515aSWenping Zhang if (!(gd->flags & GD_FLG_RELOC))
77693a7515aSWenping Zhang return 0;
77793a7515aSWenping Zhang
77893a7515aSWenping Zhang ret = uclass_get_device_by_phandle(UCLASS_EBC, dev,
77993a7515aSWenping Zhang "ebc_tcon",
78093a7515aSWenping Zhang &priv->ebc_tcon_dev);
781449de1d3SWenping Zhang if (ret) {
78293a7515aSWenping Zhang dev_err(dev, "Cannot get ebc_tcon: %d\n", ret);
78393a7515aSWenping Zhang return ret;
78493a7515aSWenping Zhang }
78593a7515aSWenping Zhang
78681ad4508SZorro Liu list = dev_read_prop(dev, "pmic", &size);
78781ad4508SZorro Liu if (!list) {
78881ad4508SZorro Liu dev_err(dev, "Cannot get pmic prop\n");
78981ad4508SZorro Liu return -EINVAL;
79081ad4508SZorro Liu }
79181ad4508SZorro Liu
79281ad4508SZorro Liu size /= sizeof(*list);
79381ad4508SZorro Liu for (i = 0; i < size; i++) {
79481ad4508SZorro Liu phandle = fdt32_to_cpu(*list++);
795f71d410fSChaoyi Chen /* TODO: migrate to pmic */
79681ad4508SZorro Liu ret = uclass_get_device_by_phandle_id(UCLASS_I2C_GENERIC,
79781ad4508SZorro Liu phandle,
79893a7515aSWenping Zhang &priv->ebc_pwr_dev);
79981ad4508SZorro Liu if (!ret) {
800f71d410fSChaoyi Chen find_pmic = true;
801f71d410fSChaoyi Chen break;
802f71d410fSChaoyi Chen }
803f71d410fSChaoyi Chen
804f71d410fSChaoyi Chen ret = uclass_get_device_by_phandle_id(UCLASS_PMIC, phandle, &pmic_dev);
805f71d410fSChaoyi Chen if (!ret) {
806f71d410fSChaoyi Chen for (device_find_first_child(pmic_dev, &child); child;
807f71d410fSChaoyi Chen device_find_next_child(&child)) {
808f71d410fSChaoyi Chen uclass_id = device_get_uclass_id(child);
809f71d410fSChaoyi Chen ret = device_probe(child);
810f71d410fSChaoyi Chen if (ret) {
811f71d410fSChaoyi Chen dev_warn(dev, "Failed to probe pmic %s\n", child->name);
812f71d410fSChaoyi Chen continue;
813f71d410fSChaoyi Chen }
814f71d410fSChaoyi Chen
815976b84eaSChaoyi Chen if (uclass_id == UCLASS_REGULATOR) {
816976b84eaSChaoyi Chen uc_pdata = dev_get_uclass_platdata(child);
817976b84eaSChaoyi Chen if (!strcmp(uc_pdata->name, "vcom"))
818f71d410fSChaoyi Chen priv->regulator_dev = child;
819976b84eaSChaoyi Chen } else if (uclass_id == UCLASS_THERMAL) {
820f71d410fSChaoyi Chen priv->thermal_dev = child;
821f71d410fSChaoyi Chen }
822976b84eaSChaoyi Chen }
823f71d410fSChaoyi Chen
824f71d410fSChaoyi Chen find_pmic = true;
82581ad4508SZorro Liu break;
82681ad4508SZorro Liu }
82781ad4508SZorro Liu }
828f71d410fSChaoyi Chen
829f71d410fSChaoyi Chen if (!find_pmic) {
83093a7515aSWenping Zhang dev_err(dev, "Cannot get pmic: %d\n", ret);
83193a7515aSWenping Zhang return ret;
83293a7515aSWenping Zhang }
83393a7515aSWenping Zhang
8343f41a942SWeixin Zhou ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
8353f41a942SWeixin Zhou "backlight", &priv->backlight);
8363f41a942SWeixin Zhou if (ret && ret != -ENOENT) {
8373f41a942SWeixin Zhou printf("%s: Cannot get backlight: %d\n", __func__, ret);
8383f41a942SWeixin Zhou }
8393f41a942SWeixin Zhou
84093a7515aSWenping Zhang vcom = read_vcom_from_vendor();
84193a7515aSWenping Zhang if (vcom <= 0) {
84293a7515aSWenping Zhang printf("read vcom from vendor failed, use default vcom\n");
84393a7515aSWenping Zhang priv->vcom = VCOM_DEFAULT_VALUE;
84493a7515aSWenping Zhang } else {
84593a7515aSWenping Zhang priv->vcom = vcom;
84693a7515aSWenping Zhang }
84793a7515aSWenping Zhang
848f71d410fSChaoyi Chen if (priv->ebc_pwr_dev)
84993a7515aSWenping Zhang pwr_ops = ebc_pwr_get_ops(priv->ebc_pwr_dev);
850f71d410fSChaoyi Chen
851f71d410fSChaoyi Chen if (priv->ebc_pwr_dev)
85293a7515aSWenping Zhang ret = pwr_ops->vcom_set(priv->ebc_pwr_dev, priv->vcom);
853f71d410fSChaoyi Chen else
854f71d410fSChaoyi Chen ret = regulator_set_value(priv->regulator_dev, priv->vcom * 1000);
85593a7515aSWenping Zhang if (ret) {
85693a7515aSWenping Zhang printf("%s, vcom_set failed\n", __func__);
85793a7515aSWenping Zhang return -EIO;
85893a7515aSWenping Zhang }
85993a7515aSWenping Zhang
86093a7515aSWenping Zhang // read lut to ram, and get lut ops
86193a7515aSWenping Zhang ret = read_waveform(dev);
86293a7515aSWenping Zhang if (ret < 0) {
86393a7515aSWenping Zhang printf("read wavform failed\n");
86493a7515aSWenping Zhang return -EIO;
86593a7515aSWenping Zhang }
86693a7515aSWenping Zhang
86793a7515aSWenping Zhang eink_dev = dev;
8683f41a942SWeixin Zhou
86993a7515aSWenping Zhang return 0;
87093a7515aSWenping Zhang }
87193a7515aSWenping Zhang
rockchip_eink_display_ofdata_to_platdata(struct udevice * dev)87293a7515aSWenping Zhang static int rockchip_eink_display_ofdata_to_platdata(struct udevice *dev)
87393a7515aSWenping Zhang {
87493a7515aSWenping Zhang fdt_size_t size;
87593a7515aSWenping Zhang fdt_addr_t tmp_addr;
87693a7515aSWenping Zhang struct device_node *disp_mem;
87793a7515aSWenping Zhang struct device_node *waveform_mem;
87893a7515aSWenping Zhang struct ebc_panel *plat = dev_get_platdata(dev);
879*1e54c433SZhibin Huang void * data;
880*1e54c433SZhibin Huang int len;
881*1e54c433SZhibin Huang
882*1e54c433SZhibin Huang data = (void *)dev_read_prop(dev, "wf,mode_table", &len);
883*1e54c433SZhibin Huang if (len > 0 && pvi_wf_add_custom_mode_table(data, len))
884*1e54c433SZhibin Huang return -ENODEV;
88593a7515aSWenping Zhang
88693a7515aSWenping Zhang plat->width = dev_read_u32_default(dev, "panel,width", 0);
88793a7515aSWenping Zhang plat->height = dev_read_u32_default(dev, "panel,height", 0);
8889876686dSWenping Zhang plat->vir_width = dev_read_u32_default(dev, "panel,vir_width", plat->width);
8899876686dSWenping Zhang plat->vir_height = dev_read_u32_default(dev, "panel,vir_height", plat->height);
89093a7515aSWenping Zhang plat->sdck = dev_read_u32_default(dev, "panel,sdck", 0);
89193a7515aSWenping Zhang plat->lsl = dev_read_u32_default(dev, "panel,lsl", 0);
89293a7515aSWenping Zhang plat->lbl = dev_read_u32_default(dev, "panel,lbl", 0);
89393a7515aSWenping Zhang plat->ldl = dev_read_u32_default(dev, "panel,ldl", 0);
89493a7515aSWenping Zhang plat->lel = dev_read_u32_default(dev, "panel,lel", 0);
89593a7515aSWenping Zhang plat->gdck_sta = dev_read_u32_default(dev, "panel,gdck-sta", 0);
89693a7515aSWenping Zhang plat->lgonl = dev_read_u32_default(dev, "panel,lgonl", 0);
89793a7515aSWenping Zhang plat->fsl = dev_read_u32_default(dev, "panel,fsl", 0);
89893a7515aSWenping Zhang plat->fbl = dev_read_u32_default(dev, "panel,fbl", 0);
89993a7515aSWenping Zhang plat->fdl = dev_read_u32_default(dev, "panel,fdl", 0);
90093a7515aSWenping Zhang plat->fel = dev_read_u32_default(dev, "panel,fel", 0);
90193a7515aSWenping Zhang plat->panel_16bit = dev_read_u32_default(dev, "panel,panel_16bit", 0);
90293a7515aSWenping Zhang plat->panel_color = dev_read_u32_default(dev, "panel,panel_color", 0);
90393a7515aSWenping Zhang plat->mirror = dev_read_u32_default(dev, "panel,mirror", 0);
904dfc47936SZorro Liu plat->rearrange = dev_read_u32_default(dev, "panel,rearrange", 0);
90593a7515aSWenping Zhang plat->width_mm = dev_read_u32_default(dev, "panel,width-mm", 0);
90693a7515aSWenping Zhang plat->height_mm = dev_read_u32_default(dev, "panel,height-mm", 0);
9071020d760SChaoyi Chen plat->sdce_width = dev_read_u32_default(dev, "panel,sdce_width", 0);
9081020d760SChaoyi Chen plat->sdoe_mode = dev_read_u32_default(dev, "panel,sdoe_mode", 0);
90993a7515aSWenping Zhang
91093a7515aSWenping Zhang disp_mem = of_parse_phandle(ofnode_to_np(dev_ofnode(dev)),
91193a7515aSWenping Zhang "memory-region", 0);
91293a7515aSWenping Zhang if (!disp_mem) {
91393a7515aSWenping Zhang dev_err(dev, "Cannot get memory-region from dts\n");
91493a7515aSWenping Zhang return -ENODEV;
91593a7515aSWenping Zhang }
91693a7515aSWenping Zhang tmp_addr = ofnode_get_addr_size(np_to_ofnode(disp_mem), "reg", &size);
91793a7515aSWenping Zhang if (tmp_addr == FDT_ADDR_T_NONE) {
91893a7515aSWenping Zhang printf("get display memory address failed\n");
91993a7515aSWenping Zhang return -ENODEV;
92093a7515aSWenping Zhang }
92193a7515aSWenping Zhang
92293a7515aSWenping Zhang plat->disp_pbuf = (u64)map_sysmem(tmp_addr, 0);
92393a7515aSWenping Zhang plat->disp_pbuf_size = size;
92493a7515aSWenping Zhang debug("display mem=0x%x, size=%x\n", plat->disp_pbuf,
92593a7515aSWenping Zhang plat->disp_pbuf_size);
92693a7515aSWenping Zhang waveform_mem = of_parse_phandle(ofnode_to_np(dev_ofnode(dev)),
92793a7515aSWenping Zhang "waveform-region", 0);
92893a7515aSWenping Zhang if (!waveform_mem) {
92993a7515aSWenping Zhang printf("Cannot get waveform-region from dts\n");
93093a7515aSWenping Zhang return -ENODEV;
93193a7515aSWenping Zhang }
93293a7515aSWenping Zhang tmp_addr = ofnode_get_addr_size(np_to_ofnode(waveform_mem),
93393a7515aSWenping Zhang "reg", &size);
93493a7515aSWenping Zhang if (tmp_addr == FDT_ADDR_T_NONE) {
93593a7515aSWenping Zhang printf("get waveform memory address failed\n");
93693a7515aSWenping Zhang return -ENODEV;
93793a7515aSWenping Zhang }
93893a7515aSWenping Zhang
93993a7515aSWenping Zhang plat->lut_pbuf = map_sysmem(tmp_addr, 0);
94093a7515aSWenping Zhang plat->lut_pbuf_size = size;
94193a7515aSWenping Zhang debug("lut mem=0x%p, size=%x\n", plat->lut_pbuf, plat->lut_pbuf_size);
94293a7515aSWenping Zhang return 0;
94393a7515aSWenping Zhang }
94493a7515aSWenping Zhang
94593a7515aSWenping Zhang static const struct udevice_id rockchip_eink_display_ids[] = {
94693a7515aSWenping Zhang { .compatible = "rockchip,ebc-dev", },
94793a7515aSWenping Zhang {}
94893a7515aSWenping Zhang };
94993a7515aSWenping Zhang
95093a7515aSWenping Zhang U_BOOT_DRIVER(rk_eink_display) = {
95193a7515aSWenping Zhang .name = "rockchip_eink_display",
95293a7515aSWenping Zhang .id = UCLASS_EINK_DISPLAY,
95393a7515aSWenping Zhang .of_match = rockchip_eink_display_ids,
95493a7515aSWenping Zhang .ofdata_to_platdata = rockchip_eink_display_ofdata_to_platdata,
95593a7515aSWenping Zhang .probe = rockchip_eink_display_probe,
95693a7515aSWenping Zhang .priv_auto_alloc_size = sizeof(struct rockchip_eink_display_priv),
95793a7515aSWenping Zhang .platdata_auto_alloc_size = sizeof(struct ebc_panel),
95893a7515aSWenping Zhang };
95993a7515aSWenping Zhang
96093a7515aSWenping Zhang UCLASS_DRIVER(rk_eink) = {
96193a7515aSWenping Zhang .id = UCLASS_EINK_DISPLAY,
96293a7515aSWenping Zhang .name = "rk_eink",
96393a7515aSWenping Zhang };
96493a7515aSWenping Zhang
965