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