8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/pkg/devices/lcd/src/jz4780/lcd-jz4780-spi-device.cc Sat Nov 11 23:23:22 2023 +0100
8.3 @@ -0,0 +1,276 @@
8.4 +/*
8.5 + * Common SPI-based screen support for the JZ4780 and related SoCs.
8.6 + *
8.7 + * Copyright (C) 2018, 2020, 2021, 2023 Paul Boddie <paul@boddie.org.uk>
8.8 + *
8.9 + * This program is free software; you can redistribute it and/or
8.10 + * modify it under the terms of the GNU General Public License as
8.11 + * published by the Free Software Foundation; either version 2 of
8.12 + * the License, or (at your option) any later version.
8.13 + *
8.14 + * This program is distributed in the hope that it will be useful,
8.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
8.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8.17 + * GNU General Public License for more details.
8.18 + *
8.19 + * You should have received a copy of the GNU General Public License
8.20 + * along with this program; if not, write to the Free Software
8.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
8.22 + * Boston, MA 02110-1301, USA
8.23 + */
8.24 +
8.25 +#include <l4/devices/cpm-jz4780.h>
8.26 +#include <l4/devices/dma-jz4780.h>
8.27 +#include <l4/devices/gpio-jz4780.h>
8.28 +#include <l4/devices/spi-hybrid.h>
8.29 +#include <l4/devices/spi-jz4780.h>
8.30 +
8.31 +#include <l4/devices/dma.h>
8.32 +#include <l4/devices/memory.h>
8.33 +#include "lcd-jz4780-spi-device.h"
8.34 +
8.35 +#include <l4/re/c/dataspace.h>
8.36 +#include <l4/re/env.h>
8.37 +#include <l4/sys/cache.h>
8.38 +#include <l4/sys/types.h>
8.39 +
8.40 +#include <ipc/cap_alloc.h>
8.41 +#include <ipc/mem_ipc.h>
8.42 +
8.43 +#include <stdio.h>
8.44 +#include <unistd.h>
8.45 +
8.46 +
8.47 +
8.48 +/* Virtual addresses for the CPM, DMA, GPIO and SSI/SPI register blocks.
8.49 +
8.50 + NOTE: Should be able to use component abstractions for some of these
8.51 + eventually. */
8.52 +
8.53 +static l4_addr_t cpm_virt_base = 0, cpm_virt_base_end = 0;
8.54 +static l4_addr_t dma_virt_base = 0, dma_virt_base_end = 0;
8.55 +static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0;
8.56 +static l4_addr_t spi_virt_base = 0, spi_virt_base_end = 0;
8.57 +static l4_addr_t spi_phys_base = 0, spi_phys_base_end = 0;
8.58 +
8.59 +static l4_uint32_t dma_irq_start = 0, dma_irq_end = 0;
8.60 +
8.61 +// CPM, DMA, GPIO, SSI/SPI and display device abstractions.
8.62 +
8.63 +static Cpm_jz4780_chip *cpm_chip;
8.64 +static Dma_jz4780_chip *dma_chip;
8.65 +static Gpio_jz4780_chip *gpio_chip;
8.66 +static Spi_jz4780_chip *spi_chip;
8.67 +
8.68 +static Dma_jz4780_channel *dma_channel;
8.69 +static Spi_hybrid *spi_channel;
8.70 +static Spi_jz4780_channel *spi_jz4780_channel;
8.71 +
8.72 +struct gpio_port
8.73 +{
8.74 + uint32_t pull_ups, pull_downs;
8.75 +};
8.76 +
8.77 +static struct gpio_port gpio_ports[] = {
8.78 + {0x3fff00ff, 0x00000000},
8.79 + {0xfff0f3fc, 0x000f0c03},
8.80 + {0x0fffffff, 0x00000000},
8.81 + {0xffff4fff, 0x0000b000},
8.82 + {0xf0fff37c, 0x00000483},
8.83 + {0x7fa7f00f, 0x00580ff0},
8.84 +};
8.85 +
8.86 +
8.87 +
8.88 +// Disable the display.
8.89 +
8.90 +void
8.91 +Lcd_jz4780_spi_device::disable()
8.92 +{
8.93 + // NOTE: Need to support cancelling the transfer or disabling the peripheral.
8.94 +
8.95 + //disable_display();
8.96 +}
8.97 +
8.98 +// Configure the peripheral and enable the display.
8.99 +
8.100 +void
8.101 +Lcd_jz4780_spi_device::enable()
8.102 +{
8.103 + // Set up the GPIO pins for the peripheral.
8.104 + // NOTE: Hard-coded pin usage!
8.105 +
8.106 + gpio_chip->setup(20, Hw::Gpio_chip::Output, 1); // backlight
8.107 + gpio_chip->config_pad(21, Hw::Gpio_chip::Function_alt, 2); // CE1
8.108 + gpio_chip->config_pad(28, Hw::Gpio_chip::Function_alt, 2); // SCLK
8.109 + gpio_chip->config_pad(29, Hw::Gpio_chip::Function_alt, 2); // MOSI
8.110 + gpio_chip->config_pad(30, Hw::Gpio_chip::Function_alt, 2); // GPC
8.111 +
8.112 + // Initialise the screen.
8.113 + // NOTE: Specific to the ST7789.
8.114 +
8.115 + spi_channel->send_units(2, (const uint8_t []) {0x00, 0x01}, 2, 8);
8.116 + usleep(120000);
8.117 + spi_channel->send_units(2, (const uint8_t []) {0x00, 0x11}, 2, 8);
8.118 + usleep(5000);
8.119 + spi_channel->send_units(4, (const uint8_t []) {0x00, 0x36, 0x01, 0x00}, 2, 8);
8.120 + usleep(5000);
8.121 + spi_channel->send_units(10, (const uint8_t []) {0x00, 0x3a, 0x01, 0x05, 0x00, 0x13, 0x00, 0x29, 0x00, 0x21}, 2, 8);
8.122 + usleep(5000);
8.123 + spi_channel->send_units(22, (const uint8_t []) {0x00, 0x2a, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef,
8.124 + 0x00, 0x2b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef,
8.125 + 0x00, 0x2c}, 2, 8);
8.126 + usleep(5000);
8.127 +
8.128 + for (l4_addr_t addr = fb_vaddr; addr < fb_vaddr + get_framebuffer_size(); addr += sizeof(uint32_t))
8.129 + *((uint32_t *) addr) = 0;
8.130 +
8.131 + l4_cache_clean_data(fb_vaddr, fb_vaddr + get_framebuffer_size());
8.132 +
8.133 + // Initialise the display transfer.
8.134 + // NOTE: Asserting the data signal.
8.135 +
8.136 + spi_channel->acquire_control(true);
8.137 +
8.138 + spi_jz4780_channel->transfer(fb_paddr, get_framebuffer_size(), 2, 16,
8.139 + desc_vaddr, desc_paddr);
8.140 +
8.141 + //enable_display();
8.142 +}
8.143 +
8.144 +// Set up memory addresses for the peripheral, initialising the framebuffer and
8.145 +// descriptor members.
8.146 +
8.147 +int
8.148 +Lcd_jz4780_spi_device::_setup_memory()
8.149 +{
8.150 + // Test for existing setup.
8.151 +
8.152 + if (fb_vaddr)
8.153 + return 0;
8.154 +
8.155 + // Framebuffer and descriptor sizes.
8.156 +
8.157 + unsigned long fb_size = get_framebuffer_size(), desc_size = 32;
8.158 +
8.159 + // Allocate memory for the framebuffer and descriptors with 2**5 = 32 byte ==
8.160 + // 8 word alignment, sufficient for the descriptors.
8.161 +
8.162 + long err = get_dma_region(fb_size, 5, &fb_vaddr, &fb_paddr, &_fbmem);
8.163 +
8.164 + if (err)
8.165 + return 1;
8.166 +
8.167 + l4_cap_idx_t descmem;
8.168 +
8.169 + err = get_dma_region(desc_size, 5, &desc_vaddr, &desc_paddr, &descmem);
8.170 +
8.171 + if (err)
8.172 + return 1;
8.173 +
8.174 + return 0;
8.175 +}
8.176 +
8.177 +l4_size_t Lcd_jz4780_spi_device::get_framebuffer_size()
8.178 +{
8.179 + // NOTE: Hard-coded size.
8.180 +
8.181 + return 240 * 240 * 2;
8.182 +}
8.183 +
8.184 +// Populate a view information structure.
8.185 +
8.186 +void Lcd_jz4780_spi_device::get_view_info(l4re_video_view_info_t *view_info)
8.187 +{
8.188 + // NOTE: Hard-coded properties.
8.189 +
8.190 + view_info->width = 240;
8.191 + view_info->height = 240;
8.192 + view_info->pixel_info.bytes_per_pixel = 2;
8.193 + view_info->bytes_per_line = 480;
8.194 +
8.195 + view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5;
8.196 + view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6;
8.197 + view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5;
8.198 +
8.199 + view_info->pixel_info.a.shift = 0;
8.200 + view_info->pixel_info.a.size = 0;
8.201 +}
8.202 +
8.203 +// Access to peripheral memory.
8.204 +
8.205 +static int setup_memory()
8.206 +{
8.207 + if (get_memory("jz4780-cpm", &cpm_virt_base, &cpm_virt_base_end))
8.208 + return 1;
8.209 +
8.210 + if (get_memory("jz4780-dma", &dma_virt_base, &dma_virt_base_end))
8.211 + return 1;
8.212 +
8.213 + if (get_irq("jz4780-dma", &dma_irq_start, &dma_irq_end) < 0)
8.214 + return 1;
8.215 +
8.216 + if (get_memory("jz4780-gpio", &gpio_virt_base, &gpio_virt_base_end))
8.217 + return 1;
8.218 +
8.219 + if (get_memory_complete("jz4780-ssi", &spi_virt_base, &spi_virt_base_end,
8.220 + &spi_phys_base, &spi_phys_base_end))
8.221 + return 1;
8.222 +
8.223 + // Initialise the abstractions.
8.224 + // NOTE: Using a preset GPIO configuration.
8.225 +
8.226 + int gpio_port = 1;
8.227 +
8.228 + cpm_chip = new Cpm_jz4780_chip(cpm_virt_base);
8.229 +
8.230 + dma_chip = new Dma_jz4780_chip(dma_virt_base, dma_virt_base_end, cpm_chip);
8.231 +
8.232 + dma_chip->enable();
8.233 +
8.234 + gpio_chip = new Gpio_jz4780_chip(gpio_virt_base + gpio_port * 0x100,
8.235 + gpio_virt_base + (gpio_port + 1) * 0x100,
8.236 + 32, gpio_ports[gpio_port].pull_ups,
8.237 + gpio_ports[gpio_port].pull_downs);
8.238 +
8.239 + // Initialise the clocks for the SPI peripheral before obtaining the
8.240 + // peripheral abstraction.
8.241 +
8.242 + /* NOTE: Set a suitably high but arbitrary frequency to support the SPI bit
8.243 + clock. */
8.244 +
8.245 + cpm_chip->set_frequency(Clock_ssi, 100000000);
8.246 +
8.247 + spi_chip = new Spi_jz4780_chip(spi_phys_base, spi_virt_base,
8.248 + spi_virt_base_end, cpm_chip);
8.249 +
8.250 + // Obtain a DMA channel and an SPI channel for updating the display.
8.251 + // NOTE: Using preset channels, frequency and pin configuration.
8.252 +
8.253 + dma_channel = dma_chip->get_channel(12);
8.254 +
8.255 + spi_jz4780_channel = spi_chip->get_channel(1, dma_channel, 25000000);
8.256 +
8.257 + spi_channel = new Spi_hybrid(spi_jz4780_channel, gpio_chip, 30, 2);
8.258 +
8.259 + return 0;
8.260 +}
8.261 +
8.262 +// Device initialisation.
8.263 +
8.264 +Lcd_jz4780_spi_device *Lcd_jz4780_spi_device::device = 0;
8.265 +
8.266 +Lcd_device *Lcd_device::get_device()
8.267 +{
8.268 + if (Lcd_jz4780_spi_device::device)
8.269 + return Lcd_jz4780_spi_device::device;
8.270 +
8.271 + if (setup_memory())
8.272 + return 0;
8.273 +
8.274 + // Initialise the common device.
8.275 +
8.276 + Lcd_jz4780_spi_device::device = new Lcd_jz4780_spi_device(NULL /* display_device */);
8.277 +
8.278 + return Lcd_jz4780_spi_device::device;
8.279 +}