2022-10-05 15:36:22 +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-05 16:21:33 +08:00
|
|
|
|
//! PinePhone MIPI DSI Driver for Apache NuttX RTOS.
|
|
|
|
|
//! This MIPI DSI Interface is compatible with Zephyr MIPI DSI:
|
|
|
|
|
//! https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/drivers/mipi_dsi.h
|
|
|
|
|
|
|
|
|
|
/// Import the Zig Standard Library
|
2022-10-05 15:36:22 +08:00
|
|
|
|
const std = @import("std");
|
|
|
|
|
|
2022-10-05 16:21:33 +08:00
|
|
|
|
/// Import the LoRaWAN Library from C
|
|
|
|
|
const c = @cImport({
|
|
|
|
|
// NuttX Defines
|
|
|
|
|
@cDefine("__NuttX__", "");
|
|
|
|
|
@cDefine("NDEBUG", "");
|
2022-10-05 18:15:58 +08:00
|
|
|
|
@cDefine("FAR", "");
|
2022-10-05 16:21:33 +08:00
|
|
|
|
|
|
|
|
|
// 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");
|
|
|
|
|
});
|
2022-10-05 15:36:22 +08:00
|
|
|
|
|
|
|
|
|
/// MIPI DSI Processor-to-Peripheral transaction types
|
2022-10-06 10:50:19 +08:00
|
|
|
|
const MIPI_DSI_DCS_LONG_WRITE = 0x39;
|
2022-10-05 15:36:22 +08:00
|
|
|
|
|
2022-10-06 09:03:37 +08:00
|
|
|
|
/// Write to MIPI DSI. See https://lupyuen.github.io/articles/dsi#transmit-packet-over-mipi-dsi
|
2022-10-05 16:47:38 +08:00
|
|
|
|
pub export fn nuttx_mipi_dsi_dcs_write(
|
2022-10-05 15:36:22 +08:00
|
|
|
|
dev: [*c]const mipi_dsi_device, // MIPI DSI Host Device
|
|
|
|
|
channel: u8, // Virtual Channel ID
|
2022-10-05 16:21:33 +08:00
|
|
|
|
cmd: u8, // DCS Command
|
|
|
|
|
buf: [*c]u8, // Transmit Buffer
|
|
|
|
|
len: usize // Length of Buffer
|
|
|
|
|
) isize { // On Success: Return number of written bytes. On Error: Return negative error code
|
2022-10-06 14:11:34 +08:00
|
|
|
|
_ = dev;
|
2022-10-05 16:39:41 +08:00
|
|
|
|
debug("mipi_dsi_dcs_write: channel={}, cmd={x}, len={}", .{ channel, cmd, len });
|
2022-10-06 14:11:34 +08:00
|
|
|
|
assert(cmd == MIPI_DSI_DCS_LONG_WRITE);
|
2022-10-06 09:03:37 +08:00
|
|
|
|
|
2022-10-06 11:01:49 +08:00
|
|
|
|
// Compose Long Packet
|
2022-10-06 14:46:36 +08:00
|
|
|
|
const pkt = composeLongPacket(channel, cmd, buf, len);
|
2022-10-06 14:11:34 +08:00
|
|
|
|
|
|
|
|
|
// TODO: Dump the packet
|
2022-10-06 11:01:49 +08:00
|
|
|
|
_ = pkt;
|
|
|
|
|
|
2022-10-06 09:03:37 +08:00
|
|
|
|
// TODO
|
2022-10-06 09:16:37 +08:00
|
|
|
|
// - Write the Long Packet to DSI_CMD_TX_REG
|
|
|
|
|
// (DSI Low Power Transmit Package Register) at Offset 0x300 to 0x3FC.
|
2022-10-06 09:03:37 +08:00
|
|
|
|
//
|
2022-10-06 09:16:37 +08:00
|
|
|
|
// - Set the Packet Length (TX_Size) in Bits 0 to 7 of
|
|
|
|
|
// DSI_CMD_CTL_REG (DSI Low Power Control Register) at Offset 0x200.
|
2022-10-06 09:03:37 +08:00
|
|
|
|
//
|
2022-10-06 09:16:37 +08:00
|
|
|
|
// - Set DSI_INST_JUMP_SEL_REG (Offset 0x48, undocumented)
|
2022-10-06 09:03:37 +08:00
|
|
|
|
// to begin the Low Power Transmission.
|
|
|
|
|
//
|
2022-10-06 09:16:37 +08:00
|
|
|
|
// - Disable DSI Processing: Set Instru_En to 0.
|
|
|
|
|
// - Then Enable DSI Processing: Set Instru_En to 1.
|
2022-10-06 09:03:37 +08:00
|
|
|
|
//
|
2022-10-06 09:16:37 +08:00
|
|
|
|
// - To check whether the transmission is complete, we poll on Instru_En.
|
2022-10-06 09:03:37 +08:00
|
|
|
|
//
|
2022-10-06 09:16:37 +08:00
|
|
|
|
// Instru_En is Bit 0 of DSI_BASIC_CTL0_REG
|
|
|
|
|
// (DSI Configuration Register 0) at Offset 0x10.
|
2022-10-06 09:03:37 +08:00
|
|
|
|
|
2022-10-05 16:47:38 +08:00
|
|
|
|
std.debug.panic("nuttx_mipi_dsi_dcs_write not implemented", .{});
|
2022-10-05 15:36:22 +08:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 11:02:49 +08:00
|
|
|
|
// Compose MIPI DSI Long Packet. See https://lupyuen.github.io/articles/dsi#long-packet-for-mipi-dsi
|
2022-10-06 14:46:36 +08:00
|
|
|
|
fn composeLongPacket(
|
2022-10-06 10:57:56 +08:00
|
|
|
|
channel: u8, // Virtual Channel ID
|
|
|
|
|
cmd: u8, // DCS Command
|
|
|
|
|
buf: [*c]u8, // Transmit Buffer
|
|
|
|
|
len: usize // Length of Buffer
|
|
|
|
|
) []u8 {
|
|
|
|
|
_ = buf;
|
2022-10-06 14:46:36 +08:00
|
|
|
|
debug("composeLongPacket: channel={}, cmd={x}, len={}", .{ channel, cmd, len });
|
2022-10-06 11:58:31 +08:00
|
|
|
|
// Data Identifier (DI) (1 byte):
|
2022-10-06 14:49:34 +08:00
|
|
|
|
// - Virtual Channel Identifier (Bits 6 to 7)
|
|
|
|
|
// - Data Type (Bits 0 to 5)
|
2022-10-06 11:58:31 +08:00
|
|
|
|
// (Virtual Channel should be 0, I think)
|
|
|
|
|
assert(cmd < (1 << 6));
|
|
|
|
|
const vc: u8 = 0;
|
|
|
|
|
const dt: u8 = cmd;
|
|
|
|
|
const di: u8 = (vc << 6) | dt;
|
|
|
|
|
|
|
|
|
|
// Word Count (WC) (2 bytes):
|
2022-10-06 14:49:34 +08:00
|
|
|
|
// - Number of bytes in the Packet Payload
|
2022-10-06 11:58:31 +08:00
|
|
|
|
const wc: u16 = @intCast(u16, len);
|
|
|
|
|
const wcl: u8 = @intCast(u8, wc & 0xff);
|
|
|
|
|
const wch: u8 = @intCast(u8, wc >> 8);
|
|
|
|
|
|
2022-10-06 14:46:36 +08:00
|
|
|
|
// Data Identifier + Word Count (3 bytes): For computing Error Correction Code (ECC)
|
|
|
|
|
const di_wc = [3]u8 { di, wcl, wch };
|
|
|
|
|
|
|
|
|
|
// Compute Error Correction Code (ECC) for Data Identifier + Word Count
|
|
|
|
|
const ecc: u8 = computeEcc(di_wc);
|
2022-10-06 11:58:31 +08:00
|
|
|
|
|
|
|
|
|
// TODO: Checksum (CS) (2 bytes):
|
2022-10-06 14:49:34 +08:00
|
|
|
|
// - 16-bit Cyclic Redundancy Check (CRC)
|
2022-10-06 13:54:11 +08:00
|
|
|
|
// See "12.3.6.13: Packet Footer", Page 210 of BL808 Reference Manual:
|
2022-10-06 11:58:31 +08:00
|
|
|
|
// https://github.com/sipeed/sipeed2022_autumn_competition/blob/main/assets/BL808_RM_en.pdf)
|
|
|
|
|
const cs: u16 = 0;
|
|
|
|
|
const csl: u8 = @intCast(u8, cs & 0xff);
|
|
|
|
|
const csh: u8 = @intCast(u8, cs >> 8);
|
|
|
|
|
|
2022-10-06 14:49:34 +08:00
|
|
|
|
// Packet Header (4 bytes):
|
|
|
|
|
// - Data Identifier + Word Count + Error Correction COde
|
2022-10-06 14:46:36 +08:00
|
|
|
|
const header = [4]u8 { di_wc[0], di_wc[1], di_wc[2], ecc };
|
2022-10-06 11:07:21 +08:00
|
|
|
|
|
2022-10-06 10:57:56 +08:00
|
|
|
|
// Packet Payload:
|
2022-10-06 14:49:34 +08:00
|
|
|
|
// - Data (0 to 65,541 bytes):
|
2022-10-06 11:58:31 +08:00
|
|
|
|
// Number of data bytes should match the Word Count (WC)
|
2022-10-06 14:08:24 +08:00
|
|
|
|
assert(len <= 65_541);
|
|
|
|
|
const payload = buf[0..len];
|
2022-10-06 11:07:21 +08:00
|
|
|
|
|
2022-10-06 14:49:34 +08:00
|
|
|
|
// Packet Footer (2 bytes)
|
|
|
|
|
// - Checksum (CS)
|
2022-10-06 11:58:31 +08:00
|
|
|
|
const footer = [2]u8 { csl, csh };
|
2022-10-06 11:01:49 +08:00
|
|
|
|
|
2022-10-06 14:08:24 +08:00
|
|
|
|
// Packet = Packet Header + Payload + Packet Footer
|
2022-10-06 11:44:22 +08:00
|
|
|
|
var pkt = std.mem.zeroes([1024]u8);
|
|
|
|
|
assert(pkt.len >= header.len + len + footer.len); // Increase pkt size
|
|
|
|
|
std.mem.copy(u8, pkt[0..header.len], &header);
|
2022-10-06 14:08:24 +08:00
|
|
|
|
std.mem.copy(u8, pkt[header.len..], payload);
|
2022-10-06 12:06:38 +08:00
|
|
|
|
std.mem.copy(u8, pkt[(header.len + len)..], &footer);
|
2022-10-06 11:44:22 +08:00
|
|
|
|
const pktlen = header.len + len + footer.len;
|
|
|
|
|
|
|
|
|
|
// Return the packet
|
|
|
|
|
return pkt[0..pktlen];
|
2022-10-06 10:57:56 +08:00
|
|
|
|
}
|
2022-10-06 10:48:23 +08:00
|
|
|
|
|
2022-10-06 14:46:36 +08:00
|
|
|
|
/// Compute the Error Correction Code (ECC) (1 byte):
|
|
|
|
|
/// Allow single-bit errors to be corrected and 2-bit errors to be detected in the Packet Header
|
|
|
|
|
/// See "12.3.6.12: Error Correction Code", Page 208 of BL808 Reference Manual:
|
|
|
|
|
/// https://github.com/sipeed/sipeed2022_autumn_competition/blob/main/assets/BL808_RM_en.pdf)
|
|
|
|
|
fn computeEcc(
|
|
|
|
|
di_wc: [3]u8 // Data Identifier + Word Count (3 bytes)
|
|
|
|
|
) u8 {
|
|
|
|
|
// Combine DI and WC into a 24-bit word
|
|
|
|
|
var di_wc_word: u32 =
|
|
|
|
|
di_wc[0]
|
|
|
|
|
| (@intCast(u32, di_wc[1]) << 8)
|
|
|
|
|
| (@intCast(u32, di_wc[2]) << 16);
|
|
|
|
|
|
|
|
|
|
// Extract the 24 bits from the word
|
|
|
|
|
var d = std.mem.zeroes([24]u1);
|
|
|
|
|
var i: usize = 0;
|
|
|
|
|
while (i < 24) : (i += 1) {
|
|
|
|
|
d[i] = @intCast(u1, di_wc_word & 1);
|
|
|
|
|
di_wc_word >>= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compute the ECC bits
|
|
|
|
|
var ecc = std.mem.zeroes([8]u1);
|
|
|
|
|
ecc[7] = 0;
|
|
|
|
|
ecc[6] = 0;
|
|
|
|
|
ecc[5] = d[10] ^ d[11] ^ d[12] ^ d[13] ^ d[14] ^ d[15] ^ d[16] ^ d[17] ^ d[18] ^ d[19] ^ d[21] ^ d[22] ^ d[23];
|
|
|
|
|
ecc[4] = d[4] ^ d[5] ^ d[6] ^ d[7] ^ d[8] ^ d[9] ^ d[16] ^ d[17] ^ d[18] ^ d[19] ^ d[20] ^ d[22] ^ d[23];
|
|
|
|
|
ecc[3] = d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9] ^ d[13] ^ d[14] ^ d[15] ^ d[19] ^ d[20] ^ d[21] ^ d[23];
|
|
|
|
|
ecc[2] = d[0] ^ d[2] ^ d[3] ^ d[5] ^ d[6] ^ d[9] ^ d[11] ^ d[12] ^ d[15] ^ d[18] ^ d[20] ^ d[21] ^ d[22];
|
|
|
|
|
ecc[1] = d[0] ^ d[1] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[10] ^ d[12] ^ d[14] ^ d[17] ^ d[20] ^ d[21] ^ d[22] ^ d[23];
|
|
|
|
|
ecc[0] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[5] ^ d[7] ^ d[10] ^ d[11] ^ d[13] ^ d[16] ^ d[20] ^ d[21] ^ d[22] ^ d[23];
|
|
|
|
|
|
|
|
|
|
// Merge the ECC bits
|
|
|
|
|
return @intCast(u8, ecc[0])
|
|
|
|
|
| (@intCast(u8, ecc[1]) << 1)
|
|
|
|
|
| (@intCast(u8, ecc[2]) << 2)
|
|
|
|
|
| (@intCast(u8, ecc[3]) << 3)
|
|
|
|
|
| (@intCast(u8, ecc[4]) << 4)
|
|
|
|
|
| (@intCast(u8, ecc[5]) << 5)
|
|
|
|
|
| (@intCast(u8, ecc[6]) << 6)
|
|
|
|
|
| (@intCast(u8, ecc[7]) << 7);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-05 15:36:22 +08:00
|
|
|
|
/// MIPI DSI Device
|
2022-10-05 16:21:33 +08:00
|
|
|
|
pub const mipi_dsi_device = extern struct {
|
|
|
|
|
/// Number of Data Lanes
|
|
|
|
|
data_lanes: u8,
|
2022-10-05 15:36:22 +08:00
|
|
|
|
/// Display Timings
|
2022-10-05 16:21:33 +08:00
|
|
|
|
timings: mipi_dsi_timings,
|
|
|
|
|
/// Pixel Format
|
|
|
|
|
pixfmt: u32,
|
|
|
|
|
/// Mode Flags
|
|
|
|
|
mode_flags: u32,
|
2022-10-05 15:36:22 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// MIPI DSI Read / Write Message
|
2022-10-05 16:21:33 +08:00
|
|
|
|
pub const mipi_dsi_msg = extern struct {
|
|
|
|
|
/// Payload Data Type
|
|
|
|
|
type: u8,
|
|
|
|
|
/// Flags controlling message transmission
|
|
|
|
|
flags: u16,
|
|
|
|
|
/// Command (only for DCS)
|
|
|
|
|
cmd: u8,
|
|
|
|
|
/// Transmit Buffer Length
|
|
|
|
|
tx_len: usize,
|
|
|
|
|
/// Transmit Buffer
|
|
|
|
|
tx_buf: [*c]const u8,
|
|
|
|
|
/// Receive Buffer Length
|
|
|
|
|
rx_len: usize,
|
|
|
|
|
/// Receive Buffer
|
|
|
|
|
rx_buf: [*c]u8,
|
2022-10-05 15:36:22 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// MIPI DSI Display Timings
|
2022-10-05 16:21:33 +08:00
|
|
|
|
pub const mipi_dsi_timings = extern struct {
|
|
|
|
|
/// Horizontal active video
|
|
|
|
|
hactive: u32,
|
|
|
|
|
/// Horizontal front porch
|
|
|
|
|
hfp: u32,
|
|
|
|
|
/// Horizontal back porch
|
|
|
|
|
hbp: u32,
|
|
|
|
|
/// Horizontal sync length
|
|
|
|
|
hsync: u32,
|
|
|
|
|
/// Vertical active video
|
|
|
|
|
vactive: u32,
|
|
|
|
|
/// Vertical front porch
|
|
|
|
|
vfp: u32,
|
|
|
|
|
/// Vertical back porch
|
|
|
|
|
vbp: u32,
|
|
|
|
|
/// Vertical sync length
|
|
|
|
|
vsync: u32,
|
2022-10-05 15:36:22 +08:00
|
|
|
|
};
|
|
|
|
|
|
2022-10-05 18:15:58 +08:00
|
|
|
|
/// Main Function for Null App
|
2022-10-05 15:36:22 +08:00
|
|
|
|
pub export fn null_main(_argc: c_int, _argv: [*]const [*]const u8) c_int {
|
|
|
|
|
_ = _argc;
|
|
|
|
|
_ = _argv;
|
|
|
|
|
test_zig();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-05 18:15:58 +08:00
|
|
|
|
/// Zig Test Function
|
2022-10-05 15:36:22 +08:00
|
|
|
|
pub export fn test_zig() void {
|
|
|
|
|
_ = printf("HELLO ZIG ON PINEPHONE!\n");
|
2022-10-06 10:57:56 +08:00
|
|
|
|
_ = nuttx_mipi_dsi_dcs_write(null, 0, MIPI_DSI_DCS_LONG_WRITE, null, 0);
|
2022-10-05 15:36:22 +08:00
|
|
|
|
}
|
2022-10-05 16:32:45 +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
|
2022-10-05 18:15:58 +08:00
|
|
|
|
c.exit(1);
|
2022-10-05 16:32:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
|
/// LoRaWAN Event Queue
|
|
|
|
|
extern var event_queue: c.struct_ble_npl_eventq;
|
|
|
|
|
|
|
|
|
|
/// Aliases for Zig Standard Library
|
|
|
|
|
const assert = std.debug.assert;
|
|
|
|
|
const debug = std.log.debug;
|