1 /*
2 * Copyright (c) 2024 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)] = ELC_EVENT_GLCDC_LINE_DETECT; \
279 IRQ_CONNECT(DT_INST_IRQ_BY_NAME(id, line, irq), \
280 DT_INST_IRQ_BY_NAME(id, line, priority), renesas_ra_glcdc_isr, \
281 DEVICE_DT_INST_GET(id), 0); \
282 irq_enable(DT_INST_IRQ_BY_NAME(id, line, irq)); \
283 }
284
285 #define IRQ_CONFIGURE_DEFINE(id) .irq_configure = glcdc_renesas_ra_configure_func_##id
286
287 #define RENESAS_RA_FRAME_BUFFER_LEN(id) \
288 (BYTE_PER_PIXEL * DT_INST_PROP(id, height) * DT_INST_PROP(id, width))
289
290 #define RENESAS_RA_DEVICE_INIT(id) \
291 PINCTRL_DT_INST_DEFINE(id); \
292 IRQ_CONFIGURE_FUNC(id) \
293 Z_GENERIC_SECTION(".sdram") \
294 static uint8_t __aligned(64) \
295 fb_background##id[CONFIG_RENESAS_RA_GLCDC_FB_NUM * RENESAS_RA_FRAME_BUFFER_LEN(id)]; \
296 static const glcdc_extended_cfg_t display_extend_cfg##id = { \
297 .tcon_hsync = GLCDC_TCON_PIN_1, \
298 .tcon_vsync = GLCDC_TCON_PIN_0, \
299 .tcon_de = GLCDC_TCON_PIN_2, \
300 .correction_proc_order = GLCDC_CORRECTION_PROC_ORDER_BRIGHTNESS_CONTRAST2GAMMA, \
301 .clksrc = GLCDC_CLK_SRC_INTERNAL, \
302 .clock_div_ratio = GLCDC_PANEL_CLK_DIVISOR_8, \
303 .dithering_mode = GLCDC_DITHERING_MODE_TRUNCATE, \
304 .dithering_pattern_A = GLCDC_DITHERING_PATTERN_11, \
305 .dithering_pattern_B = GLCDC_DITHERING_PATTERN_11, \
306 .dithering_pattern_C = GLCDC_DITHERING_PATTERN_11, \
307 .dithering_pattern_D = GLCDC_DITHERING_PATTERN_11, \
308 .phy_layer = NULL, \
309 }; \
310 static struct display_ra_data ra_data##id = { \
311 .frame_buffer = fb_background##id, \
312 .frame_buffer_len = RENESAS_RA_FRAME_BUFFER_LEN(id), \
313 .front_buf = fb_background##id, \
314 .pend_buf = fb_background##id, \
315 .pixel_size = BYTE_PER_PIXEL, \
316 .p_base = (uint8_t *)&fb_background##id, \
317 .display_fsp_cfg = { \
318 .input[0] = {.p_base = (uint32_t *)&fb_background##id, \
319 .hsize = DISPLAY_HSIZE, \
320 .vsize = DISPLAY_VSIZE, \
321 .hstride = DISPLAY_BUFFER_STRIDE_PIXELS_INPUT0, \
322 .format = \
323 (INPUT_FORMAT_PIXEL == PANEL_PIXEL_FORMAT_RGB_565) \
324 ? DISPLAY_IN_FORMAT_16BITS_RGB565 \
325 : (INPUT_FORMAT_PIXEL == PANEL_PIXEL_FORMAT_RGB_888) \
326 ? DISPLAY_IN_FORMAT_32BITS_RGB888 \
327 : DISPLAY_IN_FORMAT_32BITS_ARGB8888, \
328 .line_descending_enable = false, \
329 .lines_repeat_enable = false, \
330 .lines_repeat_times = 0}, \
331 .input[1] = {.p_base = NULL, \
332 .hsize = DISPLAY_HSIZE, \
333 .vsize = DISPLAY_VSIZE, \
334 .hstride = DISPLAY_BUFFER_STRIDE_PIXELS_INPUT1, \
335 .format = DISPLAY_IN_FORMAT_16BITS_RGB565, \
336 .line_descending_enable = false, \
337 .lines_repeat_enable = false, \
338 .lines_repeat_times = 0}, \
339 .layer[0] = {.coordinate = {.x = 0, .y = 0}, \
340 .bg_color = {.byte = {.a = LAYER_ALPHA, \
341 .r = LAYER_RED, \
342 .g = LAYER_GREEN, \
343 .b = LAYER_BLUE}}, \
344 .fade_control = DISPLAY_FADE_CONTROL_NONE, \
345 .fade_speed = 0}, \
346 .layer[1] = {.coordinate = {.x = 0, .y = 0}, \
347 .bg_color = {.byte = {.a = LAYER_ALPHA, \
348 .r = LAYER_RED, \
349 .g = LAYER_GREEN, \
350 .b = LAYER_BLUE}}, \
351 .fade_control = DISPLAY_FADE_CONTROL_NONE, \
352 .fade_speed = 0}, \
353 .output = {.htiming = {.total_cyc = \
354 DT_INST_PROP(id, width) + \
355 DT_PROP(DT_INST_CHILD(id, display_timings), \
356 hback_porch) + \
357 DT_PROP(DT_INST_CHILD(id, display_timings), \
358 hfront_porch) + \
359 DT_PROP(DT_INST_CHILD(id, display_timings), \
360 hsync_len), \
361 .display_cyc = DT_INST_PROP(id, width), \
362 .back_porch = \
363 DT_PROP(DT_INST_CHILD(id, display_timings), \
364 hback_porch), \
365 .sync_width = \
366 DT_PROP(DT_INST_CHILD(id, display_timings), \
367 hsync_len), \
368 .sync_polarity = \
369 DT_PROP(DT_INST_CHILD(id, display_timings), \
370 hsync_active)}, \
371 .vtiming = {.total_cyc = \
372 DT_INST_PROP(id, height) + \
373 DT_PROP(DT_INST_CHILD(id, display_timings), \
374 vback_porch) + \
375 DT_PROP(DT_INST_CHILD(id, display_timings), \
376 vfront_porch) + \
377 DT_PROP(DT_INST_CHILD(id, display_timings), \
378 vsync_len), \
379 .display_cyc = DT_INST_PROP(id, height), \
380 .back_porch = \
381 DT_PROP(DT_INST_CHILD(id, display_timings), \
382 vback_porch), \
383 .sync_width = \
384 DT_PROP(DT_INST_CHILD(id, display_timings), \
385 vsync_len), \
386 .sync_polarity = \
387 DT_PROP(DT_INST_CHILD(id, display_timings), \
388 vsync_active)}, \
389 .format = (OUTPUT_FORMAT_PIXEL == PANEL_PIXEL_FORMAT_RGB_565) \
390 ? DISPLAY_OUT_FORMAT_16BITS_RGB565 \
391 : DISPLAY_OUT_FORMAT_24BITS_RGB888, \
392 .endian = DISPLAY_ENDIAN_LITTLE, \
393 .color_order = DISPLAY_COLOR_ORDER_RGB, \
394 .data_enable_polarity = DISPLAY_SIGNAL_POLARITY_HIACTIVE, \
395 .sync_edge = DISPLAY_SIGNAL_SYNC_EDGE_FALLING, \
396 .bg_color = {.byte = {.a = OUTPUT_ALPHA, \
397 .r = OUTPUT_RED, \
398 .g = OUTPUT_GREEN, \
399 .b = OUTPUT_BLUE}}, \
400 .brightness = {.enable = false, \
401 .r = OUTPUT_RED, \
402 .g = OUTPUT_GREEN, \
403 .b = OUTPUT_BLUE}, \
404 .contrast = {.enable = false, \
405 .r = OUTPUT_RED, \
406 .g = OUTPUT_GREEN, \
407 .b = OUTPUT_BLUE}, \
408 .dithering_on = false}, \
409 .p_callback = NULL, \
410 .p_context = NULL, \
411 .p_extend = (void *)(&display_extend_cfg##id), \
412 .line_detect_ipl = BSP_IRQ_DISABLED, \
413 .underflow_1_ipl = BSP_IRQ_DISABLED, \
414 .underflow_2_ipl = BSP_IRQ_DISABLED}}; \
415 static struct display_ra_config ra_config##id = { \
416 IRQ_CONFIGURE_DEFINE(id), \
417 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \
418 .backlight_gpio = GPIO_DT_SPEC_INST_GET(id, backlight_gpios), \
419 .height = DT_INST_PROP(id, height), \
420 .width = DT_INST_PROP(id, width), \
421 .pixel_format = DT_INST_PROP(id, input_pixel_format), \
422 .display_frame_size = \
423 (DT_INST_PROP(id, width)) * (DT_INST_PROP(id, height)) * BYTE_PER_PIXEL, \
424 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)), \
425 .clock_glcdc_subsys = {.mstp = (uint32_t)DT_INST_CLOCKS_CELL_BY_IDX(id, 0, mstp), \
426 .stop_bit = DT_INST_CLOCKS_CELL_BY_IDX(id, 0, stop_bit)}, \
427 }; \
428 DEVICE_DT_INST_DEFINE(id, &display_init, NULL, &ra_data##id, &ra_config##id, POST_KERNEL, \
429 CONFIG_DISPLAY_INIT_PRIORITY, &display_api);
430
431 DT_INST_FOREACH_STATUS_OKAY(RENESAS_RA_DEVICE_INIT)
432