1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lcd/src/jz4780/lcd-jz4780-spi-device.cc Sat Nov 11 23:23:22 2023 +0100
1.3 @@ -0,0 +1,276 @@
1.4 +/*
1.5 + * Common SPI-based screen support for the JZ4780 and related SoCs.
1.6 + *
1.7 + * Copyright (C) 2018, 2020, 2021, 2023 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <l4/devices/cpm-jz4780.h>
1.26 +#include <l4/devices/dma-jz4780.h>
1.27 +#include <l4/devices/gpio-jz4780.h>
1.28 +#include <l4/devices/spi-hybrid.h>
1.29 +#include <l4/devices/spi-jz4780.h>
1.30 +
1.31 +#include <l4/devices/dma.h>
1.32 +#include <l4/devices/memory.h>
1.33 +#include "lcd-jz4780-spi-device.h"
1.34 +
1.35 +#include <l4/re/c/dataspace.h>
1.36 +#include <l4/re/env.h>
1.37 +#include <l4/sys/cache.h>
1.38 +#include <l4/sys/types.h>
1.39 +
1.40 +#include <ipc/cap_alloc.h>
1.41 +#include <ipc/mem_ipc.h>
1.42 +
1.43 +#include <stdio.h>
1.44 +#include <unistd.h>
1.45 +
1.46 +
1.47 +
1.48 +/* Virtual addresses for the CPM, DMA, GPIO and SSI/SPI register blocks.
1.49 +
1.50 + NOTE: Should be able to use component abstractions for some of these
1.51 + eventually. */
1.52 +
1.53 +static l4_addr_t cpm_virt_base = 0, cpm_virt_base_end = 0;
1.54 +static l4_addr_t dma_virt_base = 0, dma_virt_base_end = 0;
1.55 +static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0;
1.56 +static l4_addr_t spi_virt_base = 0, spi_virt_base_end = 0;
1.57 +static l4_addr_t spi_phys_base = 0, spi_phys_base_end = 0;
1.58 +
1.59 +static l4_uint32_t dma_irq_start = 0, dma_irq_end = 0;
1.60 +
1.61 +// CPM, DMA, GPIO, SSI/SPI and display device abstractions.
1.62 +
1.63 +static Cpm_jz4780_chip *cpm_chip;
1.64 +static Dma_jz4780_chip *dma_chip;
1.65 +static Gpio_jz4780_chip *gpio_chip;
1.66 +static Spi_jz4780_chip *spi_chip;
1.67 +
1.68 +static Dma_jz4780_channel *dma_channel;
1.69 +static Spi_hybrid *spi_channel;
1.70 +static Spi_jz4780_channel *spi_jz4780_channel;
1.71 +
1.72 +struct gpio_port
1.73 +{
1.74 + uint32_t pull_ups, pull_downs;
1.75 +};
1.76 +
1.77 +static struct gpio_port gpio_ports[] = {
1.78 + {0x3fff00ff, 0x00000000},
1.79 + {0xfff0f3fc, 0x000f0c03},
1.80 + {0x0fffffff, 0x00000000},
1.81 + {0xffff4fff, 0x0000b000},
1.82 + {0xf0fff37c, 0x00000483},
1.83 + {0x7fa7f00f, 0x00580ff0},
1.84 +};
1.85 +
1.86 +
1.87 +
1.88 +// Disable the display.
1.89 +
1.90 +void
1.91 +Lcd_jz4780_spi_device::disable()
1.92 +{
1.93 + // NOTE: Need to support cancelling the transfer or disabling the peripheral.
1.94 +
1.95 + //disable_display();
1.96 +}
1.97 +
1.98 +// Configure the peripheral and enable the display.
1.99 +
1.100 +void
1.101 +Lcd_jz4780_spi_device::enable()
1.102 +{
1.103 + // Set up the GPIO pins for the peripheral.
1.104 + // NOTE: Hard-coded pin usage!
1.105 +
1.106 + gpio_chip->setup(20, Hw::Gpio_chip::Output, 1); // backlight
1.107 + gpio_chip->config_pad(21, Hw::Gpio_chip::Function_alt, 2); // CE1
1.108 + gpio_chip->config_pad(28, Hw::Gpio_chip::Function_alt, 2); // SCLK
1.109 + gpio_chip->config_pad(29, Hw::Gpio_chip::Function_alt, 2); // MOSI
1.110 + gpio_chip->config_pad(30, Hw::Gpio_chip::Function_alt, 2); // GPC
1.111 +
1.112 + // Initialise the screen.
1.113 + // NOTE: Specific to the ST7789.
1.114 +
1.115 + spi_channel->send_units(2, (const uint8_t []) {0x00, 0x01}, 2, 8);
1.116 + usleep(120000);
1.117 + spi_channel->send_units(2, (const uint8_t []) {0x00, 0x11}, 2, 8);
1.118 + usleep(5000);
1.119 + spi_channel->send_units(4, (const uint8_t []) {0x00, 0x36, 0x01, 0x00}, 2, 8);
1.120 + usleep(5000);
1.121 + spi_channel->send_units(10, (const uint8_t []) {0x00, 0x3a, 0x01, 0x05, 0x00, 0x13, 0x00, 0x29, 0x00, 0x21}, 2, 8);
1.122 + usleep(5000);
1.123 + spi_channel->send_units(22, (const uint8_t []) {0x00, 0x2a, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef,
1.124 + 0x00, 0x2b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef,
1.125 + 0x00, 0x2c}, 2, 8);
1.126 + usleep(5000);
1.127 +
1.128 + for (l4_addr_t addr = fb_vaddr; addr < fb_vaddr + get_framebuffer_size(); addr += sizeof(uint32_t))
1.129 + *((uint32_t *) addr) = 0;
1.130 +
1.131 + l4_cache_clean_data(fb_vaddr, fb_vaddr + get_framebuffer_size());
1.132 +
1.133 + // Initialise the display transfer.
1.134 + // NOTE: Asserting the data signal.
1.135 +
1.136 + spi_channel->acquire_control(true);
1.137 +
1.138 + spi_jz4780_channel->transfer(fb_paddr, get_framebuffer_size(), 2, 16,
1.139 + desc_vaddr, desc_paddr);
1.140 +
1.141 + //enable_display();
1.142 +}
1.143 +
1.144 +// Set up memory addresses for the peripheral, initialising the framebuffer and
1.145 +// descriptor members.
1.146 +
1.147 +int
1.148 +Lcd_jz4780_spi_device::_setup_memory()
1.149 +{
1.150 + // Test for existing setup.
1.151 +
1.152 + if (fb_vaddr)
1.153 + return 0;
1.154 +
1.155 + // Framebuffer and descriptor sizes.
1.156 +
1.157 + unsigned long fb_size = get_framebuffer_size(), desc_size = 32;
1.158 +
1.159 + // Allocate memory for the framebuffer and descriptors with 2**5 = 32 byte ==
1.160 + // 8 word alignment, sufficient for the descriptors.
1.161 +
1.162 + long err = get_dma_region(fb_size, 5, &fb_vaddr, &fb_paddr, &_fbmem);
1.163 +
1.164 + if (err)
1.165 + return 1;
1.166 +
1.167 + l4_cap_idx_t descmem;
1.168 +
1.169 + err = get_dma_region(desc_size, 5, &desc_vaddr, &desc_paddr, &descmem);
1.170 +
1.171 + if (err)
1.172 + return 1;
1.173 +
1.174 + return 0;
1.175 +}
1.176 +
1.177 +l4_size_t Lcd_jz4780_spi_device::get_framebuffer_size()
1.178 +{
1.179 + // NOTE: Hard-coded size.
1.180 +
1.181 + return 240 * 240 * 2;
1.182 +}
1.183 +
1.184 +// Populate a view information structure.
1.185 +
1.186 +void Lcd_jz4780_spi_device::get_view_info(l4re_video_view_info_t *view_info)
1.187 +{
1.188 + // NOTE: Hard-coded properties.
1.189 +
1.190 + view_info->width = 240;
1.191 + view_info->height = 240;
1.192 + view_info->pixel_info.bytes_per_pixel = 2;
1.193 + view_info->bytes_per_line = 480;
1.194 +
1.195 + view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5;
1.196 + view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6;
1.197 + view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5;
1.198 +
1.199 + view_info->pixel_info.a.shift = 0;
1.200 + view_info->pixel_info.a.size = 0;
1.201 +}
1.202 +
1.203 +// Access to peripheral memory.
1.204 +
1.205 +static int setup_memory()
1.206 +{
1.207 + if (get_memory("jz4780-cpm", &cpm_virt_base, &cpm_virt_base_end))
1.208 + return 1;
1.209 +
1.210 + if (get_memory("jz4780-dma", &dma_virt_base, &dma_virt_base_end))
1.211 + return 1;
1.212 +
1.213 + if (get_irq("jz4780-dma", &dma_irq_start, &dma_irq_end) < 0)
1.214 + return 1;
1.215 +
1.216 + if (get_memory("jz4780-gpio", &gpio_virt_base, &gpio_virt_base_end))
1.217 + return 1;
1.218 +
1.219 + if (get_memory_complete("jz4780-ssi", &spi_virt_base, &spi_virt_base_end,
1.220 + &spi_phys_base, &spi_phys_base_end))
1.221 + return 1;
1.222 +
1.223 + // Initialise the abstractions.
1.224 + // NOTE: Using a preset GPIO configuration.
1.225 +
1.226 + int gpio_port = 1;
1.227 +
1.228 + cpm_chip = new Cpm_jz4780_chip(cpm_virt_base);
1.229 +
1.230 + dma_chip = new Dma_jz4780_chip(dma_virt_base, dma_virt_base_end, cpm_chip);
1.231 +
1.232 + dma_chip->enable();
1.233 +
1.234 + gpio_chip = new Gpio_jz4780_chip(gpio_virt_base + gpio_port * 0x100,
1.235 + gpio_virt_base + (gpio_port + 1) * 0x100,
1.236 + 32, gpio_ports[gpio_port].pull_ups,
1.237 + gpio_ports[gpio_port].pull_downs);
1.238 +
1.239 + // Initialise the clocks for the SPI peripheral before obtaining the
1.240 + // peripheral abstraction.
1.241 +
1.242 + /* NOTE: Set a suitably high but arbitrary frequency to support the SPI bit
1.243 + clock. */
1.244 +
1.245 + cpm_chip->set_frequency(Clock_ssi, 100000000);
1.246 +
1.247 + spi_chip = new Spi_jz4780_chip(spi_phys_base, spi_virt_base,
1.248 + spi_virt_base_end, cpm_chip);
1.249 +
1.250 + // Obtain a DMA channel and an SPI channel for updating the display.
1.251 + // NOTE: Using preset channels, frequency and pin configuration.
1.252 +
1.253 + dma_channel = dma_chip->get_channel(12);
1.254 +
1.255 + spi_jz4780_channel = spi_chip->get_channel(1, dma_channel, 25000000);
1.256 +
1.257 + spi_channel = new Spi_hybrid(spi_jz4780_channel, gpio_chip, 30, 2);
1.258 +
1.259 + return 0;
1.260 +}
1.261 +
1.262 +// Device initialisation.
1.263 +
1.264 +Lcd_jz4780_spi_device *Lcd_jz4780_spi_device::device = 0;
1.265 +
1.266 +Lcd_device *Lcd_device::get_device()
1.267 +{
1.268 + if (Lcd_jz4780_spi_device::device)
1.269 + return Lcd_jz4780_spi_device::device;
1.270 +
1.271 + if (setup_memory())
1.272 + return 0;
1.273 +
1.274 + // Initialise the common device.
1.275 +
1.276 + Lcd_jz4780_spi_device::device = new Lcd_jz4780_spi_device(NULL /* display_device */);
1.277 +
1.278 + return Lcd_jz4780_spi_device::device;
1.279 +}