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