# HG changeset patch # User Paul Boddie # Date 1591019234 -7200 # Node ID e058e16fc4854c22d6e8708148f6369764f891c5 # Parent b4d7f83752a146eafc49071677613f6daac6b095 Added tentative adjustments for JZ4780 support. diff -r b4d7f83752a1 -r e058e16fc485 pkg/devices/lib/lcd/include/lcd-jz4740.h --- a/pkg/devices/lib/lcd/include/lcd-jz4740.h Mon Jun 01 15:43:34 2020 +0200 +++ b/pkg/devices/lib/lcd/include/lcd-jz4740.h Mon Jun 01 15:47:14 2020 +0200 @@ -28,7 +28,8 @@ #include -/* Descriptor referenced by the DMA mechanism. */ +/* Descriptor referenced by the DMA mechanism extended to 8 words for JZ4780 + support. */ struct Jz4740_lcd_descriptor { @@ -36,16 +37,9 @@ uint32_t source; /* FSADR: frame source address */ uint32_t identifier; /* FIDR: frame identifier */ uint32_t command; /* CMD: command */ -}; - -/* 8-word "new descriptor" variant for the JZ4780. */ -struct Jz4780_lcd_descriptor -{ - struct Jz4780_lcd_descriptor *next; /* FDADR: frame descriptor address */ - uint32_t source; /* FSADR: frame source address */ - uint32_t identifier; /* FIDR: frame identifier */ - uint32_t command; /* CMD: command */ + /* "New" descriptor fields. */ + uint32_t offset; /* OFFSIZE: offset in words between lines */ uint32_t page_width; /* PW: number of words per line (16x16 block mode) */ uint32_t command_position; /* CNUM: command number (smart LCD mode) or @@ -53,6 +47,15 @@ uint32_t fg_size; /* DESSIZE: foreground size and alpha properties */ }; +/* Interrupt conditions. */ + +enum Jz4740_lcd_irq_condition +{ + Lcd_irq_none = 0, + Lcd_irq_frame_start = 1, + Lcd_irq_frame_end = 2, +}; + /* C++ language interface. */ @@ -69,14 +72,37 @@ Hw::Register_block<32> _regs; Jz4740_lcd_panel *_panel; int _burst_size; + enum Jz4740_lcd_irq_condition _irq_conditions = Lcd_irq_none; + l4_cap_idx_t _irq = L4_INVALID_CAP; /* Control register value calculation. */ uint32_t _control_bpp(); + uint32_t _control_irq(); uint32_t _control_panel(); uint32_t _control_stn_frc(); uint32_t _control_transfer(); + /* OSD configure register value calculation. */ + + uint32_t _osd_config_irq(); + + /* Command register value calculation. */ + + uint32_t _command_irq(); + + /* Status register value calculation. */ + + uint32_t _status_irq(); + + /* Priority level threshold value calculation. */ + + uint32_t _priority_transfer(); + + /* Position value calculation. */ + + uint32_t _position_bpp(); + /* Panel mode access. */ uint32_t _mode(); @@ -103,6 +129,7 @@ void disable(); void disable_quick(); void enable(); + bool enabled(); /* Peripheral properties. */ @@ -117,6 +144,7 @@ /* Memory properties. */ + l4_size_t get_pixel_size(); l4_size_t get_line_size(); l4_size_t get_screen_size(); l4_size_t get_aligned_size(); @@ -142,6 +170,14 @@ void config(struct Jz4740_lcd_descriptor *desc_vaddr, struct Jz4740_lcd_descriptor *desc_paddr, l4_addr_t fb_paddr); + + /* Interrupt configuration. */ + + void set_irq(l4_cap_idx_t irq, enum Jz4740_lcd_irq_condition conditions); + + /* Interrupt handling. */ + + long wait_for_irq(); }; #endif @@ -158,9 +194,18 @@ struct Jz4740_lcd_descriptor *desc_paddr, l4_addr_t fb_paddr); +void jz4740_lcd_set_irq(void *lcd, l4_cap_idx_t irq, + enum Jz4740_lcd_irq_condition conditions); + +long jz4740_lcd_wait_for_irq(void *lcd); + void jz4740_lcd_disable(void *lcd); void jz4740_lcd_disable_quick(void *lcd); void jz4740_lcd_enable(void *lcd); +int jz4740_lcd_enabled(void *lcd); + +l4_size_t jz4740_lcd_get_descriptors_size(void *lcd); +l4_size_t jz4740_lcd_get_screen_size(void *lcd); l4_addr_t jz4740_lcd_get_palette(void *lcd, l4_addr_t base); int jz4740_lcd_get_pixel_clock(void *lcd); diff -r b4d7f83752a1 -r e058e16fc485 pkg/devices/lib/lcd/src/jz4740/lcd-jz4740.cc --- a/pkg/devices/lib/lcd/src/jz4740/lcd-jz4740.cc Mon Jun 01 15:43:34 2020 +0200 +++ b/pkg/devices/lib/lcd/src/jz4740/lcd-jz4740.cc Mon Jun 01 15:47:14 2020 +0200 @@ -23,19 +23,22 @@ #include #include +#include #include +#include #include "lcd-jz4740.h" #include "lcd-jz4740-config.h" #include +#include enum Regs : unsigned { Lcd_config = 0x000, // LCD_CFG Lcd_vsync = 0x004, // LCD_VSYNC Lcd_hsync = 0x008, // LCD_HSYNC - Vertical_area = 0x00c, // LCD_VAT + Virtual_area = 0x00c, // LCD_VAT Display_hlimits = 0x010, // LCD_DAH Display_vlimits = 0x014, // LCD_DAV Lcd_ps = 0x018, // LCD_PS @@ -53,6 +56,14 @@ Source_address_1 = 0x054, // LCD_SA1 Frame_id_1 = 0x058, // LCD_FID1 Command_1 = 0x05c, // LCD_CMD1 + Rgb_control = 0x090, // LCD_RGBC (JZ4780) + Priority_level = 0x2c0, // LCD_PCFG + + // OSD registers. + + Osd_config = 0x100, // LCD_OSDC + Osd_control = 0x104, // LCD_OSDCTRL + Osd_status = 0x108, // LCD_OSDS }; // Lcd_config descriptions. @@ -72,12 +83,12 @@ Value_second = 0, }; -// Vertical_area bits. +// Virtual area bits. -enum Vertical_area_values : unsigned +enum Virtual_area_values : unsigned { - Vertical_area_horizontal_size = Value_first, // sum of display and blank regions (dot/pixel clock periods) - Vertical_area_vertical_size = Value_second, // sum of display and blank regions (line periods) + Virtual_area_horizontal_size = Value_first, // sum of display and blank regions (dot/pixel clock periods) + Virtual_area_vertical_size = Value_second, // sum of display and blank regions (line periods) }; // Lcd_control descriptions. @@ -169,6 +180,102 @@ Command_buffer_length_mask = 0x00ffffff, }; +// Status descriptions. + +enum Status_bits : unsigned +{ + Status_frame_end_irq = 5, + Status_frame_start_irq = 4, + Status_disabled = 0, +}; + +// OSD configuration bits (JZ4780). + +enum Osd_config_bits : unsigned +{ + Osd_config_fg1_frame_start_irq_enable = 15, + Osd_config_fg1_frame_end_irq_enable = 14, + Osd_config_fg0_frame_start_irq_enable = 11, + Osd_config_fg0_frame_end_irq_enable = 10, + Osd_config_enable = 0, +}; + +// RGB control (JZ4780). + +enum Rgb_control_bits : unsigned +{ + Rgb_data_padded = 15, // RGBDM + Rgb_padding_mode = 14, // DMM + Rgb_422 = 8, // 422 + Rgb_format_enable = 7, // RGBFMT + Rgb_odd_line = 4, // OddRGB + Rgb_even_line = 0, // EvenRGB +}; + +enum Rgb_control_values : unsigned +{ + Rgb_padding_end = 0U << Rgb_padding_mode, + Rgb_padding_start = 1U << Rgb_padding_mode, + Rgb_odd_line_rgb = 0U << Rgb_odd_line, + Rgb_odd_line_rbg = 1U << Rgb_odd_line, + Rgb_odd_line_grb = 2U << Rgb_odd_line, + Rgb_odd_line_gbr = 3U << Rgb_odd_line, + Rgb_odd_line_brg = 4U << Rgb_odd_line, + Rgb_odd_line_bgr = 5U << Rgb_odd_line, + Rgb_even_line_rgb = 0U << Rgb_even_line, + Rgb_even_line_rbg = 1U << Rgb_even_line, + Rgb_even_line_grb = 2U << Rgb_even_line, + Rgb_even_line_gbr = 3U << Rgb_even_line, + Rgb_even_line_brg = 4U << Rgb_even_line, + Rgb_even_line_bgr = 5U << Rgb_even_line, +}; + +// Priority level. + +enum Priority_level_bits : unsigned +{ + Priority_mode = 31, + Priority_highest_burst = 28, + Priority_threshold2 = 18, + Priority_threshold1 = 9, + Priority_threshold0 = 0, +}; + +enum Priority_level_values : unsigned +{ + Priority_mode_dynamic = 0U << Priority_mode, + Priority_mode_arbiter = 1U << Priority_mode, +}; + +enum Priority_burst_values : unsigned +{ + Priority_burst_4 = 0, + Priority_burst_8 = 1, + Priority_burst_16 = 2, + Priority_burst_32 = 3, + Priority_burst_64 = 4, + Priority_burst_16_cont = 5, + Priority_burst_disable = 7, +}; + +// Position descriptor member. + +enum Position_bits : unsigned +{ + Position_bpp = 27, + Position_premultiply_lcd = 26, + Position_coefficient = 24, + Position_y_position = 12, + Position_x_position = 0, +}; + +enum Position_values : unsigned +{ + Position_bpp_15_16bpp = 4, + Position_bpp_18_24bpp = 5, + Position_bpp_30bpp = 7, +}; + // Utility functions. @@ -210,6 +317,7 @@ : _panel(panel) { _regs = new Hw::Mmio_register_block<32>(addr); + //_burst_size = 64; // 64-word burst size (JZ4780) _burst_size = 16; // 16-word burst size // add_cid("lcd"); @@ -227,7 +335,7 @@ { // Set the disable bit for normal shutdown. - _regs[Lcd_control] = _regs[Lcd_control] | (1 << Control_disable); + _regs[Lcd_control] = _regs[Lcd_control] | (1U << Control_disable); } void @@ -235,15 +343,25 @@ { // Clear the enable bit for quick shutdown. - _regs[Lcd_control] = _regs[Lcd_control] & ~(1 << Control_enable); + _regs[Lcd_control] = _regs[Lcd_control] & ~(1U << Control_enable); } void Lcd_jz4740_chip::enable() { // Clear the disable bit and set the enable bit. + // JZ4780: OSD status set. - _regs[Lcd_control] = (_regs[Lcd_control] & ~(1 << Control_disable)) | (1 << Control_enable); + _regs[Osd_status] = 0; + _regs[Lcd_status] = 0; + _regs[Lcd_control] = (_regs[Lcd_control] & ~(1U << Control_disable)) | (1U << Control_enable); + printf("LCD control: %x\n", (unsigned int) _regs[Lcd_control]); +} + +bool +Lcd_jz4740_chip::enabled() +{ + return !(_regs[Lcd_status] & (1U << Status_disabled)); } // Calculate and return the pixel clock frequency. @@ -346,6 +464,19 @@ +// Return the pixel memory size in bits. + +l4_size_t +Lcd_jz4740_chip::get_pixel_size() +{ + if (_panel->bpp > 16) + return 32; + else if (_panel->bpp > 8) + return 16; + else + return _panel->bpp; +} + // Return the line memory size. l4_size_t @@ -353,7 +484,7 @@ { // Lines must be aligned to a word boundary. - return align((_panel->width * _panel->bpp) / 8, sizeof(uint32_t)); + return align((_panel->width * get_pixel_size()) / 8, sizeof(uint32_t)); } // Return the screen memory size. @@ -383,7 +514,7 @@ // Get the size of a collection of two-byte entries, one per colour. - return (1 << (_panel->bpp)) * sizeof(uint16_t); + return (1U << (_panel->bpp)) * sizeof(uint16_t); } // Return the aligned size of the palette for the DMA transfer. @@ -438,7 +569,7 @@ void Lcd_jz4740_chip::init_palette(l4_addr_t palette) { - uint8_t colours = 1 << (_panel->bpp); + uint8_t colours = 1U << (_panel->bpp); uint16_t *entry = (uint16_t *) palette; uint16_t *end = entry + colours; uint8_t value = 0; @@ -486,13 +617,32 @@ } } +// Return colour depth control value. +// JZ4780 position details only. + +uint32_t +Lcd_jz4740_chip::_position_bpp() +{ + uint32_t value; + + switch (_panel->bpp) + { + case 15: case 16: value = Position_bpp_15_16bpp; break; + case 18: case 24: value = Position_bpp_18_24bpp; break; + case 30: value = Position_bpp_30bpp; break; + default: value = 0; break; + } + + return value << Position_bpp; +} + // Return a panel-related control value. uint32_t Lcd_jz4740_chip::_control_panel() { if (have_stn_panel()) - return _control_stn_frc(); + return _control_stn_frc() << Control_frc_algorithm; else return 0; } @@ -526,7 +676,65 @@ default: length = Burst_length_16; break; } - return (length << Control_burst_length) | (1 << Control_out_underrun); + return (length << Control_burst_length) | (1U << Control_out_underrun); +} + +// Return an interrupt-related control value. + +uint32_t +Lcd_jz4740_chip::_control_irq() +{ + return ((_irq_conditions & Lcd_irq_frame_start) ? (1U << Control_frame_start_irq_enable) : 0) | + ((_irq_conditions & Lcd_irq_frame_end) ? (1U << Control_frame_end_irq_enable) : 0); +} + +// Return an interrupt-related OSD configuration value. + +uint32_t +Lcd_jz4740_chip::_osd_config_irq() +{ + return ((_irq_conditions & Lcd_irq_frame_start) ? (1U << Osd_config_fg0_frame_start_irq_enable) : 0) | + ((_irq_conditions & Lcd_irq_frame_end) ? (1U << Osd_config_fg0_frame_end_irq_enable) : 0); +} + +// Return an interrupt-related command value. + +uint32_t +Lcd_jz4740_chip::_command_irq() +{ + return ((_irq_conditions & Lcd_irq_frame_start) ? (1U << Command_frame_start_irq) : 0) | + ((_irq_conditions & Lcd_irq_frame_end) ? (1U << Command_frame_end_irq) : 0); +} + +// Return an interrupt-related status value. + +uint32_t +Lcd_jz4740_chip::_status_irq() +{ + return ((_irq_conditions & Lcd_irq_frame_start) ? (1U << Status_frame_start_irq) : 0) | + ((_irq_conditions & Lcd_irq_frame_end) ? (1U << Status_frame_end_irq) : 0); +} + +uint32_t +Lcd_jz4740_chip::_priority_transfer() +{ + uint32_t length; + + switch (_burst_size) + { + case 4: length = Priority_burst_4; break; + case 8: length = Priority_burst_8; break; + case 32: length = Priority_burst_32; break; + case 64: length = Priority_burst_64; break; + case 16: + default: length = Priority_burst_16; break; + } + + return Priority_mode_arbiter | + (length << Priority_highest_burst) | + (511U << Priority_threshold2) | + (400U << Priority_threshold1) | + (256U << Priority_threshold0); } // STN panel-specific initialisation. @@ -564,8 +772,8 @@ // Set the display area and limits. - _regs[Vertical_area] = encode_pair(line_end_pos + hsync + line_end, - frame_end_pos + _panel->vsync + _panel->frame_end + _panel->frame_start); + _regs[Virtual_area] = encode_pair(line_end_pos + hsync + line_end, + frame_end_pos + _panel->vsync + _panel->frame_end + _panel->frame_start); _regs[Display_hlimits] = encode_pair(line_start_pos, line_end_pos); _regs[Display_vlimits] = encode_pair(frame_start_pos, frame_end_pos); @@ -594,8 +802,8 @@ // Set the display area and limits. - _regs[Vertical_area] = encode_pair(line_end_pos + _panel->line_end, - frame_end_pos + _panel->frame_end); + _regs[Virtual_area] = encode_pair(line_end_pos + _panel->line_end, + frame_end_pos + _panel->frame_end); _regs[Display_hlimits] = encode_pair(line_start_pos, line_end_pos); _regs[Display_vlimits] = encode_pair(frame_start_pos, frame_end_pos); @@ -632,8 +840,24 @@ desc.next = next; desc.source = source; - desc.identifier = 0; - desc.command = ((size / sizeof(uint32_t)) & Command_buffer_length_mask) | flags; + desc.identifier = source; + desc.command = ((size / sizeof(uint32_t)) & Command_buffer_length_mask) | + (1U << Command_frame_enable) | + flags; + + printf("next = %08x\n", desc.next); + printf("source = %08x\n", desc.source); + printf("identifier = %08x\n", desc.identifier); + printf("command = %08x\n", desc.command); + + // Initialise "new" descriptor fields. + + desc.offset = 0; + desc.page_width = 0; + desc.command_position = (1UL << Position_premultiply_lcd) | + (1UL << Position_coefficient) | + _position_bpp(); + desc.fg_size = 0xff000000 | (1023 << 12) | (1279 << 0); // JZ4780 driver magic } @@ -646,28 +870,33 @@ struct Jz4740_lcd_descriptor *desc_paddr, l4_addr_t fb_paddr) { + // NOTE: Remarks in the Ingenic Linux 3.0.8 driver suggest that the JZ4775 and + // NOTE: JZ4780 do not support palettes. + int have_palette = (_panel->bpp <= 8); // Provide the first framebuffer descriptor in single and dual modes. // Flip back and forth between any palette and the framebuffer. _set_descriptor(desc_vaddr[0], get_framebuffer(0, fb_paddr), - get_screen_size(), - have_palette ? desc_paddr + 2 : desc_paddr); + get_aligned_size(), + have_palette ? desc_paddr + 2 : desc_paddr, + _command_irq()); // Provide the second framebuffer descriptor only in dual-panel mode. // Only employ this descriptor in the second DMA channel. if (get_panels() == 2) _set_descriptor(desc_vaddr[1], get_framebuffer(1, fb_paddr), - get_screen_size(), - desc_paddr + 1); + get_aligned_size(), + desc_paddr + 1, + _command_irq()); // Initialise palette descriptor details for lower colour depths. if (have_palette) _set_descriptor(desc_vaddr[2], get_palette(fb_paddr), - get_palette_size(), + get_aligned_palette_size(), desc_paddr, Command_palette_buffer); @@ -681,6 +910,7 @@ // Provide the palette descriptor address first, if employed. _regs[Desc_address_0] = (uint32_t) (have_palette ? desc_paddr + 2 : desc_paddr); + printf("descriptor = %08x\n", (uint32_t) _regs[Desc_address_0]); // Provide a descriptor for the second DMA channel in dual-panel mode. @@ -692,9 +922,66 @@ _init_panel(); // Initialise the control and configuration registers. + // NOTE: JZ4780 does not support bpp setting here. - _regs[Lcd_control] = _control_panel() | _control_bpp() | _control_transfer(); + _regs[Lcd_control] = _control_panel() | _control_bpp() | _control_transfer() | _control_irq(); _regs[Lcd_config] = _panel->config; + + // NOTE: JZ4780 only. + + _regs[Rgb_control] = (1U << Rgb_format_enable) | Rgb_odd_line_rgb | Rgb_even_line_rgb; + _regs[Priority_level] = _priority_transfer(); + _regs[Osd_config] = 0; // (1U << Osd_config_enable) | _osd_config_irq(); + + printf("LCD control: %08x\n", (unsigned int) _regs[Lcd_control]); + printf("LCD status: %08x\n", (unsigned int) _regs[Lcd_status]); + printf("OSD config: %08x\n", (unsigned int) _regs[Osd_config]); + printf("OSD status: %08x\n", (unsigned int) _regs[Osd_status]); +} + +// Set the interrupt for controller-related events. + +void +Lcd_jz4740_chip::set_irq(l4_cap_idx_t irq, enum Jz4740_lcd_irq_condition conditions) +{ + _irq = irq; + _irq_conditions = conditions; +} + +// Wait for an interrupt condition. + +long +Lcd_jz4740_chip::wait_for_irq() +{ + long err; + l4_msgtag_t tag; + + _regs[Lcd_status] = _regs[Lcd_status] & ~(_status_irq()); + + // Wait for a condition. + + printf("Waiting for IRQ...\n"); + tag = l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to(1000000))); + printf("LCD status: %08x\n", (unsigned int) _regs[Lcd_status]); + printf("OSD status: %08x\n", (unsigned int) _regs[Osd_status]); + printf("LCD control: %08x\n", (unsigned int) _regs[Lcd_control]); + printf("LCD command: %08x\n", (unsigned int) _regs[Command_0]); + printf("LCD IRQ id: %08x\n", (unsigned int) _regs[Lcd_irq_id]); + printf("LCD descriptor: %08x\n", (unsigned int) _regs[Desc_address_0]); + printf("LCD source: %08x\n", (unsigned int) _regs[Source_address_0]); + printf("LCD frame id: %08x\n", (unsigned int) _regs[Frame_id_0]); + + // Return errors immediately. + + err = l4_ipc_error(tag, l4_utcb()); + if (err) + return err; + + // Acknowledge interrupts. + + _regs[Lcd_status] = _regs[Lcd_status] & ~(_status_irq()); + + return L4_EOK; } @@ -716,6 +1003,18 @@ } void +jz4740_lcd_set_irq(void *lcd, l4_cap_idx_t irq, enum Jz4740_lcd_irq_condition conditions) +{ + static_cast(lcd)->set_irq(irq, conditions); +} + +long +jz4740_lcd_wait_for_irq(void *lcd) +{ + return static_cast(lcd)->wait_for_irq(); +} + +void jz4740_lcd_disable(void *lcd) { static_cast(lcd)->disable(); @@ -734,11 +1033,29 @@ } int +jz4740_lcd_enabled(void *lcd) +{ + return (int) static_cast(lcd)->enabled(); +} + +int jz4740_lcd_get_pixel_clock(void *lcd) { return static_cast(lcd)->get_pixel_clock(); } +l4_size_t +jz4740_lcd_get_descriptors_size(void *lcd) +{ + return static_cast(lcd)->get_descriptors_size(); +} + +l4_size_t +jz4740_lcd_get_screen_size(void *lcd) +{ + return static_cast(lcd)->get_screen_size(); +} + l4_addr_t jz4740_lcd_get_palette(void *lcd, l4_addr_t base) {