1 /* 2 * LCD peripheral support for the JZ4740 and related SoCs. 3 * 4 * Copyright (C) Xiangfu Liu <xiangfu@sharism.cc> 5 * Copyright (C) 2015, 2016, 2017, 2018, 6 * 2020, 2024 Paul Boddie <paul@boddie.org.uk> 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation; either version 2 of 11 * the License, or (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA 22 */ 23 24 #include <l4/devices/hw_mmio_register_block.h> 25 #include <l4/sys/cache.h> 26 #include <l4/sys/irq.h> 27 #include <l4/sys/rcv_endpoint.h> 28 #include <l4/sys/types.h> 29 #include <l4/util/util.h> 30 31 #include <pthread.h> 32 #include <pthread-l4.h> 33 34 #include "lcd-jz4740.h" 35 #include "lcd-jz4740-config.h" 36 #include "lcd-jz4740-regs.h" 37 38 #include <stdint.h> 39 40 41 42 // Utility functions. 43 44 // Round values up according to the resolution. 45 46 static uint32_t align(uint32_t value, uint32_t resolution) 47 { 48 return (value + (resolution - 1)) & ~(resolution - 1); 49 } 50 51 // Value pair encoding. 52 53 static uint32_t encode_pair(uint32_t start, uint32_t end) 54 { 55 return (start << Value_first) | (end << Value_second); 56 } 57 58 // RGB conversions. 59 60 static uint16_t rgb8_to_rgb16(uint8_t rgb) 61 { 62 return ((((rgb & 0xe0) >> 5) * 4) << 11) | ((((rgb & 0x1c) >> 2) * 9) << 6) | ((rgb & 0x03) * 10); 63 } 64 65 static uint16_t rgb4_to_rgb16(uint8_t rgb) 66 { 67 return ((((rgb & 8) >> 3) * 0x1f) << 11) | ((((rgb & 6) >> 1) * 0x15) << 5) | ((rgb & 1) * 0x1f); 68 } 69 70 71 72 73 // If implemented as a Hw::Device, various properties would be 74 // initialised in the constructor and obtained from the device tree 75 // definitions. 76 77 Lcd_jz4740_chip::Lcd_jz4740_chip(l4_addr_t addr, Jz4740_lcd_panel *panel) 78 : _panel(panel) 79 { 80 _regs = new Hw::Mmio_register_block<32>(addr); 81 _burst_size = 16; // 16-word burst size 82 } 83 84 struct Jz4740_lcd_panel * 85 Lcd_jz4740_chip::get_panel() 86 { 87 return _panel; 88 } 89 90 void 91 Lcd_jz4740_chip::disable() 92 { 93 // Set the disable bit for normal shutdown. 94 95 _regs[Lcd_control] = _regs[Lcd_control] | (1U << Control_disable); 96 } 97 98 void 99 Lcd_jz4740_chip::disable_quick() 100 { 101 // Clear the enable bit for quick shutdown. 102 103 _regs[Lcd_control] = _regs[Lcd_control] & ~(1U << Control_enable); 104 } 105 106 void 107 Lcd_jz4740_chip::enable() 108 { 109 // Clear the disable bit and set the enable bit. 110 111 _regs[Lcd_status] = 0; 112 _regs[Lcd_control] = (_regs[Lcd_control] & ~(1U << Control_disable)) | (1U << Control_enable); 113 } 114 115 bool 116 Lcd_jz4740_chip::enabled() 117 { 118 return !(_regs[Lcd_status] & (1U << Status_disabled)); 119 } 120 121 // Calculate and return the pixel clock frequency. 122 123 int 124 Lcd_jz4740_chip::get_pixel_clock() 125 { 126 int pclk, multiplier; 127 128 // Serial mode: 3 pixel clock cycles per pixel (one per channel). 129 // Parallel mode: 1 pixel clock cycle per pixel. 130 131 multiplier = have_serial_tft() ? 3 : 1; 132 133 // Derive pixel clock rate from frame rate. 134 // This multiplies the number of pixel periods in a line by the number of 135 // lines in a frame, thus obtaining the number of such periods in a frame. 136 // Multiplying this result with the frame rate yields the pixel frequency. 137 138 pclk = _panel->frame_rate * 139 (_panel->width * multiplier + 140 _panel->hsync + _panel->line_start + _panel->line_end) * 141 (_panel->height + 142 _panel->vsync + _panel->frame_start + _panel->frame_end); 143 144 // STN panel adjustments. 145 146 if (have_stn_panel()) 147 { 148 // Colour STN panels apparently need to be driven at three times the rate. 149 150 if (have_colour_stn()) pclk = (pclk * 3); 151 152 // Reduce the rate according to the width of the STN connection. 153 // Since the pins setting employs log2(pins), a shift by this value is 154 // equivalent to a division by the number of pins. 155 156 pclk = pclk >> ((_panel->config & Config_stn_pins_mask) >> Jz4740_lcd_config_stn_pins); 157 158 // Divide the rate by the number of panels. 159 160 pclk /= get_panels(); 161 } 162 163 return pclk; 164 } 165 166 167 168 // Return the panel mode. 169 170 uint32_t 171 Lcd_jz4740_chip::_mode() 172 { 173 return _panel->config & Config_mode_mask; 174 } 175 176 // Return the number of panels available. 177 178 int 179 Lcd_jz4740_chip::get_panels() 180 { 181 uint32_t mode = _mode(); 182 183 return (mode == Jz4740_lcd_mode_stn_dual_colour) || 184 (mode == Jz4740_lcd_mode_stn_dual_mono) ? 2 : 1; 185 } 186 187 // Return whether the panel is STN. 188 189 int 190 Lcd_jz4740_chip::have_stn_panel() 191 { 192 uint32_t mode = _mode(); 193 194 return ((mode == Jz4740_lcd_mode_stn_single_colour) || 195 (mode == Jz4740_lcd_mode_stn_dual_colour) || 196 (mode == Jz4740_lcd_mode_stn_single_mono) || 197 (mode == Jz4740_lcd_mode_stn_dual_mono)); 198 } 199 200 // Return whether the panel is colour STN. 201 202 int 203 Lcd_jz4740_chip::have_colour_stn() 204 { 205 uint32_t mode = _mode(); 206 207 return ((mode == Jz4740_lcd_mode_stn_single_colour) || 208 (mode == Jz4740_lcd_mode_stn_dual_colour)); 209 } 210 211 // Return whether the panel is colour STN. 212 213 int 214 Lcd_jz4740_chip::have_serial_tft() 215 { 216 return _mode() == Jz4740_lcd_mode_tft_serial; 217 } 218 219 220 221 // Return the pixel memory size in bits. 222 223 l4_size_t 224 Lcd_jz4740_chip::get_pixel_size() 225 { 226 if (_panel->bpp > 16) 227 return 32; 228 else if (_panel->bpp > 8) 229 return 16; 230 else 231 return _panel->bpp; 232 } 233 234 // Return the line memory size. 235 236 l4_size_t 237 Lcd_jz4740_chip::get_line_size() 238 { 239 // Lines must be aligned to a word boundary. 240 241 return align((_panel->width * get_pixel_size()) / 8, sizeof(uint32_t)); 242 } 243 244 // Return the screen memory size. 245 246 l4_size_t 247 Lcd_jz4740_chip::get_screen_size() 248 { 249 return get_line_size() * _panel->height; 250 } 251 252 // Return the aligned size for the DMA transfer. 253 254 l4_size_t 255 Lcd_jz4740_chip::get_aligned_size() 256 { 257 return align(get_screen_size(), _burst_size * sizeof(uint32_t)); 258 } 259 260 // Return the size of the palette. 261 262 l4_size_t 263 Lcd_jz4740_chip::get_palette_size() 264 { 265 // No palette for modes with more than eight bits per pixel. 266 267 if (_panel->bpp > 8) return 0; 268 269 // Get the size of a collection of two-byte entries, one per colour. 270 271 return (1U << (_panel->bpp)) * sizeof(uint16_t); 272 } 273 274 // Return the aligned size of the palette for the DMA transfer. 275 276 l4_size_t 277 Lcd_jz4740_chip::get_aligned_palette_size() 278 { 279 return align(get_palette_size(), _burst_size * sizeof(uint32_t)); 280 } 281 282 // Return the total memory requirements of the framebuffers and palette. 283 284 l4_size_t 285 Lcd_jz4740_chip::get_total_size() 286 { 287 return get_aligned_size() * get_panels() + get_aligned_palette_size(); 288 } 289 290 // Return the total memory requirements of any DMA descriptors. 291 292 l4_size_t 293 Lcd_jz4740_chip::get_descriptors_size() 294 { 295 return 3 * sizeof(struct Jz4740_lcd_descriptor); 296 } 297 298 299 300 // Functions returning addresses of each data region. 301 // The base parameter permits the retrieval of virtual or physical addresses. 302 303 l4_addr_t 304 Lcd_jz4740_chip::get_palette(l4_addr_t base) 305 { 306 // Use memory at the end of the allocated region for the palette. 307 308 return base + (get_panels() * get_aligned_size()) - get_aligned_palette_size(); 309 } 310 311 l4_addr_t 312 Lcd_jz4740_chip::get_framebuffer(int panel, l4_addr_t base) 313 { 314 // Framebuffers for panels are allocated at the start of the region. 315 316 return base + (panel * get_aligned_size()); 317 } 318 319 320 321 // Palette initialisation. 322 323 void 324 Lcd_jz4740_chip::init_palette(l4_addr_t palette) 325 { 326 uint8_t colours = 1U << (_panel->bpp); 327 uint16_t *entry = (uint16_t *) palette; 328 uint16_t *end = entry + colours; 329 uint8_t value = 0; 330 331 while (entry < end) 332 { 333 switch (_panel->bpp) 334 { 335 case 4: 336 *entry = rgb4_to_rgb16(value); 337 break; 338 339 case 8: 340 *entry = rgb8_to_rgb16(value); 341 break; 342 343 default: 344 break; 345 } 346 347 value++; 348 entry++; 349 } 350 } 351 352 353 354 // Return colour depth control value. 355 // NOTE: Not supporting JZ4780 options. 356 357 uint32_t 358 Lcd_jz4740_chip::_control_bpp() 359 { 360 switch (_panel->bpp) 361 { 362 case 1: return Control_bpp_1bpp; 363 case 2: return Control_bpp_2bpp; 364 case 3 ... 4: return Control_bpp_4bpp; 365 case 5 ... 8: return Control_bpp_8bpp; 366 case 9 ... 15: return Control_bpp_15bpp | (Rgb_mode_555 << Control_rgb_mode); 367 case 17 ... 18: return Control_bpp_18bpp; 368 case 19 ... 32: return Control_bpp_24bpp; 369 case 16: 370 default: return Control_bpp_16bpp; 371 } 372 } 373 374 // Return a panel-related control value. 375 376 uint32_t 377 Lcd_jz4740_chip::_control_panel() 378 { 379 if (have_stn_panel()) 380 return _control_stn_frc() << Control_frc_algorithm; 381 else 382 return 0; 383 } 384 385 // Return a STN-related control value. 386 387 uint32_t 388 Lcd_jz4740_chip::_control_stn_frc() 389 { 390 if (_panel->bpp <= 2) 391 return Frc_greyscales_2; 392 if (_panel->bpp <= 4) 393 return Frc_greyscales_4; 394 return Frc_greyscales_16; 395 } 396 397 // Return a transfer-related control value. 398 399 uint32_t 400 Lcd_jz4740_chip::_control_transfer() 401 { 402 uint32_t length; 403 404 switch (_burst_size) 405 { 406 case 4: length = Burst_length_4; break; 407 case 8: length = Burst_length_8; break; 408 case 32: length = Burst_length_32; break; 409 case 64: length = Burst_length_64; break; 410 case 16: 411 default: length = Burst_length_16; break; 412 } 413 414 // NOTE: Underrun supposedly not needed. 415 416 return (length << Control_burst_length) | (1U << Control_out_underrun); 417 } 418 419 // Return an interrupt-related control value. 420 421 uint32_t 422 Lcd_jz4740_chip::_control_irq() 423 { 424 return ((_irq_conditions & Lcd_irq_frame_start) ? (1U << Control_frame_start_irq_enable) : 0) | 425 ((_irq_conditions & Lcd_irq_frame_end) ? (1U << Control_frame_end_irq_enable) : 0); 426 } 427 428 // Return an interrupt-related command value. 429 430 uint32_t 431 Lcd_jz4740_chip::_command_irq() 432 { 433 return ((_irq_conditions & Lcd_irq_frame_start) ? (1U << Command_frame_start_irq) : 0) | 434 ((_irq_conditions & Lcd_irq_frame_end) ? (1U << Command_frame_end_irq) : 0); 435 } 436 437 // Return an interrupt-related status value. 438 439 uint32_t 440 Lcd_jz4740_chip::_status_irq() 441 { 442 return ((_irq_conditions & Lcd_irq_frame_start) ? (1U << Status_frame_start_irq) : 0) | 443 ((_irq_conditions & Lcd_irq_frame_end) ? (1U << Status_frame_end_irq) : 0); 444 } 445 446 // STN panel-specific initialisation. 447 448 void 449 Lcd_jz4740_chip::_init_stn() 450 { 451 // Divide the height by the number of panels. 452 453 uint32_t height = _panel->height / get_panels(); 454 455 // Since the value is log2(pins), 1 << value yields the number of pins. 456 457 int pins = 1 << ((_panel->config & Config_stn_pins_mask) >> Jz4740_lcd_config_stn_pins); 458 459 // Round parameters up to a multiple of the number of pins. 460 461 uint32_t hsync = align(_panel->hsync, pins); 462 uint32_t line_start = align(_panel->line_start, pins); 463 uint32_t line_end = align(_panel->line_end, pins); 464 465 // Define the start and end positions of visible data on a line and in a frame. 466 // Visible frame data is anchored at line zero, with the start region 467 // preceding this line (and thus appearing at the end of the preceding frame). 468 469 uint32_t line_start_pos = line_start; 470 uint32_t line_end_pos = line_start_pos + _panel->width; 471 uint32_t frame_start_pos = 0; 472 uint32_t frame_end_pos = frame_start_pos + height; 473 474 // Define sync pulse locations, with hsync occurring after the visible data. 475 476 _regs[Lcd_hsync] = encode_pair(line_end_pos, line_end_pos + hsync); 477 _regs[Lcd_vsync] = encode_pair(0, _panel->vsync); 478 479 // Set the display area and limits. 480 481 _regs[Virtual_area] = encode_pair(line_end_pos + hsync + line_end, 482 frame_end_pos + _panel->vsync + _panel->frame_end + _panel->frame_start); 483 484 _regs[Display_hlimits] = encode_pair(line_start_pos, line_end_pos); 485 _regs[Display_vlimits] = encode_pair(frame_start_pos, frame_end_pos); 486 487 // Set the AC bias signal. 488 489 _regs[Lcd_ps] = encode_pair(0, _panel->frame_start + height + _panel->vsync + _panel->frame_end); 490 } 491 492 // TFT panel-specific initialisation. 493 494 void 495 Lcd_jz4740_chip::_init_tft() 496 { 497 // Define the start and end positions of visible data on a line and in a frame. 498 499 uint32_t line_start_pos = _panel->line_start + _panel->hsync; 500 uint32_t line_end_pos = line_start_pos + _panel->width; 501 uint32_t frame_start_pos = _panel->frame_start + _panel->vsync; 502 uint32_t frame_end_pos = frame_start_pos + _panel->height; 503 504 // Define sync pulse locations, with pulses appearing before visible data. 505 506 _regs[Lcd_hsync] = encode_pair(0, _panel->hsync); 507 _regs[Lcd_vsync] = encode_pair(0, _panel->vsync); 508 509 // Set the display area and limits. 510 511 _regs[Virtual_area] = encode_pair(line_end_pos + _panel->line_end, 512 frame_end_pos + _panel->frame_end); 513 514 _regs[Display_hlimits] = encode_pair(line_start_pos, line_end_pos); 515 _regs[Display_vlimits] = encode_pair(frame_start_pos, frame_end_pos); 516 } 517 518 // Initialise the panel. 519 // NOTE: Only generic STN and TFT panels are supported. 520 521 void 522 Lcd_jz4740_chip::_init_panel() 523 { 524 if (have_stn_panel()) 525 _init_stn(); 526 else 527 switch (_mode()) 528 { 529 case Jz4740_lcd_mode_tft_generic: 530 case Jz4740_lcd_mode_tft_casio: 531 case Jz4740_lcd_mode_tft_serial: _init_tft(); 532 533 default: break; 534 } 535 } 536 537 // Initialise a DMA descriptor. 538 539 void 540 Lcd_jz4740_chip::_set_descriptor(struct Jz4740_lcd_descriptor &desc, 541 l4_addr_t source, l4_size_t size, 542 struct Jz4740_lcd_descriptor *next, 543 uint32_t flags) 544 { 545 // In the command, indicate the number of words from the source for transfer. 546 547 desc.next = next; 548 desc.source = source; 549 desc.identifier = source; 550 desc.command = ((size / sizeof(uint32_t)) & Command_buffer_length_mask) | 551 flags; 552 } 553 554 // Initialise the LCD controller with the memory, panel and framebuffer details. 555 // Any palette must be initialised separately using get_palette and init_palette. 556 557 void 558 Lcd_jz4740_chip::config(struct Jz4740_lcd_descriptor *desc_vaddr, 559 struct Jz4740_lcd_descriptor *desc_paddr, 560 l4_addr_t fb_paddr) 561 { 562 int have_palette = (_panel->bpp <= 8); 563 564 // Provide the first framebuffer descriptor in single and dual modes. 565 // Flip back and forth between any palette and the framebuffer. 566 567 _set_descriptor(desc_vaddr[0], get_framebuffer(0, fb_paddr), 568 get_aligned_size(), 569 have_palette ? desc_paddr + 2 : desc_paddr, 570 _command_irq()); 571 572 // Provide the second framebuffer descriptor only in dual-panel mode. 573 // Only employ this descriptor in the second DMA channel. 574 575 if (get_panels() == 2) 576 _set_descriptor(desc_vaddr[1], get_framebuffer(1, fb_paddr), 577 get_aligned_size(), 578 desc_paddr + 1, 579 _command_irq()); 580 581 // Initialise palette descriptor details for lower colour depths. 582 583 if (have_palette) 584 _set_descriptor(desc_vaddr[2], get_palette(fb_paddr), 585 get_aligned_palette_size(), 586 desc_paddr, 587 Command_palette_buffer); 588 589 // Flush cached structure data. 590 591 l4_cache_clean_data((unsigned long) desc_vaddr, 592 (unsigned long) desc_vaddr + get_descriptors_size()); 593 594 // Configure DMA by setting frame descriptor addresses. 595 596 // Provide the palette descriptor address first, if employed. 597 598 _regs[Desc_address_0] = (uint32_t) (have_palette ? desc_paddr + 2 : desc_paddr); 599 600 // Provide a descriptor for the second DMA channel in dual-panel mode. 601 602 if (get_panels() == 2) 603 _regs[Desc_address_1] = (uint32_t) (desc_paddr + 1); 604 605 // Initialise panel-related registers. 606 607 _init_panel(); 608 609 // Initialise the control and configuration registers. 610 611 _regs[Lcd_control] = _control_panel() | _control_bpp() | _control_transfer() | _control_irq(); 612 _regs[Lcd_config] = _panel->config; 613 } 614 615 // Set the interrupt for controller-related events. 616 617 void 618 Lcd_jz4740_chip::set_irq(l4_cap_idx_t irq, enum Jz4740_lcd_irq_condition conditions) 619 { 620 _irq = irq; 621 _irq_conditions = conditions; 622 } 623 624 // Wait for an interrupt condition. 625 626 long 627 Lcd_jz4740_chip::wait_for_irq() 628 { 629 l4_msgtag_t tag; 630 631 _regs[Lcd_status] = _regs[Lcd_status] & ~(_status_irq()); 632 633 long err = l4_error(l4_rcv_ep_bind_thread(_irq, pthread_l4_cap(pthread_self()), 0)); 634 635 if (err) 636 return err; 637 638 // Wait for a condition. 639 640 tag = l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to(2000000))); 641 642 // Acknowledge interrupts. 643 644 _regs[Lcd_status] = 0; 645 646 // Return errors. 647 648 return l4_error(tag); 649 } 650 651 652 653 // C language interface functions. 654 655 void * 656 jz4740_lcd_init(l4_addr_t lcd_base, struct Jz4740_lcd_panel *panel) 657 { 658 return (void *) new Lcd_jz4740_chip(lcd_base, panel); 659 } 660 661 void 662 jz4740_lcd_config(void *lcd, struct Jz4740_lcd_descriptor *desc_vaddr, 663 struct Jz4740_lcd_descriptor *desc_paddr, 664 l4_addr_t fb_paddr) 665 { 666 static_cast<Lcd_jz4740_chip *>(lcd)->config(desc_vaddr, desc_paddr, fb_paddr); 667 } 668 669 void 670 jz4740_lcd_set_irq(void *lcd, l4_cap_idx_t irq, enum Jz4740_lcd_irq_condition conditions) 671 { 672 static_cast<Lcd_jz4740_chip *>(lcd)->set_irq(irq, conditions); 673 } 674 675 long 676 jz4740_lcd_wait_for_irq(void *lcd) 677 { 678 return static_cast<Lcd_jz4740_chip *>(lcd)->wait_for_irq(); 679 } 680 681 void 682 jz4740_lcd_disable(void *lcd) 683 { 684 static_cast<Lcd_jz4740_chip *>(lcd)->disable(); 685 } 686 687 void 688 jz4740_lcd_disable_quick(void *lcd) 689 { 690 static_cast<Lcd_jz4740_chip *>(lcd)->disable_quick(); 691 } 692 693 void 694 jz4740_lcd_enable(void *lcd) 695 { 696 static_cast<Lcd_jz4740_chip *>(lcd)->enable(); 697 } 698 699 int 700 jz4740_lcd_enabled(void *lcd) 701 { 702 return (int) static_cast<Lcd_jz4740_chip *>(lcd)->enabled(); 703 } 704 705 int 706 jz4740_lcd_get_pixel_clock(void *lcd) 707 { 708 return static_cast<Lcd_jz4740_chip *>(lcd)->get_pixel_clock(); 709 } 710 711 l4_size_t 712 jz4740_lcd_get_descriptors_size(void *lcd) 713 { 714 return static_cast<Lcd_jz4740_chip *>(lcd)->get_descriptors_size(); 715 } 716 717 l4_size_t 718 jz4740_lcd_get_line_size(void *lcd) 719 { 720 return static_cast<Lcd_jz4740_chip *>(lcd)->get_line_size(); 721 } 722 723 l4_size_t 724 jz4740_lcd_get_screen_size(void *lcd) 725 { 726 return static_cast<Lcd_jz4740_chip *>(lcd)->get_screen_size(); 727 } 728 729 l4_addr_t 730 jz4740_lcd_get_palette(void *lcd, l4_addr_t base) 731 { 732 return static_cast<Lcd_jz4740_chip *>(lcd)->get_palette(base); 733 } 734 735 void 736 jz4740_lcd_init_palette(void *lcd, l4_addr_t palette) 737 { 738 static_cast<Lcd_jz4740_chip *>(lcd)->init_palette(palette); 739 }