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