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