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