7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/pkg/devices/lcd/src/x1600/lcd-x1600-spi-device.cc Fri Jun 07 16:16:49 2024 +0200
7.3 @@ -0,0 +1,258 @@
7.4 +/*
7.5 + * Common SPI-based screen support for the X1600.
7.6 + *
7.7 + * Copyright (C) 2018, 2020, 2021, 2023, 2024 Paul Boddie <paul@boddie.org.uk>
7.8 + *
7.9 + * This program is free software; you can redistribute it and/or
7.10 + * modify it under the terms of the GNU General Public License as
7.11 + * published by the Free Software Foundation; either version 2 of
7.12 + * the License, or (at your option) any later version.
7.13 + *
7.14 + * This program is distributed in the hope that it will be useful,
7.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
7.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7.17 + * GNU General Public License for more details.
7.18 + *
7.19 + * You should have received a copy of the GNU General Public License
7.20 + * along with this program; if not, write to the Free Software
7.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
7.22 + * Boston, MA 02110-1301, USA
7.23 + */
7.24 +
7.25 +#include <l4/devices/cpm-x1600.h>
7.26 +#include <l4/devices/dma-x1600.h>
7.27 +#include <l4/devices/gpio-x1600.h>
7.28 +#include <l4/devices/spi-hybrid.h>
7.29 +#include <l4/devices/spi-x1600.h>
7.30 +
7.31 +#include <l4/devices/dma.h>
7.32 +#include <l4/devices/memory.h>
7.33 +#include "lcd-x1600-spi-device.h"
7.34 +
7.35 +#include <l4/re/c/dataspace.h>
7.36 +#include <l4/re/env.h>
7.37 +#include <l4/sys/cache.h>
7.38 +#include <l4/sys/types.h>
7.39 +
7.40 +#include <ipc/cap_alloc.h>
7.41 +#include <ipc/mem_ipc.h>
7.42 +
7.43 +#include <stdio.h>
7.44 +#include <unistd.h>
7.45 +
7.46 +
7.47 +
7.48 +/* Virtual addresses for the CPM, DMA, GPIO and SSI/SPI register blocks.
7.49 +
7.50 + NOTE: Should be able to use component abstractions for some of these
7.51 + eventually. */
7.52 +
7.53 +static l4_addr_t cpm_virt_base = 0, cpm_virt_base_end = 0;
7.54 +static l4_addr_t dma_virt_base = 0, dma_virt_base_end = 0;
7.55 +static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0;
7.56 +static l4_addr_t spi_virt_base = 0, spi_virt_base_end = 0;
7.57 +static l4_addr_t spi_phys_base = 0, spi_phys_base_end = 0;
7.58 +
7.59 +static l4_uint32_t dma_irq_start = 0, dma_irq_end = 0;
7.60 +
7.61 +// CPM, DMA, GPIO, SSI/SPI and display device abstractions.
7.62 +
7.63 +static Cpm_x1600_chip *cpm_chip;
7.64 +static Dma_x1600_chip *dma_chip;
7.65 +static Gpio_x1600_chip *gpio_chip_a, *gpio_chip_b, *gpio_chip_c;
7.66 +static Spi_x1600_chip *spi_chip;
7.67 +
7.68 +static Dma_channel *dma_channel;
7.69 +static Spi_hybrid *spi_channel;
7.70 +static Spi_channel *spi_x1600_channel;
7.71 +
7.72 +
7.73 +
7.74 +// Disable the display.
7.75 +
7.76 +void
7.77 +Lcd_x1600_spi_device::disable()
7.78 +{
7.79 + // NOTE: Need to support cancelling the transfer or disabling the peripheral.
7.80 +
7.81 + //disable_display();
7.82 +}
7.83 +
7.84 +// Configure the peripheral and enable the display.
7.85 +
7.86 +void
7.87 +Lcd_x1600_spi_device::enable()
7.88 +{
7.89 + // Set up the GPIO pins for the peripheral.
7.90 + // NOTE: Hard-coded pin usage!
7.91 +
7.92 + gpio_chip_c->setup(26, Hw::Gpio_chip::Output, 1); // backlight
7.93 + gpio_chip_b->config_pad(16, Hw::Gpio_chip::Function_alt, 1); // CE1
7.94 + gpio_chip_a->config_pad(31, Hw::Gpio_chip::Function_alt, 0); // SCLK
7.95 + gpio_chip_a->config_pad(30, Hw::Gpio_chip::Function_alt, 0); // MOSI
7.96 + gpio_chip_a->setup(29, Hw::Gpio_chip::Output, 1); // GPC
7.97 +
7.98 + // Initialise the screen.
7.99 + // NOTE: Specific to the ST7789.
7.100 +
7.101 + spi_channel->send_units(2, (const uint8_t []) {0x00, 0x01}, 2, 8);
7.102 + usleep(120000);
7.103 + spi_channel->send_units(2, (const uint8_t []) {0x00, 0x11}, 2, 8);
7.104 + usleep(5000);
7.105 + spi_channel->send_units(4, (const uint8_t []) {0x00, 0x36, 0x01, 0x00}, 2, 8);
7.106 + usleep(5000);
7.107 + spi_channel->send_units(10, (const uint8_t []) {0x00, 0x3a, 0x01, 0x05, 0x00, 0x13, 0x00, 0x29, 0x00, 0x21}, 2, 8);
7.108 + usleep(5000);
7.109 + spi_channel->send_units(22, (const uint8_t []) {0x00, 0x2a, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef,
7.110 + 0x00, 0x2b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef,
7.111 + 0x00, 0x2c}, 2, 8);
7.112 + usleep(5000);
7.113 +
7.114 + for (l4_addr_t addr = get_framebuffer(); addr < get_framebuffer() + get_framebuffer_size(); addr += sizeof(uint32_t))
7.115 + *((uint32_t *) addr) = 0;
7.116 +
7.117 + l4_cache_clean_data(get_framebuffer(), get_framebuffer() + get_framebuffer_size());
7.118 +
7.119 + // Initialise the display transfer.
7.120 + // NOTE: Asserting the data signal.
7.121 +
7.122 + spi_channel->acquire_control(true);
7.123 +
7.124 + spi_x1600_channel->transfer(_fb_region.vaddr, _fb_region.paddr,
7.125 + get_framebuffer_size(), 2, 16,
7.126 + _desc_region.vaddr, _desc_region.paddr);
7.127 +
7.128 + //enable_display();
7.129 +}
7.130 +
7.131 +// Set up memory addresses for the peripheral, initialising the framebuffer and
7.132 +// descriptor members.
7.133 +
7.134 +int
7.135 +Lcd_x1600_spi_device::_setup_memory()
7.136 +{
7.137 + // Test for existing setup.
7.138 +
7.139 + if (_fb_region.vaddr)
7.140 + return 0;
7.141 +
7.142 + // Framebuffer and descriptor sizes.
7.143 +
7.144 + unsigned long fb_size = get_framebuffer_size(), desc_size = 32;
7.145 +
7.146 + // Allocate memory for the framebuffer and descriptors with 2**5 = 32 byte ==
7.147 + // 8 word alignment, sufficient for the descriptors.
7.148 +
7.149 + long err = get_dma_region(fb_size, 5, &_fb_region);
7.150 +
7.151 + if (err)
7.152 + return 1;
7.153 +
7.154 + err = get_dma_region(desc_size, 5, &_desc_region);
7.155 +
7.156 + if (err)
7.157 + return 1;
7.158 +
7.159 + return 0;
7.160 +}
7.161 +
7.162 +l4_size_t Lcd_x1600_spi_device::get_framebuffer_size()
7.163 +{
7.164 + // NOTE: Hard-coded size.
7.165 +
7.166 + return 240 * 240 * 2;
7.167 +}
7.168 +
7.169 +// Populate a view information structure.
7.170 +
7.171 +void Lcd_x1600_spi_device::get_view_info(l4re_video_view_info_t *view_info)
7.172 +{
7.173 + // NOTE: Hard-coded properties.
7.174 +
7.175 + view_info->width = 240;
7.176 + view_info->height = 240;
7.177 + view_info->pixel_info.bytes_per_pixel = 2;
7.178 + view_info->bytes_per_line = 480;
7.179 +
7.180 + view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5;
7.181 + view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6;
7.182 + view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5;
7.183 +
7.184 + view_info->pixel_info.a.shift = 0;
7.185 + view_info->pixel_info.a.size = 0;
7.186 +}
7.187 +
7.188 +// Access to peripheral memory.
7.189 +
7.190 +static int setup_memory()
7.191 +{
7.192 + if (get_memory("x1600-cpm", &cpm_virt_base, &cpm_virt_base_end))
7.193 + return 1;
7.194 +
7.195 + if (get_memory("x1600-dma", &dma_virt_base, &dma_virt_base_end))
7.196 + return 1;
7.197 +
7.198 + if (get_irq("x1600-dma", &dma_irq_start, &dma_irq_end) < 0)
7.199 + return 1;
7.200 +
7.201 + if (get_memory("x1600-gpio", &gpio_virt_base, &gpio_virt_base_end))
7.202 + return 1;
7.203 +
7.204 + if (get_memory_complete("x1600-ssi", &spi_virt_base, &spi_virt_base_end,
7.205 + &spi_phys_base, &spi_phys_base_end))
7.206 + return 1;
7.207 +
7.208 + // Initialise the abstractions.
7.209 + // NOTE: Using a preset GPIO configuration.
7.210 +
7.211 + cpm_chip = new Cpm_x1600_chip(cpm_virt_base);
7.212 +
7.213 + dma_chip = new Dma_x1600_chip(dma_virt_base, dma_virt_base_end, cpm_chip);
7.214 +
7.215 + dma_chip->enable();
7.216 +
7.217 + gpio_chip_a = new Gpio_x1600_chip(gpio_virt_base, 0);
7.218 + gpio_chip_b = new Gpio_x1600_chip(gpio_virt_base, 1);
7.219 + gpio_chip_c = new Gpio_x1600_chip(gpio_virt_base, 2);
7.220 +
7.221 + // Initialise the clocks for the SPI peripheral before obtaining the
7.222 + // peripheral abstraction.
7.223 +
7.224 + /* NOTE: Set a suitably high but arbitrary frequency to support the SPI bit
7.225 + clock. */
7.226 +
7.227 + cpm_chip->set_frequency(Clock_ssi0, 100000000);
7.228 +
7.229 + spi_chip = new Spi_x1600_chip(spi_phys_base, spi_virt_base,
7.230 + spi_virt_base_end, cpm_chip);
7.231 +
7.232 + // Obtain a DMA channel and an SPI channel for updating the display.
7.233 + // NOTE: Using preset channels, frequency and pin configuration.
7.234 +
7.235 + dma_channel = dma_chip->get_channel(12);
7.236 +
7.237 + spi_x1600_channel = spi_chip->get_channel(0, dma_channel, 25000000);
7.238 +
7.239 + spi_channel = new Spi_hybrid(spi_x1600_channel, gpio_chip_a, 29, -1);
7.240 +
7.241 + return 0;
7.242 +}
7.243 +
7.244 +// Device initialisation.
7.245 +
7.246 +Lcd_x1600_spi_device *Lcd_x1600_spi_device::device = 0;
7.247 +
7.248 +Lcd_device *Lcd_device::get_device()
7.249 +{
7.250 + if (Lcd_x1600_spi_device::device)
7.251 + return Lcd_x1600_spi_device::device;
7.252 +
7.253 + if (setup_memory())
7.254 + return 0;
7.255 +
7.256 + // Initialise the common device.
7.257 +
7.258 + Lcd_x1600_spi_device::device = new Lcd_x1600_spi_device(NULL /* display_device */);
7.259 +
7.260 + return Lcd_x1600_spi_device::device;
7.261 +}