# HG changeset patch # User Paul Boddie # Date 1713536875 -7200 # Node ID 1198da9b3bee5a22736137c623f75150b57120ba # Parent c32074a0ec803894f515b92b9748932456098b3f# Parent 9e2c034b3a29a154bce07b38d39a645a8d849b01 Updated and merged jz4780-spi-lcd changes. diff -r c32074a0ec80 -r 1198da9b3bee conf/landfall-examples/mips-ci20-spi-spectrum.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/landfall-examples/mips-ci20-spi-spectrum.cfg Fri Apr 19 16:27:55 2024 +0200 @@ -0,0 +1,62 @@ +-- vim: ft=lua ts=2 et sw=2 + +-- Start Mag to multiplex the framebuffer showing only a single program. +-- This example shows an animation showing off a spectrum of colours. +-- The target platform is the MIPS Creator CI20. + +local L4 = require("L4"); + +local l = L4.default_loader; + +-- Define general access to peripherals. + +local io_buses = { + common = l:new_channel(); + }; + +l:start({ + caps = { + common = io_buses.common:svr(), + icu = L4.Env.icu, + sigma0 = L4.cast(L4.Proto.Factory, L4.Env.sigma0):create(L4.Proto.Sigma0), + }, + }, + "rom/io rom/hw_devices.io rom/mips-ci20-common.io"); + +-- Expose a framebuffer device. + +local fbdrv_fb = l:new_channel(); + +l:start({ + caps = { + vbus = io_buses.common, + fb = fbdrv_fb:svr(), + }, + }, + "rom/fb-drv"); + +-- Multiplex the framebuffer. + +local mag_caps = { + mag = l:new_channel(), + svc = l:new_channel(), + }; + +l:start({ + caps = { + vbus = io_buses.common, -- needed by input driver + fb = fbdrv_fb, + mag = mag_caps.mag:svr(), + svc = mag_caps.svc:svr(), + }, + }, + "rom/mag"); + +-- Show the spectrum example. + +l:start({ + caps = { + fb = mag_caps.svc:create(L4.Proto.Goos, "g=240x240+0+0", "barheight=10"), + }, + }, + "rom/ex_fb_spectrum_cc"); diff -r c32074a0ec80 -r 1198da9b3bee conf/landfall-examples/mips-ci20-spi-spectrum.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/landfall-examples/mips-ci20-spi-spectrum.list Fri Apr 19 16:27:55 2024 +0200 @@ -0,0 +1,16 @@ + +modaddr 0x1100000 + +entry mips-ci20-spi-spectrum-example +bootstrap bootstrap -serial +kernel fiasco -serial_esc +roottask moe rom/mips-ci20-spi-spectrum.cfg +module mips-ci20-spi-spectrum.cfg +module mips-ci20-common.io +module plat-ingenic-jz4780/hw_devices.io +module l4re +module io +module ned +module fb-drv +module mag +module ex_fb_spectrum_cc diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/Control --- a/pkg/devices/Control Fri Apr 19 16:25:48 2024 +0200 +++ b/pkg/devices/Control Fri Apr 19 16:27:55 2024 +0200 @@ -20,6 +20,7 @@ provides: libdevice-keypad-server provides: libdevice-lcd provides: libdevice-lcd-jz4740 +provides: libdevice-lcd-jz4780-spi provides: libdevice-util provides: libdrivers-aic provides: libdrivers-common diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/lcd/include/lcd-device.h --- a/pkg/devices/lcd/include/lcd-device.h Fri Apr 19 16:25:48 2024 +0200 +++ b/pkg/devices/lcd/include/lcd-device.h Fri Apr 19 16:27:55 2024 +0200 @@ -38,10 +38,6 @@ class Lcd_device { protected: - /* LCD peripheral abstraction. */ - - Lcd_chip *_chip; - /* Display server abstraction. */ Activation *_display; @@ -59,8 +55,8 @@ public: /* Initialise a device with a controller and display object reference. */ - Lcd_device(Lcd_chip *chip, Activation *display) - : _chip(chip), _display(display) + Lcd_device(Activation *display) + : _display(display) { /* Subclasses must set up any memory. */ diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/lcd/include/lcd-jz4740-device.h --- a/pkg/devices/lcd/include/lcd-jz4740-device.h Fri Apr 19 16:25:48 2024 +0200 +++ b/pkg/devices/lcd/include/lcd-jz4740-device.h Fri Apr 19 16:27:55 2024 +0200 @@ -42,6 +42,10 @@ struct dma_region _desc_region; + /* LCD peripheral abstraction. */ + + Lcd_chip *_chip; + protected: /* Device-specific memory allocation. */ @@ -51,7 +55,7 @@ /* Inherit constructor. */ Lcd_jz4740_device(Lcd_chip *chip, Activation *display) - : Lcd_device(chip, display) + : Lcd_device(display), _chip(chip) { _setup_memory(); } diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/lcd/include/lcd-jz4780-spi-device.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lcd/include/lcd-jz4780-spi-device.h Fri Apr 19 16:27:55 2024 +0200 @@ -0,0 +1,71 @@ +/* + * LCD device support for the JZ4740 and related SoCs. + * + * Copyright (C) 2018, 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#include +#include + +#include +#include +#include + + + +/* C++ language interface. */ + +#ifdef __cplusplus + +/* Support for specific JZ4780-based devices. */ + +class Lcd_jz4780_spi_device : public Lcd_device +{ + /* DMA region providing the DMA descriptor virtual and physical addresses. */ + + struct dma_region _desc_region; + +protected: + /* Device-specific memory allocation. */ + + int _setup_memory(); + +public: + Lcd_jz4780_spi_device(Activation *display) + : Lcd_device(display) + { + _setup_memory(); + } + + /* Device operations. */ + + void disable(); + void enable(); + + l4_size_t get_framebuffer_size(); + + void get_view_info(l4re_video_view_info_t *view_info); + + /* Common device instance. */ + + static Lcd_jz4780_spi_device *device; +}; + +#endif diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/lcd/src/Makefile --- a/pkg/devices/lcd/src/Makefile Fri Apr 19 16:25:48 2024 +0200 +++ b/pkg/devices/lcd/src/Makefile Fri Apr 19 16:27:55 2024 +0200 @@ -1,8 +1,9 @@ PKGDIR ?= ../.. L4DIR ?= $(PKGDIR)/../.. -TARGET := common jz4740 +TARGET := common jz4740 jz4780 include $(L4DIR)/mk/subdir.mk jz4740: common +jz4780: common diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/lcd/src/common/lcd-device.cc --- a/pkg/devices/lcd/src/common/lcd-device.cc Fri Apr 19 16:25:48 2024 +0200 +++ b/pkg/devices/lcd/src/common/lcd-device.cc Fri Apr 19 16:27:55 2024 +0200 @@ -27,12 +27,14 @@ void Lcd_device::disable_display() { - _display->disable(); + if (_display != NULL) + _display->disable(); } void Lcd_device::enable_display() { - _display->enable(); + if (_display != NULL) + _display->enable(); } diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/lcd/src/jz4780/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lcd/src/jz4780/Makefile Fri Apr 19 16:27:55 2024 +0200 @@ -0,0 +1,41 @@ +PKGDIR ?= ../../.. +L4DIR ?= $(PKGDIR)/../.. + +TARGET = liblcd_dev_jz4780.o.a liblcd_dev_jz4780.o.so +PC_FILENAME = libdevice-lcd-jz4780-spi + +# Locations for interface input and generated output. + +IDL_DIR = $(PKGDIR)/idl +IDL_MK_DIR = $(L4DIR)/idl4re/mk +IDL_BUILD_DIR = . +IDL_EXPORT_DIR = $(OBJ_BASE)/include/l4/devices/lcd + +include $(IDL_MK_DIR)/idl.mk + +# Individual interfaces. + +CLIENT_INTERFACES_CC = activation + +# Generated and plain source files. + +CLIENT_INTERFACES_SRC_CC = $(call interfaces_to_client_cc,$(CLIENT_INTERFACES_CC)) + +PLAIN_SRC_CC = lcd-jz4780-spi-device.cc + +# Normal definitions. + +SRC_CC = $(CLIENT_INTERFACES_SRC_CC) $(PLAIN_SRC_CC) + +REQUIRES_LIBS = \ + l4re_c l4re_c-util \ + libdevice-lcd \ + libdrivers-cpm libdrivers-dma libdrivers-gpio libdrivers-spi \ + libdevice-util + +PRIVATE_INCDIR = $(PKGDIR)/lcd/include $(IDL_BUILD_DIR) $(IDL_EXPORT_DIR) + +include $(L4DIR)/mk/lib.mk +include $(IDL_MK_DIR)/interface_rules.mk + +$(PLAIN_SRC_CC): $(CLIENT_INTERFACES_SRC_CC) diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/lcd/src/jz4780/lcd-jz4780-spi-device.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lcd/src/jz4780/lcd-jz4780-spi-device.cc Fri Apr 19 16:27:55 2024 +0200 @@ -0,0 +1,275 @@ +/* + * Common SPI-based screen support for the JZ4780 and related SoCs. + * + * Copyright (C) 2018, 2020, 2021, 2023, 2024 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "lcd-jz4780-spi-device.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include + + + +/* Virtual addresses for the CPM, DMA, GPIO and SSI/SPI register blocks. + + NOTE: Should be able to use component abstractions for some of these + eventually. */ + +static l4_addr_t cpm_virt_base = 0, cpm_virt_base_end = 0; +static l4_addr_t dma_virt_base = 0, dma_virt_base_end = 0; +static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0; +static l4_addr_t spi_virt_base = 0, spi_virt_base_end = 0; +static l4_addr_t spi_phys_base = 0, spi_phys_base_end = 0; + +static l4_uint32_t dma_irq_start = 0, dma_irq_end = 0; + +// CPM, DMA, GPIO, SSI/SPI and display device abstractions. + +static Cpm_jz4780_chip *cpm_chip; +static Dma_jz4780_chip *dma_chip; +static Gpio_jz4780_chip *gpio_chip; +static Spi_jz4780_chip *spi_chip; + +static Dma_jz4780_channel *dma_channel; +static Spi_hybrid *spi_channel; +static Spi_jz4780_channel *spi_jz4780_channel; + +struct gpio_port +{ + uint32_t pull_ups, pull_downs; +}; + +static struct gpio_port gpio_ports[] = { + {0x3fff00ff, 0x00000000}, + {0xfff0f3fc, 0x000f0c03}, + {0x0fffffff, 0x00000000}, + {0xffff4fff, 0x0000b000}, + {0xf0fff37c, 0x00000483}, + {0x7fa7f00f, 0x00580ff0}, +}; + + + +// Disable the display. + +void +Lcd_jz4780_spi_device::disable() +{ + // NOTE: Need to support cancelling the transfer or disabling the peripheral. + + //disable_display(); +} + +// Configure the peripheral and enable the display. + +void +Lcd_jz4780_spi_device::enable() +{ + // Set up the GPIO pins for the peripheral. + // NOTE: Hard-coded pin usage! + + gpio_chip->setup(20, Hw::Gpio_chip::Output, 1); // backlight + gpio_chip->config_pad(21, Hw::Gpio_chip::Function_alt, 2); // CE1 + gpio_chip->config_pad(28, Hw::Gpio_chip::Function_alt, 2); // SCLK + gpio_chip->config_pad(29, Hw::Gpio_chip::Function_alt, 2); // MOSI + gpio_chip->config_pad(30, Hw::Gpio_chip::Function_alt, 2); // GPC + + // Initialise the screen. + // NOTE: Specific to the ST7789. + + spi_channel->send_units(2, (const uint8_t []) {0x00, 0x01}, 2, 8); + usleep(120000); + spi_channel->send_units(2, (const uint8_t []) {0x00, 0x11}, 2, 8); + usleep(5000); + spi_channel->send_units(4, (const uint8_t []) {0x00, 0x36, 0x01, 0x00}, 2, 8); + usleep(5000); + spi_channel->send_units(10, (const uint8_t []) {0x00, 0x3a, 0x01, 0x05, 0x00, 0x13, 0x00, 0x29, 0x00, 0x21}, 2, 8); + usleep(5000); + spi_channel->send_units(22, (const uint8_t []) {0x00, 0x2a, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef, + 0x00, 0x2b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef, + 0x00, 0x2c}, 2, 8); + usleep(5000); + + for (l4_addr_t addr = get_framebuffer(); addr < get_framebuffer() + get_framebuffer_size(); addr += sizeof(uint32_t)) + *((uint32_t *) addr) = 0; + + l4_cache_clean_data(get_framebuffer(), get_framebuffer() + get_framebuffer_size()); + + // Initialise the display transfer. + // NOTE: Asserting the data signal. + + spi_channel->acquire_control(true); + + spi_jz4780_channel->transfer(_fb_region.vaddr, _fb_region.paddr, + get_framebuffer_size(), 2, 16, + _desc_region.vaddr, _desc_region.paddr); + + //enable_display(); +} + +// Set up memory addresses for the peripheral, initialising the framebuffer and +// descriptor members. + +int +Lcd_jz4780_spi_device::_setup_memory() +{ + // Test for existing setup. + + if (_fb_region.vaddr) + return 0; + + // Framebuffer and descriptor sizes. + + unsigned long fb_size = get_framebuffer_size(), desc_size = 32; + + // Allocate memory for the framebuffer and descriptors with 2**5 = 32 byte == + // 8 word alignment, sufficient for the descriptors. + + long err = get_dma_region(fb_size, 5, &_fb_region); + + if (err) + return 1; + + err = get_dma_region(desc_size, 5, &_desc_region); + + if (err) + return 1; + + return 0; +} + +l4_size_t Lcd_jz4780_spi_device::get_framebuffer_size() +{ + // NOTE: Hard-coded size. + + return 240 * 240 * 2; +} + +// Populate a view information structure. + +void Lcd_jz4780_spi_device::get_view_info(l4re_video_view_info_t *view_info) +{ + // NOTE: Hard-coded properties. + + view_info->width = 240; + view_info->height = 240; + view_info->pixel_info.bytes_per_pixel = 2; + view_info->bytes_per_line = 480; + + view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5; + view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6; + view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5; + + view_info->pixel_info.a.shift = 0; + view_info->pixel_info.a.size = 0; +} + +// Access to peripheral memory. + +static int setup_memory() +{ + if (get_memory("jz4780-cpm", &cpm_virt_base, &cpm_virt_base_end)) + return 1; + + if (get_memory("jz4780-dma", &dma_virt_base, &dma_virt_base_end)) + return 1; + + if (get_irq("jz4780-dma", &dma_irq_start, &dma_irq_end) < 0) + return 1; + + if (get_memory("jz4780-gpio", &gpio_virt_base, &gpio_virt_base_end)) + return 1; + + if (get_memory_complete("jz4780-ssi", &spi_virt_base, &spi_virt_base_end, + &spi_phys_base, &spi_phys_base_end)) + return 1; + + // Initialise the abstractions. + // NOTE: Using a preset GPIO configuration. + + int gpio_port = 1; + + cpm_chip = new Cpm_jz4780_chip(cpm_virt_base); + + dma_chip = new Dma_jz4780_chip(dma_virt_base, dma_virt_base_end, cpm_chip); + + dma_chip->enable(); + + gpio_chip = new Gpio_jz4780_chip(gpio_virt_base + gpio_port * 0x100, + gpio_virt_base + (gpio_port + 1) * 0x100, + 32, gpio_ports[gpio_port].pull_ups, + gpio_ports[gpio_port].pull_downs); + + // Initialise the clocks for the SPI peripheral before obtaining the + // peripheral abstraction. + + /* NOTE: Set a suitably high but arbitrary frequency to support the SPI bit + clock. */ + + cpm_chip->set_frequency(Clock_ssi, 100000000); + + spi_chip = new Spi_jz4780_chip(spi_phys_base, spi_virt_base, + spi_virt_base_end, cpm_chip); + + // Obtain a DMA channel and an SPI channel for updating the display. + // NOTE: Using preset channels, frequency and pin configuration. + + dma_channel = dma_chip->get_channel(12); + + spi_jz4780_channel = spi_chip->get_channel(1, dma_channel, 25000000); + + spi_channel = new Spi_hybrid(spi_jz4780_channel, gpio_chip, 30, 2); + + return 0; +} + +// Device initialisation. + +Lcd_jz4780_spi_device *Lcd_jz4780_spi_device::device = 0; + +Lcd_device *Lcd_device::get_device() +{ + if (Lcd_jz4780_spi_device::device) + return Lcd_jz4780_spi_device::device; + + if (setup_memory()) + return 0; + + // Initialise the common device. + + Lcd_jz4780_spi_device::device = new Lcd_jz4780_spi_device(NULL /* display_device */); + + return Lcd_jz4780_spi_device::device; +} diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/lib/spi/include/spi-hybrid.h --- a/pkg/devices/lib/spi/include/spi-hybrid.h Fri Apr 19 16:25:48 2024 +0200 +++ b/pkg/devices/lib/spi/include/spi-hybrid.h Fri Apr 19 16:27:55 2024 +0200 @@ -2,7 +2,7 @@ * Perform SPI communication using a suitable abstraction augmented with * explicit manipulation of a control signal. * - * Copyright (C) 2023 Paul Boddie + * Copyright (C) 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -55,10 +55,11 @@ uint32_t send(uint32_t bytes, const uint8_t data[]); uint32_t send_dc(uint32_t bytes, const uint8_t data[], const int dc[], - uint8_t char_size, bool big_endian); + uint8_t char_size, bool big_endian = true); uint32_t send_units(uint32_t bytes, const uint8_t data[], - uint8_t unit_size, uint8_t char_size, bool big_endian); + uint8_t unit_size, uint8_t char_size, + bool big_endian = true); uint32_t transfer(l4_addr_t vaddr, l4re_dma_space_dma_addr_t paddr, uint32_t count, uint8_t unit_size, uint8_t char_size, diff -r c32074a0ec80 -r 1198da9b3bee pkg/devices/lib/spi/include/spi-jz4780.h --- a/pkg/devices/lib/spi/include/spi-jz4780.h Fri Apr 19 16:25:48 2024 +0200 +++ b/pkg/devices/lib/spi/include/spi-jz4780.h Fri Apr 19 16:27:55 2024 +0200 @@ -1,7 +1,7 @@ /* * Perform SPI communication using the JZ4780 SPI peripheral. * - * Copyright (C) 2023 Paul Boddie + * Copyright (C) 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -68,10 +68,10 @@ virtual uint32_t send(uint32_t bytes, const uint8_t data[]); virtual uint32_t send_dc(uint32_t bytes, const uint8_t data[], const int dc[], - uint8_t char_size, bool big_endian); + uint8_t char_size, bool big_endian = true); uint32_t send_units(uint32_t bytes, const uint8_t data[], uint8_t unit_size, - uint8_t char_size, bool big_endian); + uint8_t char_size, bool big_endian = true); /* DMA operations. */