ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_display_pal_lcd.c
Go to the documentation of this file.
1
19
20#include "ra8_display_pal_lcd.h"
21
22#include <stddef.h>
23#include <stdint.h>
24#include <string.h>
25
26#include "ra8_attributes.h"
27#include "ra8_board_ek_ra8d2.h"
28#include "ra8_cache.h"
29#include "ra8_check.h"
30#include "ra8_display_pal.h"
32#include "ra8_err.h"
33#include "ra8_glcdc.h"
34#include "ra8_log.h"
35#include "ra8_time.h"
36
37/* =============================================================================
38 * Module-static storage
39 * =============================================================================
40 */
41
43static const char* const s_tag = "ra8_display_pal_lcd";
44
46typedef enum : uint32_t {
47 k_rgb565_mask = 0xFFFFU,
49
62
78
81
100static inline void internal_lcd_dsb(void)
101{
102#ifndef RA8_OFF_TARGET
103 __asm__ volatile("dsb 0xF" ::: "memory");
104#endif
105}
106
107/* =============================================================================
108 * Internal helpers (kept short for NASA P10 Rule 4)
109 * =============================================================================
110 */
111
140{
141 RA8_CHECK_NULL_PTR(cfg->framebuffer, s_tag, "cfg->framebuffer");
142 if (cfg->width_px == 0U || cfg->height_px == 0U) {
144 }
145 if (cfg->pixfmt != k_display_pixfmt_rgb565) {
147 }
148 /* The GLCDC backend needs the panel's RGB timing; the board BSP supplies it
149 * (e.g. &s_ra8_panel_ek_ra8d2_timing). Host / e-ink backends leave it null. */
150 if (cfg->panel_timing == nullptr) {
152 }
153 const uint32_t need_bytes =
154 (uint32_t)cfg->width_px * (uint32_t)cfg->height_px * (uint32_t)k_lcd_rgb565_bpp;
155 if (cfg->framebuffer_bytes < need_bytes) {
157 }
158 return k_ra8_ok;
159}
160
191{
193 if (err != k_ra8_ok) {
194 return err;
195 }
197 if (err != k_ra8_ok) {
198 return err;
199 }
201
202 const ra8_glcdc_config_t glcdc_cfg = {
203 .framebuffer_addr = (uint32_t)(uintptr_t)cfg->framebuffer,
204 .width_px = cfg->width_px,
205 .height_px = cfg->height_px,
206 .format = k_ra8_glcdc_fmt_rgb565,
207 .timing = *(const ra8_glcdc_timing_t*)cfg->panel_timing,
208 };
209 err = ra8_glcdc_init(&glcdc_cfg);
210 if (err != k_ra8_ok) {
211 return err;
212 }
214 if (err != k_ra8_ok) {
215 return err;
216 }
217 err = ra8_glcdc_start(true);
218 if (err != k_ra8_ok) {
219 return err;
220 }
221 return ra8_glcdc_layer1_show((uintptr_t)cfg->framebuffer);
222}
223
245{
246 const uint32_t stride = (uint32_t)cfg->width_px * (uint32_t)k_lcd_rgb565_bpp;
247 s_lcd_ctx.caps.width_px = cfg->width_px;
248 s_lcd_ctx.caps.height_px = cfg->height_px;
250 s_lcd_ctx.caps.stride_bytes = stride;
251 s_lcd_ctx.caps.refresh_latency_us_typ = 0U;
252 s_lcd_ctx.caps.supports_partial_update = true;
253 s_lcd_ctx.caps.continuous_refresh = true;
254
255 s_lcd_ctx.fb.pixels = cfg->framebuffer;
256 s_lcd_ctx.fb.width_px = cfg->width_px;
257 s_lcd_ctx.fb.height_px = cfg->height_px;
258 s_lcd_ctx.fb.stride_bytes = stride;
260 s_lcd_ctx.started = true;
261}
262
288{
289 if (r.x > caps->width_px) {
291 }
292 if (r.y > caps->height_px) {
294 }
295 if ((uint32_t)r.x + (uint32_t)r.w > (uint32_t)caps->width_px) {
297 }
298 if ((uint32_t)r.y + (uint32_t)r.h > (uint32_t)caps->height_px) {
300 }
301 return k_ra8_ok;
302}
303
304/* =============================================================================
305 * Vtable callbacks
306 * =============================================================================
307 */
308
337static ra8_err_t internal_lcd_init(const display_cfg_t* cfg, void** out_ctx)
338{
339 RA8_CHECK_NULL_PTR(cfg, s_tag, "cfg");
340 RA8_CHECK_NULL_PTR(out_ctx, s_tag, "out_ctx");
341
343 if (v != k_ra8_ok) {
344 return v;
345 }
346 if (s_lcd_ctx.started) {
347 ra8_log_error(s_tag, "internal_lcd_init: already started");
348 return k_ra8_err_busy;
349 }
350 const ra8_err_t err = internal_lcd_bringup_panel(cfg);
351 if (err != k_ra8_ok) {
352 return err;
353 }
355 *out_ctx = &s_lcd_ctx;
356 return k_ra8_ok;
357}
358
381static ra8_err_t internal_lcd_get_caps(const void* ctx, display_caps_t* out)
382{
383 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
384 RA8_CHECK_NULL_PTR(out, s_tag, "out");
385 const lcd_ctx_t* c = (const lcd_ctx_t*)ctx;
386 *out = c->caps;
387 return k_ra8_ok;
388}
389
412/* cppcheck-suppress constParameterCallback -- display_pal_ops_t fixes this callback's context type as void*. */
414{
415 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
416 RA8_CHECK_NULL_PTR(out, s_tag, "out");
417 const lcd_ctx_t* c = (const lcd_ctx_t*)ctx;
418 *out = c->fb;
419 return k_ra8_ok;
420}
421
452{
453 (void)hint; /* LCD scans continuously; hint is purely informational. */
454 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
455 const lcd_ctx_t* c = (const lcd_ctx_t*)ctx;
456 const ra8_err_t v = internal_lcd_check_rect(&c->caps, rect);
457 if (v != k_ra8_ok) {
458 return v;
459 }
460#ifdef RA8_BOOT_ENABLE_CACHE_MPU
461 /* D-cache is enabled for this build: the CPU paints the cacheable SDRAM
462 * framebuffer through the write-back L1 D-cache, but the GLCDC scans SDRAM
463 * directly over AXI and never sees the cache. Clean (write back) the painted
464 * rows so the next scan latches the new pixels instead of stale SDRAM. The
465 * clean covers the flushed rect's contiguous row-span [y, y+h) -- a superset
466 * of the rect, exact for the full-width flushes the reader issues, and safe
467 * for a partial-width rect (whole rows, never a strided w*h slice).
468 * ra8_cache_dcache_clean_by_addr rounds to whole cache lines internally. */
469 {
470 const uint8_t* row_base = (const uint8_t*)c->fb.pixels + (uint32_t)rect.y * c->fb.stride_bytes;
471 const uint32_t row_span = (uint32_t)rect.h * c->fb.stride_bytes;
472 (void)ra8_cache_dcache_clean_by_addr(row_base, row_span);
473 }
474#endif
475 /* Memory barrier so any pixel writes the caller did with NEON / DMA
476 * are visible to the GLCDC's AXI controller before the next scan. */
478 return k_ra8_ok;
479}
480
504static ra8_err_t internal_lcd_clear(void* ctx, uint32_t color)
505{
506 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
507 lcd_ctx_t* c = (lcd_ctx_t*)ctx;
508 uint16_t* pixels = (uint16_t*)c->fb.pixels;
509 const uint32_t pixels_total = (uint32_t)c->fb.width_px * (uint32_t)c->fb.height_px;
510 const uint16_t rgb565 = (uint16_t)(color & k_rgb565_mask);
511 for (uint32_t i = 0U; i < pixels_total; ++i) {
512 pixels[i] = rgb565;
513 }
514#ifdef RA8_BOOT_ENABLE_CACHE_MPU
515 /* Write the cleared pixels back from the write-back D-cache to SDRAM so the
516 * GLCDC scan sees them (see internal_lcd_flush for the coherency rationale). The whole
517 * framebuffer was just touched, so clean the whole contiguous span. */
519 (uint32_t)c->fb.stride_bytes * (uint32_t)c->fb.height_px);
520#endif
522 return k_ra8_ok;
523}
524
552{
553 RA8_CHECK_NULL_PTR(ctx, s_tag, "ctx");
554 lcd_ctx_t* c = (lcd_ctx_t*)ctx;
555 const ra8_err_t err = ra8_glcdc_deinit();
556 c->started = false;
557 c->fb.pixels = nullptr;
558 return err;
559}
560
561/* =============================================================================
562 * Public iface instance
563 * =============================================================================
564 */
565
567 .init = internal_lcd_init,
568 .get_caps = internal_lcd_get_caps,
569 .get_framebuffer = internal_lcd_get_framebuffer,
570 .flush = internal_lcd_flush,
571 .clear = internal_lcd_clear,
572 .deinit = internal_lcd_deinit,
573};
static void internal_lcd_bringup_panel(void)
Bring up SDRAM, panel power, GLCDC and prime the BG plane.
Definition main.c:183
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Board-support layer for the Renesas EK-RA8D2 v1 evaluation kit.
ra8_err_t ra8_board_lcd_panel_power_on(void)
Pulse the J1 panel's RESET_L line and assert BLEN backlight.
ra8_err_t ra8_board_glcdc_init(ra8_board_glcdc_fmt_t fmt)
Program every J1 pin for fmt to its GLCDC alternate function.
@ k_ra8_board_glcdc_fmt_rgb888
24-bit, 8 bits/colour.
Cortex-M85 L1 cache maintenance, enable/disable, and I-cache invalidate.
ra8_err_t ra8_cache_dcache_clean_by_addr(const void *addr, uint32_t size)
Clean (write back) the D-cache lines covering a byte range.
Definition ra8_cache.c:180
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Display Platform Abstraction Layer for the RA8D2.
@ k_display_pixfmt_rgb565
16 bpp, 5/6/5 packed.
struct display_backend_iface display_backend_iface_t
display_refresh_hint_t
Intent passed into display_flush; backends map this onto whatever waveform/mode their controller offe...
Internal vtable + handle types for the display PAL.
disp_lcd_mask_t
RGB565 pixel mask.
@ k_rgb565_mask
Rgb565 mask.
static ra8_err_t internal_lcd_check_rect(const display_caps_t *caps, display_rect_t r)
Confirm a rectangle stays inside the active framebuffer.
static ra8_err_t internal_lcd_validate_cfg(const display_cfg_t *cfg)
Validate the caller's display_cfg_t against the LCD backend's expectations.
static void internal_lcd_dsb(void)
DSB memory barrier on Cortex-M, no-op on the host test build.
static void internal_lcd_snapshot(const display_cfg_t *cfg)
Populate s_lcd_ctx from the validated cfg.
static ra8_err_t internal_lcd_get_framebuffer(void *ctx, display_fb_t *out)
Vtable get_framebuffer – hand back the FB descriptor.
ra8_display_pal_lcd_const_t
Numeric constants used by the LCD backend bring-up.
@ k_lcd_bringup_settle_ms
Pin/clock settle after board_glcdc_init.
@ k_lcd_fb_align_bytes
AXI burst alignment (HUM Ch 63).
@ k_lcd_bg_color_black
24-bit ARGB for BG plane.
@ k_lcd_rgb565_bpp
Bytes per RGB565 pixel.
static ra8_err_t internal_lcd_flush(void *ctx, display_rect_t rect, display_refresh_hint_t hint)
Vtable flush – DSB barrier, validate rect.
static ra8_err_t internal_lcd_deinit(void *ctx)
Vtable deinit – tear the GLCDC down and drop state.
static lcd_ctx_t s_lcd_ctx
Single LCD backend context – one display per board.
static ra8_err_t internal_lcd_clear(void *ctx, uint32_t color)
Vtable clear – fill the framebuffer with a uniform RGB565 value.
static ra8_err_t internal_lcd_get_caps(const void *ctx, display_caps_t *out)
Vtable get_caps – O(1) copy from s_lcd_ctx.caps.
static ra8_err_t internal_lcd_init(const display_cfg_t *cfg, void **out_ctx)
LCD backend init – validate, panel bring-up, snapshot.
LCD backend (ra8_glcdc) for the display PAL.
const display_backend_iface_t k_display_backend_lcd_ra8_glcdc
LCD backend vtable – pass its address through display_cfg_t.iface to drive the EK-RA8D2 panel.
Error Code Definitions for ra8-firmware.
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_busy
Resource busy – blocking operation cannot proceed.
Definition ra8_err.h:195
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Graphics LCD Controller driver (two-layer with alpha blending).
ra8_err_t ra8_glcdc_init(const ra8_glcdc_config_t *cfg)
Initialise GLCDC with the EK-RA8D2 1024x600 timings and the supplied graphics layer 1 framebuffer.
Definition ra8_glcdc.c:662
ra8_err_t ra8_glcdc_set_background_color(uint32_t argb)
Set the panel-wide background colour shown where neither layer covers a pixel.
ra8_err_t ra8_glcdc_start(bool enable)
Start or stop the panel output.
Definition ra8_glcdc.c:709
ra8_err_t ra8_glcdc_layer1_show(uintptr_t fb_addr)
Make graphics layer 1 visible by pointing it at a framebuffer.
ra8_err_t ra8_glcdc_deinit(void)
Tear down GLCDC (stop + MSTP release).
Definition ra8_glcdc.c:818
@ k_ra8_glcdc_fmt_rgb565
16-bit RGB565.
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
SysTick-based tick counter, delay and timestamp helpers.
void ra8_delay_ms(uint32_t ms)
Busy-wait for at least ms milliseconds.
Definition ra8_time.c:129
Capabilities a backend reports after display_init.
uint16_t width_px
Display visible width.
uint16_t height_px
Display visible height.
Configuration descriptor passed into display_init.
void * framebuffer
Application framebuffer storage (caller owns).
display_pixfmt_t pixfmt
Pixel format of framebuffer.
uint16_t width_px
Visible framebuffer width in pixels.
uint32_t framebuffer_bytes
Size of the framebuffer buffer in bytes.
uint16_t height_px
Visible framebuffer height in pixels.
const void * panel_timing
Panel RGB timing for parallel-RGB backends, opaque here so the generic PAL stays HAL-agnostic.
Framebuffer descriptor returned by display_get_framebuffer.
uint16_t width_px
Framebuffer width.
uint16_t height_px
Framebuffer height.
uint32_t stride_bytes
Row stride in bytes.
void * pixels
Base of pixel data (RGB565 unless noted).
Rectangle in framebuffer coordinates passed into display_flush.
uint16_t h
Height in pixels.
uint16_t w
Width in pixels.
uint16_t y
Top edge in pixels.
uint16_t x
Left edge in pixels.
LCD backend private context attached to the PAL handle.
display_fb_t fb
Fb.
bool started
Started.
display_caps_t caps
Caps.
Minimal GLCDC configuration for a single-layer panel.
Definition ra8_glcdc.h:55
Panel RGB timing the driver programs (accessory parameters).
Definition ra8_glcdc.h:35