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