1 /* 2 * Common LCD device support for the JZ4740 and related SoCs. 3 * 4 * (c) 2018 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/devices/cpm-client.h> 23 #include <l4/devices/display-client.h> 24 #include <l4/devices/panel-client.h> 25 #include <l4/devices/lcd-jz4740.h> 26 #include <l4/devices/memory.h> 27 #include "lcd-jz4740-device.h" 28 29 #include <l4/re/dataspace> 30 #include <l4/re/env> 31 #include <l4/re/mem_alloc> 32 #include <l4/re/rm> 33 #include <l4/re/util/cap_alloc> 34 #include <l4/sys/types.h> 35 36 #include <stdint.h> 37 38 // Virtual addresses for the LCD register block. 39 40 static l4_addr_t lcd_virt_base = 0, lcd_virt_base_end = 0; 41 42 // LCD, CPM, display and panel device abstractions. 43 44 static Lcd_jz4740_chip *lcd_chip = 0; 45 46 static L4::Cap<Cpm_device_interface> cpm_device; 47 static L4::Cap<Display_device_interface> display_device; 48 static L4::Cap<Panel_device_interface> panel_device; 49 50 // Panel definition. 51 52 static struct Jz4740_lcd_panel panel; 53 54 55 56 // CPM operations. 57 58 static void set_timing() 59 { 60 uint32_t pclk = lcd_chip->get_pixel_clock(); 61 62 cpm_device->stop_lcd(); 63 64 // Original comment: LCDClock > 2.5*Pixclock 65 // However, the documentation indicates that a TFT panel needs a device clock 66 // 1.5 times that of the pixel clock, and a STN panel needs a device clock 3 67 // times that of the pixel clock. 68 69 cpm_device->set_lcd_frequencies(pclk, 3); 70 cpm_device->update_output_frequency(); 71 cpm_device->start_lcd(); 72 73 l4_sleep(1); // 1ms == 1000us 74 } 75 76 77 78 // Disable the display. 79 80 void 81 Lcd_jz4740_device::disable() 82 { 83 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 84 85 disable_display(); 86 chip->disable(); 87 } 88 89 // Configure the peripheral and enable the display. 90 91 void 92 Lcd_jz4740_device::enable() 93 { 94 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 95 96 chip->disable(); 97 chip->config((struct Jz4740_lcd_descriptor *) desc_vaddr, 98 (struct Jz4740_lcd_descriptor *) desc_paddr, 99 fb_paddr); 100 101 // Initialise the clocks for the LCD controller. 102 103 set_timing(); 104 enable_display(); 105 106 chip->enable(); 107 } 108 109 // Set up memory addresses for the peripheral, initialising the framebuffer and 110 // descriptor members. 111 112 int 113 Lcd_jz4740_device::_setup_memory() 114 { 115 // Framebuffer and descriptor sizes. 116 117 unsigned long fb_size, desc_size; 118 119 // Size of physically contiguous regions to be allocated. 120 121 l4_size_t fb_size_out, desc_size_out; 122 123 // Memory allocation capability. 124 125 L4::Cap<L4Re::Dataspace> descmem; 126 127 // Test for existing setup. 128 129 if (fb_vaddr) 130 return 0; 131 132 // Obtain capabilities for allocating memory. 133 134 _fbmem = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>(); 135 if (!_fbmem.is_valid()) return 1; 136 137 descmem = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>(); 138 if (!descmem.is_valid()) return 1; 139 140 // Obtain the memory requirements. 141 142 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 143 144 fb_size = chip->get_screen_size(); 145 desc_size = chip->get_descriptors_size(); 146 147 // Allocate memory for the framebuffer at 2**6 == 64 byte == 16 word alignment, 148 // also for the descriptors. 149 150 const l4_size_t alloc_flags = L4Re::Mem_alloc::Continuous | L4Re::Mem_alloc::Pinned; 151 152 if (L4Re::Env::env()->mem_alloc()->alloc(fb_size, _fbmem, alloc_flags, 6) || 153 L4Re::Env::env()->mem_alloc()->alloc(desc_size, descmem, alloc_flags, 6)) 154 return 1; 155 156 // Map the allocated memory, obtaining virtual addresses. 157 158 const l4_size_t attach_flags = L4Re::Rm::Search_addr | L4Re::Rm::Eager_map; 159 160 fb_vaddr = 0; 161 desc_vaddr = 0; 162 163 if (L4Re::Env::env()->rm()->attach(&fb_vaddr, fb_size, attach_flags, _fbmem, 0) || 164 L4Re::Env::env()->rm()->attach(&desc_vaddr, desc_size, attach_flags, descmem, 0)) 165 return 1; 166 167 // Obtain physical addresses for the framebuffer and descriptors. 168 169 fb_paddr = 0; 170 desc_paddr = 0; 171 172 fb_size_out = fb_size; 173 desc_size_out = desc_size; 174 175 if (_fbmem->phys(0, fb_paddr, fb_size_out) || 176 descmem->phys(0, desc_paddr, desc_size_out)) 177 return 1; 178 179 // Test the mapped region sizes. 180 181 if ((fb_size_out != fb_size) || (desc_size_out != desc_size)) 182 return 1; 183 184 return 0; 185 } 186 187 // Populate a view information structure. 188 189 void 190 Lcd_jz4740_device::get_view_info(l4re_video_view_info_t *view_info) 191 { 192 // Populate the L4Re framebuffer description with details from the 193 // somewhat similar panel description. 194 195 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 196 struct Jz4740_lcd_panel *panel = chip->get_panel(); 197 198 view_info->width = panel->width; 199 view_info->height = panel->height; 200 view_info->pixel_info.bytes_per_pixel = (panel->bpp + 7) / 8; 201 view_info->bytes_per_line = chip->get_line_size(); 202 203 switch (panel->bpp) 204 { 205 case 32: 206 view_info->pixel_info.r.shift = 16; view_info->pixel_info.r.size = 8; 207 view_info->pixel_info.g.shift = 8; view_info->pixel_info.g.size = 8; 208 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 8; 209 break; 210 211 case 8: 212 view_info->pixel_info.r.shift = 5; view_info->pixel_info.r.size = 3; 213 view_info->pixel_info.g.shift = 2; view_info->pixel_info.g.size = 3; 214 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 2; 215 break; 216 217 case 4: 218 view_info->pixel_info.r.shift = 3; view_info->pixel_info.r.size = 1; 219 view_info->pixel_info.g.shift = 1; view_info->pixel_info.g.size = 2; 220 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 1; 221 break; 222 223 case 16: 224 default: 225 view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5; 226 view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6; 227 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5; 228 break; 229 } 230 231 view_info->pixel_info.a.shift = 0; 232 view_info->pixel_info.a.size = 0; 233 } 234 235 // Access to peripheral memory. 236 237 static int setup_memory() 238 { 239 if (get_memory("jz4740-lcd", &lcd_virt_base, &lcd_virt_base_end)) 240 return 1; 241 242 // Obtain access to the CPM, display and panel devices. 243 244 cpm_device = L4Re::Env::env()->get_cap<Cpm_device_interface>("cpm"); 245 if (!cpm_device.is_valid()) return 1; 246 247 display_device = L4Re::Env::env()->get_cap<Display_device_interface>("display"); 248 if (!display_device.is_valid()) return 1; 249 250 panel_device = L4Re::Env::env()->get_cap<Panel_device_interface>("panel"); 251 if (!panel_device.is_valid()) return 1; 252 253 // Populate the panel. 254 255 if (panel_device->get_panel((uint8_t *) &panel, sizeof(panel))) 256 return 1; 257 258 // Initialise the LCD abstraction. 259 260 lcd_chip = new Lcd_jz4740_chip(lcd_virt_base, &panel); 261 262 return 0; 263 } 264 265 // Device initialisation. 266 267 Lcd_jz4740_device *Lcd_jz4740_device::device = 0; 268 269 Lcd_device *Lcd_device::get_device() 270 { 271 if (Lcd_jz4740_device::device) return Lcd_jz4740_device::device; 272 273 if (setup_memory()) return 0; 274 275 // Initialise the common device. 276 277 Lcd_jz4740_device::device = new Lcd_jz4740_device(lcd_chip, display_device); 278 279 return Lcd_jz4740_device::device; 280 }