one pixel

as you can see, no pixels here i got this funny thing in the mail about a month ago and i had one plan for it. port my extremely basic "operating system" AlpacOS to it (which i will hopefully write another post about). i initially wrote it in x86 but i quickly realised i wanted to port it to risc-v after seeing this thing online. porting virtual memory and interrupts would be a hurdle but i thought i would start with the display. my thought is that if i have any kind of output, debugging would be easier and i could have something cool to show off. considering it came with a microsd card that booted it into linux already, i thought i could wrestle the display driver into my own code through some esoteric means.

boy was i wrong

one of many

one pixel was all i needed to work up the rest of a rudimentary display system. hell, even the backlight didn't need to work for me to be sated. no matter how slow, placing one pixel at a time could construct something meaningful on the screen. i had used the grub framebuffer when working on alpacos in a virtual setting (qemu), so i thought that this could be similar.

r01/d1

this is the brain of the uconsole. there are others but this one is the only risc-v core available. uconsole r01(source)
the big square in the middle is the allwinner d1, my partner in crime for the last month.
to be honest, i didn't have a real path to that one pixel at this point so i kinda just messed around with what i had. the uconsole came with a microsd card that booted into linux though, so i had something to start with. uconsole running linux this website has been a crucial resource this whole time. they host a mirror of the allwinner sdk (which i tried to get access to, but the whole website was in chinese) and link to the user manual

cwd686

update 2024-10-19: cwd686 isn't the right driver for the uconsole, it's the cwu50

this patch has the only device tree info specific to the uconsole's screen (cwd686), so it's kind of all i had to go on. there is a driver here that looks the same as the one in that patch. there are functions in here like "LCD_power_on", "LCD_panel_init", etc. which look promising, but they are all initiated by these "flow" functions. it was a mess of tracking down function definitions to finally get to the linux base driver file. to be honest, this is stuff i still don't really understand. there are probes and activations and stuff but i haven't read enough to make sense of it. i tried to extract as much as i could of the cwd686 code into it's own folder and build it as a standalone program, but i think i was in too deep at this point. i took another approach to try to learn more before heading back to the display driver.

build 2 boot

at some point i might try to write a bootloader, but not for a board with as little documentation as this one. i tried editing the bootloader initial commands (uEnv.txt), but that didn't work for me. i also tried replacing the linux kernel with my own kernel, but that didn't work either (even through uart). at this point, i knew their bundled version of linux would boot but i wanted to pin down what i could change in the actual bootloader so i could boot my own code. the boot loader build process was fraught, but it allowed me to do a couple of things i otherwise wouldn't have (more on that later). as of the time this post was written (2024-10-12) this official tutorial from Clockwork for the DevTerm (a similar device) does *not* work but i found... a multipart workaround. this image from the fedora wiki gives a great rundown of what the build process is like. I focused on just the bootloader part (pre-GRUB)
diagram of the boot sequence of the Allwinner D1(source)
when building the OpenSBI repo, i hit this error: Error: unrecognized opcode 'fence.i', extension 'zifencei' required which i fixed by adding "_zifencei" to a line in the Makefile (found out where to do this because of this insightful series from sifive): added _zifencei to the riscv toolchain MMC Controller Host Support" is under "Device Drivers" in the menu config (also make sure your actual terminal is large enough to run menuconfig)
when building u-boot i ran into a few issues, the first one was this version error: Invalid version: 'u-boot-2022.01 which i had to remove a line in u-boot/scripts/dtc/pylibfdt/Makefile to make sure the version numbers were compatible VERSION="$(UBOOTVERSION)" the second one was this error: Error: unrecognized opcode 'csrs sstatus,a5', extension 'zicsr' required which i fixed by adding "_zifencei_zicsr" to a line in u-boot/arch/riscv/Makefile: added _zicsr to the riscv ARCH_FLAGS at this point, i had a machine that would... start up and not do anything. the main trick though, was that if i gave this bootloader the same boot commands and linux kernel as the included sd card, it would boot! successfully! which was crazy to me. it meant my bootloader was actually running the code i had intended it to and that maybe a pixel was not too far away.

taking a step back: the serial port

so after many hours of trial and error, trying to get anything out of the screen with my own code, i tried another route. there are some exposed UART pins inside the uconsole that are... not the easiest to get to
no pixels here either??? GPIO 32 and GPIO 33 highlighted
they are pins GPIO 32 (TX) and GPIO 33 (RX)
diagram of the uconsole mPCI adapter, GPIO 32 & GPIO 33 are present(source)
i used a uart -> usb adapter and initially read the boot log just by touching a wire to the TX pin on the mini pcie port. i got lots of helpful data out of this (raw data) and finally understood what was going on inside the device even though the screen was totally dark.

getting a response

now i wanted to finally run my own code on this thing. i took advantage of the fact that the uart port was already set up from the bootloader. the source code is here - it just prints "hello world" to the serial port, which is enough for me to know that i can run my own code. i compiled it and placed it in the boot partition of the sd card, then changed my u-boot default boot command. i used the load and go commands. it's under Boot options > bootcmd value edit that setting to load mmc 0:1 43000000 hello_world.bin; go 43000000;
that gave me this
screenshot of serial output with the phrase "hello world :)"
(although i don't know why the executable is so large)

two-way communication

after the test run i decided to get a little more serious about the serial port. when booting up, u-boot has an option to interrupt the boot process and interact with a shell. i thought this would be pretty useful because i could figure out in real time what my computer had access to and i could read what was actually going on in memory. however now i needed to write to the port, not just read from it, and touching two wires in there while typing on my keyboard was kinda unworkable. so i picked up my soldering iron and hit up amazon to get an mpcie extender...
BEHOLD

THIS BEAUTIFUL CREATION is my serial communicator.
mPCI adapter with wires loosely soldered onto lead wires into a USB-UART adapter
weep your tears of ecstacy

aside: the device tree

the .dts files in the patch provided by clockwork have settings & all of the memory locations for hardware access. those are passed in to u-boot & the linux kernel to find hardware locations for drivers. many of my hours have been spent crawling through these files & i kinda find them fascinating. this website was helpful for understanding what they are.

das u-boot

now that i had a shell with u-boot, i could start interacting with the computer a little bit more. i saw this entry in the device tree from that patch and got interested...
screenshot of the device tree entry with ocp817_backlight highlighted. there is a mention of a PD20 pin which is what caught my attention
this is from the clockwork r01 patch, in the device tree file. there is a gpio pin referenced there, PD20, which i could fortunately control with u-boot. i typed in gpio set PD20 and like magic, the backlight turned on. this was the closest i got lol. i tried the gpio pin referenced in the lcd section (PD19) but idk what i really expected to happen.

what the hell is MIPI?

i couldn't get anywhere with the linux drivers, nor the drivers in u-boot, so i was kind of stuck. my last hope was to make my own driver but... the lcd screen uses a protocol called MIPI DSI to communicate with the cpu. MIPI stands for "Mobile Industry Processor Interface", a closed-source standard ;-;. I couldn't really figure out what to do at this point. I found a MIPI driver in U-Boot, as well as the patch for the linux kernel from here, but I couldn't extract enough meaningful information to make my own driver and any edits to the device tree were not hooking into the u-boot MIPI driver.

2024-10-16 update: dryver

tried to actually write that driver for u-boot, got something that at least showed up in the device tree (dm tree in uboot) but no actual communication. i don't really know what i need a gpio pin for and what i don't. there is also something in the d1 called the "Display Engine" that i see tossed around a lot, but i don't think the driver in u boot interfaces with most of that. trying to figure out if the DSI ports are a subset of that (the user guide says so? but doesn't give any info), but there isn't a clean way for me to test stuff outside of just compiling the driver code by itself and running that because my microsd card reader isn't working.

2024-10-19 update: more discussion

been discussing with someone on the clockwork forums about this, link here

2024-10-20 update: the pixel!

the pixel! huge props to ylyamin on the clockwork forums!