pinephone-nuttx/render.zig

1256 lines
50 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-11-06 14:23:10 +08:00
//! PinePhone Display Engine Driver for Apache NuttX RTOS, based on NuttX Framebuffers:
//! https://github.com/apache/incubator-nuttx/blob/master/include/nuttx/video/fb.h
//! "DE Page ???" refers to Allwinner Display Engine 2.0 Specification: https://linux-sunxi.org/images/7/7b/Allwinner_DE2.0_Spec_V1.0.pdf
//! "A64 Page ???" refers to Allwinner A64 User Manual: https://linux-sunxi.org/images/b/b4/Allwinner_A64_User_Manual_V1.1.pdf
//! "A31 Page ???" refers to Allwinner A31 User Manual: https://github.com/allwinner-zh/documents/raw/master/A31/A31_User_Manual_v1.3_20150510.pdf
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-12-03 20:57:21 +08:00
/// Import the MIPI Display Physical Layer Module
const dphy = @import("./dphy.zig");
/// Import the Timing Controller Module
const tcon = @import("./tcon.zig");
2022-12-02 12:40:00 +08:00
/// Import the Backlight Module
const backlight = @import("./backlight.zig");
2022-12-03 12:19:03 +08:00
/// Import the Power Management IC Module
const pmic = @import("./pmic.zig");
2022-12-03 20:57:21 +08:00
/// Import the LCD Panel Module
const panel = @import("./panel.zig");
2022-12-03 17:03:19 +08:00
2022-11-01 20:53:09 +08:00
/// Import NuttX Functions from C
2022-10-31 11:56:43 +08:00
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");
2022-12-08 21:46:49 +08:00
@cInclude("sys/ioctl.h");
2022-10-31 11:56:43 +08:00
@cInclude("inttypes.h");
@cInclude("unistd.h");
@cInclude("stdlib.h");
@cInclude("stdio.h");
2022-12-08 21:46:49 +08:00
@cInclude("fcntl.h");
@cInclude("nuttx/leds/userled.h");
2022-10-31 11:56:43 +08:00
// 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 17:08:08 +08:00
/// See https://lupyuen.github.io/articles/de#appendix-programming-the-allwinner-a64-display-engine
2022-11-11 17:53:25 +08:00
fn renderGraphics(
comptime channels: u8 // Number of UI Channels to render: 1 or 3
) void {
2022-11-12 07:28:20 +08:00
debug("renderGraphics: start", .{});
defer { debug("renderGraphics: end", .{}); }
2022-10-31 16:47:32 +08:00
2022-10-31 14:25:41 +08:00
// Validate the Framebuffer Sizes at Compile Time
comptime {
2022-11-11 17:53:25 +08:00
assert(channels == 1 or channels == 3);
2022-10-31 14:25:41 +08:00
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-11-11 17:53:25 +08:00
// TODO: Handle DMB non-relaxed write
// https://developer.arm.com/documentation/dui0489/c/arm-and-thumb-instructions/miscellaneous-instructions/dmb--dsb--and-isb
2022-11-01 21:04:01 +08:00
2022-11-01 13:43:18 +08:00
// Init Framebuffer 0:
// Fill with Blue, Green and Red
var i: usize = 0;
while (i < fb0.len) : (i += 1) {
// Colours are in XRGB 8888 format
if (i < fb0.len / 4) {
// Blue for top quarter
2022-11-09 14:24:00 +08:00
fb0[i] = 0x8000_0080;
2022-11-01 13:43:18 +08:00
} else if (i < fb0.len / 2) {
// Green for next quarter
2022-11-09 14:24:00 +08:00
fb0[i] = 0x8000_8000;
2022-11-01 13:43:18 +08:00
} else {
// Red for lower half
2022-11-09 14:24:00 +08:00
fb0[i] = 0x8080_0000;
2022-11-01 13:43:18 +08:00
}
}
// Init Framebuffer 1:
// Fill with Semi-Transparent Blue
i = 0;
while (i < fb1.len) : (i += 1) {
// Colours are in ARGB 8888 format
2022-11-09 14:24:00 +08:00
fb1[i] = 0x8000_0080;
2022-11-01 13:43:18 +08:00
}
// Init Framebuffer 2:
// Fill with Semi-Transparent Green Circle
var y: usize = 0;
2022-12-08 11:20:04 +08:00
while (y < PANEL_HEIGHT) : (y += 1) {
2022-11-01 13:43:18 +08:00
var x: usize = 0;
2022-12-08 11:20:04 +08:00
while (x < PANEL_WIDTH) : (x += 1) {
2022-11-01 13:43:18 +08:00
// Get pixel index
2022-12-08 11:20:04 +08:00
const p = (y * PANEL_WIDTH) + x;
2022-11-01 13:43:18 +08:00
assert(p < fb2.len);
// Shift coordinates so that centre of screen is (0,0)
2022-12-08 11:20:04 +08:00
const half_width = PANEL_WIDTH / 2;
const half_height = PANEL_HEIGHT / 2;
const x_shift = @intCast(isize, x) - half_width;
const y_shift = @intCast(isize, y) - half_height;
2022-11-01 13:43:18 +08:00
// If x^2 + y^2 < radius^2, set the pixel to Semi-Transparent Green
2022-12-08 11:20:04 +08:00
if (x_shift*x_shift + y_shift*y_shift < half_width*half_width) {
2022-11-09 14:24:00 +08:00
fb2[p] = 0x8000_8000; // Semi-Transparent Green in ARGB 8888 Format
2022-11-01 13:43:18 +08:00
} else { // Otherwise set to Transparent Black
2022-11-09 14:24:00 +08:00
fb2[p] = 0x0000_0000; // Transparent Black in ARGB 8888 Format
2022-11-01 13:43:18 +08:00
}
}
}
2022-10-31 16:40:59 +08:00
// 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
2022-11-03 08:43:05 +08:00
);
// Init the 2 Overlay UI Channels
2022-11-06 19:49:08 +08:00
inline for (overlayInfo) | ov, ov_index | {
2022-11-03 08:43:05 +08:00
initUiChannel(
@intCast(u8, ov_index + 2), // UI Channel Number (2 and 3 for Overlay UI Channels)
2022-11-11 17:53:25 +08:00
if (channels == 3) ov.fbmem else null, // Start of frame buffer memory
2022-11-03 08:43:05 +08:00
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-11-12 12:08:54 +08:00
// Set UI Blender Route, enable Blender Pipes and apply the settings
2022-11-11 17:53:25 +08:00
applySettings(channels);
2022-11-03 08:43:05 +08:00
}
2022-11-11 17:53:25 +08:00
/// Render a Test Pattern on PinePhone's Display.
/// Called by test_display() in https://github.com/lupyuen/incubator-nuttx-apps/blob/de3/examples/hello/test_display.c
pub export fn test_render(
channels: c_int // Number of UI Channels to render: 0, 1 or 3
2022-11-11 17:53:25 +08:00
) void {
debug("test_render: start, channels={}", .{ channels });
defer { debug("test_render: end", .{}); }
// Turn on Display Backlight
if (channels != 0) {
backlight.backlight_enable(90);
}
2022-12-03 08:43:19 +08:00
// Init PMIC not needed. Maybe already done by U-Boot?
// https://megous.com/git/p-boot/tree/src/pmic.c#n279
2022-12-03 17:03:19 +08:00
// Init Timing Controller TCON0
2022-12-04 08:59:47 +08:00
tcon.tcon0_init();
2022-12-08 21:46:49 +08:00
// Init Power Mgmt IC
2022-12-04 08:59:47 +08:00
pmic.display_board_init();
2022-12-03 08:43:19 +08:00
2022-12-04 08:59:47 +08:00
// Enable MIPI DSI Block
dsi.enable_dsi_block();
2022-12-03 08:43:19 +08:00
2022-12-04 08:59:47 +08:00
// Enable MIPI Display Physical Layer
dphy.dphy_enable();
2022-12-03 08:43:19 +08:00
2022-12-04 08:59:47 +08:00
// Reset LCD Panel
panel.panel_reset();
2022-12-03 08:43:19 +08:00
// Init LCD Panel
dsi.panel_init();
2022-12-04 08:59:47 +08:00
// Start MIPI DSI HSC and HSD
dsi.start_dsi();
// Init Display Engine
de2_init();
// Wait a while
_ = c.usleep(160000);
// Render Graphics with Display Engine
2022-11-11 17:53:25 +08:00
switch (channels) {
0 => renderGraphics(3), // Render 3 UI Channels
2022-11-11 17:53:25 +08:00
1 => renderGraphics(1), // Render 1 UI Channel
3 => renderGraphics(3), // Render 3 UI Channels
else => debug("Argument must be 1 or 3", .{}),
2022-10-31 15:43:07 +08:00
}
2022-10-31 16:40:59 +08:00
}
2022-10-31 18:14:19 +08:00
/// Hardware Registers for PinePhone's A64 Display Engine.
/// See https://lupyuen.github.io/articles/de#appendix-overview-of-allwinner-a64-display-engine
2022-11-06 18:45:04 +08:00
/// Display Engine Base Address is 0x0100 0000 (DE Page 24)
2022-10-31 18:02:26 +08:00
const DISPLAY_ENGINE_BASE_ADDRESS = 0x0100_0000;
2022-11-06 18:45:04 +08:00
/// MIXER0 is at DE Offset 0x10 0000 (DE Page 24, 0x110 0000)
2022-10-31 18:02:26 +08:00
const MIXER0_BASE_ADDRESS = DISPLAY_ENGINE_BASE_ADDRESS + 0x10_0000;
2022-11-06 18:45:04 +08:00
/// GLB (Global Registers) is at MIXER0 Offset 0x0000 (DE Page 90, 0x110 0000)
2022-10-31 18:02:26 +08:00
const GLB_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x0000;
2022-11-06 18:45:04 +08:00
/// BLD (Blender) is at MIXER0 Offset 0x1000 (DE Page 90, 0x110 1000)
2022-10-31 18:02:26 +08:00
const BLD_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x1000;
2022-11-06 17:20:57 +08:00
/// OVL_UI(CH1) (UI Overlay 1) is at MIXER0 Offset 0x3000 (DE Page 102, 0x110 3000)
2022-10-31 18:14:19 +08:00
const OVL_UI_CH1_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x3000;
2022-10-31 18:02:26 +08:00
2022-11-06 18:45:04 +08:00
/// UI_SCALER1(CH1) (UI Scaler 1) is at MIXER0 Offset 0x04 0000 (DE Page 90, 0x114 0000)
2022-11-06 17:20:57 +08:00
const UI_SCALER1_CH1_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x04_0000;
2022-10-31 17:08:08 +08:00
/// Initialise the UI Blender for PinePhone's A64 Display Engine.
/// See https://lupyuen.github.io/articles/de#appendix-programming-the-allwinner-a64-display-engine
2022-10-31 16:51:01 +08:00
fn initUiBlender() void {
2022-11-03 13:40:25 +08:00
debug("initUiBlender: start", .{});
defer { debug("initUiBlender: end", .{}); }
2022-10-31 18:04:44 +08:00
2022-11-06 18:45:04 +08:00
// Set Blender Background
// BLD_BK_COLOR (Blender Background Color) at BLD Offset 0x88
// Set to 0xFF00 0000 (Black Background Color)
// RESERVED (Bits 24 to 31) = 0xFF (Undocumented)
// RED (Bits 16 to 23) = 0
// GREEN (Bits 8 to 15) = 0
// BLUE (Bits 0 to 7) = 0
// (DE Page 109, 0x110 1088)
2022-11-06 18:19:02 +08:00
debug("Set Blender Background", .{});
2022-11-06 20:49:53 +08:00
const RESERVED: u32 = 0xFF << 24;
const RED: u24 = 0 << 16;
const GREEN: u16 = 0 << 8;
const BLUE: u8 = 0 << 0;
2022-11-06 18:45:04 +08:00
const color = RESERVED
| RED
| GREEN
| BLUE;
comptime{ assert(color == 0xFF00_0000); }
2022-11-06 20:53:40 +08:00
2022-10-31 18:02:26 +08:00
const BLD_BK_COLOR = BLD_BASE_ADDRESS + 0x88;
2022-11-06 18:45:04 +08:00
comptime{ assert(BLD_BK_COLOR == 0x110_1088); }
putreg32(color, BLD_BK_COLOR);
// Set Blender Pre-Multiply
// BLD_PREMUL_CTL (Blender Pre-Multiply Control) at BLD Offset 0x84
// Set to 0 (No Pre-Multiply for Alpha, Pipes 0 to 3)
// P3_ALPHA_MODE (Bit 3) = 0 (Pipe 3: No Pre-Multiply)
// P2_ALPHA_MODE (Bit 2) = 0 (Pipe 2: No Pre-Multiply)
// P1_ALPHA_MODE (Bit 1) = 0 (Pipe 1: No Pre-Multiply)
// P0_ALPHA_MODE (Bit 0) = 0 (Pipe 0: No Pre-Multiply)
// (DE Page 109, 0x110 1084)
2022-11-06 18:19:02 +08:00
debug("Set Blender Pre-Multiply", .{});
2022-11-06 20:49:53 +08:00
const P3_ALPHA_MODE: u4 = 0 << 3; // Pipe 3: No Pre-Multiply
const P2_ALPHA_MODE: u3 = 0 << 2; // Pipe 2: No Pre-Multiply
const P1_ALPHA_MODE: u2 = 0 << 1; // Pipe 1: No Pre-Multiply
const P0_ALPHA_MODE: u1 = 0 << 0; // Pipe 0: No Pre-Multiply
2022-11-06 18:45:04 +08:00
const premultiply = P3_ALPHA_MODE
| P2_ALPHA_MODE
| P1_ALPHA_MODE
| P0_ALPHA_MODE;
comptime{ assert(premultiply == 0); }
2022-11-06 20:53:40 +08:00
2022-10-31 18:02:26 +08:00
const BLD_PREMUL_CTL = BLD_BASE_ADDRESS + 0x84;
2022-11-06 18:45:04 +08:00
comptime{ assert(BLD_PREMUL_CTL == 0x110_1084); }
putreg32(premultiply, BLD_PREMUL_CTL);
2022-10-31 11:56:43 +08:00
}
2022-10-31 12:18:57 +08:00
2022-11-12 12:08:54 +08:00
/// Set UI Blender Route, enable Blender Pipes and apply the settings for PinePhone's A64 Display Engine.
2022-11-06 18:45:04 +08:00
/// See https://lupyuen.github.io/articles/de#appendix-programming-the-allwinner-a64-display-engine
2022-11-02 15:55:25 +08:00
fn applySettings(
2022-11-06 19:49:08 +08:00
comptime channels: u8 // Number of enabled UI Channels
2022-11-02 15:55:25 +08:00
) void {
2022-11-03 13:40:25 +08:00
debug("applySettings: start", .{});
defer { debug("applySettings: end", .{}); }
2022-11-06 19:49:08 +08:00
comptime { assert(channels == 1 or channels == 3); }
// Set Blender Route
// BLD_CH_RTCTL (Blender Routing Control) at BLD Offset 0x080
// If Rendering 3 UI Channels: Set to 0x321 (DMB)
// P2_RTCTL (Bits 8 to 11) = 3 (Pipe 2 from Channel 3)
// P1_RTCTL (Bits 4 to 7) = 2 (Pipe 1 from Channel 2)
// P0_RTCTL (Bits 0 to 3) = 1 (Pipe 0 from Channel 1)
// If Rendering 1 UI Channel: Set to 1 (DMB)
// P0_RTCTL (Bits 0 to 3) = 1 (Pipe 0 from Channel 1)
// (DE Page 108, 0x110 1080)
2022-11-06 18:19:02 +08:00
debug("Set Blender Route", .{});
2022-11-06 19:56:33 +08:00
const P2_RTCTL: u12 = switch (channels) { // For Pipe 2...
2022-11-06 19:49:08 +08:00
3 => 3, // 3 UI Channels: Select Pipe 2 from UI Channel 3
2022-11-06 19:56:33 +08:00
1 => 0, // 1 UI Channel: Unused Pipe 2
2022-11-06 19:49:08 +08:00
else => unreachable,
2022-11-06 19:56:33 +08:00
} << 8; // Bits 8 to 11
2022-11-06 20:06:32 +08:00
2022-11-06 19:56:33 +08:00
const P1_RTCTL: u8 = switch (channels) { // For Pipe 1...
2022-11-06 19:49:08 +08:00
3 => 2, // 3 UI Channels: Select Pipe 1 from UI Channel 2
2022-11-06 19:56:33 +08:00
1 => 0, // 1 UI Channel: Unused Pipe 1
2022-11-06 19:49:08 +08:00
else => unreachable,
2022-11-06 19:56:33 +08:00
} << 4; // Bits 4 to 7
2022-11-06 20:06:32 +08:00
2022-11-06 19:56:33 +08:00
const P0_RTCTL: u4 = 1 << 0; // Select Pipe 0 from UI Channel 1
2022-11-06 19:49:08 +08:00
const route = P2_RTCTL
| P1_RTCTL
| P0_RTCTL;
comptime{ assert(route == 0x321 or route == 1); }
2022-11-06 20:06:32 +08:00
2022-10-31 19:04:36 +08:00
const BLD_CH_RTCTL = BLD_BASE_ADDRESS + 0x080;
2022-11-06 19:49:08 +08:00
comptime{ assert(BLD_CH_RTCTL == 0x110_1080); }
putreg32(route, BLD_CH_RTCTL); // TODO: DMB
2022-10-31 19:04:36 +08:00
2022-11-10 20:27:44 +08:00
// Enable Blender Pipes
2022-11-06 20:49:53 +08:00
// BLD_FILL_COLOR_CTL (Blender Fill Color Control) at BLD Offset 0x000
// If Rendering 3 UI Channels: Set to 0x701 (DMB)
// P2_EN (Bit 10) = 1 (Enable Pipe 2)
// P1_EN (Bit 9) = 1 (Enable Pipe 1)
// P0_EN (Bit 8) = 1 (Enable Pipe 0)
// P0_FCEN (Bit 0) = 1 (Enable Pipe 0 Fill Color)
// If Rendering 1 UI Channel: Set to 0x101 (DMB)
// P0_EN (Bit 8) = 1 (Enable Pipe 0)
// P0_FCEN (Bit 0) = 1 (Enable Pipe 0 Fill Color)
// (DE Page 106, 0x110 1000)
2022-11-10 20:27:44 +08:00
debug("Enable Blender Pipes", .{});
2022-11-06 20:49:53 +08:00
const P2_EN: u11 = switch (channels) { // For Pipe 2...
3 => 1, // 3 UI Channels: Enable Pipe 2
1 => 0, // 1 UI Channel: Disable Pipe 2
else => unreachable,
} << 10; // Bit 10
const P1_EN: u10 = switch (channels) { // For Pipe 1...
3 => 1, // 3 UI Channels: Enable Pipe 1
1 => 0, // 1 UI Channel: Disable Pipe 1
else => unreachable,
} << 9; // Bit 9
const P0_EN: u9 = 1 << 8; // Enable Pipe 0
const P0_FCEN: u1 = 1 << 0; // Enable Pipe 0 Fill Color
const fill = P2_EN
| P1_EN
| P0_EN
| P0_FCEN;
comptime{ assert(fill == 0x701 or fill == 0x101); }
2022-11-06 18:19:02 +08:00
const BLD_FILL_COLOR_CTL = BLD_BASE_ADDRESS + 0x000;
2022-11-06 20:49:53 +08:00
comptime{ assert(BLD_FILL_COLOR_CTL == 0x110_1000); }
putreg32(fill, BLD_FILL_COLOR_CTL); // TODO: DMB
2022-10-31 19:04:36 +08:00
2022-11-06 20:53:40 +08:00
// Apply Settings
// GLB_DBUFFER (Global Double Buffer Control) at GLB Offset 0x008
// Set to 1 (DMB)
// DOUBLE_BUFFER_RDY (Bit 0) = 1
// (Register Value is ready for update)
// (DE Page 93, 0x110 0008)
2022-11-01 11:36:40 +08:00
debug("Apply Settings", .{});
2022-11-06 20:53:40 +08:00
const DOUBLE_BUFFER_RDY: u1 = 1 << 0; // Register Value is ready for update
comptime{ assert(DOUBLE_BUFFER_RDY == 1); }
2022-10-31 19:04:36 +08:00
const GLB_DBUFFER = GLB_BASE_ADDRESS + 0x008;
2022-11-06 20:53:40 +08:00
comptime{ assert(GLB_DBUFFER == 0x110_0008); }
putreg32(DOUBLE_BUFFER_RDY, GLB_DBUFFER); // TODO: DMB
2022-10-31 17:18:21 +08:00
}
2022-10-31 15:43:07 +08:00
/// Initialise a UI Channel for PinePhone's A64 Display Engine.
2022-10-31 17:08:08 +08:00
/// We use 3 UI Channels: Base UI Channel (#1) plus 2 Overlay UI Channels (#2, #3).
/// See https://lupyuen.github.io/articles/de#appendix-programming-the-allwinner-a64-display-engine
2022-10-31 15:43:07 +08:00
fn initUiChannel(
2022-11-06 19:49:08 +08:00
comptime channel: u8, // UI Channel Number: 1, 2 or 3
2022-10-31 17:08:08 +08:00
fbmem: ?*anyopaque, // Start of frame buffer memory, or null if this channel should be disabled
2022-11-06 19:49:08 +08:00
comptime fblen: usize, // Length of frame buffer memory in bytes
comptime stride: c.fb_coord_t, // Length of a line in bytes (4 bytes per pixel)
comptime xres: c.fb_coord_t, // Horizontal resolution in pixel columns
comptime yres: c.fb_coord_t, // Vertical resolution in pixel rows
comptime xoffset: c.fb_coord_t, // Horizontal offset in pixel columns
comptime yoffset: c.fb_coord_t, // Vertical offset in pixel rows
2022-10-31 16:51:01 +08:00
) void {
2022-11-03 13:40:25 +08:00
debug("initUiChannel: start", .{});
defer { debug("initUiChannel: end", .{}); }
2022-11-06 20:06:32 +08:00
// Validate Framebuffer Size and Stride at Compile Time
2022-11-06 19:49:08 +08:00
comptime {
assert(channel >= 1 and channel <= 3);
assert(fblen == @intCast(usize, xres) * yres * 4);
assert(stride == @intCast(usize, xres) * 4);
}
2022-10-31 15:43:07 +08:00
2022-11-06 17:13:47 +08:00
// OVL_UI(CH1) (UI Overlay 1) is at MIXER0 Offset 0x3000
// OVL_UI(CH2) (UI Overlay 2) is at MIXER0 Offset 0x4000
// OVL_UI(CH3) (UI Overlay 3) is at MIXER0 Offset 0x5000
2022-11-06 17:20:57 +08:00
// (DE Page 102, 0x110 3000 / 0x110 4000 / 0x110 5000)
const OVL_UI_BASE_ADDRESS = OVL_UI_CH1_BASE_ADDRESS
+ @intCast(u64, channel - 1) * 0x1000;
2022-11-06 19:49:08 +08:00
comptime{ assert(OVL_UI_BASE_ADDRESS == 0x110_3000 or OVL_UI_BASE_ADDRESS == 0x110_4000 or OVL_UI_BASE_ADDRESS == 0x110_5000); }
2022-10-31 19:33:11 +08:00
2022-11-06 17:20:57 +08:00
// UI_SCALER1(CH1) is at MIXER0 Offset 0x04 0000
// UI_SCALER2(CH2) is at MIXER0 Offset 0x05 0000
// UI_SCALER3(CH3) is at MIXER0 Offset 0x06 0000
// (DE Page 90, 0x114 0000 / 0x115 0000 / 0x116 0000)
const UI_SCALER_BASE_ADDRESS = UI_SCALER1_CH1_BASE_ADDRESS
+ @intCast(u64, channel - 1) * 0x10000;
2022-10-31 17:08:08 +08:00
// If UI Channel should be disabled...
if (fbmem == null) {
2022-11-01 11:36:40 +08:00
// Disable Overlay and Pipe:
2022-11-06 17:13:47 +08:00
// OVL_UI_ATTR_CTL (UI Overlay Attribute Control) at OVL_UI Offset 0x00
// Set to 0 (Disable UI Overlay Channel)
// LAY_EN (Bit 0) = 0 (Disable Layer)
// (DE Page 102)
2022-11-01 11:36:40 +08:00
debug("Channel {}: Disable Overlay and Pipe", .{ channel });
2022-11-06 17:13:47 +08:00
const OVL_UI_ATTR_CTL = OVL_UI_BASE_ADDRESS + 0x00;
2022-11-06 19:49:08 +08:00
comptime{ assert(OVL_UI_ATTR_CTL == 0x110_3000 or OVL_UI_ATTR_CTL == 0x110_4000 or OVL_UI_ATTR_CTL == 0x110_5000); }
2022-11-06 17:13:47 +08:00
putreg32(0, OVL_UI_ATTR_CTL);
2022-10-31 17:08:08 +08:00
2022-11-01 11:36:40 +08:00
// Disable Scaler:
2022-11-06 17:13:47 +08:00
// UIS_CTRL_REG at Offset 0 of UI_SCALER1(CH1) or UI_SCALER2(CH2) or UI_SCALER3(CH3)
// Set to 0 (Disable UI Scaler)
// EN (Bit 0) = 0 (Disable UI Scaler)
// (DE Page 66)
2022-11-01 11:36:40 +08:00
debug("Channel {}: Disable Scaler", .{ channel });
2022-11-06 17:13:47 +08:00
const UIS_CTRL_REG = UI_SCALER_BASE_ADDRESS + 0;
2022-11-06 19:49:08 +08:00
comptime{ assert(UIS_CTRL_REG == 0x114_0000 or UIS_CTRL_REG == 0x115_0000 or UIS_CTRL_REG == 0x116_0000); }
2022-11-06 17:13:47 +08:00
putreg32(0, UIS_CTRL_REG);
2022-10-31 17:08:08 +08:00
2022-10-31 19:33:11 +08:00
// Skip to next UI Channel
return;
2022-10-31 17:08:08 +08:00
}
2022-11-07 08:50:04 +08:00
// Set Overlay (Assume Layer = 0)
2022-11-06 21:06:03 +08:00
// OVL_UI_ATTR_CTL (UI Overlay Attribute Control) at OVL_UI Offset 0x00
// For Channel 1: Set to 0xFF00 0405
// For Channel 2: Set to 0xFF00 0005
// For Channel 3: Set to 0x7F00 0005
// LAY_GLBALPHA (Bits 24 to 31) = 0xFF or 0x7F
// (Global Alpha Value is Opaque or Semi-Transparent)
// LAY_FBFMT (Bits 8 to 12) = 4 or 0
// (Input Data Format is XRGB 8888 or ARGB 8888)
// LAY_ALPHA_MODE (Bits 1 to 2) = 2
// (Global Alpha is mixed with Pixel Alpha)
// (Input Alpha Value = Global Alpha Value * Pixels Alpha Value)
// LAY_EN (Bit 0) = 1 (Enable Layer)
// (DE Page 102, 0x110 3000 / 0x110 4000 / 0x110 5000)
2022-11-01 11:36:40 +08:00
debug("Channel {}: Set Overlay ({} x {})", .{ channel, xres, yres });
2022-11-06 21:06:03 +08:00
const LAY_GLBALPHA: u32 = switch (channel) { // For Global Alpha Value...
1 => 0xFF, // Channel 1: Opaque
2 => 0xFF, // Channel 2: Opaque
3 => 0x7F, // Channel 3: Semi-Transparent
else => unreachable,
} << 24; // Bits 24 to 31
const LAY_FBFMT: u13 = switch (channel) { // For Input Data Format...
1 => 4, // Channel 1: XRGB 8888
2 => 0, // Channel 2: ARGB 8888
3 => 0, // Channel 3: ARGB 8888
else => unreachable,
} << 8; // Bits 8 to 12
2022-11-07 08:50:04 +08:00
const LAY_ALPHA_MODE: u3 = 2 << 1; // Global Alpha is mixed with Pixel Alpha
const LAY_EN: u1 = 1 << 0; // Enable Layer
const attr = LAY_GLBALPHA
| LAY_FBFMT
| LAY_ALPHA_MODE
| LAY_EN;
comptime{ assert(attr == 0xFF00_0405 or attr == 0xFF00_0005 or attr == 0x7F00_0005); }
2022-11-06 21:06:03 +08:00
2022-11-06 17:13:47 +08:00
const OVL_UI_ATTR_CTL = OVL_UI_BASE_ADDRESS + 0x00;
2022-11-07 08:50:04 +08:00
comptime{ assert(OVL_UI_ATTR_CTL == 0x110_3000 or OVL_UI_ATTR_CTL == 0x110_4000 or OVL_UI_ATTR_CTL == 0x110_5000); }
putreg32(attr, OVL_UI_ATTR_CTL);
2022-11-06 17:28:06 +08:00
2022-11-07 08:50:04 +08:00
// OVL_UI_TOP_LADD (UI Overlay Top Field Memory Block Low Address) at OVL_UI Offset 0x10
// Set to Framebuffer Address: fb0, fb1 or fb2
// (DE Page 104, 0x110 3010 / 0x110 4010 / 0x110 5010)
2022-11-06 17:30:10 +08:00
const ptr = @ptrToInt(fbmem.?);
2022-11-07 08:50:04 +08:00
const OVL_UI_TOP_LADD = OVL_UI_BASE_ADDRESS + 0x10;
comptime{ assert(OVL_UI_TOP_LADD == 0x110_3010 or OVL_UI_TOP_LADD == 0x110_4010 or OVL_UI_TOP_LADD == 0x110_5010); }
2022-11-06 17:30:10 +08:00
putreg32(@intCast(u32, ptr), OVL_UI_TOP_LADD);
2022-10-31 17:18:21 +08:00
2022-11-07 08:50:04 +08:00
// OVL_UI_PITCH (UI Overlay Memory Pitch) at OVL_UI Offset 0x0C
// Set to (width * 4), number of bytes per row
// (DE Page 104, 0x110 300C / 0x110 400C / 0x110 500C)
2022-11-01 11:13:59 +08:00
const OVL_UI_PITCH = OVL_UI_BASE_ADDRESS + 0x0C;
2022-11-07 08:50:04 +08:00
comptime{ assert(OVL_UI_PITCH == 0x110_300C or OVL_UI_PITCH == 0x110_400C or OVL_UI_PITCH == 0x110_500C); }
2022-10-31 19:33:11 +08:00
putreg32(xres * 4, OVL_UI_PITCH);
2022-10-31 17:18:21 +08:00
2022-11-07 08:50:04 +08:00
// OVL_UI_MBSIZE (UI Overlay Memory Block Size) at OVL_UI Offset 0x04
// Set to (height-1) << 16 + (width-1)
// (DE Page 104, 0x110 3004 / 0x110 4004 / 0x110 5004)
2022-11-01 11:13:59 +08:00
const height_width: u32 = @intCast(u32, yres - 1) << 16
| (xres - 1);
2022-11-07 08:50:04 +08:00
const OVL_UI_MBSIZE = OVL_UI_BASE_ADDRESS + 0x04;
comptime{ assert(OVL_UI_MBSIZE == 0x110_3004 or OVL_UI_MBSIZE == 0x110_4004 or OVL_UI_MBSIZE == 0x110_5004); }
2022-11-01 11:13:59 +08:00
putreg32(height_width, OVL_UI_MBSIZE);
2022-10-31 17:18:21 +08:00
2022-11-07 08:50:04 +08:00
// OVL_UI_SIZE (UI Overlay Overlay Window Size) at OVL_UI Offset 0x88
// Set to (height-1) << 16 + (width-1)
// (DE Page 106, 0x110 3088 / 0x110 4088 / 0x110 5088)
2022-11-01 11:13:59 +08:00
const OVL_UI_SIZE = OVL_UI_BASE_ADDRESS + 0x88;
2022-11-07 08:50:04 +08:00
comptime{ assert(OVL_UI_SIZE == 0x110_3088 or OVL_UI_SIZE == 0x110_4088 or OVL_UI_SIZE == 0x110_5088); }
2022-11-01 11:13:59 +08:00
putreg32(height_width, OVL_UI_SIZE);
2022-10-31 17:18:21 +08:00
2022-11-07 08:50:04 +08:00
// OVL_UI_COOR (UI Overlay Memory Block Coordinate) at OVL_UI Offset 0x08
// Set to 0 (Overlay at X=0, Y=0)
// (DE Page 104, 0x110 3008 / 0x110 4008 / 0x110 5008)
2022-11-01 11:13:59 +08:00
const OVL_UI_COOR = OVL_UI_BASE_ADDRESS + 0x08;
2022-11-07 08:50:04 +08:00
comptime{ assert(OVL_UI_COOR == 0x110_3008 or OVL_UI_COOR == 0x110_4008 or OVL_UI_COOR == 0x110_5008); }
2022-10-31 19:33:11 +08:00
putreg32(0, OVL_UI_COOR);
2022-10-31 17:18:21 +08:00
2022-11-07 08:52:51 +08:00
// For Channel 1: Set Blender Output
2022-11-01 11:36:40 +08:00
if (channel == 1) {
2022-11-07 08:52:51 +08:00
// BLD_SIZE (Blender Output Size Setting) at BLD Offset 0x08C
// Set to (height-1) << 16 + (width-1)
// (DE Page 110, 0x110 108C)
2022-11-01 11:36:40 +08:00
debug("Channel {}: Set Blender Output", .{ channel });
2022-10-31 19:33:11 +08:00
const BLD_SIZE = BLD_BASE_ADDRESS + 0x08C;
2022-11-07 08:52:51 +08:00
comptime{ assert(BLD_SIZE == 0x110_108C); }
2022-11-01 11:13:59 +08:00
putreg32(height_width, BLD_SIZE);
2022-10-31 19:33:11 +08:00
2022-11-07 08:52:51 +08:00
// GLB_SIZE (Global Size) at GLB Offset 0x00C
// Set to (height-1) << 16 + (width-1)
// (DE Page 93, 0x110 000C)
2022-10-31 19:33:11 +08:00
const GLB_SIZE = GLB_BASE_ADDRESS + 0x00C;
2022-11-07 08:52:51 +08:00
comptime{ assert(GLB_SIZE == 0x110_000C); }
2022-11-01 11:13:59 +08:00
putreg32(height_width, GLB_SIZE);
2022-10-31 19:33:11 +08:00
}
2022-10-31 17:18:21 +08:00
2022-11-07 09:00:00 +08:00
// Set Blender Input Pipe (N = Pipe Number, from 0 to 2 for Channels 1 to 3)
2022-10-31 19:33:11 +08:00
const pipe: u64 = channel - 1;
2022-11-01 11:36:40 +08:00
debug("Channel {}: Set Blender Input Pipe {} ({} x {})", .{ channel, pipe, xres, yres });
2022-10-31 19:33:11 +08:00
2022-11-07 09:13:01 +08:00
// Note: DE Page 91 shows incorrect offset N*0x14 for
// BLD_CH_ISIZE, BLD_FILL_COLOR and BLD_CH_OFFSET.
// Correct offset is N*0x10, see DE Page 108
2022-11-07 09:00:00 +08:00
// BLD_CH_ISIZE (Blender Input Memory Size) at BLD Offset 0x008 + N*0x10 (N=0,1,2,3,4)
// Set to (height-1) << 16 + (width-1)
// (DE Page 108, 0x110 1008 / 0x110 1018 / 0x110 1028)
2022-10-31 19:33:11 +08:00
const BLD_CH_ISIZE = BLD_BASE_ADDRESS + 0x008 + pipe * 0x10;
2022-11-07 09:00:00 +08:00
comptime{ assert(BLD_CH_ISIZE == 0x110_1008 or BLD_CH_ISIZE == 0x110_1018 or BLD_CH_ISIZE == 0x110_1028); }
2022-11-01 11:13:59 +08:00
putreg32(height_width, BLD_CH_ISIZE);
2022-10-31 17:18:21 +08:00
2022-11-07 09:00:00 +08:00
// BLD_FILL_COLOR (Blender Fill Color) at BLD Offset 0x004 + N*0x10 (N=0,1,2,3,4)
// Set to 0xFF00 0000 (Opaque Black)
// ALPHA (Bits 24 to 31) = 0xFF
2022-11-11 11:44:25 +08:00
// RED (Bits 16 to 23) = 0
// GREEN (Bits 8 to 15) = 0
// BLUE (Bits 0 to 7) = 0
2022-11-07 09:00:00 +08:00
// (DE Page 107, 0x110 1004 / 0x110 1014 / 0x110 1024)
const ALPHA: u32 = 0xFF << 24; // Opaque
const RED: u24 = 0 << 16; // Black
const GREEN: u18 = 0 << 8;
const BLUE: u8 = 0 << 0;
const color = ALPHA
| RED
| GREEN
| BLUE;
comptime{ assert(color == 0xFF00_0000); }
2022-11-06 18:19:02 +08:00
2022-10-31 19:33:11 +08:00
const BLD_FILL_COLOR = BLD_BASE_ADDRESS + 0x004 + pipe * 0x10;
2022-11-07 09:00:00 +08:00
comptime{ assert(BLD_FILL_COLOR == 0x110_1004 or BLD_FILL_COLOR == 0x110_1014 or BLD_FILL_COLOR == 0x110_1024); }
putreg32(color, BLD_FILL_COLOR);
2022-10-31 17:18:21 +08:00
2022-11-07 09:03:51 +08:00
// BLD_CH_OFFSET (Blender Input Memory Offset) at BLD Offset 0x00C + N*0x10 (N=0,1,2,3,4)
// Set to y_offset << 16 + x_offset
// For Channel 1: Set to 0
// For Channel 2: Set to 0x34 0034
// For Channel 3: Set to 0
// (DE Page 108, 0x110 100C / 0x110 101C / 0x110 102C)
const offset = @intCast(u32, yoffset) << 16
| xoffset;
comptime{ assert(offset == 0 or offset == 0x34_0034); }
2022-10-31 19:33:11 +08:00
const BLD_CH_OFFSET = BLD_BASE_ADDRESS + 0x00C + pipe * 0x10;
2022-11-07 09:03:51 +08:00
comptime{ assert(BLD_CH_OFFSET == 0x110_100C or BLD_CH_OFFSET == 0x110_101C or BLD_CH_OFFSET == 0x110_102C); }
putreg32(offset, BLD_CH_OFFSET);
2022-10-31 17:18:21 +08:00
2022-11-07 09:13:01 +08:00
// BLD_CTL (Blender Control) at BLD Offset 0x090 + N*4
// Set to 0x301 0301
// BLEND_AFD (Bits 24 to 27) = 3
// (Coefficient for destination alpha data Q[d] is 1-A[s])
// BLEND_AFS (Bits 16 to 19) = 1
// (Coefficient for source alpha data Q[s] is 1)
// BLEND_PFD (Bits 8 to 11) = 3
// (Coefficient for destination pixel data F[d] is 1-A[s])
// BLEND_PFS (Bits 0 to 3) = 1
// (Coefficient for source pixel data F[s] is 1)
// (DE Page 110, 0x110 1090 / 0x110 1094 / 0x110 1098)
const BLEND_AFD: u28 = 3 << 24; // Coefficient for destination alpha data Q[d] is 1-A[s]
const BLEND_AFS: u20 = 1 << 16; // Coefficient for source alpha data Q[s] is 1
const BLEND_PFD: u12 = 3 << 8; // Coefficient for destination pixel data F[d] is 1-A[s]
const BLEND_PFS: u4 = 1 << 0; // Coefficient for source pixel data F[s] is 1
const blend = BLEND_AFD
| BLEND_AFS
| BLEND_PFD
| BLEND_PFS;
2022-11-06 18:19:02 +08:00
2022-11-01 11:52:57 +08:00
const BLD_CTL = BLD_BASE_ADDRESS + 0x090 + pipe * 4;
2022-11-07 09:13:01 +08:00
comptime{ assert(BLD_CTL == 0x110_1090 or BLD_CTL == 0x110_1094 or BLD_CTL == 0x110_1098); }
putreg32(blend, BLD_CTL);
2022-11-06 18:23:04 +08:00
// Disable Scaler (Assume were not scaling)
// UIS_CTRL_REG at Offset 0 of UI_SCALER1(CH1) or UI_SCALER2(CH2) or UI_SCALER3(CH3)
// Set to 0 (Disable UI Scaler)
// EN (Bit 0) = 0 (Disable UI Scaler)
// (DE Page 66, 0x114 0000 / 0x115 0000 / 0x116 0000)
2022-11-01 11:36:40 +08:00
debug("Channel {}: Disable Scaler", .{ channel });
2022-11-06 18:23:04 +08:00
const UIS_CTRL_REG = UI_SCALER_BASE_ADDRESS + 0;
2022-11-06 19:49:08 +08:00
comptime{ assert(UIS_CTRL_REG == 0x114_0000 or UIS_CTRL_REG == 0x115_0000 or UIS_CTRL_REG == 0x116_0000); }
2022-11-06 18:19:02 +08:00
putreg32(0, UIS_CTRL_REG);
2022-10-31 15:43:07 +08:00
}
2022-12-08 11:20:04 +08:00
/// LCD Panel Width and Height (pixels)
const PANEL_WIDTH = 720;
const PANEL_HEIGHT = 1440;
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-12-08 11:20:04 +08:00
.xres = PANEL_WIDTH, // Horizontal resolution in pixel columns
.yres = PANEL_HEIGHT, // Vertical resolution in pixel rows
2022-10-31 13:56:10 +08:00
.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
2022-12-08 11:20:04 +08:00
.stride = PANEL_WIDTH * 4, // Length of a line in bytes (4 bytes per pixel)
2022-10-31 14:27:57 +08:00
.display = 0, // Display number (Unused)
2022-10-31 15:52:57 +08:00
.bpp = 32, // Bits per pixel (XRGB 8888)
2022-12-08 11:20:04 +08:00
.xres_virtual = PANEL_WIDTH, // Virtual Horizontal resolution in pixel columns
.yres_virtual = PANEL_HEIGHT, // Virtual Vertical resolution in pixel rows
2022-10-31 14:27:57 +08:00
.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-12-08 11:20:04 +08:00
.stride = PANEL_WIDTH * 4, // Length of a line in bytes
2022-10-31 14:04:58 +08:00
.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
2022-12-08 11:20:04 +08:00
.sarea = c.fb_area_s { .x = 0, .y = 0, .w = PANEL_WIDTH, .h = PANEL_HEIGHT }, // Selected area within the overlay
2022-10-31 14:04:58 +08:00
.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-11-03 09:21:56 +08:00
// TODO: Does alignment prevent flickering?
2022-12-08 11:20:04 +08:00
var fb0 align(0x1000) = std.mem.zeroes([PANEL_WIDTH * PANEL_HEIGHT] u32);
2022-10-31 13:48:27 +08:00
// 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-11-03 09:21:56 +08:00
// TODO: Does alignment prevent flickering?
2022-11-02 16:25:11 +08:00
var fb1 align(0x1000) = std.mem.zeroes([600 * 600] u32);
2022-10-31 13:48:27 +08:00
// 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-11-03 09:21:56 +08:00
// TODO: Does alignment prevent flickering?
2022-12-08 11:20:04 +08:00
var fb2 align(0x1000) = std.mem.zeroes([PANEL_WIDTH * PANEL_HEIGHT] u32);
2022-10-31 14:25:41 +08:00
2022-11-03 10:16:27 +08:00
///////////////////////////////////////////////////////////////////////////////
2022-11-11 17:53:25 +08:00
// Init Display Engine
2022-11-03 10:16:27 +08:00
2022-11-05 17:31:58 +08:00
// SRAM Registers Base Address is 0x01C0 0000 (A31 Page 191)
const SRAM_REGISTERS_BASE_ADDRESS = 0x01C0_0000;
// CCU (Clock Control Unit) Base Address is 0x01C2 0000 (A64 Page 81)
const CCU_BASE_ADDRESS = 0x01C2_0000;
2022-11-05 21:12:33 +08:00
// VIDEO_SCALER(CH0) is at MIXER0 Offset 0x02 0000 (DE Page 90, 0x112 0000)
2022-11-05 21:19:59 +08:00
const VIDEO_SCALER_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x02_0000;
// UI_SCALER1(CH1) is at MIXER0 Offset 0x04 0000 (DE Page 90, 0x114 0000)
2022-11-05 21:25:15 +08:00
const UI_SCALER1_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x04_0000;
2022-11-05 21:19:59 +08:00
// UI_SCALER2(CH2) is at MIXER0 Offset 0x05 0000 (DE Page 90, 0x115 0000)
2022-11-05 21:25:15 +08:00
const UI_SCALER2_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x05_0000;
2022-11-05 21:19:59 +08:00
// FCE (Fresh and Contrast Enhancement) is at MIXER0 Offset 0x0A 0000 (DE Page 61, 0x11A 0000)
const FCE_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x0A_0000;
// BWS (Black and White Stetch) is at MIXER0 Offset 0x0A 2000 (DE Page 42, 0x11A 2000)
const BWS_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x0A_2000;
// LTI (Luminance Transient Improvement) is at MIXER0 Offset 0x0A 4000 (DE Page 71, 0x11A 4000)
const LTI_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x0A_4000;
// PEAKING (Luma Peaking) is at MIXER0 Offset 0x0A 6000 (DE Page 80, 0x11A 6000)
2022-11-05 21:25:15 +08:00
const PEAKING_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x0A_6000;
2022-11-05 21:19:59 +08:00
// ASE (Adaptive Saturation Enhancement) is at MIXER0 Offset 0x0A 8000 (DE Page 40, 0x11A 8000)
const ASE_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x0A_8000;
// FCC (Fancy Color Curvature Change) is at MIXER0 Offset 0x0A A000 (DE Page 56, 0x11A A000)
const FCC_BASE_ADDRESS = MIXER0_BASE_ADDRESS + 0x0A_A000;
// DRC (Dynamic Range Controller) is at Address 0x011B 0000 (DE Page 48, 0x11B 0000)
const DRC_BASE_ADDRESS = 0x011B_0000;
2022-11-05 21:12:33 +08:00
2022-11-03 10:16:27 +08:00
// Init PinePhone's Allwinner A64 Display Engine.
2022-11-11 17:53:25 +08:00
// Called by display_init() in p-boot Display Code.
2022-11-04 13:38:50 +08:00
// See https://lupyuen.github.io/articles/de#appendix-initialising-the-allwinner-a64-display-engine
2022-11-03 10:16:27 +08:00
pub export fn de2_init() void {
2022-11-03 13:40:25 +08:00
debug("de2_init: start", .{});
defer { debug("de2_init: end", .{}); }
2022-11-05 17:31:58 +08:00
// Set High Speed SRAM to DMA Mode
// Set BIST_DMA_CTRL_SEL to 0 for DMA (DMB) (A31 Page 191, 0x1C0 0004)
// BIST_DMA_CTRL_SEL (Bist and DMA Control Select) is Bit 0 of SRAM_CTRL_REG1
// SRAM_CTRL_REG1 (SRAM Control Register 1) is at SRAM Registers Offset 0x4
debug("Set High Speed SRAM to DMA Mode", .{});
const SRAM_CTRL_REG1 = SRAM_REGISTERS_BASE_ADDRESS + 0x4;
comptime{ assert(SRAM_CTRL_REG1 == 0x1C0_0004); }
putreg32(0x0, SRAM_CTRL_REG1); // TODO: DMB
// Set Display Engine PLL to 297 MHz
// Set PLL_DE_CTRL_REG to 0x8100 1701 (DMB)
// PLL_ENABLE (Bit 31) = 1 (Enable PLL)
// PLL_MODE_SEL (Bit 24) = 1 (Integer Mode)
// PLL_FACTOR_N (Bits 8 to 14) = 23 (N = 24)
// PLL_PRE_DIV_M (Bits 0 to 3) = 1 (M = 2)
// Actual PLL Output = 24 MHz * N / M = 288 MHz
// (Slighltly below 297 MHz due to truncation)
// PLL_DE_CTRL_REG (PLL Display Engine Control Register) is at CCU Offset 0x0048
// (A64 Page 96, 0x1C2 0048)
2022-11-05 18:22:52 +08:00
debug("Set Display Engine PLL to 297 MHz", .{});
2022-11-06 20:59:52 +08:00
const PLL_ENABLE: u32 = 1 << 31; // Enable PLL
const PLL_MODE_SEL: u25 = 1 << 24; // Integer Mode
const PLL_FACTOR_N: u15 = 23 << 8; // N = 24
const PLL_PRE_DIV_M: u4 = 1 << 0; // M = 2
2022-11-05 19:19:00 +08:00
const pll = PLL_ENABLE
| PLL_MODE_SEL
| PLL_FACTOR_N
| PLL_PRE_DIV_M;
2022-11-05 17:31:58 +08:00
comptime{ assert(pll == 0x8100_1701); }
2022-11-06 20:59:52 +08:00
2022-11-05 17:31:58 +08:00
const PLL_DE_CTRL_REG = CCU_BASE_ADDRESS + 0x0048;
comptime{ assert(PLL_DE_CTRL_REG == 0x1C2_0048); }
putreg32(pll, PLL_DE_CTRL_REG); // TODO: DMB
2022-11-03 12:47:46 +08:00
2022-11-05 18:22:52 +08:00
// Wait for Display Engine PLL to be stable
// Poll PLL_DE_CTRL_REG (from above) until LOCK (Bit 28) is 1
// (PLL is Locked and Stable)
debug("Wait for Display Engine PLL to be stable", .{});
while (getreg32(PLL_DE_CTRL_REG) & (1 << 28) == 0) {}
// Set Special Clock to Display Engine PLL
// Clear DE_CLK_REG bits 0x0300 0000
// Set DE_CLK_REG bits 0x8100 0000
2022-11-06 20:59:52 +08:00
// SCLK_GATING (Bit 31) = 1 (Enable Special Clock)
2022-11-05 18:22:52 +08:00
// CLK_SRC_SEL (Bits 24 to 26) = 1 (Clock Source is Display Engine PLL)
// DE_CLK_REG (Display Engine Clock Register) is at CCU Offset 0x0104
// (A64 Page 117, 0x1C2 0104)
debug("Set Special Clock to Display Engine PLL", .{});
2022-11-06 20:59:52 +08:00
const SCLK_GATING: u32 = 1 << 31; // Enable Special Clock
const CLK_SRC_SEL: u27 = 1 << 24; // Clock Source is Display Engine PLL
2022-11-05 19:19:00 +08:00
const clk = SCLK_GATING
| CLK_SRC_SEL;
2022-11-05 18:22:52 +08:00
comptime{ assert(clk == 0x8100_0000); }
2022-11-06 20:59:52 +08:00
2022-12-08 11:43:35 +08:00
const SCLK_GATING_MASK: u32 = 0b1 << 31;
const CLK_SRC_SEL_MASK: u27 = 0b111 << 24;
const clk_mask = SCLK_GATING_MASK
| CLK_SRC_SEL_MASK;
2022-11-06 20:59:52 +08:00
const DE_CLK_REG = CCU_BASE_ADDRESS + 0x0104;
comptime{ assert(DE_CLK_REG == 0x1C2_0104); }
2022-12-08 11:43:35 +08:00
modreg32(clk, clk_mask, DE_CLK_REG);
2022-11-05 18:22:52 +08:00
// Enable AHB (AMBA High-speed Bus) for Display Engine: De-Assert Display Engine
// Set BUS_SOFT_RST_REG1 bits 0x1000
// DE_RST (Bit 12) = 1 (De-Assert Display Engine)
// BUS_SOFT_RST_REG1 (Bus Software Reset Register 1) is at CCU Offset 0x02C4
// (A64 Page 140, 0x1C2 02C4)
debug("Enable AHB for Display Engine: De-Assert Display Engine", .{});
2022-11-06 20:59:52 +08:00
const DE_RST: u13 = 1 << 12; // De-Assert Display Engine
2022-11-05 18:22:52 +08:00
const BUS_SOFT_RST_REG1 = CCU_BASE_ADDRESS + 0x02C4;
comptime{ assert(BUS_SOFT_RST_REG1 == 0x1C2_02C4); }
2022-12-08 11:43:35 +08:00
modreg32(DE_RST, DE_RST, BUS_SOFT_RST_REG1);
2022-11-05 18:22:52 +08:00
// Enable AHB (AMBA High-speed Bus) for Display Engine: Pass Display Engine
// Set BUS_CLK_GATING_REG1 bits 0x1000
// DE_GATING (Bit 12) = 1 (Pass Display Engine)
// BUS_CLK_GATING_REG1 (Bus Clock Gating Register 1) is at CCU Offset 0x0064
// (A64 Page 102, 0x1C2 0064)
debug("Enable AHB for Display Engine: Pass Display Engine", .{});
2022-11-06 20:59:52 +08:00
const DE_GATING: u13 = 1 << 12; // Pass Display Engine
2022-11-05 18:22:52 +08:00
const BUS_CLK_GATING_REG1 = CCU_BASE_ADDRESS + 0x0064;
comptime{ assert(BUS_CLK_GATING_REG1 == 0x1C2_0064); }
2022-12-08 11:43:35 +08:00
modreg32(DE_GATING, DE_GATING, BUS_CLK_GATING_REG1);
2022-11-03 10:16:27 +08:00
2022-11-05 19:30:32 +08:00
// Enable Clock for MIXER0: SCLK Clock Pass
// Set SCLK_GATE bits 0x1
// CORE0_SCLK_GATE (Bit 0) = 1 (Clock Pass)
// SCLK_GATE is at DE Offset 0x000
// (DE Page 25, 0x100 0000)
debug("Enable Clock for MIXER0: SCLK Clock Pass", .{});
2022-11-06 20:59:52 +08:00
const CORE0_SCLK_GATE: u1 = 1 << 0; // Clock Pass
2022-11-05 19:30:32 +08:00
const SCLK_GATE = DISPLAY_ENGINE_BASE_ADDRESS + 0x000;
comptime{ assert(SCLK_GATE == 0x100_0000); }
2022-12-08 11:43:35 +08:00
modreg32(CORE0_SCLK_GATE, CORE0_SCLK_GATE, SCLK_GATE);
2022-11-05 19:30:32 +08:00
// Enable Clock for MIXER0: HCLK Clock Reset Off
// Set AHB_RESET bits 0x1
// CORE0_HCLK_RESET (Bit 0) = 1 (Reset Off)
// AHB_RESET is at DE Offset 0x008
// (DE Page 25, 0x100 0008)
debug("Enable Clock for MIXER0: HCLK Clock Reset Off", .{});
2022-11-06 20:59:52 +08:00
const CORE0_HCLK_RESET: u1 = 1 << 0; // Reset Off
2022-11-05 19:30:32 +08:00
const AHB_RESET = DISPLAY_ENGINE_BASE_ADDRESS + 0x008;
comptime{ assert(AHB_RESET == 0x100_0008); }
2022-12-08 11:43:35 +08:00
modreg32(CORE0_HCLK_RESET, CORE0_HCLK_RESET, AHB_RESET);
2022-11-05 19:30:32 +08:00
// Enable Clock for MIXER0: HCLK Clock Pass
// Set HCLK_GATE bits 0x1
// CORE0_HCLK_GATE (Bit 0) = 1 (Clock Pass)
// HCLK_GATE is at DE Offset 0x004
// (DE Page 25, 0x100 0004)
debug("Enable Clock for MIXER0: HCLK Clock Pass", .{});
2022-11-06 20:59:52 +08:00
const CORE0_HCLK_GATE: u1 = 1 << 0; // Clock Pass
2022-11-05 19:30:32 +08:00
const HCLK_GATE = DISPLAY_ENGINE_BASE_ADDRESS + 0x004;
comptime{ assert(HCLK_GATE == 0x100_0004); }
2022-12-08 11:43:35 +08:00
modreg32(CORE0_HCLK_GATE, CORE0_HCLK_GATE, HCLK_GATE);
2022-11-05 19:30:32 +08:00
// Route MIXER0 to TCON0
// Clear DE2TCON_MUX bits 0x1
// DE2TCON_MUX (Bit 0) = 0
// (Route MIXER0 to TCON0; Route MIXER1 to TCON1)
// DE2TCON_MUX is at DE Offset 0x010
// (DE Page 26, 0x100 0010)
debug("Route MIXER0 to TCON0", .{});
2022-11-06 20:59:52 +08:00
const DE2TCON_MUX_MASK: u1 = 1 << 0; // Route MIXER0 to TCON0; Route MIXER1 to TCON1
2022-11-05 19:30:32 +08:00
const DE2TCON_MUX = DISPLAY_ENGINE_BASE_ADDRESS + 0x010;
comptime{ assert(DE2TCON_MUX == 0x100_0010); }
2022-12-08 11:43:35 +08:00
modreg32(0, DE2TCON_MUX_MASK, DE2TCON_MUX);
2022-11-03 10:16:27 +08:00
2022-11-05 20:32:51 +08:00
// Clear MIXER0 Registers: Global Registers (GLB), Blender (BLD), Video Overlay (OVL_V), UI Overlay (OVL_UI)
// Set MIXER0 Offsets 0x0000 - 0x5FFF to 0
// GLB (Global Regisers) at MIXER0 Offset 0x0000
// BLD (Blender) at MIXER0 Offset 0x1000
// OVL_V(CH0) (Video Overlay) at MIXER0 Offset 0x2000
// OVL_UI(CH1) (UI Overlay 1) at MIXER0 Offset 0x3000
// OVL_UI(CH2) (UI Overlay 2) at MIXER0 Offset 0x4000
// OVL_UI(CH3) (UI Overlay 3) at MIXER0 Offset 0x5000
// (DE Page 90, 0x110 0000 - 0x110 5FFF)
debug("Clear MIXER0 Registers: GLB, BLD, OVL_V, OVL_UI", .{});
2022-11-03 12:47:46 +08:00
var i: usize = 0;
2022-11-03 13:40:25 +08:00
while (i < 0x6000) : (i += 4) {
2022-11-05 21:12:33 +08:00
putreg32(0, MIXER0_BASE_ADDRESS + i);
2022-11-03 12:47:46 +08:00
enableLog = false;
}
enableLog = true;
2022-11-07 09:13:01 +08:00
debug(" to *0x{x} = 0x0", .{ MIXER0_BASE_ADDRESS + i - 1 });
2022-11-03 12:47:46 +08:00
2022-11-05 21:12:33 +08:00
// Disable MIXER0 Video Scaler (VSU)
// Set to 0: VS_CTRL_REG at VIDEO_SCALER(CH0) Offset 0
// EN (Bit 0) = 0 (Disable Video Scaler)
// (DE Page 130, 0x112 0000)
debug("Disable MIXER0 VSU", .{});
2022-11-05 21:19:59 +08:00
const VS_CTRL_REG = VIDEO_SCALER_BASE_ADDRESS + 0;
2022-11-05 21:12:33 +08:00
comptime{ assert(VS_CTRL_REG == 0x112_0000); }
putreg32(0, VS_CTRL_REG);
2022-11-05 21:25:15 +08:00
// TODO: 0x113 0000 is undocumented
// Is there a mixup with UI_SCALER3?
debug("Disable MIXER0 Undocumented", .{});
2022-11-03 12:47:46 +08:00
const _1130000 = 0x1130000;
2022-11-05 21:12:33 +08:00
putreg32(0, _1130000);
2022-11-03 12:47:46 +08:00
2022-11-05 21:25:15 +08:00
// Disable MIXER0 UI_SCALER1
2022-11-05 21:29:22 +08:00
// Set to 0: UIS_CTRL_REG at UI_SCALER1(CH1) Offset 0
2022-11-05 21:25:15 +08:00
// EN (Bit 0) = 0 (Disable UI Scaler)
// (DE Page 66, 0x114 0000)
debug("Disable MIXER0 UI_SCALER1", .{});
2022-11-05 21:29:22 +08:00
const UIS_CTRL_REG1 = UI_SCALER1_BASE_ADDRESS + 0;
comptime{ assert(UIS_CTRL_REG1 == 0x114_0000); }
putreg32(0, UIS_CTRL_REG1);
2022-11-03 12:47:46 +08:00
2022-11-05 21:29:22 +08:00
// Disable MIXER0 UI_SCALER2
// Set to 0: UIS_CTRL_REG at UI_SCALER2(CH2) Offset 0
// EN (Bit 0) = 0 (Disable UI Scaler)
// (DE Page 66, 0x115 0000)
debug("Disable MIXER0 UI_SCALER2", .{});
const UIS_CTRL_REG2 = UI_SCALER2_BASE_ADDRESS + 0;
comptime{ assert(UIS_CTRL_REG2 == 0x115_0000); }
putreg32(0, UIS_CTRL_REG2);
2022-11-03 12:47:46 +08:00
2022-11-05 21:40:57 +08:00
// TODO: Missing UI_SCALER3(CH3) at MIXER0 Offset 0x06 0000 (DE Page 90, 0x116 0000)
// Is there a mixup with 0x113 0000 above?
// Disable MIXER0 FCE
// Set to 0: GCTRL_REG(FCE) at FCE Offset 0
// EN (Bit 0) = 0 (Disable FCE)
// (DE Page 62, 0x11A 0000)
debug("Disable MIXER0 FCE", .{});
const GCTRL_REG_FCE = FCE_BASE_ADDRESS + 0;
comptime{ assert(GCTRL_REG_FCE == 0x11A_0000); }
putreg32(0, GCTRL_REG_FCE);
// Disable MIXER0 BWS
// Set to 0: GCTRL_REG(BWS) at BWS Offset 0
// EN (Bit 0) = 0 (Disable BWS)
// (DE Page 42, 0x11A 2000)
debug("Disable MIXER0 BWS", .{});
const GCTRL_REG_BWS = BWS_BASE_ADDRESS + 0;
comptime{ assert(GCTRL_REG_BWS == 0x11A_2000); }
putreg32(0, GCTRL_REG_BWS);
// Disable MIXER0 LTI
// Set to 0: LTI_CTL at LTI Offset 0
// LTI_EN (Bit 0) = 0 (Close LTI)
// (DE Page 72, 0x11A 4000)
debug("Disable MIXER0 LTI", .{});
const LTI_CTL = LTI_BASE_ADDRESS + 0;
comptime{ assert(LTI_CTL == 0x11A_4000); }
putreg32(0, LTI_CTL);
// Disable MIXER0 PEAKING
// Set to 0: LP_CTRL_REG at PEAKING Offset 0
// EN (Bit 0) = 0 (Disable PEAKING)
// (DE Page 80, 0x11A 6000)
debug("Disable MIXER0 PEAKING", .{});
const LP_CTRL_REG = PEAKING_BASE_ADDRESS + 0;
comptime{ assert(LP_CTRL_REG == 0x11A_6000); }
putreg32(0, LP_CTRL_REG);
// Disable MIXER0 ASE
// Set to 0: ASE_CTL_REG at ASE Offset 0
// ASE_EN (Bit 0) = 0 (Disable ASE)
// (DE Page 40, 0x11A 8000)
debug("Disable MIXER0 ASE", .{});
const ASE_CTL_REG = ASE_BASE_ADDRESS + 0;
comptime{ assert(ASE_CTL_REG == 0x11A_8000); }
putreg32(0, ASE_CTL_REG);
// Disable MIXER0 FCC
// Set to 0: FCC_CTL_REG at FCC Offset 0
// Enable (Bit 0) = 0 (Disable FCC)
// (DE Page 56, 0x11A A000)
debug("Disable MIXER0 FCC", .{});
const FCC_CTL_REG = FCC_BASE_ADDRESS + 0;
comptime{ assert(FCC_CTL_REG == 0x11A_A000); }
putreg32(0, FCC_CTL_REG);
// Disable MIXER0 DRC
// Set to 0: GNECTL_REG at DRC Offset 0
// BIST_EN (Bit 0) = 0 (Disable BIST)
// (DE Page 49, 0x11B 0000)
debug("Disable MIXER0 DRC", .{});
const GNECTL_REG = DRC_BASE_ADDRESS + 0;
comptime{ assert(GNECTL_REG == 0x11B_0000); }
putreg32(0, GNECTL_REG);
// Enable MIXER0
// Set GLB_CTL to 1 (DMB)
// EN (Bit 0) = 1 (Enable Mixer)
// (DE Page 92)
// GLB_CTL is at MIXER0 Offset 0
// (DE Page 90, 0x110 0000)
debug("Enable MIXER0", .{});
2022-11-06 20:59:52 +08:00
const EN_MIXER: u1 = 1 << 0; // Enable Mixer
2022-11-05 21:40:57 +08:00
const GLB_CTL = MIXER0_BASE_ADDRESS + 0;
comptime{ assert(GLB_CTL == 0x110_0000); }
2022-11-05 21:46:56 +08:00
putreg32(EN_MIXER, GLB_CTL); // TODO: DMB
2022-11-03 12:47:46 +08:00
}
2022-12-03 08:43:19 +08:00
/// Export Zig Functions to C. (Why is this needed?)
2022-11-03 12:47:46 +08:00
pub export fn export_dsi_functions() void {
2022-12-03 19:33:15 +08:00
// Export DSI Functions
2022-12-03 08:43:19 +08:00
dsi.panel_init();
2022-12-03 19:33:15 +08:00
dsi.enable_dsi_block();
2022-12-04 08:59:47 +08:00
dsi.start_dsi();
// Export Backlight Functions
2022-12-02 12:40:00 +08:00
backlight.backlight_enable(100);
2022-12-04 08:59:47 +08:00
// Export PMIC Functions
2022-12-03 12:19:03 +08:00
pmic.display_board_init();
2022-12-04 08:59:47 +08:00
// Export TCON Functions
2022-12-03 17:03:19 +08:00
tcon.tcon0_init();
2022-12-04 08:59:47 +08:00
// Export DPHY Functions
dphy.dphy_enable();
// Export Panel Functions
panel.panel_reset();
2022-11-03 12:47:46 +08:00
}
2022-12-08 11:43:35 +08:00
/// Modify the specified bits in a memory mapped register.
/// Based on https://github.com/apache/nuttx/blob/master/arch/arm64/src/common/arm64_arch.h#L473
fn modreg32(
comptime val: u32, // Bits to set, like (1 << bit)
comptime mask: u32, // Bits to clear, like (1 << bit)
addr: u64 // Address to modify
2022-11-03 12:47:46 +08:00
) void {
2022-12-08 11:43:35 +08:00
comptime{ assert(val & mask == val); }
debug(" *0x{x}: clear 0x{x}, set 0x{x}", .{ addr, mask, val & mask });
putreg32(
(getreg32(addr) & ~(mask))
| ((val) & (mask)),
(addr)
);
2022-11-03 10:16:27 +08:00
}
2022-11-03 12:47:46 +08:00
/// Get the 32-bit value at the address
fn getreg32(addr: u64) u32 {
const ptr = @intToPtr(*const volatile u32, addr);
return ptr.*;
}
/// Set the 32-bit value at the address
fn putreg32(val: u32, addr: u64) void {
if (enableLog) { debug(" *0x{x} = 0x{x}", .{ addr, val }); }
const ptr = @intToPtr(*volatile u32, addr);
ptr.* = val;
}
/// Set to False to disable log
var enableLog = true;
2022-12-08 21:20:59 +08:00
///////////////////////////////////////////////////////////////////////////////
// Main Function
2022-12-08 21:46:49 +08:00
/// Main Function that will be called by NuttX when we run the `hello` app.
2022-12-08 21:20:59 +08:00
/// Comment out this function if NuttX Build fails due to duplicate `hello_main`.
pub export fn hello_main(
argc: c_int,
argv: [*c]const [*c]u8
) c_int {
2022-12-08 22:02:00 +08:00
debug("pinephone-nuttx/render.zig: hello_main", .{});
2022-12-08 21:20:59 +08:00
// Quit if no args specified
if (argc <= 1) { usage(); return -1; }
2022-12-08 21:46:49 +08:00
// Run a command like "a" or "b"
2022-12-08 21:20:59 +08:00
if (argc == 2) {
const cmd = std.mem.span(argv[1]);
2022-12-08 21:46:49 +08:00
if (std.mem.eql(u8, cmd, "a")) {
// Turn on Display Backlight
backlight.backlight_enable(90);
} else if (std.mem.eql(u8, cmd, "b")) {
// Init Timing Controller TCON0
tcon.tcon0_init();
} else if (std.mem.eql(u8, cmd, "c")) {
// Init PMIC
pmic.display_board_init();
} else if (std.mem.eql(u8, cmd, "d")) {
// Enable MIPI DSI Block
dsi.enable_dsi_block();
////TODO
} else if (std.mem.eql(u8, cmd, "e")) {
// Enable MIPI Display Physical Layer
dphy.dphy_enable();
} else if (std.mem.eql(u8, cmd, "f")) {
// Reset LCD Panel
panel.panel_reset();
} else if (std.mem.eql(u8, cmd, "g")) {
// Init LCD Panel
////dsi.panel_init();
const fd = c.open("/dev/userleds", c.O_WRONLY); assert(fd > 0);
2022-12-08 22:02:00 +08:00
const ret = c.ioctl(fd, c.ULEDIOC_SETALL, @intCast(c_int, 6)); assert(ret >= 0);
2022-12-08 21:46:49 +08:00
_ = c.close(fd);
} else if (std.mem.eql(u8, cmd, "h")) {
// Start MIPI DSI HSC and HSD
dsi.start_dsi();
////TODO
} else if (std.mem.eql(u8, cmd, "i")) {
// Init Display Engine
de2_init();
// Wait a while
_ = c.usleep(160000);
// Render Graphics with Display Engine
renderGraphics(3); // Render 3 UI Channels
2022-12-08 21:50:22 +08:00
} else if (std.mem.eql(u8, cmd, "1")) {
// Render 1 UI Channel
test_render(1);
} else if (std.mem.eql(u8, cmd, "3")) {
// Render 3 UI Channels
test_render(1);
2022-12-08 21:46:49 +08:00
} else {
usage(); return -1;
2022-12-08 21:20:59 +08:00
}
}
return 0;
}
/// Print the Command-Line Options
fn usage() void {
const err = std.log.err;
2022-12-09 10:58:06 +08:00
err("hello 1", .{});
err(" Render 1 UI Channel", .{});
err("hello 3", .{});
err(" Render 3 UI Channels", .{});
2022-12-08 21:46:49 +08:00
err("hello a", .{});
err(" Turn on Display Backlight", .{});
err("hello b", .{});
err(" Init Timing Controller TCON0", .{});
err("hello c", .{});
err(" Init PMIC", .{});
err("hello d", .{});
err(" Enable MIPI DSI Block", .{});
err("hello e", .{});
err(" Enable MIPI Display Physical Layer", .{});
err("hello f", .{});
err(" Reset LCD Panel", .{});
err("hello g", .{});
err(" Init LCD Panel (pinephone_panel_init)", .{});
err("hello h", .{});
err(" Start MIPI DSI HSC and HSD", .{});
err("hello i", .{});
err(" Render Graphics with Display Engine", .{});
2022-12-08 21:20:59 +08:00
}
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;