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