1 /* 2 * Common LCD device support for the JZ4740 and related SoCs. 3 * 4 * Copyright (C) 2018, 2020, 2021, 2023 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/dma.h> 23 #include <l4/devices/lcd-jz4740.h> 24 #include <l4/devices/lcd-jz4780.h> 25 #include <l4/devices/panel-loader.h> 26 #include <l4/devices/memory.h> 27 #include "lcd-jz4740-device.h" 28 29 #include <l4/re/c/dataspace.h> 30 #include <l4/re/env.h> 31 #include <l4/sys/types.h> 32 33 #include <ipc/cap_alloc.h> 34 #include <ipc/mem_ipc.h> 35 36 #include <stdint.h> 37 38 #include "activation_client.h" 39 #include "cpm_client.h" 40 41 42 43 // Virtual addresses for the LCD register block. 44 45 static l4_addr_t lcd_virt_base = 0, lcd_virt_base_end = 0; 46 47 // LCD, CPM and display device abstractions. 48 49 static Lcd_jz4740_chip *lcd_chip = 0; 50 51 static Activation *display_device; 52 static CPM *cpm_device; 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_clock(Clock_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_frequency(Clock_lcd, pclk * 3); 70 cpm_device->set_frequency(Clock_lcd_pixel, pclk); 71 cpm_device->start_clock(Clock_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 // Initialise the clocks for the LCD controller. Without this, the controller 97 // cannot be operated. 98 99 set_timing(); 100 101 // Configure the controller. 102 103 chip->disable(); 104 chip->config((struct Jz4740_lcd_descriptor *) desc_vaddr, 105 (struct Jz4740_lcd_descriptor *) desc_paddr, 106 fb_paddr); 107 108 // Activate the display channel. 109 110 enable_display(); 111 112 // Activate the controller output. 113 114 chip->enable(); 115 } 116 117 // Set up memory addresses for the peripheral, initialising the framebuffer and 118 // descriptor members. 119 120 int 121 Lcd_jz4740_device::_setup_memory() 122 { 123 // Test for existing setup. 124 125 if (fb_vaddr) 126 return 0; 127 128 // Obtain the memory requirements. 129 130 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 131 132 // Framebuffer and descriptor sizes. 133 134 unsigned long fb_size = chip->get_screen_size(), 135 desc_size = chip->get_descriptors_size(); 136 137 // Allocate memory for the framebuffer at 2**6 == 64 byte == 16 word alignment, 138 // also for the descriptors. 139 // NOTE: A 64 word burst mode is available on the JZ4780 that would 140 // NOTE: necessitate 64 word alignment for the framebuffer. 141 142 long err = get_dma_region(fb_size, 8, &fb_vaddr, &fb_paddr, &_fbmem); 143 144 if (err) 145 return 1; 146 147 l4_cap_idx_t descmem; 148 149 err = get_dma_region(desc_size, 6, &desc_vaddr, &desc_paddr, &descmem); 150 151 if (err) 152 return 1; 153 154 return 0; 155 } 156 157 // Populate a view information structure. 158 159 void Lcd_jz4740_device::get_view_info(l4re_video_view_info_t *view_info) 160 { 161 // Populate the L4Re framebuffer description with details from the 162 // somewhat similar panel description. 163 164 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 165 struct Jz4740_lcd_panel *panel = chip->get_panel(); 166 167 view_info->width = panel->width; 168 view_info->height = panel->height; 169 view_info->pixel_info.bytes_per_pixel = (panel->bpp + 7) / 8; 170 view_info->bytes_per_line = chip->get_line_size(); 171 172 switch (panel->bpp) 173 { 174 // NOTE: 24bpp here is actually RGBA or RGB padded. 175 176 case 24: 177 view_info->pixel_info.bytes_per_pixel = 4; 178 view_info->pixel_info.r.shift = 16; view_info->pixel_info.r.size = 8; 179 view_info->pixel_info.g.shift = 8; view_info->pixel_info.g.size = 8; 180 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 8; 181 break; 182 183 case 32: 184 view_info->pixel_info.r.shift = 16; view_info->pixel_info.r.size = 8; 185 view_info->pixel_info.g.shift = 8; view_info->pixel_info.g.size = 8; 186 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 8; 187 break; 188 189 case 8: 190 view_info->pixel_info.r.shift = 5; view_info->pixel_info.r.size = 3; 191 view_info->pixel_info.g.shift = 2; view_info->pixel_info.g.size = 3; 192 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 2; 193 break; 194 195 case 4: 196 view_info->pixel_info.r.shift = 3; view_info->pixel_info.r.size = 1; 197 view_info->pixel_info.g.shift = 1; view_info->pixel_info.g.size = 2; 198 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 1; 199 break; 200 201 case 16: 202 default: 203 view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5; 204 view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6; 205 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5; 206 break; 207 } 208 209 view_info->pixel_info.a.shift = 0; 210 view_info->pixel_info.a.size = 0; 211 } 212 213 // Access to peripheral memory. 214 215 static int setup_memory() 216 { 217 bool is_jz4780 = false; 218 219 if (get_memory("jz4740-lcd", &lcd_virt_base, &lcd_virt_base_end)) 220 { 221 if (get_memory("jz4780-lcd", &lcd_virt_base, &lcd_virt_base_end)) 222 return 1; 223 else 224 is_jz4780 = true; 225 } 226 227 // Obtain access to the CPM and display devices. 228 229 l4_cap_idx_t cpm = l4re_env_get_cap("cpm"); 230 231 if (!l4_is_valid_cap(cpm)) 232 return 1; 233 234 l4_cap_idx_t display = l4re_env_get_cap("display"); 235 236 if (!l4_is_valid_cap(display)) 237 return 1; 238 239 static client_Activation display_obj(display); 240 display_device = &display_obj; 241 242 static client_CPM cpm_obj(cpm); 243 cpm_device = &cpm_obj; 244 245 // Load the panel data from the configured library. 246 247 struct Jz4740_lcd_panel *panel = (struct Jz4740_lcd_panel *) load_panel(); 248 249 // Initialise the LCD abstraction. 250 251 if (is_jz4780) 252 lcd_chip = new Lcd_jz4780_chip(lcd_virt_base, panel); 253 else 254 lcd_chip = new Lcd_jz4740_chip(lcd_virt_base, panel); 255 256 return 0; 257 } 258 259 // Device initialisation. 260 261 Lcd_jz4740_device *Lcd_jz4740_device::device = 0; 262 263 Lcd_device *Lcd_device::get_device() 264 { 265 if (Lcd_jz4740_device::device) 266 return Lcd_jz4740_device::device; 267 268 if (setup_memory()) 269 return 0; 270 271 // Initialise the common device. 272 273 Lcd_jz4740_device::device = new Lcd_jz4740_device(lcd_chip, display_device); 274 275 return Lcd_jz4740_device::device; 276 }