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, 2020, 6 * 2021 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 // Return an interrupt-related OSD configuration value. 45 46 uint32_t 47 Lcd_jz4780_chip::_osd_config_irq() 48 { 49 return ((_irq_conditions & Lcd_irq_frame_start) ? (1U << Osd_config_fg0_frame_start_irq_enable) : 0) | 50 ((_irq_conditions & Lcd_irq_frame_end) ? (1U << Osd_config_fg0_frame_end_irq_enable) : 0); 51 } 52 53 // Return colour depth control value. 54 // JZ4780 position details only. 55 56 uint32_t 57 Lcd_jz4780_chip::_position_bpp() 58 { 59 uint32_t value; 60 61 switch (_panel->bpp) 62 { 63 case 15: case 16: value = Position_bpp_15_16bpp; break; 64 case 18: case 24: value = Position_bpp_18_24bpp; break; 65 case 30: value = Position_bpp_30bpp; break; 66 default: value = 0; break; 67 } 68 69 return value << Position_bpp; 70 } 71 72 uint32_t 73 Lcd_jz4780_chip::_priority_transfer() 74 { 75 uint32_t length; 76 77 switch (_burst_size) 78 { 79 case 4: length = Priority_burst_4; break; 80 case 8: length = Priority_burst_8; break; 81 case 32: length = Priority_burst_32; break; 82 case 64: length = Priority_burst_64; break; 83 case 16: 84 default: length = Priority_burst_16; break; 85 } 86 87 return Priority_mode_arbiter | 88 (length << Priority_highest_burst) | 89 (511U << Priority_threshold2) | 90 (400U << Priority_threshold1) | 91 (256U << Priority_threshold0); 92 } 93 94 // Initialise a DMA descriptor for the JZ4780. The principal differences with 95 // earlier SoCs are the "new" descriptor fields which populate additional 96 // registers controlling OSD foreground planes, and the frame enable flag which 97 // allows the descriptors/planes to be disabled and left unused. 98 99 void 100 Lcd_jz4780_chip::_set_descriptor(struct Jz4740_lcd_descriptor &desc, 101 l4_addr_t source, l4_size_t size, 102 struct Jz4740_lcd_descriptor *next, 103 uint32_t flags, 104 bool frame_enable) 105 { 106 // In the command, indicate the number of words from the source for transfer. 107 108 desc.next = next; 109 desc.source = frame_enable ? source : 0; 110 desc.identifier = source; 111 desc.command = ((size / sizeof(uint32_t)) & Command_buffer_length_mask) | 112 (frame_enable ? (1U << Command_frame_enable) : 0) | 113 flags; 114 115 // Initialise "new" descriptor fields. 116 117 desc.offset = 0; 118 desc.page_width = 0; 119 120 desc.command_position = (1U << Position_premultiply_lcd) | 121 (1U << Position_coefficient) | 122 _position_bpp(); 123 124 desc.fg_size = (0xff << Alpha_size_alpha) | 125 ((_panel->height - 1) << Alpha_size_height) | 126 ((_panel->width - 1) << Alpha_size_width); 127 } 128 129 // HDMI-compatible JZ4780 configuration. 130 // Remarks in the Ingenic Linux 3.0.8 driver suggest that the JZ4775 and JZ4780 131 // do not support palettes. Here, multiple panels are also not supported. 132 133 void 134 Lcd_jz4780_chip::config(struct Jz4740_lcd_descriptor *desc_vaddr, 135 struct Jz4740_lcd_descriptor *desc_paddr, 136 l4_addr_t fb_paddr) 137 { 138 // Descriptor for the first DMA channel. 139 140 _set_descriptor(desc_vaddr[0], get_framebuffer(0, fb_paddr), 141 get_aligned_size(), 142 desc_paddr, 143 _command_irq()); 144 145 // Descriptor for the second DMA channel. 146 // This just sets an inactive frame. 147 148 _set_descriptor(desc_vaddr[1], 0, 149 0, 150 desc_paddr + 1, 151 _command_irq(), 152 false); 153 154 // Flush cached structure data. 155 156 l4_cache_clean_data((unsigned long) desc_vaddr, 157 (unsigned long) desc_vaddr + get_descriptors_size()); 158 159 // Configure DMA by setting frame descriptor addresses. 160 161 // Provide the palette descriptor address first, if employed. 162 163 _regs[Desc_address_0] = (uint32_t) desc_paddr; 164 165 // Provide a descriptor for the second DMA channel, currently not used. 166 167 _regs[Desc_address_1] = (uint32_t) (desc_paddr + 1); 168 169 // Initialise panel-related registers. 170 171 _init_panel(); 172 173 // Initialise the control and configuration registers. 174 // JZ4780 does not support bpp setting here. Otherwise, this is the same as 175 // with earlier SoCs. 176 177 _regs[Lcd_control] = _control_panel() | _control_transfer() | _control_irq(); 178 _regs[Lcd_config] = _panel->config; 179 } 180 181 182 183 // C language interface functions. 184 185 void * 186 jz4780_lcd_init(l4_addr_t lcd_base, struct Jz4740_lcd_panel *panel) 187 { 188 return (void *) new Lcd_jz4780_chip(lcd_base, panel); 189 }