paul@75 | 1 | /* |
paul@75 | 2 | * LCD peripheral support for the JZ4740 and related SoCs. |
paul@75 | 3 | * |
paul@75 | 4 | * Copyright (C) Xiangfu Liu <xiangfu@sharism.cc> |
paul@75 | 5 | * Copyright (C) 2015, 2016, 2017, 2018, |
paul@75 | 6 | * 2020 Paul Boddie <paul@boddie.org.uk> |
paul@75 | 7 | * |
paul@75 | 8 | * This program is free software; you can redistribute it and/or |
paul@75 | 9 | * modify it under the terms of the GNU General Public License as |
paul@75 | 10 | * published by the Free Software Foundation; either version 2 of |
paul@75 | 11 | * the License, or (at your option) any later version. |
paul@75 | 12 | * |
paul@75 | 13 | * This program is distributed in the hope that it will be useful, |
paul@75 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@75 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@75 | 16 | * GNU General Public License for more details. |
paul@75 | 17 | * |
paul@75 | 18 | * You should have received a copy of the GNU General Public License |
paul@75 | 19 | * along with this program; if not, write to the Free Software |
paul@75 | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@75 | 21 | * Boston, MA 02110-1301, USA |
paul@75 | 22 | */ |
paul@75 | 23 | |
paul@75 | 24 | #include <l4/devices/hw_mmio_register_block.h> |
paul@75 | 25 | #include <l4/sys/cache.h> |
paul@75 | 26 | #include <l4/sys/types.h> |
paul@75 | 27 | |
paul@75 | 28 | #include "lcd-jz4780.h" |
paul@75 | 29 | #include "lcd-jz4740-config.h" |
paul@75 | 30 | #include "lcd-jz4740-regs.h" |
paul@75 | 31 | |
paul@75 | 32 | #include <stdint.h> |
paul@75 | 33 | |
paul@75 | 34 | |
paul@75 | 35 | |
paul@75 | 36 | // JZ4780-specific methods. |
paul@75 | 37 | |
paul@75 | 38 | Lcd_jz4780_chip::Lcd_jz4780_chip(l4_addr_t addr, Jz4740_lcd_panel *panel) |
paul@75 | 39 | : Lcd_jz4740_chip(addr, panel) |
paul@75 | 40 | { |
paul@75 | 41 | _burst_size = 64; // 64-word burst size available in the JZ4780 |
paul@75 | 42 | } |
paul@75 | 43 | |
paul@99 | 44 | void |
paul@99 | 45 | Lcd_jz4780_chip::enable() |
paul@99 | 46 | { |
paul@99 | 47 | _regs[Osd_status] = 0; |
paul@99 | 48 | Lcd_jz4740_chip::enable(); |
paul@99 | 49 | } |
paul@99 | 50 | |
paul@99 | 51 | // Return an interrupt-related OSD configuration value. |
paul@99 | 52 | |
paul@99 | 53 | uint32_t |
paul@99 | 54 | Lcd_jz4780_chip::_osd_config_irq() |
paul@99 | 55 | { |
paul@99 | 56 | return ((_irq_conditions & Lcd_irq_frame_start) ? (1U << Osd_config_fg0_frame_start_irq_enable) : 0) | |
paul@99 | 57 | ((_irq_conditions & Lcd_irq_frame_end) ? (1U << Osd_config_fg0_frame_end_irq_enable) : 0); |
paul@99 | 58 | } |
paul@99 | 59 | |
paul@75 | 60 | // Return colour depth control value. |
paul@75 | 61 | // JZ4780 position details only. |
paul@75 | 62 | |
paul@75 | 63 | uint32_t |
paul@75 | 64 | Lcd_jz4780_chip::_position_bpp() |
paul@75 | 65 | { |
paul@75 | 66 | uint32_t value; |
paul@75 | 67 | |
paul@75 | 68 | switch (_panel->bpp) |
paul@75 | 69 | { |
paul@75 | 70 | case 15: case 16: value = Position_bpp_15_16bpp; break; |
paul@75 | 71 | case 18: case 24: value = Position_bpp_18_24bpp; break; |
paul@75 | 72 | case 30: value = Position_bpp_30bpp; break; |
paul@75 | 73 | default: value = 0; break; |
paul@75 | 74 | } |
paul@75 | 75 | |
paul@75 | 76 | return value << Position_bpp; |
paul@75 | 77 | } |
paul@75 | 78 | |
paul@75 | 79 | uint32_t |
paul@75 | 80 | Lcd_jz4780_chip::_priority_transfer() |
paul@75 | 81 | { |
paul@75 | 82 | uint32_t length; |
paul@75 | 83 | |
paul@75 | 84 | switch (_burst_size) |
paul@75 | 85 | { |
paul@75 | 86 | case 4: length = Priority_burst_4; break; |
paul@75 | 87 | case 8: length = Priority_burst_8; break; |
paul@75 | 88 | case 32: length = Priority_burst_32; break; |
paul@75 | 89 | case 64: length = Priority_burst_64; break; |
paul@75 | 90 | case 16: |
paul@75 | 91 | default: length = Priority_burst_16; break; |
paul@75 | 92 | } |
paul@75 | 93 | |
paul@75 | 94 | return Priority_mode_arbiter | |
paul@75 | 95 | (length << Priority_highest_burst) | |
paul@75 | 96 | (511U << Priority_threshold2) | |
paul@75 | 97 | (400U << Priority_threshold1) | |
paul@75 | 98 | (256U << Priority_threshold0); |
paul@75 | 99 | } |
paul@75 | 100 | |
paul@75 | 101 | // Initialise a DMA descriptor for the JZ4780. The principal differences with |
paul@75 | 102 | // earlier SoCs are the "new" descriptor fields which populate additional |
paul@75 | 103 | // registers controlling OSD foreground planes, and the frame enable flag which |
paul@75 | 104 | // allows the descriptors/planes to be disabled and left unused. |
paul@75 | 105 | |
paul@75 | 106 | void |
paul@75 | 107 | Lcd_jz4780_chip::_set_descriptor(struct Jz4740_lcd_descriptor &desc, |
paul@75 | 108 | l4_addr_t source, l4_size_t size, |
paul@75 | 109 | struct Jz4740_lcd_descriptor *next, |
paul@75 | 110 | uint32_t flags, |
paul@75 | 111 | bool frame_enable) |
paul@75 | 112 | { |
paul@75 | 113 | // In the command, indicate the number of words from the source for transfer. |
paul@75 | 114 | |
paul@75 | 115 | desc.next = next; |
paul@75 | 116 | desc.source = frame_enable ? source : 0; |
paul@75 | 117 | desc.identifier = source; |
paul@75 | 118 | desc.command = ((size / sizeof(uint32_t)) & Command_buffer_length_mask) | |
paul@75 | 119 | (frame_enable ? (1U << Command_frame_enable) : 0) | |
paul@75 | 120 | flags; |
paul@75 | 121 | |
paul@75 | 122 | // Initialise "new" descriptor fields. |
paul@75 | 123 | |
paul@75 | 124 | desc.offset = 0; |
paul@75 | 125 | desc.page_width = 0; |
paul@75 | 126 | desc.command_position = (1U << Position_premultiply_lcd) | |
paul@75 | 127 | ((frame_enable ? 1U : 3U) << Position_coefficient) | |
paul@75 | 128 | _position_bpp(); |
paul@75 | 129 | desc.fg_size = 0xff000000 | |
paul@75 | 130 | ((_panel->height - 1) << 12) | |
paul@75 | 131 | ((_panel->width - 1) << 0); |
paul@75 | 132 | } |
paul@75 | 133 | |
paul@75 | 134 | // HDMI-compatible JZ4780 configuration. |
paul@75 | 135 | // Remarks in the Ingenic Linux 3.0.8 driver suggest that the JZ4775 and JZ4780 |
paul@75 | 136 | // do not support palettes. Here, multiple panels are also not supported. |
paul@75 | 137 | |
paul@75 | 138 | void |
paul@75 | 139 | Lcd_jz4780_chip::config(struct Jz4740_lcd_descriptor *desc_vaddr, |
paul@75 | 140 | struct Jz4740_lcd_descriptor *desc_paddr, |
paul@75 | 141 | l4_addr_t fb_paddr) |
paul@75 | 142 | { |
paul@75 | 143 | // Provide the first framebuffer descriptor in single and dual modes. |
paul@75 | 144 | // Flip back and forth between any palette and the framebuffer. |
paul@75 | 145 | |
paul@75 | 146 | _set_descriptor(desc_vaddr[0], get_framebuffer(0, fb_paddr), |
paul@75 | 147 | get_aligned_size(), |
paul@75 | 148 | desc_paddr, |
paul@75 | 149 | _command_irq()); |
paul@75 | 150 | |
paul@75 | 151 | // Provide the second framebuffer descriptor only in dual-panel mode. |
paul@75 | 152 | // Only employ this descriptor in the second DMA channel. |
paul@75 | 153 | |
paul@75 | 154 | _set_descriptor(desc_vaddr[1], get_framebuffer(1, fb_paddr), |
paul@75 | 155 | get_aligned_size(), |
paul@75 | 156 | desc_paddr + 1, |
paul@75 | 157 | _command_irq(), |
paul@75 | 158 | false); |
paul@75 | 159 | |
paul@75 | 160 | // Flush cached structure data. |
paul@75 | 161 | |
paul@75 | 162 | l4_cache_clean_data((unsigned long) desc_vaddr, |
paul@75 | 163 | (unsigned long) desc_vaddr + get_descriptors_size()); |
paul@75 | 164 | |
paul@75 | 165 | // Configure DMA by setting frame descriptor addresses. |
paul@75 | 166 | |
paul@75 | 167 | // Provide the palette descriptor address first, if employed. |
paul@75 | 168 | |
paul@75 | 169 | _regs[Desc_address_0] = (uint32_t) desc_paddr; |
paul@75 | 170 | |
paul@75 | 171 | // Provide a descriptor for the second DMA channel, providing foreground 1. |
paul@75 | 172 | |
paul@75 | 173 | _regs[Desc_address_1] = (uint32_t) (desc_paddr + 1); |
paul@75 | 174 | |
paul@75 | 175 | // Initialise panel-related registers. |
paul@75 | 176 | |
paul@75 | 177 | _init_panel(); |
paul@75 | 178 | |
paul@75 | 179 | // Initialise the control and configuration registers. |
paul@75 | 180 | // JZ4780 does not support bpp setting here. Otherwise, this is the same as |
paul@75 | 181 | // with earlier SoCs. |
paul@75 | 182 | |
paul@75 | 183 | _regs[Lcd_control] = _control_panel() | _control_transfer() | _control_irq(); |
paul@75 | 184 | _regs[Lcd_config] = _panel->config; |
paul@75 | 185 | |
paul@75 | 186 | // JZ4780-specific configuration. |
paul@75 | 187 | // The RGB control register usage may be superfluous. |
paul@75 | 188 | |
paul@75 | 189 | _regs[Rgb_control] = (1U << Rgb_format_enable) | Rgb_odd_line_rgb | Rgb_even_line_rgb; |
paul@75 | 190 | _regs[Priority_level] = _priority_transfer(); |
paul@75 | 191 | |
paul@75 | 192 | // Employ whole image alpha levels by default. |
paul@75 | 193 | |
paul@75 | 194 | _regs[Osd_config] = (1U << Osd_config_enable) | |
paul@75 | 195 | (1U << Osd_config_alpha_enable); |
paul@75 | 196 | _regs[Alpha_levels] = ((255U << Alpha_level_fg1) & Alpha_level_fg1_mask) | |
paul@75 | 197 | ((255U << Alpha_level_fg0) & Alpha_level_fg0_mask); |
paul@75 | 198 | } |
paul@75 | 199 | |
paul@75 | 200 | |
paul@75 | 201 | |
paul@75 | 202 | // C language interface functions. |
paul@75 | 203 | |
paul@75 | 204 | void * |
paul@75 | 205 | jz4780_lcd_init(l4_addr_t lcd_base, struct Jz4740_lcd_panel *panel) |
paul@75 | 206 | { |
paul@75 | 207 | return (void *) new Lcd_jz4780_chip(lcd_base, panel); |
paul@75 | 208 | } |