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