1 /*
2  * Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <lvgl.h>
9 #include <string.h>
10 #include "lvgl_display.h"
11 
12 static uint8_t *mono_conv_buf;
13 static uint32_t mono_conv_buf_size;
14 
set_px_at_pos(uint8_t * dst_buf,uint32_t x,uint32_t y,uint32_t width,const struct display_capabilities * caps)15 static ALWAYS_INLINE void set_px_at_pos(uint8_t *dst_buf, uint32_t x, uint32_t y, uint32_t width,
16 					const struct display_capabilities *caps)
17 {
18 	uint8_t bit;
19 	uint8_t *buf;
20 
21 	if (caps->screen_info & SCREEN_INFO_MONO_VTILED) {
22 		buf = dst_buf + x + y / 8 * width;
23 
24 		if (caps->screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
25 			bit = 7 - y % 8;
26 		} else {
27 			bit = y % 8;
28 		}
29 	} else {
30 		buf = dst_buf + x / 8 + y * width / 8;
31 
32 		if (caps->screen_info & SCREEN_INFO_MONO_MSB_FIRST) {
33 			bit = 7 - x % 8;
34 		} else {
35 			bit = x % 8;
36 		}
37 	}
38 
39 	if (caps->current_pixel_format == PIXEL_FORMAT_MONO10) {
40 		*buf |= BIT(bit);
41 	} else {
42 		*buf &= ~BIT(bit);
43 	}
44 }
45 
lvgl_transform_buffer(uint8_t ** px_map,uint32_t width,uint32_t height,const struct display_capabilities * caps)46 static void lvgl_transform_buffer(uint8_t **px_map, uint32_t width, uint32_t height,
47 				  const struct display_capabilities *caps)
48 {
49 	uint8_t clear_color = caps->current_pixel_format == PIXEL_FORMAT_MONO10 ? 0x00 : 0xFF;
50 
51 	memset(mono_conv_buf, clear_color, mono_conv_buf_size);
52 
53 	/* Needed because LVGL reserves 2x4 bytes in the buffer for the color palette. */
54 	*px_map += 8;
55 	uint8_t *src_buf = *px_map;
56 	uint32_t stride = (width + CONFIG_LV_DRAW_BUF_STRIDE_ALIGN - 1) &
57 			  ~(CONFIG_LV_DRAW_BUF_STRIDE_ALIGN - 1);
58 
59 	for (uint32_t y = 0; y < height; y++) {
60 		for (uint32_t x = 0; x < width; x++) {
61 			uint32_t bit_idx = x + y * stride;
62 			uint8_t src_bit = (src_buf[bit_idx / 8] >> (7 - (bit_idx % 8))) & 1;
63 
64 			if (src_bit) {
65 				set_px_at_pos(mono_conv_buf, x, y, width, caps);
66 			}
67 		}
68 	}
69 
70 	memcpy(src_buf, mono_conv_buf, mono_conv_buf_size);
71 }
72 
lvgl_flush_cb_mono(lv_display_t * display,const lv_area_t * area,uint8_t * px_map)73 void lvgl_flush_cb_mono(lv_display_t *display, const lv_area_t *area, uint8_t *px_map)
74 {
75 	uint16_t w = area->x2 - area->x1 + 1;
76 	uint16_t h = area->y2 - area->y1 + 1;
77 	struct lvgl_disp_data *data = (struct lvgl_disp_data *)lv_display_get_user_data(display);
78 	const struct device *display_dev = data->display_dev;
79 	const bool is_epd = data->cap.screen_info & SCREEN_INFO_EPD;
80 	const bool is_last = lv_display_flush_is_last(display);
81 
82 	lvgl_transform_buffer(&px_map, w, h, &data->cap);
83 
84 	if (is_epd && !data->blanking_on && !is_last) {
85 		/*
86 		 * Turn on display blanking when using an EPD
87 		 * display. This prevents updates and the associated
88 		 * flicker if the screen is rendered in multiple
89 		 * steps.
90 		 */
91 		display_blanking_on(display_dev);
92 		data->blanking_on = true;
93 	}
94 
95 	struct display_buffer_descriptor desc = {
96 		.buf_size = (w * h) / 8U,
97 		.width = w,
98 		.pitch = w,
99 		.height = h,
100 		.frame_incomplete = !is_last,
101 	};
102 
103 	display_write(display_dev, area->x1, area->y1, &desc, (void *)px_map);
104 	if (data->cap.screen_info & SCREEN_INFO_DOUBLE_BUFFER) {
105 		display_write(display_dev, area->x1, area->y1, &desc, (void *)px_map);
106 	}
107 
108 	if (is_epd && is_last && data->blanking_on) {
109 		/*
110 		 * The entire screen has now been rendered. Update the
111 		 * display by disabling blanking.
112 		 */
113 		display_blanking_off(display_dev);
114 		data->blanking_on = false;
115 	}
116 
117 	lv_display_flush_ready(display);
118 }
119 
lvgl_rounder_cb_mono(lv_event_t * e)120 void lvgl_rounder_cb_mono(lv_event_t *e)
121 {
122 	lv_area_t *area = lv_event_get_param(e);
123 	lv_display_t *display = lv_event_get_user_data(e);
124 	struct lvgl_disp_data *data = (struct lvgl_disp_data *)lv_display_get_user_data(display);
125 
126 	if (data->cap.screen_info & SCREEN_INFO_X_ALIGNMENT_WIDTH) {
127 		area->x1 = 0;
128 		area->x2 = data->cap.x_resolution - 1;
129 	} else {
130 		area->x1 &= ~0x7;
131 		area->x2 |= 0x7;
132 		if (data->cap.screen_info & SCREEN_INFO_MONO_VTILED) {
133 			area->y1 &= ~0x7;
134 			area->y2 |= 0x7;
135 		}
136 	}
137 }
138 
lvgl_set_mono_conversion_buffer(uint8_t * buffer,uint32_t buffer_size)139 void lvgl_set_mono_conversion_buffer(uint8_t *buffer, uint32_t buffer_size)
140 {
141 	mono_conv_buf = buffer;
142 	mono_conv_buf_size = buffer_size;
143 }
144