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 Fri Apr 19 16:27:55 2024 +0200
1.3 @@ -0,0 +1,275 @@
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, 2024 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 = get_framebuffer(); addr < get_framebuffer() + get_framebuffer_size(); addr += sizeof(uint32_t))
1.129 + *((uint32_t *) addr) = 0;
1.130 +
1.131 + l4_cache_clean_data(get_framebuffer(), get_framebuffer() + 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_region.vaddr, _fb_region.paddr,
1.139 + get_framebuffer_size(), 2, 16,
1.140 + _desc_region.vaddr, _desc_region.paddr);
1.141 +
1.142 + //enable_display();
1.143 +}
1.144 +
1.145 +// Set up memory addresses for the peripheral, initialising the framebuffer and
1.146 +// descriptor members.
1.147 +
1.148 +int
1.149 +Lcd_jz4780_spi_device::_setup_memory()
1.150 +{
1.151 + // Test for existing setup.
1.152 +
1.153 + if (_fb_region.vaddr)
1.154 + return 0;
1.155 +
1.156 + // Framebuffer and descriptor sizes.
1.157 +
1.158 + unsigned long fb_size = get_framebuffer_size(), desc_size = 32;
1.159 +
1.160 + // Allocate memory for the framebuffer and descriptors with 2**5 = 32 byte ==
1.161 + // 8 word alignment, sufficient for the descriptors.
1.162 +
1.163 + long err = get_dma_region(fb_size, 5, &_fb_region);
1.164 +
1.165 + if (err)
1.166 + return 1;
1.167 +
1.168 + err = get_dma_region(desc_size, 5, &_desc_region);
1.169 +
1.170 + if (err)
1.171 + return 1;
1.172 +
1.173 + return 0;
1.174 +}
1.175 +
1.176 +l4_size_t Lcd_jz4780_spi_device::get_framebuffer_size()
1.177 +{
1.178 + // NOTE: Hard-coded size.
1.179 +
1.180 + return 240 * 240 * 2;
1.181 +}
1.182 +
1.183 +// Populate a view information structure.
1.184 +
1.185 +void Lcd_jz4780_spi_device::get_view_info(l4re_video_view_info_t *view_info)
1.186 +{
1.187 + // NOTE: Hard-coded properties.
1.188 +
1.189 + view_info->width = 240;
1.190 + view_info->height = 240;
1.191 + view_info->pixel_info.bytes_per_pixel = 2;
1.192 + view_info->bytes_per_line = 480;
1.193 +
1.194 + view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5;
1.195 + view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6;
1.196 + view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5;
1.197 +
1.198 + view_info->pixel_info.a.shift = 0;
1.199 + view_info->pixel_info.a.size = 0;
1.200 +}
1.201 +
1.202 +// Access to peripheral memory.
1.203 +
1.204 +static int setup_memory()
1.205 +{
1.206 + if (get_memory("jz4780-cpm", &cpm_virt_base, &cpm_virt_base_end))
1.207 + return 1;
1.208 +
1.209 + if (get_memory("jz4780-dma", &dma_virt_base, &dma_virt_base_end))
1.210 + return 1;
1.211 +
1.212 + if (get_irq("jz4780-dma", &dma_irq_start, &dma_irq_end) < 0)
1.213 + return 1;
1.214 +
1.215 + if (get_memory("jz4780-gpio", &gpio_virt_base, &gpio_virt_base_end))
1.216 + return 1;
1.217 +
1.218 + if (get_memory_complete("jz4780-ssi", &spi_virt_base, &spi_virt_base_end,
1.219 + &spi_phys_base, &spi_phys_base_end))
1.220 + return 1;
1.221 +
1.222 + // Initialise the abstractions.
1.223 + // NOTE: Using a preset GPIO configuration.
1.224 +
1.225 + int gpio_port = 1;
1.226 +
1.227 + cpm_chip = new Cpm_jz4780_chip(cpm_virt_base);
1.228 +
1.229 + dma_chip = new Dma_jz4780_chip(dma_virt_base, dma_virt_base_end, cpm_chip);
1.230 +
1.231 + dma_chip->enable();
1.232 +
1.233 + gpio_chip = new Gpio_jz4780_chip(gpio_virt_base + gpio_port * 0x100,
1.234 + gpio_virt_base + (gpio_port + 1) * 0x100,
1.235 + 32, gpio_ports[gpio_port].pull_ups,
1.236 + gpio_ports[gpio_port].pull_downs);
1.237 +
1.238 + // Initialise the clocks for the SPI peripheral before obtaining the
1.239 + // peripheral abstraction.
1.240 +
1.241 + /* NOTE: Set a suitably high but arbitrary frequency to support the SPI bit
1.242 + clock. */
1.243 +
1.244 + cpm_chip->set_frequency(Clock_ssi, 100000000);
1.245 +
1.246 + spi_chip = new Spi_jz4780_chip(spi_phys_base, spi_virt_base,
1.247 + spi_virt_base_end, cpm_chip);
1.248 +
1.249 + // Obtain a DMA channel and an SPI channel for updating the display.
1.250 + // NOTE: Using preset channels, frequency and pin configuration.
1.251 +
1.252 + dma_channel = dma_chip->get_channel(12);
1.253 +
1.254 + spi_jz4780_channel = spi_chip->get_channel(1, dma_channel, 25000000);
1.255 +
1.256 + spi_channel = new Spi_hybrid(spi_jz4780_channel, gpio_chip, 30, 2);
1.257 +
1.258 + return 0;
1.259 +}
1.260 +
1.261 +// Device initialisation.
1.262 +
1.263 +Lcd_jz4780_spi_device *Lcd_jz4780_spi_device::device = 0;
1.264 +
1.265 +Lcd_device *Lcd_device::get_device()
1.266 +{
1.267 + if (Lcd_jz4780_spi_device::device)
1.268 + return Lcd_jz4780_spi_device::device;
1.269 +
1.270 + if (setup_memory())
1.271 + return 0;
1.272 +
1.273 + // Initialise the common device.
1.274 +
1.275 + Lcd_jz4780_spi_device::device = new Lcd_jz4780_spi_device(NULL /* display_device */);
1.276 +
1.277 + return Lcd_jz4780_spi_device::device;
1.278 +}