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 long err = get_dma_region(fb_size, 8, &_fb_region); 144 145 if (err) 146 return 1; 147 148 err = get_dma_region(desc_size, 6, &_desc_region); 149 150 if (err) 151 return 1; 152 153 return 0; 154 } 155 156 // Populate a view information structure. 157 158 void Lcd_jz4740_device::get_view_info(l4re_video_view_info_t *view_info) 159 { 160 // Populate the L4Re framebuffer description with details from the 161 // somewhat similar panel description. 162 163 Lcd_jz4740_chip *chip = static_cast<Lcd_jz4740_chip *>(_chip); 164 struct Jz4740_lcd_panel *panel = chip->get_panel(); 165 166 view_info->width = panel->width; 167 view_info->height = panel->height; 168 view_info->pixel_info.bytes_per_pixel = (panel->bpp + 7) / 8; 169 view_info->bytes_per_line = chip->get_line_size(); 170 171 switch (panel->bpp) 172 { 173 // NOTE: 24bpp here is actually RGBA or RGB padded. 174 175 case 24: 176 view_info->pixel_info.bytes_per_pixel = 4; 177 view_info->pixel_info.r.shift = 16; view_info->pixel_info.r.size = 8; 178 view_info->pixel_info.g.shift = 8; view_info->pixel_info.g.size = 8; 179 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 8; 180 break; 181 182 case 32: 183 view_info->pixel_info.r.shift = 16; view_info->pixel_info.r.size = 8; 184 view_info->pixel_info.g.shift = 8; view_info->pixel_info.g.size = 8; 185 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 8; 186 break; 187 188 case 8: 189 view_info->pixel_info.r.shift = 5; view_info->pixel_info.r.size = 3; 190 view_info->pixel_info.g.shift = 2; view_info->pixel_info.g.size = 3; 191 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 2; 192 break; 193 194 case 4: 195 view_info->pixel_info.r.shift = 3; view_info->pixel_info.r.size = 1; 196 view_info->pixel_info.g.shift = 1; view_info->pixel_info.g.size = 2; 197 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 1; 198 break; 199 200 case 16: 201 default: 202 view_info->pixel_info.r.shift = 11; view_info->pixel_info.r.size = 5; 203 view_info->pixel_info.g.shift = 5; view_info->pixel_info.g.size = 6; 204 view_info->pixel_info.b.shift = 0; view_info->pixel_info.b.size = 5; 205 break; 206 } 207 208 view_info->pixel_info.a.shift = 0; 209 view_info->pixel_info.a.size = 0; 210 } 211 212 // Access to peripheral memory. 213 214 static int setup_memory() 215 { 216 bool is_jz4780 = false; 217 218 if (get_memory("jz4740-lcd", &lcd_virt_base, &lcd_virt_base_end)) 219 { 220 if (get_memory("jz4780-lcd", &lcd_virt_base, &lcd_virt_base_end)) 221 return 1; 222 else 223 is_jz4780 = true; 224 } 225 226 // Obtain access to the CPM and display devices. 227 228 l4_cap_idx_t cpm = l4re_env_get_cap("cpm"); 229 230 if (!l4_is_valid_cap(cpm)) 231 return 1; 232 233 l4_cap_idx_t display = l4re_env_get_cap("display"); 234 235 if (!l4_is_valid_cap(display)) 236 return 1; 237 238 static client_Activation display_obj(display); 239 display_device = &display_obj; 240 241 static client_CPM cpm_obj(cpm); 242 cpm_device = &cpm_obj; 243 244 // Load the panel data from the configured library. 245 246 struct Jz4740_lcd_panel *panel = (struct Jz4740_lcd_panel *) load_panel(); 247 248 // Initialise the LCD abstraction. 249 250 if (is_jz4780) 251 lcd_chip = new Lcd_jz4780_chip(lcd_virt_base, panel); 252 else 253 lcd_chip = new Lcd_jz4740_chip(lcd_virt_base, panel); 254 255 return 0; 256 } 257 258 // Device initialisation. 259 260 Lcd_jz4740_device *Lcd_jz4740_device::device = 0; 261 262 Lcd_device *Lcd_device::get_device() 263 { 264 if (Lcd_jz4740_device::device) 265 return Lcd_jz4740_device::device; 266 267 if (setup_memory()) 268 return 0; 269 270 // Initialise the common device. 271 272 Lcd_jz4740_device::device = new Lcd_jz4740_device(lcd_chip, display_device); 273 274 return Lcd_jz4740_device::device; 275 }