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/re/protocols.h> 33 #include <l4/sys/factory.h> 34 #include <l4/sys/types.h> 35 #include <l4/vbus/vbus.h> 36 37 #include <ipc/cap_alloc.h> 38 #include <ipc/mem_ipc.h> 39 40 #include <stdint.h> 41 42 #include "activation_client.h" 43 #include "cpm_client.h" 44 45 46 47 // Virtual addresses for the LCD register block. 48 49 static l4_addr_t lcd_virt_base = 0, lcd_virt_base_end = 0; 50 51 // LCD, CPM and display device abstractions. 52 53 static Lcd_jz4740_chip *lcd_chip = 0; 54 55 static Activation *display_device; 56 static CPM *cpm_device; 57 58 59 60 // CPM operations. 61 62 static void set_timing() 63 { 64 uint32_t pclk = lcd_chip->get_pixel_clock(); 65 66 cpm_device->stop_clock(Clock_lcd); 67 68 // Original comment: LCDClock > 2.5*Pixclock 69 // However, the documentation indicates that a TFT panel needs a device clock 70 // 1.5 times that of the pixel clock, and a STN panel needs a device clock 3 71 // times that of the pixel clock. 72 73 cpm_device->set_frequency(Clock_lcd, pclk * 3); 74 cpm_device->set_frequency(Clock_lcd_pixel, pclk); 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 148 if (l4_is_invalid_cap(dma)) 149 return 1; 150 151 vbus = l4re_env_get_cap("vbus"); 152 153 if (l4_is_invalid_cap(vbus)) 154 return 1; 155 156 if (l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma))) 157 return 1; 158 159 l4vbus_device_handle_t device = L4VBUS_NULL; 160 l4vbus_resource_t dma_resource; 161 162 if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN)) 163 return 1; 164 165 if (l4vbus_assign_dma_domain(vbus, dma_resource.start, 166 L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE, 167 dma)) 168 return 1; 169 170 // Obtain the memory requirements. 171 172 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 173 174 fb_size = chip->get_screen_size(); 175 desc_size = chip->get_descriptors_size(); 176 177 // Allocate memory for the framebuffer at 2**6 == 64 byte == 16 word alignment, 178 // also for the descriptors. 179 // NOTE: A 64 word burst mode is available on the JZ4780 that would 180 // NOTE: necessitate 64 word alignment for the framebuffer. 181 182 const l4_size_t alloc_flags = L4RE_MA_CONTINUOUS | L4RE_MA_PINNED; 183 const l4re_rm_flags_t attach_flags = L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW; 184 185 // Map the allocated memory, obtaining virtual addresses. 186 187 fb_vaddr = 0; 188 desc_vaddr = 0; 189 190 if (ipc_new_dataspace(fb_size, alloc_flags, 8, &_fbmem)) 191 return 1; 192 193 if (ipc_new_dataspace(desc_size, alloc_flags, 6, &descmem)) 194 return 1; 195 196 if (ipc_attach_dataspace_align(_fbmem, fb_size, attach_flags, 8, (void **) &fb_vaddr)) 197 return 1; 198 199 if (ipc_attach_dataspace_align(descmem, desc_size, attach_flags, 6, (void **) &desc_vaddr)) 200 return 1; 201 202 // Obtain physical addresses for the framebuffer and descriptors. 203 204 fb_paddr = 0; 205 desc_paddr = 0; 206 207 fb_size_out = fb_size; 208 desc_size_out = desc_size; 209 210 if (l4re_dma_space_map(dma, _fbmem | L4_CAP_FPAGE_RW, 0, &fb_size_out, 0, 211 L4RE_DMA_SPACE_TO_DEVICE, &fb_paddr)) 212 return 1; 213 214 if (l4re_dma_space_map(dma, descmem | L4_CAP_FPAGE_RW, 0, &desc_size_out, 0, 215 L4RE_DMA_SPACE_TO_DEVICE, &desc_paddr)) 216 return 1; 217 218 // Test the mapped region sizes. 219 220 if ((fb_size_out != fb_size) || (desc_size_out != desc_size)) 221 return 1; 222 223 return 0; 224 } 225 226 // Populate a view information structure. 227 228 void Lcd_jz4740_device::get_view_info(l4re_video_view_info_t *view_info) 229 { 230 // Populate the L4Re framebuffer description with details from the 231 // somewhat similar panel description. 232 233 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 234 struct Jz4740_lcd_panel *panel = chip->get_panel(); 235 236 view_info->width = panel->width; 237 view_info->height = panel->height; 238 view_info->pixel_info.bytes_per_pixel = (panel->bpp + 7) / 8; 239 view_info->bytes_per_line = chip->get_line_size(); 240 241 switch (panel->bpp) 242 { 243 // NOTE: 24bpp here is actually RGBA or RGB padded. 244 245 case 24: 246 view_info->pixel_info.bytes_per_pixel = 4; 247 view_info->pixel_info.r.shift = 16; view_info->pixel_info.r.size = 8; 248 view_info->pixel_info.g.shift = 8; view_info->pixel_info.g.size = 8; 249 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 8; 250 break; 251 252 case 32: 253 view_info->pixel_info.r.shift = 16; view_info->pixel_info.r.size = 8; 254 view_info->pixel_info.g.shift = 8; view_info->pixel_info.g.size = 8; 255 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 8; 256 break; 257 258 case 8: 259 view_info->pixel_info.r.shift = 5; view_info->pixel_info.r.size = 3; 260 view_info->pixel_info.g.shift = 2; view_info->pixel_info.g.size = 3; 261 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 2; 262 break; 263 264 case 4: 265 view_info->pixel_info.r.shift = 3; view_info->pixel_info.r.size = 1; 266 view_info->pixel_info.g.shift = 1; view_info->pixel_info.g.size = 2; 267 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 1; 268 break; 269 270 case 16: 271 default: 272 view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5; 273 view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6; 274 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5; 275 break; 276 } 277 278 view_info->pixel_info.a.shift = 0; 279 view_info->pixel_info.a.size = 0; 280 } 281 282 // Access to peripheral memory. 283 284 static int setup_memory() 285 { 286 bool is_jz4780 = false; 287 288 if (get_memory("jz4740-lcd", &lcd_virt_base, &lcd_virt_base_end)) 289 { 290 if (get_memory("jz4780-lcd", &lcd_virt_base, &lcd_virt_base_end)) 291 return 1; 292 else 293 is_jz4780 = true; 294 } 295 296 // Obtain access to the CPM and display devices. 297 298 l4_cap_idx_t cpm = l4re_env_get_cap("cpm"); 299 300 if (!l4_is_valid_cap(cpm)) 301 return 1; 302 303 l4_cap_idx_t display = l4re_env_get_cap("display"); 304 305 if (!l4_is_valid_cap(display)) 306 return 1; 307 308 static client_Activation display_obj(display); 309 display_device = &display_obj; 310 311 static client_CPM cpm_obj(cpm); 312 cpm_device = &cpm_obj; 313 314 // Load the panel data from the configured library. 315 316 struct Jz4740_lcd_panel *panel = (struct Jz4740_lcd_panel *) load_panel(); 317 318 // Initialise the LCD abstraction. 319 320 if (is_jz4780) 321 lcd_chip = new Lcd_jz4780_chip(lcd_virt_base, panel); 322 else 323 lcd_chip = new Lcd_jz4740_chip(lcd_virt_base, panel); 324 325 return 0; 326 } 327 328 // Device initialisation. 329 330 Lcd_jz4740_device *Lcd_jz4740_device::device = 0; 331 332 Lcd_device *Lcd_device::get_device() 333 { 334 if (Lcd_jz4740_device::device) 335 return Lcd_jz4740_device::device; 336 337 if (setup_memory()) 338 return 0; 339 340 // Initialise the common device. 341 342 Lcd_jz4740_device::device = new Lcd_jz4740_device(lcd_chip, display_device); 343 344 return Lcd_jz4740_device::device; 345 }