1 /* 2 * Common LCD device support for the JZ4740 and related SoCs. 3 * 4 * Copyright (C) 2018, 2020, 2021, 2023 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/lcd-jz4740.h> 23 #include <l4/devices/lcd-jz4780.h> 24 #include <l4/devices/panel-loader.h> 25 #include <l4/devices/memory.h> 26 #include "lcd-jz4740-device.h" 27 28 #include <l4/re/c/dataspace.h> 29 #include <l4/re/c/dma_space.h> 30 #include <l4/re/c/mem_alloc.h> 31 #include <l4/re/env.h> 32 #include <l4/sys/factory.h> 33 #include <l4/sys/types.h> 34 #include <l4/vbus/vbus.h> 35 36 #include <ipc/cap_alloc.h> 37 #include <ipc/mem_ipc.h> 38 39 #include <stdint.h> 40 41 #include "activation_client.h" 42 #include "cpm_client.h" 43 44 45 46 // Virtual addresses for the LCD register block. 47 48 static l4_addr_t lcd_virt_base = 0, lcd_virt_base_end = 0; 49 50 // LCD, CPM and display device abstractions. 51 52 static Lcd_jz4740_chip *lcd_chip = 0; 53 54 static Activation *display_device; 55 static CPM *cpm_device; 56 57 58 59 // CPM operations. 60 61 static void set_timing() 62 { 63 uint32_t pclk = lcd_chip->get_pixel_clock(); 64 65 cpm_device->stop_clock(Clock_lcd); 66 67 // Original comment: LCDClock > 2.5*Pixclock 68 // However, the documentation indicates that a TFT panel needs a device clock 69 // 1.5 times that of the pixel clock, and a STN panel needs a device clock 3 70 // times that of the pixel clock. 71 72 cpm_device->set_frequency(Clock_frequency_lcd, pclk * 3); 73 cpm_device->set_frequency(Clock_frequency_lcd_pixel, pclk); 74 cpm_device->update_output_frequency(); 75 cpm_device->start_clock(Clock_lcd); 76 77 l4_sleep(1); // 1ms == 1000us 78 } 79 80 81 82 // Disable the display. 83 84 void 85 Lcd_jz4740_device::disable() 86 { 87 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 88 89 disable_display(); 90 chip->disable(); 91 } 92 93 // Configure the peripheral and enable the display. 94 95 void 96 Lcd_jz4740_device::enable() 97 { 98 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 99 100 // Initialise the clocks for the LCD controller. Without this, the controller 101 // cannot be operated. 102 103 set_timing(); 104 105 // Configure the controller. 106 107 chip->disable(); 108 chip->config((struct Jz4740_lcd_descriptor *) desc_vaddr, 109 (struct Jz4740_lcd_descriptor *) desc_paddr, 110 fb_paddr); 111 112 // Activate the display channel. 113 114 enable_display(); 115 116 // Activate the controller output. 117 118 chip->enable(); 119 } 120 121 // Set up memory addresses for the peripheral, initialising the framebuffer and 122 // descriptor members. 123 124 int 125 Lcd_jz4740_device::_setup_memory() 126 { 127 // Framebuffer and descriptor sizes. 128 129 unsigned long fb_size, desc_size; 130 131 // Size of physically contiguous regions to be allocated. 132 133 l4_size_t fb_size_out, desc_size_out; 134 135 // Memory allocation capabilities. 136 137 l4_cap_idx_t descmem, dma, vbus; 138 139 // Test for existing setup. 140 141 if (fb_vaddr) 142 return 0; 143 144 // Create the DMA space. 145 146 dma = ipc_cap_alloc(); 147 if (l4_is_invalid_cap(dma)) return 1; 148 149 vbus = l4re_env_get_cap("vbus"); 150 if (l4_is_invalid_cap(vbus)) return 1; 151 152 if (l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma))) 153 return 1; 154 155 l4vbus_device_handle_t device = L4VBUS_NULL; 156 l4vbus_resource_t dma_resource; 157 158 if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN)) 159 return 1; 160 161 if (l4vbus_assign_dma_domain(vbus, dma_resource.start, 162 L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE, 163 dma)) 164 return 1; 165 166 // Obtain the memory requirements. 167 168 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 169 170 fb_size = chip->get_screen_size(); 171 desc_size = chip->get_descriptors_size(); 172 173 // Allocate memory for the framebuffer at 2**6 == 64 byte == 16 word alignment, 174 // also for the descriptors. 175 // NOTE: A 64 word burst mode is available on the JZ4780 that would 176 // NOTE: necessitate 64 word alignment for the framebuffer. 177 178 const l4_size_t alloc_flags = L4RE_MA_CONTINUOUS | L4RE_MA_PINNED; 179 180 // Map the allocated memory, obtaining virtual addresses. 181 182 fb_vaddr = 0; 183 desc_vaddr = 0; 184 185 if (ipc_allocate_align(fb_size, alloc_flags, 8, &fb_vaddr, &_fbmem)) 186 return 1; 187 188 if (ipc_allocate_align(desc_size, alloc_flags, 6, &desc_vaddr, &descmem)) 189 return 1; 190 191 // Obtain physical addresses for the framebuffer and descriptors. 192 193 fb_paddr = 0; 194 desc_paddr = 0; 195 196 fb_size_out = fb_size; 197 desc_size_out = desc_size; 198 199 if (l4re_dma_space_map(dma, _fbmem | L4_CAP_FPAGE_RW, 0, &fb_size_out, 0, 200 L4RE_DMA_SPACE_TO_DEVICE, &fb_paddr)) 201 return 1; 202 203 if (l4re_dma_space_map(dma, descmem | L4_CAP_FPAGE_RW, 0, &desc_size_out, 0, 204 L4RE_DMA_SPACE_TO_DEVICE, &desc_paddr)) 205 return 1; 206 207 // Test the mapped region sizes. 208 209 if ((fb_size_out != fb_size) || (desc_size_out != desc_size)) 210 return 1; 211 212 return 0; 213 } 214 215 // Populate a view information structure. 216 217 void Lcd_jz4740_device::get_view_info(l4re_video_view_info_t *view_info) 218 { 219 // Populate the L4Re framebuffer description with details from the 220 // somewhat similar panel description. 221 222 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 223 struct Jz4740_lcd_panel *panel = chip->get_panel(); 224 225 view_info->width = panel->width; 226 view_info->height = panel->height; 227 view_info->pixel_info.bytes_per_pixel = (panel->bpp + 7) / 8; 228 view_info->bytes_per_line = chip->get_line_size(); 229 230 switch (panel->bpp) 231 { 232 // NOTE: 24bpp here is actually RGBA or RGB padded. 233 234 case 24: 235 view_info->pixel_info.bytes_per_pixel = 4; 236 view_info->pixel_info.r.shift = 16; view_info->pixel_info.r.size = 8; 237 view_info->pixel_info.g.shift = 8; view_info->pixel_info.g.size = 8; 238 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 8; 239 break; 240 241 case 32: 242 view_info->pixel_info.r.shift = 16; view_info->pixel_info.r.size = 8; 243 view_info->pixel_info.g.shift = 8; view_info->pixel_info.g.size = 8; 244 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 8; 245 break; 246 247 case 8: 248 view_info->pixel_info.r.shift = 5; view_info->pixel_info.r.size = 3; 249 view_info->pixel_info.g.shift = 2; view_info->pixel_info.g.size = 3; 250 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 2; 251 break; 252 253 case 4: 254 view_info->pixel_info.r.shift = 3; view_info->pixel_info.r.size = 1; 255 view_info->pixel_info.g.shift = 1; view_info->pixel_info.g.size = 2; 256 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 1; 257 break; 258 259 case 16: 260 default: 261 view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5; 262 view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6; 263 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5; 264 break; 265 } 266 267 view_info->pixel_info.a.shift = 0; 268 view_info->pixel_info.a.size = 0; 269 } 270 271 // Access to peripheral memory. 272 273 static int setup_memory() 274 { 275 bool is_jz4780 = false; 276 277 if (get_memory("jz4740-lcd", &lcd_virt_base, &lcd_virt_base_end)) 278 { 279 if (get_memory("jz4780-lcd", &lcd_virt_base, &lcd_virt_base_end)) 280 return 1; 281 else 282 is_jz4780 = true; 283 } 284 285 // Obtain access to the CPM and display devices. 286 287 l4_cap_idx_t cpm = l4re_env_get_cap("cpm"); 288 289 if (!l4_is_valid_cap(cpm)) 290 return 1; 291 292 l4_cap_idx_t display = l4re_env_get_cap("display"); 293 294 if (!l4_is_valid_cap(display)) 295 return 1; 296 297 static client_Activation display_obj(display); 298 display_device = &display_obj; 299 300 static client_CPM cpm_obj(cpm); 301 cpm_device = &cpm_obj; 302 303 // Load the panel data from the configured library. 304 305 struct Jz4740_lcd_panel *panel = (struct Jz4740_lcd_panel *) load_panel(); 306 307 // Initialise the LCD abstraction. 308 309 if (is_jz4780) 310 lcd_chip = new Lcd_jz4780_chip(lcd_virt_base, panel); 311 else 312 lcd_chip = new Lcd_jz4740_chip(lcd_virt_base, panel); 313 314 return 0; 315 } 316 317 // Device initialisation. 318 319 Lcd_jz4740_device *Lcd_jz4740_device::device = 0; 320 321 Lcd_device *Lcd_device::get_device() 322 { 323 if (Lcd_jz4740_device::device) 324 return Lcd_jz4740_device::device; 325 326 if (setup_memory()) 327 return 0; 328 329 // Initialise the common device. 330 331 Lcd_jz4740_device::device = new Lcd_jz4740_device(lcd_chip, display_device); 332 333 return Lcd_jz4740_device::device; 334 }