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