# HG changeset patch # User Paul Boddie # Date 1699741402 -3600 # Node ID 49b57ce7cd3591559ff6e4c1267279b0e650289d # Parent ba0ca04c6e22349edb5dbbff2dda7cad6b02d8e3 A demonstration of an SPI-based framebuffer using an ST7789 controller and a 240x240 screen. diff -r ba0ca04c6e22 -r 49b57ce7cd35 pkg/devices/Control --- a/pkg/devices/Control Fri Nov 10 22:30:39 2023 +0100 +++ b/pkg/devices/Control Sat Nov 11 23:23:22 2023 +0100 @@ -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 ba0ca04c6e22 -r 49b57ce7cd35 pkg/devices/lcd/include/lcd-device.h --- a/pkg/devices/lcd/include/lcd-device.h Fri Nov 10 22:30:39 2023 +0100 +++ b/pkg/devices/lcd/include/lcd-device.h Sat Nov 11 23:23:22 2023 +0100 @@ -37,10 +37,6 @@ class Lcd_device { protected: - /* LCD peripheral abstraction. */ - - Lcd_chip *_chip; - /* Display server abstraction. */ Activation *_display; @@ -62,8 +58,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 ba0ca04c6e22 -r 49b57ce7cd35 pkg/devices/lcd/include/lcd-jz4740-device.h --- a/pkg/devices/lcd/include/lcd-jz4740-device.h Fri Nov 10 22:30:39 2023 +0100 +++ b/pkg/devices/lcd/include/lcd-jz4740-device.h Sat Nov 11 23:23:22 2023 +0100 @@ -43,6 +43,10 @@ l4_addr_t desc_vaddr; l4re_dma_space_dma_addr_t desc_paddr; + /* LCD peripheral abstraction. */ + + Lcd_chip *_chip; + protected: /* Device-specific memory allocation. */ @@ -52,7 +56,7 @@ /* Inherit constructor. */ Lcd_jz4740_device(Lcd_chip *chip, Activation *display) - : Lcd_device(chip, display) + : Lcd_device(display), _chip(chip) { _setup_memory(); } diff -r ba0ca04c6e22 -r 49b57ce7cd35 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 Sat Nov 11 23:23:22 2023 +0100 @@ -0,0 +1,72 @@ +/* + * 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 descriptor virtual and physical addresses. */ + + l4_addr_t desc_vaddr; + l4re_dma_space_dma_addr_t desc_paddr; + +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 ba0ca04c6e22 -r 49b57ce7cd35 pkg/devices/lcd/src/Makefile --- a/pkg/devices/lcd/src/Makefile Fri Nov 10 22:30:39 2023 +0100 +++ b/pkg/devices/lcd/src/Makefile Sat Nov 11 23:23:22 2023 +0100 @@ -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 ba0ca04c6e22 -r 49b57ce7cd35 pkg/devices/lcd/src/common/lcd-device.cc --- a/pkg/devices/lcd/src/common/lcd-device.cc Fri Nov 10 22:30:39 2023 +0100 +++ b/pkg/devices/lcd/src/common/lcd-device.cc Sat Nov 11 23:23:22 2023 +0100 @@ -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 ba0ca04c6e22 -r 49b57ce7cd35 pkg/devices/lcd/src/jz4780/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lcd/src/jz4780/Makefile Sat Nov 11 23:23:22 2023 +0100 @@ -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 ba0ca04c6e22 -r 49b57ce7cd35 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 Sat Nov 11 23:23:22 2023 +0100 @@ -0,0 +1,276 @@ +/* + * Common SPI-based screen support for the JZ4780 and related SoCs. + * + * Copyright (C) 2018, 2020, 2021, 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 + */ + +#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 = fb_vaddr; addr < fb_vaddr + get_framebuffer_size(); addr += sizeof(uint32_t)) + *((uint32_t *) addr) = 0; + + l4_cache_clean_data(fb_vaddr, fb_vaddr + get_framebuffer_size()); + + // Initialise the display transfer. + // NOTE: Asserting the data signal. + + spi_channel->acquire_control(true); + + spi_jz4780_channel->transfer(fb_paddr, get_framebuffer_size(), 2, 16, + desc_vaddr, desc_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_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_vaddr, &fb_paddr, &_fbmem); + + if (err) + return 1; + + l4_cap_idx_t descmem; + + err = get_dma_region(desc_size, 5, &desc_vaddr, &desc_paddr, &descmem); + + 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; +}