1 /* 2 * Common LCD device support for the JZ4740 and related SoCs. 3 * 4 * Copyright (C) 2018, 2020, 2021, 2023, 2024 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 71 // NOTE: Should support multiple pixel clocks. 72 73 cpm_device->set_frequency(Clock_lcd_pixel0, pclk); 74 cpm_device->start_clock(Clock_lcd); 75 76 l4_sleep(1); // 1ms == 1000us 77 } 78 79 80 81 // Disable the display. 82 83 void 84 Lcd_jz4740_device::disable() 85 { 86 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 87 88 disable_display(); 89 chip->disable(); 90 } 91 92 // Configure the peripheral and enable the display. 93 94 void 95 Lcd_jz4740_device::enable() 96 { 97 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 98 99 // Initialise the clocks for the LCD controller. Without this, the controller 100 // cannot be operated. 101 102 set_timing(); 103 104 // Configure the controller. 105 106 chip->disable(); 107 chip->config((struct Jz4740_lcd_descriptor *) _desc_region.vaddr, 108 (struct Jz4740_lcd_descriptor *) _desc_region.paddr, 109 _fb_region.paddr); 110 111 // Activate the display channel. 112 113 enable_display(); 114 115 // Activate the controller output. 116 117 chip->enable(); 118 } 119 120 // Set up memory addresses for the peripheral, initialising the framebuffer and 121 // descriptor members. 122 123 int 124 Lcd_jz4740_device::_setup_memory() 125 { 126 // Test for existing setup. 127 128 if (_fb_region.vaddr) 129 return 0; 130 131 // Obtain the memory requirements. 132 133 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 134 135 // Framebuffer and descriptor sizes. 136 137 unsigned long fb_size = chip->get_screen_size(), 138 desc_size = chip->get_descriptors_size(); 139 140 // Allocate memory for the framebuffer at 2**8 == 256 byte == 64 word 141 // alignment, with 2**6 == 64 byte == 16 word alignment for the descriptors. 142 143 struct dma_region region; 144 long err = get_dma_region(fb_size, 8, &_fb_region); 145 146 if (err) 147 return 1; 148 149 err = get_dma_region(desc_size, 6, &_desc_region); 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 }