1 /*
2 * Copyright (c) 2024-2025 Renesas Electronics Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT renesas_ra_glcdc
8
9 #include "display_renesas_ra.h"
10 #include "r_glcdc.h"
11 #include <zephyr/drivers/clock_control/renesas_ra_cgc.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <zephyr/drivers/pinctrl.h>
14 #include <zephyr/irq.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/logging/log.h>
17
18 LOG_MODULE_REGISTER(display_renesas_ra, CONFIG_DISPLAY_LOG_LEVEL);
19
20 void glcdc_line_detect_isr(void);
21 struct display_ra_config {
22 const struct pinctrl_dev_config *pincfg;
23 const struct gpio_dt_spec backlight_gpio;
24 const struct device *clock_dev;
25 struct clock_control_ra_subsys_cfg clock_glcdc_subsys;
26 uint16_t height;
27 uint16_t width;
28 uint32_t display_frame_size;
29 enum display_pixel_format pixel_format;
30 void (*irq_configure)(void);
31 };
32
33 struct display_ra_data {
34 glcdc_instance_ctrl_t display_ctrl;
35 display_cfg_t display_fsp_cfg;
36 uint8_t *p_base;
37 uint32_t frame_buffer_len;
38 const uint8_t *pend_buf;
39 const uint8_t *front_buf;
40 uint8_t pixel_size;
41 uint8_t *frame_buffer;
42 struct k_sem sem;
43 };
44
renesas_ra_glcdc_isr(const struct device * dev)45 static void renesas_ra_glcdc_isr(const struct device *dev)
46 {
47 struct display_ra_data *data = dev->data;
48
49 glcdc_line_detect_isr();
50 if (data->front_buf != data->pend_buf) {
51 data->front_buf = data->pend_buf;
52 k_sem_give(&data->sem);
53 }
54 }
55
ra_display_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)56 static int ra_display_write(const struct device *dev, const uint16_t x, const uint16_t y,
57 const struct display_buffer_descriptor *desc, const void *buf)
58 {
59 struct display_ra_data *data = dev->data;
60 const struct display_ra_config *config = dev->config;
61 uint8_t *dst = NULL;
62 const uint8_t *src = buf;
63 const uint8_t *l_pend_buf = NULL;
64 uint16_t row;
65 int err;
66
67 __ASSERT(desc->width <= desc->pitch, "Pitch is smaller than width");
68 __ASSERT((desc->pitch * BYTE_PER_PIXEL * desc->height) <= desc->buf_size,
69 "Input buffer too small");
70
71 if (x == 0 && y == 0 && desc->height == DISPLAY_VSIZE && desc->width == DISPLAY_HSIZE &&
72 desc->pitch == DISPLAY_HSIZE) {
73 l_pend_buf = buf;
74 } else {
75 if (CONFIG_RENESAS_RA_GLCDC_FB_NUM == 0) {
76 LOG_ERR("Partial write requires internal frame buffer");
77 return -ENOTSUP;
78 }
79 dst = data->frame_buffer;
80
81 if (CONFIG_RENESAS_RA_GLCDC_FB_NUM == 2) {
82 if (data->front_buf == data->frame_buffer) {
83 dst = data->frame_buffer + data->frame_buffer_len;
84 }
85
86 memcpy(dst, data->front_buf, data->frame_buffer_len);
87 }
88
89 l_pend_buf = dst;
90
91 /* dst = pointer to upper left pixel of the rectangle
92 * to be updated in frame buffer.
93 */
94 dst += (x * data->pixel_size);
95 dst += (y * config->width * data->pixel_size);
96
97 for (row = 0; row < desc->height; row++) {
98 (void)memcpy(dst, src, desc->width * data->pixel_size);
99 dst += (config->width * data->pixel_size);
100 src += (desc->pitch * data->pixel_size);
101 }
102 }
103
104 if (data->front_buf == l_pend_buf) {
105 return 0;
106 }
107
108 k_sem_reset(&data->sem);
109
110 data->pend_buf = l_pend_buf;
111
112 err = R_GLCDC_BufferChange(&data->display_ctrl, (uint8_t *)data->pend_buf,
113 DISPLAY_FRAME_LAYER_1);
114 if (err) {
115 LOG_ERR("GLCDC buffer change failed");
116 return -EIO;
117 }
118
119 k_sem_take(&data->sem, K_FOREVER);
120
121 return 0;
122 }
123
ra_display_read(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,void * buf)124 static int ra_display_read(const struct device *dev, const uint16_t x, const uint16_t y,
125 const struct display_buffer_descriptor *desc, void *buf)
126 {
127 struct display_ra_data *data = dev->data;
128 const struct display_ra_config *config = dev->config;
129 uint8_t *dst = buf;
130 const uint8_t *src = data->front_buf;
131 uint16_t row;
132
133 /* src = pointer to upper left pixel of the rectangle to be read from frame buffer */
134 src += (x * data->pixel_size);
135 src += (y * config->width * data->pixel_size);
136
137 for (row = 0; row < desc->height; row++) {
138 (void)memcpy(dst, src, desc->width * data->pixel_size);
139 src += (config->width * data->pixel_size);
140 dst += (desc->pitch * data->pixel_size);
141 }
142
143 return 0;
144 }
145
ra_display_blanking_on(const struct device * dev)146 static int ra_display_blanking_on(const struct device *dev)
147 {
148 const struct display_ra_config *config = dev->config;
149 int err;
150
151 if (config->backlight_gpio.port != NULL) {
152 err = gpio_pin_set_dt(&config->backlight_gpio, 0);
153 if (err) {
154 LOG_ERR("Disable backlight failed! (%d)", err);
155 return err;
156 }
157 } else {
158 return -ENOTSUP;
159 }
160
161 return 0;
162 }
163
ra_display_blanking_off(const struct device * dev)164 static int ra_display_blanking_off(const struct device *dev)
165 {
166 const struct display_ra_config *config = dev->config;
167 int err;
168
169 if (config->backlight_gpio.port != NULL) {
170 err = gpio_pin_set_dt(&config->backlight_gpio, 1);
171 if (err) {
172 LOG_ERR("Enable backlight failed! (%d)", err);
173 return err;
174 }
175 } else {
176 return -ENOTSUP;
177 }
178
179 return 0;
180 }
181
ra_display_get_capabilities(const struct device * dev,struct display_capabilities * capabilities)182 static void ra_display_get_capabilities(const struct device *dev,
183 struct display_capabilities *capabilities)
184 {
185 const struct display_ra_config *config = dev->config;
186
187 memset(capabilities, 0, sizeof(struct display_capabilities));
188 capabilities->x_resolution = config->width;
189 capabilities->y_resolution = config->height;
190 capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL;
191 capabilities->supported_pixel_formats =
192 PIXEL_FORMAT_RGB_888 | PIXEL_FORMAT_ARGB_8888 | PIXEL_FORMAT_RGB_565;
193 capabilities->current_pixel_format = (config->pixel_format == PIXEL_FORMAT_RGB_888)
194 ? PIXEL_FORMAT_ARGB_8888
195 : config->pixel_format;
196 }
197
ra_display_set_pixel_format(const struct device * dev,const enum display_pixel_format pixel_format)198 static int ra_display_set_pixel_format(const struct device *dev,
199 const enum display_pixel_format pixel_format)
200 {
201 const struct display_ra_config *config = dev->config;
202
203 if (pixel_format == config->pixel_format) {
204 return 0;
205 }
206 LOG_ERR("Pixel format changes must be set in dts at build time.");
207 return -ENOTSUP;
208 }
209
210 static DEVICE_API(display, display_api) = {
211 .blanking_on = ra_display_blanking_on,
212 .blanking_off = ra_display_blanking_off,
213 .get_capabilities = ra_display_get_capabilities,
214 .set_pixel_format = ra_display_set_pixel_format,
215 .write = ra_display_write,
216 .read = ra_display_read,
217 };
218
display_init(const struct device * dev)219 static int display_init(const struct device *dev)
220 {
221 const struct display_ra_config *config = dev->config;
222 struct display_ra_data *data = dev->data;
223 int err;
224
225 #if BSP_FEATURE_BSP_HAS_GRAPHICS_DOMAIN
226
227 R_BSP_RegisterProtectDisable(BSP_REG_PROTECT_OM_LPC_BATT);
228 FSP_HARDWARE_REGISTER_WAIT(
229 (R_SYSTEM->PDCTRGD & (R_SYSTEM_PDCTRGD_PDCSF_Msk | R_SYSTEM_PDCTRGD_PDPGSF_Msk)),
230 R_SYSTEM_PDCTRGD_PDPGSF_Msk);
231 R_SYSTEM->PDCTRGD = 0;
232 FSP_HARDWARE_REGISTER_WAIT(
233 (R_SYSTEM->PDCTRGD & (R_SYSTEM_PDCTRGD_PDCSF_Msk | R_SYSTEM_PDCTRGD_PDPGSF_Msk)),
234 0);
235 R_BSP_RegisterProtectEnable(BSP_REG_PROTECT_OM_LPC_BATT);
236
237 #endif
238 err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
239 if (err) {
240 LOG_ERR("pin function initial failed");
241 return err;
242 }
243 k_sem_init(&data->sem, 0, 1);
244
245 err = clock_control_on(config->clock_dev,
246 (clock_control_subsys_t)&config->clock_glcdc_subsys);
247 if (err) {
248 LOG_ERR("Enable GLCDC clock failed!");
249 return err;
250 }
251
252 err = R_GLCDC_Open(&data->display_ctrl, &data->display_fsp_cfg);
253 if (err) {
254 LOG_ERR("GLCDC open failed");
255 return -EIO;
256 }
257
258 err = gpio_pin_configure_dt(&config->backlight_gpio, GPIO_OUTPUT_ACTIVE);
259 if (err) {
260 LOG_ERR("config backlight gpio failed");
261 return err;
262 }
263
264 err = R_GLCDC_Start(&data->display_ctrl);
265 if (err) {
266 LOG_ERR("GLCDC start failed");
267 return -EIO;
268 }
269
270 config->irq_configure();
271
272 return 0;
273 }
274
275 #define IRQ_CONFIGURE_FUNC(id) \
276 static void glcdc_renesas_ra_configure_func_##id(void) \
277 { \
278 R_ICU->IELSR[DT_INST_IRQ_BY_NAME(id, line, irq)] = \
279 BSP_PRV_IELS_ENUM(EVENT_GLCDC_LINE_DETECT); \
280 IRQ_CONNECT(DT_INST_IRQ_BY_NAME(id, line, irq), \
281 DT_INST_IRQ_BY_NAME(id, line, priority), renesas_ra_glcdc_isr, \
282 DEVICE_DT_INST_GET(id), 0); \
283 irq_enable(DT_INST_IRQ_BY_NAME(id, line, irq)); \
284 }
285
286 #define IRQ_CONFIGURE_DEFINE(id) .irq_configure = glcdc_renesas_ra_configure_func_##id
287
288 #define RENESAS_RA_FRAME_BUFFER_LEN(id) \
289 (BYTE_PER_PIXEL * DT_INST_PROP(id, height) * DT_INST_PROP(id, width))
290
291 #define RENESAS_RA_DEVICE_INIT(id) \
292 PINCTRL_DT_INST_DEFINE(id); \
293 IRQ_CONFIGURE_FUNC(id) \
294 Z_GENERIC_SECTION(".sdram") \
295 static uint8_t __aligned(64) \
296 fb_background##id[CONFIG_RENESAS_RA_GLCDC_FB_NUM * RENESAS_RA_FRAME_BUFFER_LEN(id)]; \
297 static const glcdc_extended_cfg_t display_extend_cfg##id = { \
298 .tcon_hsync = GLCDC_TCON_PIN_1, \
299 .tcon_vsync = GLCDC_TCON_PIN_0, \
300 .tcon_de = GLCDC_TCON_PIN_2, \
301 .correction_proc_order = GLCDC_CORRECTION_PROC_ORDER_BRIGHTNESS_CONTRAST2GAMMA, \
302 .clksrc = GLCDC_CLK_SRC_INTERNAL, \
303 .clock_div_ratio = GLCDC_PANEL_CLK_DIVISOR_8, \
304 .dithering_mode = GLCDC_DITHERING_MODE_TRUNCATE, \
305 .dithering_pattern_A = GLCDC_DITHERING_PATTERN_11, \
306 .dithering_pattern_B = GLCDC_DITHERING_PATTERN_11, \
307 .dithering_pattern_C = GLCDC_DITHERING_PATTERN_11, \
308 .dithering_pattern_D = GLCDC_DITHERING_PATTERN_11, \
309 .phy_layer = NULL, \
310 }; \
311 static struct display_ra_data ra_data##id = { \
312 .frame_buffer = fb_background##id, \
313 .frame_buffer_len = RENESAS_RA_FRAME_BUFFER_LEN(id), \
314 .front_buf = fb_background##id, \
315 .pend_buf = fb_background##id, \
316 .pixel_size = BYTE_PER_PIXEL, \
317 .p_base = (uint8_t *)&fb_background##id, \
318 .display_fsp_cfg = { \
319 .input[0] = {.p_base = (uint32_t *)&fb_background##id, \
320 .hsize = DISPLAY_HSIZE, \
321 .vsize = DISPLAY_VSIZE, \
322 .hstride = DISPLAY_BUFFER_STRIDE_PIXELS_INPUT0, \
323 .format = \
324 (INPUT_FORMAT_PIXEL == PANEL_PIXEL_FORMAT_RGB_565) \
325 ? DISPLAY_IN_FORMAT_16BITS_RGB565 \
326 : (INPUT_FORMAT_PIXEL == PANEL_PIXEL_FORMAT_RGB_888) \
327 ? DISPLAY_IN_FORMAT_32BITS_RGB888 \
328 : DISPLAY_IN_FORMAT_32BITS_ARGB8888, \
329 .line_descending_enable = false, \
330 .lines_repeat_enable = false, \
331 .lines_repeat_times = 0}, \
332 .input[1] = {.p_base = NULL, \
333 .hsize = DISPLAY_HSIZE, \
334 .vsize = DISPLAY_VSIZE, \
335 .hstride = DISPLAY_BUFFER_STRIDE_PIXELS_INPUT1, \
336 .format = DISPLAY_IN_FORMAT_16BITS_RGB565, \
337 .line_descending_enable = false, \
338 .lines_repeat_enable = false, \
339 .lines_repeat_times = 0}, \
340 .layer[0] = {.coordinate = {.x = 0, .y = 0}, \
341 .bg_color = {.byte = {.a = LAYER_ALPHA, \
342 .r = LAYER_RED, \
343 .g = LAYER_GREEN, \
344 .b = LAYER_BLUE}}, \
345 .fade_control = DISPLAY_FADE_CONTROL_NONE, \
346 .fade_speed = 0}, \
347 .layer[1] = {.coordinate = {.x = 0, .y = 0}, \
348 .bg_color = {.byte = {.a = LAYER_ALPHA, \
349 .r = LAYER_RED, \
350 .g = LAYER_GREEN, \
351 .b = LAYER_BLUE}}, \
352 .fade_control = DISPLAY_FADE_CONTROL_NONE, \
353 .fade_speed = 0}, \
354 .output = {.htiming = {.total_cyc = \
355 DT_INST_PROP(id, width) + \
356 DT_PROP(DT_INST_CHILD(id, display_timings), \
357 hback_porch) + \
358 DT_PROP(DT_INST_CHILD(id, display_timings), \
359 hfront_porch) + \
360 DT_PROP(DT_INST_CHILD(id, display_timings), \
361 hsync_len), \
362 .display_cyc = DT_INST_PROP(id, width), \
363 .back_porch = \
364 DT_PROP(DT_INST_CHILD(id, display_timings), \
365 hback_porch), \
366 .sync_width = \
367 DT_PROP(DT_INST_CHILD(id, display_timings), \
368 hsync_len), \
369 .sync_polarity = \
370 DT_PROP(DT_INST_CHILD(id, display_timings), \
371 hsync_active)}, \
372 .vtiming = {.total_cyc = \
373 DT_INST_PROP(id, height) + \
374 DT_PROP(DT_INST_CHILD(id, display_timings), \
375 vback_porch) + \
376 DT_PROP(DT_INST_CHILD(id, display_timings), \
377 vfront_porch) + \
378 DT_PROP(DT_INST_CHILD(id, display_timings), \
379 vsync_len), \
380 .display_cyc = DT_INST_PROP(id, height), \
381 .back_porch = \
382 DT_PROP(DT_INST_CHILD(id, display_timings), \
383 vback_porch), \
384 .sync_width = \
385 DT_PROP(DT_INST_CHILD(id, display_timings), \
386 vsync_len), \
387 .sync_polarity = \
388 DT_PROP(DT_INST_CHILD(id, display_timings), \
389 vsync_active)}, \
390 .format = (OUTPUT_FORMAT_PIXEL == PANEL_PIXEL_FORMAT_RGB_565) \
391 ? DISPLAY_OUT_FORMAT_16BITS_RGB565 \
392 : DISPLAY_OUT_FORMAT_24BITS_RGB888, \
393 .endian = DISPLAY_ENDIAN_LITTLE, \
394 .color_order = DISPLAY_COLOR_ORDER_RGB, \
395 .data_enable_polarity = DISPLAY_SIGNAL_POLARITY_HIACTIVE, \
396 .sync_edge = DISPLAY_SIGNAL_SYNC_EDGE_FALLING, \
397 .bg_color = {.byte = {.a = OUTPUT_ALPHA, \
398 .r = OUTPUT_RED, \
399 .g = OUTPUT_GREEN, \
400 .b = OUTPUT_BLUE}}, \
401 .brightness = {.enable = false, \
402 .r = OUTPUT_RED, \
403 .g = OUTPUT_GREEN, \
404 .b = OUTPUT_BLUE}, \
405 .contrast = {.enable = false, \
406 .r = OUTPUT_RED, \
407 .g = OUTPUT_GREEN, \
408 .b = OUTPUT_BLUE}, \
409 .dithering_on = false}, \
410 .p_callback = NULL, \
411 .p_context = NULL, \
412 .p_extend = (void *)(&display_extend_cfg##id), \
413 .line_detect_ipl = BSP_IRQ_DISABLED, \
414 .underflow_1_ipl = BSP_IRQ_DISABLED, \
415 .underflow_2_ipl = BSP_IRQ_DISABLED}}; \
416 static struct display_ra_config ra_config##id = { \
417 IRQ_CONFIGURE_DEFINE(id), \
418 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \
419 .backlight_gpio = GPIO_DT_SPEC_INST_GET(id, backlight_gpios), \
420 .height = DT_INST_PROP(id, height), \
421 .width = DT_INST_PROP(id, width), \
422 .pixel_format = DT_INST_PROP(id, input_pixel_format), \
423 .display_frame_size = \
424 (DT_INST_PROP(id, width)) * (DT_INST_PROP(id, height)) * BYTE_PER_PIXEL, \
425 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)), \
426 .clock_glcdc_subsys = {.mstp = (uint32_t)DT_INST_CLOCKS_CELL_BY_IDX(id, 0, mstp), \
427 .stop_bit = DT_INST_CLOCKS_CELL_BY_IDX(id, 0, stop_bit)}, \
428 }; \
429 DEVICE_DT_INST_DEFINE(id, &display_init, NULL, &ra_data##id, &ra_config##id, POST_KERNEL, \
430 CONFIG_DISPLAY_INIT_PRIORITY, &display_api);
431
432 DT_INST_FOREACH_STATUS_OKAY(RENESAS_RA_DEVICE_INIT)
433