pinephone-nuttx/render.zig

279 lines
11 KiB
Zig
Raw Normal View History

2022-10-31 11:56:43 +08:00
//***************************************************************************
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. The
// ASF licenses this file to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//
//***************************************************************************
2022-10-31 12:12:26 +08:00
//! PinePhone Display Engine Driver for Apache NuttX RTOS.
2022-10-31 12:04:05 +08:00
//! This Framebuffer Interface is compatible with NuttX Framebuffers:
//! https://github.com/lupyuen/incubator-nuttx/blob/master/include/nuttx/video/fb.h
2022-10-31 11:56:43 +08:00
/// Import the Zig Standard Library
const std = @import("std");
2022-10-31 12:12:26 +08:00
/// Import the MIPI Display Serial Interface Module
const dsi = @import("./display.zig");
2022-10-31 11:56:43 +08:00
/// Import the LoRaWAN Library from C
const c = @cImport({
// NuttX Defines
@cDefine("__NuttX__", "");
@cDefine("NDEBUG", "");
@cDefine("FAR", "");
// NuttX Framebuffer Defines
@cDefine("CONFIG_FB_OVERLAY", "");
// NuttX Header Files
@cInclude("arch/types.h");
@cInclude("../../nuttx/include/limits.h");
@cInclude("nuttx/config.h");
@cInclude("inttypes.h");
@cInclude("unistd.h");
@cInclude("stdlib.h");
@cInclude("stdio.h");
// NuttX Framebuffer Header Files
@cInclude("nuttx/video/fb.h");
});
2022-10-31 12:18:57 +08:00
/// Render a Test Pattern on PinePhone's Display.
/// Calls Allwinner A64 Display Engine, Timing Controller and MIPI Display Serial Interface.
2022-10-31 11:56:43 +08:00
pub export fn test_render() void {
2022-10-31 16:47:32 +08:00
debug("test_render", .{});
2022-10-31 14:25:41 +08:00
// Validate the Framebuffer Sizes at Compile Time
comptime {
assert(planeInfo.xres_virtual == videoInfo.xres);
assert(planeInfo.yres_virtual == videoInfo.yres);
assert(planeInfo.fblen == planeInfo.xres_virtual * planeInfo.yres_virtual * 4);
assert(planeInfo.stride == planeInfo.xres_virtual * 4);
assert(overlayInfo[0].fblen == @intCast(usize, overlayInfo[0].sarea.w) * overlayInfo[0].sarea.h * 4);
assert(overlayInfo[0].stride == overlayInfo[0].sarea.w * 4);
assert(overlayInfo[1].fblen == @intCast(usize, overlayInfo[1].sarea.w) * overlayInfo[1].sarea.h * 4);
assert(overlayInfo[1].stride == overlayInfo[1].sarea.w * 4);
}
2022-10-31 14:34:21 +08:00
2022-10-31 16:40:59 +08:00
// TODO: Populate the Framebuffers
// Init the UI Blender for PinePhone's A64 Display Engine
2022-10-31 16:51:01 +08:00
initUiBlender();
2022-10-31 16:40:59 +08:00
2022-10-31 15:43:07 +08:00
// Init the Base UI Channel
2022-10-31 16:51:01 +08:00
initUiChannel(
2022-10-31 15:43:07 +08:00
1, // UI Channel Number (1 for Base UI Channel)
planeInfo.fbmem, // Start of frame buffer memory
planeInfo.fblen, // Length of frame buffer memory in bytes
planeInfo.stride, // Length of a line in bytes (4 bytes per pixel)
planeInfo.xres_virtual, // Horizontal resolution in pixel columns
planeInfo.yres_virtual, // Vertical resolution in pixel rows
planeInfo.xoffset, // Horizontal offset in pixel columns
planeInfo.yoffset, // Vertical offset in pixel rows
);
// Init the 2 Overlay UI Channels
for (overlayInfo) | ov, i | {
2022-10-31 16:51:01 +08:00
initUiChannel(
2022-10-31 15:43:07 +08:00
@intCast(u8, i + 2), // UI Channel Number (2 and 3 for Overlay UI Channels)
ov.fbmem, // Start of frame buffer memory
ov.fblen, // Length of frame buffer memory in bytes
ov.stride, // Length of a line in bytes (4 bytes per pixel)
ov.sarea.w, // Horizontal resolution in pixel columns
ov.sarea.h, // Vertical resolution in pixel rows
ov.sarea.x, // Horizontal offset in pixel columns
ov.sarea.y, // Vertical offset in pixel rows
);
}
2022-10-31 16:40:59 +08:00
}
/// Initialise the UI Blender for PinePhone's A64 Display Engine
2022-10-31 16:51:01 +08:00
fn initUiBlender() void {
2022-10-31 16:40:59 +08:00
// BLD_BK_COLOR @ BLD Offset 0x88: BLD background color register
// TODO: Set to `0xff00` `0000` _(Why?)_
2022-10-31 15:43:07 +08:00
2022-10-31 16:40:59 +08:00
// BLD_PREMUL_CTL @ BLD Offset 0x84: BLD pre-multiply control register
// TODO: Set to 0
2022-10-31 11:56:43 +08:00
}
2022-10-31 12:18:57 +08:00
2022-10-31 15:43:07 +08:00
/// Initialise a UI Channel for PinePhone's A64 Display Engine.
/// We use 3 UI Channels: Base UI Channel (#1) plus 2 Overlay UI Channels (#2, #3)
fn initUiChannel(
channel: u8, // UI Channel Number: 1, 2 or 3
fbmem: ?*anyopaque, // Start of frame buffer memory
fblen: usize, // Length of frame buffer memory in bytes
stride: c.fb_coord_t, // Length of a line in bytes (4 bytes per pixel)
xres: c.fb_coord_t, // Horizontal resolution in pixel columns
yres: c.fb_coord_t, // Vertical resolution in pixel rows
xoffset: c.fb_coord_t, // Horizontal offset in pixel columns
yoffset: c.fb_coord_t, // Vertical offset in pixel rows
2022-10-31 16:51:01 +08:00
) void {
2022-10-31 15:43:07 +08:00
assert(channel >= 1 and channel <= 3);
// TODO: Init UI Channel
_ = fbmem;
_ = fblen;
_ = stride;
_ = xres;
_ = yres;
_ = xoffset;
_ = yoffset;
}
2022-10-31 14:27:57 +08:00
/// Export MIPI DSI Functions to C. (Why is this needed?)
pub export fn export_dsi_functions() void {
// Export Panel Init Function
2022-10-31 12:18:57 +08:00
dsi.nuttx_panel_init();
}
2022-10-31 12:37:58 +08:00
2022-10-31 14:34:21 +08:00
/// NuttX Video Controller for PinePhone (3 UI Channels)
2022-10-31 12:37:58 +08:00
const videoInfo = c.fb_videoinfo_s {
2022-10-31 15:52:57 +08:00
.fmt = c.FB_FMT_RGBA32, // Pixel format (XRGB 8888)
2022-10-31 13:56:10 +08:00
.xres = 720, // Horizontal resolution in pixel columns
.yres = 1440, // Vertical resolution in pixel rows
.nplanes = 1, // Number of color planes supported (Base UI Channel)
.noverlays = 2, // Number of overlays supported (2 Overlay UI Channels)
2022-10-31 12:37:58 +08:00
};
2022-10-31 15:52:57 +08:00
/// NuttX Color Plane for PinePhone (Base UI Channel):
/// Fullscreen 720 x 1440 (4 bytes per XRGB 8888 pixel)
2022-10-31 12:37:58 +08:00
const planeInfo = c.fb_planeinfo_s {
2022-10-31 14:27:57 +08:00
.fbmem = &fb0, // Start of frame buffer memory
.fblen = @sizeOf( @TypeOf(fb0) ), // Length of frame buffer memory in bytes
.stride = 720 * 4, // Length of a line in bytes (4 bytes per pixel)
.display = 0, // Display number (Unused)
2022-10-31 15:52:57 +08:00
.bpp = 32, // Bits per pixel (XRGB 8888)
2022-10-31 14:27:57 +08:00
.xres_virtual = 720, // Virtual Horizontal resolution in pixel columns
.yres_virtual = 1440, // Virtual Vertical resolution in pixel rows
.xoffset = 0, // Offset from virtual to visible resolution
.yoffset = 0, // Offset from virtual to visible resolution
2022-10-31 12:45:56 +08:00
};
2022-10-31 14:34:21 +08:00
/// NuttX Overlays for PinePhone (2 Overlay UI Channels)
2022-10-31 13:56:10 +08:00
const overlayInfo = [2] c.fb_overlayinfo_s {
// First Overlay UI Channel:
2022-10-31 15:52:57 +08:00
// Square 600 x 600 (4 bytes per ARGB 8888 pixel)
2022-10-31 13:56:10 +08:00
.{
2022-10-31 14:04:58 +08:00
.fbmem = &fb1, // Start of frame buffer memory
2022-10-31 14:25:41 +08:00
.fblen = @sizeOf( @TypeOf(fb1) ), // Length of frame buffer memory in bytes
2022-10-31 14:04:58 +08:00
.stride = 600 * 4, // Length of a line in bytes
.overlay = 0, // Overlay number (First Overlay)
2022-10-31 15:52:57 +08:00
.bpp = 32, // Bits per pixel (ARGB 8888)
2022-10-31 14:04:58 +08:00
.blank = 0, // TODO: Blank or unblank
.chromakey = 0, // TODO: Chroma key argb8888 formatted
.color = 0, // TODO: Color argb8888 formatted
.transp = c.fb_transp_s { .transp = 0, .transp_mode = 0 }, // TODO: Transparency
.sarea = c.fb_area_s { .x = 52, .y = 52, .w = 600, .h = 600 }, // Selected area within the overlay
.accl = 0, // TODO: Supported hardware acceleration
2022-10-31 13:56:10 +08:00
},
// Second Overlay UI Channel:
2022-10-31 15:52:57 +08:00
// Fullscreen 720 x 1440 (4 bytes per ARGB 8888 pixel)
2022-10-31 13:56:10 +08:00
.{
2022-10-31 14:04:58 +08:00
.fbmem = &fb2, // Start of frame buffer memory
2022-10-31 14:25:41 +08:00
.fblen = @sizeOf( @TypeOf(fb2) ), // Length of frame buffer memory in bytes
2022-10-31 14:04:58 +08:00
.stride = 720 * 4, // Length of a line in bytes
.overlay = 1, // Overlay number (Second Overlay)
2022-10-31 15:52:57 +08:00
.bpp = 32, // Bits per pixel (ARGB 8888)
2022-10-31 14:04:58 +08:00
.blank = 0, // TODO: Blank or unblank
.chromakey = 0, // TODO: Chroma key argb8888 formatted
.color = 0, // TODO: Color argb8888 formatted
.transp = c.fb_transp_s { .transp = 0, .transp_mode = 0 }, // TODO: Transparency
.sarea = c.fb_area_s { .x = 0, .y = 0, .w = 720, .h = 1440 }, // Selected area within the overlay
.accl = 0, // TODO: Supported hardware acceleration
2022-10-31 13:56:10 +08:00
},
};
2022-10-31 13:48:27 +08:00
// Framebuffer 0: (Base UI Channel)
2022-10-31 15:52:57 +08:00
// Fullscreen 720 x 1440 (4 bytes per XRGB 8888 pixel)
2022-10-31 13:48:27 +08:00
var fb0 = std.mem.zeroes([720 * 1440] u32);
// Framebuffer 1: (First Overlay UI Channel)
2022-10-31 15:52:57 +08:00
// Square 600 x 600 (4 bytes per ARGB 8888 pixel)
2022-10-31 13:48:27 +08:00
var fb1 = std.mem.zeroes([600 * 600] u32);
// Framebuffer 2: (Second Overlay UI Channel)
2022-10-31 15:52:57 +08:00
// Fullscreen 720 x 1440 (4 bytes per ARGB 8888 pixel)
2022-10-31 13:48:27 +08:00
var fb2 = std.mem.zeroes([720 * 1440] u32);
2022-10-31 14:25:41 +08:00
///////////////////////////////////////////////////////////////////////////////
// Panic Handler
/// Called by Zig when it hits a Panic. We print the Panic Message, Stack Trace and halt. See
/// https://andrewkelley.me/post/zig-stack-traces-kernel-panic-bare-bones-os.html
/// https://github.com/ziglang/zig/blob/master/lib/std/builtin.zig#L763-L847
pub fn panic(
message: []const u8,
_stack_trace: ?*std.builtin.StackTrace
) noreturn {
// Print the Panic Message
_ = _stack_trace;
_ = puts("\n!ZIG PANIC!");
_ = puts(@ptrCast([*c]const u8, message));
// Print the Stack Trace
_ = puts("Stack Trace:");
var it = std.debug.StackIterator.init(@returnAddress(), null);
while (it.next()) |return_address| {
_ = printf("%p\n", return_address);
}
// Halt
c.exit(1);
}
///////////////////////////////////////////////////////////////////////////////
// Logging
/// Called by Zig for `std.log.debug`, `std.log.info`, `std.log.err`, ...
/// https://gist.github.com/leecannon/d6f5d7e5af5881c466161270347ce84d
pub fn log(
comptime _message_level: std.log.Level,
comptime _scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: anytype,
) void {
_ = _message_level;
_ = _scope;
// Format the message
var buf: [100]u8 = undefined; // Limit to 100 chars
var slice = std.fmt.bufPrint(&buf, format, args)
catch { _ = puts("*** log error: buf too small"); return; };
// Terminate the formatted message with a null
var buf2: [buf.len + 1 : 0]u8 = undefined;
std.mem.copy(
u8,
buf2[0..slice.len],
slice[0..slice.len]
);
buf2[slice.len] = 0;
// Print the formatted message
_ = puts(&buf2);
}
///////////////////////////////////////////////////////////////////////////////
// Imported Functions and Variables
/// For safety, we import these functions ourselves to enforce Null-Terminated Strings.
/// We changed `[*c]const u8` to `[*:0]const u8`
extern fn printf(format: [*:0]const u8, ...) c_int;
extern fn puts(str: [*:0]const u8) c_int;
/// Aliases for Zig Standard Library
const assert = std.debug.assert;
const debug = std.log.debug;