paul@226 | 1 | /* |
paul@226 | 2 | * Common SPI-based screen support for the JZ4780 and related SoCs. |
paul@226 | 3 | * |
paul@282 | 4 | * Copyright (C) 2018, 2020, 2021, 2023, 2024 Paul Boddie <paul@boddie.org.uk> |
paul@226 | 5 | * |
paul@226 | 6 | * This program is free software; you can redistribute it and/or |
paul@226 | 7 | * modify it under the terms of the GNU General Public License as |
paul@226 | 8 | * published by the Free Software Foundation; either version 2 of |
paul@226 | 9 | * the License, or (at your option) any later version. |
paul@226 | 10 | * |
paul@226 | 11 | * This program is distributed in the hope that it will be useful, |
paul@226 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@226 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@226 | 14 | * GNU General Public License for more details. |
paul@226 | 15 | * |
paul@226 | 16 | * You should have received a copy of the GNU General Public License |
paul@226 | 17 | * along with this program; if not, write to the Free Software |
paul@226 | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@226 | 19 | * Boston, MA 02110-1301, USA |
paul@226 | 20 | */ |
paul@226 | 21 | |
paul@226 | 22 | #include <l4/devices/cpm-jz4780.h> |
paul@226 | 23 | #include <l4/devices/dma-jz4780.h> |
paul@226 | 24 | #include <l4/devices/gpio-jz4780.h> |
paul@226 | 25 | #include <l4/devices/spi-hybrid.h> |
paul@226 | 26 | #include <l4/devices/spi-jz4780.h> |
paul@226 | 27 | |
paul@226 | 28 | #include <l4/devices/dma.h> |
paul@226 | 29 | #include <l4/devices/memory.h> |
paul@226 | 30 | #include "lcd-jz4780-spi-device.h" |
paul@226 | 31 | |
paul@226 | 32 | #include <l4/re/c/dataspace.h> |
paul@226 | 33 | #include <l4/re/env.h> |
paul@226 | 34 | #include <l4/sys/cache.h> |
paul@226 | 35 | #include <l4/sys/types.h> |
paul@226 | 36 | |
paul@226 | 37 | #include <ipc/cap_alloc.h> |
paul@226 | 38 | #include <ipc/mem_ipc.h> |
paul@226 | 39 | |
paul@226 | 40 | #include <stdio.h> |
paul@226 | 41 | #include <unistd.h> |
paul@226 | 42 | |
paul@226 | 43 | |
paul@226 | 44 | |
paul@226 | 45 | /* Virtual addresses for the CPM, DMA, GPIO and SSI/SPI register blocks. |
paul@226 | 46 | |
paul@226 | 47 | NOTE: Should be able to use component abstractions for some of these |
paul@226 | 48 | eventually. */ |
paul@226 | 49 | |
paul@226 | 50 | static l4_addr_t cpm_virt_base = 0, cpm_virt_base_end = 0; |
paul@226 | 51 | static l4_addr_t dma_virt_base = 0, dma_virt_base_end = 0; |
paul@226 | 52 | static l4_addr_t gpio_virt_base = 0, gpio_virt_base_end = 0; |
paul@226 | 53 | static l4_addr_t spi_virt_base = 0, spi_virt_base_end = 0; |
paul@226 | 54 | static l4_addr_t spi_phys_base = 0, spi_phys_base_end = 0; |
paul@226 | 55 | |
paul@226 | 56 | static l4_uint32_t dma_irq_start = 0, dma_irq_end = 0; |
paul@226 | 57 | |
paul@226 | 58 | // CPM, DMA, GPIO, SSI/SPI and display device abstractions. |
paul@226 | 59 | |
paul@226 | 60 | static Cpm_jz4780_chip *cpm_chip; |
paul@226 | 61 | static Dma_jz4780_chip *dma_chip; |
paul@226 | 62 | static Gpio_jz4780_chip *gpio_chip; |
paul@226 | 63 | static Spi_jz4780_chip *spi_chip; |
paul@226 | 64 | |
paul@226 | 65 | static Dma_jz4780_channel *dma_channel; |
paul@226 | 66 | static Spi_hybrid *spi_channel; |
paul@226 | 67 | static Spi_jz4780_channel *spi_jz4780_channel; |
paul@226 | 68 | |
paul@226 | 69 | struct gpio_port |
paul@226 | 70 | { |
paul@226 | 71 | uint32_t pull_ups, pull_downs; |
paul@226 | 72 | }; |
paul@226 | 73 | |
paul@226 | 74 | static struct gpio_port gpio_ports[] = { |
paul@226 | 75 | {0x3fff00ff, 0x00000000}, |
paul@226 | 76 | {0xfff0f3fc, 0x000f0c03}, |
paul@226 | 77 | {0x0fffffff, 0x00000000}, |
paul@226 | 78 | {0xffff4fff, 0x0000b000}, |
paul@226 | 79 | {0xf0fff37c, 0x00000483}, |
paul@226 | 80 | {0x7fa7f00f, 0x00580ff0}, |
paul@226 | 81 | }; |
paul@226 | 82 | |
paul@226 | 83 | |
paul@226 | 84 | |
paul@226 | 85 | // Disable the display. |
paul@226 | 86 | |
paul@226 | 87 | void |
paul@226 | 88 | Lcd_jz4780_spi_device::disable() |
paul@226 | 89 | { |
paul@226 | 90 | // NOTE: Need to support cancelling the transfer or disabling the peripheral. |
paul@226 | 91 | |
paul@226 | 92 | //disable_display(); |
paul@226 | 93 | } |
paul@226 | 94 | |
paul@226 | 95 | // Configure the peripheral and enable the display. |
paul@226 | 96 | |
paul@226 | 97 | void |
paul@226 | 98 | Lcd_jz4780_spi_device::enable() |
paul@226 | 99 | { |
paul@226 | 100 | // Set up the GPIO pins for the peripheral. |
paul@226 | 101 | // NOTE: Hard-coded pin usage! |
paul@226 | 102 | |
paul@226 | 103 | gpio_chip->setup(20, Hw::Gpio_chip::Output, 1); // backlight |
paul@226 | 104 | gpio_chip->config_pad(21, Hw::Gpio_chip::Function_alt, 2); // CE1 |
paul@226 | 105 | gpio_chip->config_pad(28, Hw::Gpio_chip::Function_alt, 2); // SCLK |
paul@226 | 106 | gpio_chip->config_pad(29, Hw::Gpio_chip::Function_alt, 2); // MOSI |
paul@226 | 107 | gpio_chip->config_pad(30, Hw::Gpio_chip::Function_alt, 2); // GPC |
paul@226 | 108 | |
paul@226 | 109 | // Initialise the screen. |
paul@226 | 110 | // NOTE: Specific to the ST7789. |
paul@226 | 111 | |
paul@226 | 112 | spi_channel->send_units(2, (const uint8_t []) {0x00, 0x01}, 2, 8); |
paul@226 | 113 | usleep(120000); |
paul@226 | 114 | spi_channel->send_units(2, (const uint8_t []) {0x00, 0x11}, 2, 8); |
paul@226 | 115 | usleep(5000); |
paul@226 | 116 | spi_channel->send_units(4, (const uint8_t []) {0x00, 0x36, 0x01, 0x00}, 2, 8); |
paul@226 | 117 | usleep(5000); |
paul@226 | 118 | spi_channel->send_units(10, (const uint8_t []) {0x00, 0x3a, 0x01, 0x05, 0x00, 0x13, 0x00, 0x29, 0x00, 0x21}, 2, 8); |
paul@226 | 119 | usleep(5000); |
paul@226 | 120 | spi_channel->send_units(22, (const uint8_t []) {0x00, 0x2a, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef, |
paul@226 | 121 | 0x00, 0x2b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef, |
paul@226 | 122 | 0x00, 0x2c}, 2, 8); |
paul@226 | 123 | usleep(5000); |
paul@226 | 124 | |
paul@282 | 125 | for (l4_addr_t addr = get_framebuffer(); addr < get_framebuffer() + get_framebuffer_size(); addr += sizeof(uint32_t)) |
paul@226 | 126 | *((uint32_t *) addr) = 0; |
paul@226 | 127 | |
paul@282 | 128 | l4_cache_clean_data(get_framebuffer(), get_framebuffer() + get_framebuffer_size()); |
paul@226 | 129 | |
paul@226 | 130 | // Initialise the display transfer. |
paul@226 | 131 | // NOTE: Asserting the data signal. |
paul@226 | 132 | |
paul@226 | 133 | spi_channel->acquire_control(true); |
paul@226 | 134 | |
paul@282 | 135 | spi_jz4780_channel->transfer(_fb_region.vaddr, _fb_region.paddr, |
paul@282 | 136 | get_framebuffer_size(), 2, 16, |
paul@282 | 137 | _desc_region.vaddr, _desc_region.paddr); |
paul@226 | 138 | |
paul@226 | 139 | //enable_display(); |
paul@226 | 140 | } |
paul@226 | 141 | |
paul@226 | 142 | // Set up memory addresses for the peripheral, initialising the framebuffer and |
paul@226 | 143 | // descriptor members. |
paul@226 | 144 | |
paul@226 | 145 | int |
paul@226 | 146 | Lcd_jz4780_spi_device::_setup_memory() |
paul@226 | 147 | { |
paul@226 | 148 | // Test for existing setup. |
paul@226 | 149 | |
paul@282 | 150 | if (_fb_region.vaddr) |
paul@226 | 151 | return 0; |
paul@226 | 152 | |
paul@226 | 153 | // Framebuffer and descriptor sizes. |
paul@226 | 154 | |
paul@226 | 155 | unsigned long fb_size = get_framebuffer_size(), desc_size = 32; |
paul@226 | 156 | |
paul@226 | 157 | // Allocate memory for the framebuffer and descriptors with 2**5 = 32 byte == |
paul@226 | 158 | // 8 word alignment, sufficient for the descriptors. |
paul@226 | 159 | |
paul@282 | 160 | long err = get_dma_region(fb_size, 5, &_fb_region); |
paul@226 | 161 | |
paul@226 | 162 | if (err) |
paul@226 | 163 | return 1; |
paul@226 | 164 | |
paul@282 | 165 | err = get_dma_region(desc_size, 5, &_desc_region); |
paul@226 | 166 | |
paul@226 | 167 | if (err) |
paul@226 | 168 | return 1; |
paul@226 | 169 | |
paul@226 | 170 | return 0; |
paul@226 | 171 | } |
paul@226 | 172 | |
paul@226 | 173 | l4_size_t Lcd_jz4780_spi_device::get_framebuffer_size() |
paul@226 | 174 | { |
paul@226 | 175 | // NOTE: Hard-coded size. |
paul@226 | 176 | |
paul@226 | 177 | return 240 * 240 * 2; |
paul@226 | 178 | } |
paul@226 | 179 | |
paul@226 | 180 | // Populate a view information structure. |
paul@226 | 181 | |
paul@226 | 182 | void Lcd_jz4780_spi_device::get_view_info(l4re_video_view_info_t *view_info) |
paul@226 | 183 | { |
paul@226 | 184 | // NOTE: Hard-coded properties. |
paul@226 | 185 | |
paul@226 | 186 | view_info->width = 240; |
paul@226 | 187 | view_info->height = 240; |
paul@226 | 188 | view_info->pixel_info.bytes_per_pixel = 2; |
paul@226 | 189 | view_info->bytes_per_line = 480; |
paul@226 | 190 | |
paul@226 | 191 | view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5; |
paul@226 | 192 | view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6; |
paul@226 | 193 | view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5; |
paul@226 | 194 | |
paul@226 | 195 | view_info->pixel_info.a.shift = 0; |
paul@226 | 196 | view_info->pixel_info.a.size = 0; |
paul@226 | 197 | } |
paul@226 | 198 | |
paul@226 | 199 | // Access to peripheral memory. |
paul@226 | 200 | |
paul@226 | 201 | static int setup_memory() |
paul@226 | 202 | { |
paul@226 | 203 | if (get_memory("jz4780-cpm", &cpm_virt_base, &cpm_virt_base_end)) |
paul@226 | 204 | return 1; |
paul@226 | 205 | |
paul@226 | 206 | if (get_memory("jz4780-dma", &dma_virt_base, &dma_virt_base_end)) |
paul@226 | 207 | return 1; |
paul@226 | 208 | |
paul@226 | 209 | if (get_irq("jz4780-dma", &dma_irq_start, &dma_irq_end) < 0) |
paul@226 | 210 | return 1; |
paul@226 | 211 | |
paul@226 | 212 | if (get_memory("jz4780-gpio", &gpio_virt_base, &gpio_virt_base_end)) |
paul@226 | 213 | return 1; |
paul@226 | 214 | |
paul@226 | 215 | if (get_memory_complete("jz4780-ssi", &spi_virt_base, &spi_virt_base_end, |
paul@226 | 216 | &spi_phys_base, &spi_phys_base_end)) |
paul@226 | 217 | return 1; |
paul@226 | 218 | |
paul@226 | 219 | // Initialise the abstractions. |
paul@226 | 220 | // NOTE: Using a preset GPIO configuration. |
paul@226 | 221 | |
paul@226 | 222 | int gpio_port = 1; |
paul@226 | 223 | |
paul@226 | 224 | cpm_chip = new Cpm_jz4780_chip(cpm_virt_base); |
paul@226 | 225 | |
paul@226 | 226 | dma_chip = new Dma_jz4780_chip(dma_virt_base, dma_virt_base_end, cpm_chip); |
paul@226 | 227 | |
paul@226 | 228 | dma_chip->enable(); |
paul@226 | 229 | |
paul@226 | 230 | gpio_chip = new Gpio_jz4780_chip(gpio_virt_base + gpio_port * 0x100, |
paul@226 | 231 | gpio_virt_base + (gpio_port + 1) * 0x100, |
paul@226 | 232 | 32, gpio_ports[gpio_port].pull_ups, |
paul@226 | 233 | gpio_ports[gpio_port].pull_downs); |
paul@226 | 234 | |
paul@226 | 235 | // Initialise the clocks for the SPI peripheral before obtaining the |
paul@226 | 236 | // peripheral abstraction. |
paul@226 | 237 | |
paul@226 | 238 | /* NOTE: Set a suitably high but arbitrary frequency to support the SPI bit |
paul@226 | 239 | clock. */ |
paul@226 | 240 | |
paul@226 | 241 | cpm_chip->set_frequency(Clock_ssi, 100000000); |
paul@226 | 242 | |
paul@226 | 243 | spi_chip = new Spi_jz4780_chip(spi_phys_base, spi_virt_base, |
paul@226 | 244 | spi_virt_base_end, cpm_chip); |
paul@226 | 245 | |
paul@226 | 246 | // Obtain a DMA channel and an SPI channel for updating the display. |
paul@226 | 247 | // NOTE: Using preset channels, frequency and pin configuration. |
paul@226 | 248 | |
paul@226 | 249 | dma_channel = dma_chip->get_channel(12); |
paul@226 | 250 | |
paul@226 | 251 | spi_jz4780_channel = spi_chip->get_channel(1, dma_channel, 25000000); |
paul@226 | 252 | |
paul@226 | 253 | spi_channel = new Spi_hybrid(spi_jz4780_channel, gpio_chip, 30, 2); |
paul@226 | 254 | |
paul@226 | 255 | return 0; |
paul@226 | 256 | } |
paul@226 | 257 | |
paul@226 | 258 | // Device initialisation. |
paul@226 | 259 | |
paul@226 | 260 | Lcd_jz4780_spi_device *Lcd_jz4780_spi_device::device = 0; |
paul@226 | 261 | |
paul@226 | 262 | Lcd_device *Lcd_device::get_device() |
paul@226 | 263 | { |
paul@226 | 264 | if (Lcd_jz4780_spi_device::device) |
paul@226 | 265 | return Lcd_jz4780_spi_device::device; |
paul@226 | 266 | |
paul@226 | 267 | if (setup_memory()) |
paul@226 | 268 | return 0; |
paul@226 | 269 | |
paul@226 | 270 | // Initialise the common device. |
paul@226 | 271 | |
paul@226 | 272 | Lcd_jz4780_spi_device::device = new Lcd_jz4780_spi_device(NULL /* display_device */); |
paul@226 | 273 | |
paul@226 | 274 | return Lcd_jz4780_spi_device::device; |
paul@226 | 275 | } |