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_channel *dma_channel; 66 static Spi_hybrid *spi_channel; 67 static Spi_jz4780_channel *spi_jz4780_channel; 68 69 70 71 // Disable the display. 72 73 void 74 Lcd_jz4780_spi_device::disable() 75 { 76 // NOTE: Need to support cancelling the transfer or disabling the peripheral. 77 78 //disable_display(); 79 } 80 81 // Configure the peripheral and enable the display. 82 83 void 84 Lcd_jz4780_spi_device::enable() 85 { 86 // Set up the GPIO pins for the peripheral. 87 // NOTE: Hard-coded pin usage! 88 89 gpio_chip->setup(20, Hw::Gpio_chip::Output, 1); // backlight 90 gpio_chip->config_pad(21, Hw::Gpio_chip::Function_alt, 2); // CE1 91 gpio_chip->config_pad(28, Hw::Gpio_chip::Function_alt, 2); // SCLK 92 gpio_chip->config_pad(29, Hw::Gpio_chip::Function_alt, 2); // MOSI 93 gpio_chip->config_pad(30, Hw::Gpio_chip::Function_alt, 2); // GPC 94 95 // Initialise the screen. 96 // NOTE: Specific to the ST7789. 97 98 spi_channel->send_units(2, (const uint8_t []) {0x00, 0x01}, 2, 8); 99 usleep(120000); 100 spi_channel->send_units(2, (const uint8_t []) {0x00, 0x11}, 2, 8); 101 usleep(5000); 102 spi_channel->send_units(4, (const uint8_t []) {0x00, 0x36, 0x01, 0x00}, 2, 8); 103 usleep(5000); 104 spi_channel->send_units(10, (const uint8_t []) {0x00, 0x3a, 0x01, 0x05, 0x00, 0x13, 0x00, 0x29, 0x00, 0x21}, 2, 8); 105 usleep(5000); 106 spi_channel->send_units(22, (const uint8_t []) {0x00, 0x2a, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef, 107 0x00, 0x2b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0xef, 108 0x00, 0x2c}, 2, 8); 109 usleep(5000); 110 111 for (l4_addr_t addr = get_framebuffer(); addr < get_framebuffer() + get_framebuffer_size(); addr += sizeof(uint32_t)) 112 *((uint32_t *) addr) = 0; 113 114 l4_cache_clean_data(get_framebuffer(), get_framebuffer() + get_framebuffer_size()); 115 116 // Initialise the display transfer. 117 // NOTE: Asserting the data signal. 118 119 spi_channel->acquire_control(true); 120 121 spi_jz4780_channel->transfer(_fb_region.vaddr, _fb_region.paddr, 122 get_framebuffer_size(), 2, 16, 123 _desc_region.vaddr, _desc_region.paddr); 124 125 //enable_display(); 126 } 127 128 // Set up memory addresses for the peripheral, initialising the framebuffer and 129 // descriptor members. 130 131 int 132 Lcd_jz4780_spi_device::_setup_memory() 133 { 134 // Test for existing setup. 135 136 if (_fb_region.vaddr) 137 return 0; 138 139 // Framebuffer and descriptor sizes. 140 141 unsigned long fb_size = get_framebuffer_size(), desc_size = 32; 142 143 // Allocate memory for the framebuffer and descriptors with 2**5 = 32 byte == 144 // 8 word alignment, sufficient for the descriptors. 145 146 long err = get_dma_region(fb_size, 5, &_fb_region); 147 148 if (err) 149 return 1; 150 151 err = get_dma_region(desc_size, 5, &_desc_region); 152 153 if (err) 154 return 1; 155 156 return 0; 157 } 158 159 l4_size_t Lcd_jz4780_spi_device::get_framebuffer_size() 160 { 161 // NOTE: Hard-coded size. 162 163 return 240 * 240 * 2; 164 } 165 166 // Populate a view information structure. 167 168 void Lcd_jz4780_spi_device::get_view_info(l4re_video_view_info_t *view_info) 169 { 170 // NOTE: Hard-coded properties. 171 172 view_info->width = 240; 173 view_info->height = 240; 174 view_info->pixel_info.bytes_per_pixel = 2; 175 view_info->bytes_per_line = 480; 176 177 view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5; 178 view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6; 179 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5; 180 181 view_info->pixel_info.a.shift = 0; 182 view_info->pixel_info.a.size = 0; 183 } 184 185 // Access to peripheral memory. 186 187 static int setup_memory() 188 { 189 if (get_memory("jz4780-cpm", &cpm_virt_base, &cpm_virt_base_end)) 190 return 1; 191 192 if (get_memory("jz4780-dma", &dma_virt_base, &dma_virt_base_end)) 193 return 1; 194 195 if (get_irq("jz4780-dma", &dma_irq_start, &dma_irq_end) < 0) 196 return 1; 197 198 if (get_memory("jz4780-gpio", &gpio_virt_base, &gpio_virt_base_end)) 199 return 1; 200 201 if (get_memory_complete("jz4780-ssi", &spi_virt_base, &spi_virt_base_end, 202 &spi_phys_base, &spi_phys_base_end)) 203 return 1; 204 205 // Initialise the abstractions. 206 // NOTE: Using a preset GPIO configuration. 207 208 int gpio_port = 1; 209 210 cpm_chip = new Cpm_jz4780_chip(cpm_virt_base); 211 212 dma_chip = new Dma_jz4780_chip(dma_virt_base, dma_virt_base_end, cpm_chip); 213 214 dma_chip->enable(); 215 216 gpio_chip = new Gpio_jz4780_chip(gpio_virt_base, gpio_port); 217 218 // Initialise the clocks for the SPI peripheral before obtaining the 219 // peripheral abstraction. 220 221 /* NOTE: Set a suitably high but arbitrary frequency to support the SPI bit 222 clock. */ 223 224 cpm_chip->set_frequency(Clock_ssi, 100000000); 225 226 spi_chip = new Spi_jz4780_chip(spi_phys_base, spi_virt_base, 227 spi_virt_base_end, cpm_chip); 228 229 // Obtain a DMA channel and an SPI channel for updating the display. 230 // NOTE: Using preset channels, frequency and pin configuration. 231 232 dma_channel = dma_chip->get_channel(12); 233 234 spi_jz4780_channel = spi_chip->get_channel(1, dma_channel, 25000000); 235 236 spi_channel = new Spi_hybrid(spi_jz4780_channel, gpio_chip, 30, 2); 237 238 return 0; 239 } 240 241 // Device initialisation. 242 243 Lcd_jz4780_spi_device *Lcd_jz4780_spi_device::device = 0; 244 245 Lcd_device *Lcd_device::get_device() 246 { 247 if (Lcd_jz4780_spi_device::device) 248 return Lcd_jz4780_spi_device::device; 249 250 if (setup_memory()) 251 return 0; 252 253 // Initialise the common device. 254 255 Lcd_jz4780_spi_device::device = new Lcd_jz4780_spi_device(NULL /* display_device */); 256 257 return Lcd_jz4780_spi_device::device; 258 }