1 /* 2 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de> 3 * economic rights: Technische Universit??t Dresden (Germany) 4 * Copyright (C) 2017, 2018, 2019, 2020 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This file is part of TUD:OS and distributed under the terms of the 7 * GNU General Public License 2. 8 * Please see the COPYING-GPL-2 file for details. 9 */ 10 /* 11 * Access the HDMI I2C peripheral on the MIPS Creator CI20 board. 12 */ 13 14 #include <l4/devices/cpm-jz4780.h> 15 #include <l4/devices/gpio-jz4780.h> 16 #include <l4/devices/hdmi-jz4780.h> 17 18 #include <l4/devices/lcd-jz4740.h> 19 #include <l4/devices/lcd-jz4740-config.h> 20 #include <l4/devices/lcd-jz4740-panel.h> 21 22 #include <l4/devices/memory.h> 23 #include <l4/io/io.h> 24 #include <l4/libedid/edid.h> 25 #include <l4/re/env.h> 26 #include <l4/re/c/util/cap_alloc.h> 27 #include <l4/sys/factory.h> 28 #include <l4/sys/icu.h> 29 #include <l4/sys/ipc.h> 30 #include <l4/sys/irq.h> 31 #include <l4/sys/rcv_endpoint.h> 32 #include <l4/vbus/vbus.h> 33 #include <stdio.h> 34 #include <unistd.h> 35 #include <stdint.h> 36 #include <string.h> 37 38 39 40 enum { 41 DDCSCL = 24, /* via PORTF */ 42 DDCSDA = 25, /* via PORTF */ 43 }; 44 45 46 47 /* Test panel. */ 48 49 static struct Jz4740_lcd_panel panel = { 50 .config = ( 51 Jz4740_lcd_mode_tft_generic 52 | Jz4740_lcd_pclock_negative 53 | Jz4740_lcd_hsync_positive 54 | Jz4740_lcd_vsync_positive 55 | Jz4740_lcd_de_positive), 56 57 .width = 1280, 58 .height = 1024, 59 .bpp = 32, 60 .frame_rate = 60, 61 .hsync = 112, 62 .vsync = 3, 63 .line_start = 248, // back porch (blanking - vsyn - offset) 64 .line_end = 48, // front porch (sync offset) 65 .frame_start = 36, // back porch (blanking - vsyn - offset) 66 .frame_end = 3, // front porch (sync offset) 67 }; 68 69 70 71 /* Device and resource discovery. */ 72 73 static long item_in_range(long start, long end, long index) 74 { 75 if (start < end) 76 return start + index; 77 else 78 return start - index; 79 } 80 81 static void show_timings(uint8_t *buf) 82 { 83 unsigned int width, height; 84 85 /* Attempt to decode EDID information. */ 86 87 libedid_prefered_resolution(buf, &width, &height); 88 printf("Preferred resolution: %d x %d\n", width, height); 89 90 libedid_dump_standard_timings(buf); 91 } 92 93 int main(void) 94 { 95 long err; 96 97 /* Buffer for EDID data. */ 98 99 uint8_t edid[128]; 100 unsigned int length = 0, i; 101 102 /* Version details. */ 103 104 uint8_t hdmi_major; 105 uint16_t hdmi_minor; 106 107 /* Peripheral memory. */ 108 109 l4_addr_t cpm_base = 0, cpm_base_end = 0; 110 l4_addr_t gpio_base = 0, gpio_base_end = 0; 111 l4_addr_t hdmi_base = 0, hdmi_base_end = 0; 112 l4_addr_t lcd_base = 0, lcd_base_end = 0; 113 l4_addr_t port_f, port_f_end; 114 115 /* Peripheral abstractions. */ 116 117 void *cpm; 118 void *gpio_port_f; 119 void *hdmi; 120 void *lcd; 121 122 /* Access to IRQs. */ 123 124 l4_uint32_t hdmi_irq_start = 0, hdmi_irq_end = 0; 125 l4_uint32_t lcd_irq_start = 0, lcd_irq_end = 0; 126 l4_cap_idx_t icucap, hdmi_irq, lcd_irq; 127 128 hdmi_irq = l4re_util_cap_alloc(); 129 lcd_irq = l4re_util_cap_alloc(); 130 icucap = l4re_env_get_cap("icu"); 131 132 if (l4_is_invalid_cap(icucap)) 133 { 134 printf("No 'icu' capability available in the virtual bus.\n"); 135 return 1; 136 } 137 138 if (l4_is_invalid_cap(hdmi_irq) || l4_is_invalid_cap(lcd_irq)) 139 { 140 printf("Capabilities not available for interrupts.\n"); 141 return 1; 142 } 143 144 /* Obtain resource details describing the interrupt for HDMI I2C. */ 145 146 printf("Access IRQ...\n"); 147 148 if (get_irq("jz4780-hdmi", &hdmi_irq_start, &hdmi_irq_end) < 0) 149 return 1; 150 151 printf("HDMI IRQ range at %d...%d.\n", hdmi_irq_start, hdmi_irq_end); 152 153 if (get_irq("jz4780-lcd", &lcd_irq_start, &lcd_irq_end) < 0) 154 return 1; 155 156 printf("LCD IRQ range at %d...%d.\n", lcd_irq_start, lcd_irq_end); 157 158 /* Create interrupt objects. */ 159 160 err = l4_error(l4_factory_create_irq(l4re_global_env->factory, hdmi_irq)) || 161 l4_error(l4_factory_create_irq(l4re_global_env->factory, lcd_irq)); 162 163 if (err) 164 { 165 printf("Could not create IRQ object: %lx\n", err); 166 return 1; 167 } 168 169 /* Bind interrupt objects to IRQ numbers. Here, the first HDMI interrupt is 170 bound, this being the general HDMI interrupt. */ 171 172 err = l4_error(l4_icu_bind(icucap, 173 item_in_range(hdmi_irq_start, hdmi_irq_end, 0), 174 hdmi_irq)) || 175 l4_error(l4_icu_bind(icucap, 176 item_in_range(lcd_irq_start, lcd_irq_end, 0), 177 lcd_irq)); 178 179 if (err) 180 { 181 printf("Could not bind IRQ to the ICU: %ld\n", err); 182 return 1; 183 } 184 185 /* Attach ourselves to the interrupt handler. */ 186 187 err = l4_error(l4_rcv_ep_bind_thread(hdmi_irq, l4re_env()->main_thread, 0)) || 188 l4_error(l4_rcv_ep_bind_thread(lcd_irq, l4re_env()->main_thread, 0)); 189 190 if (err) 191 { 192 printf("Could not attach to IRQs: %ld\n", err); 193 return 1; 194 } 195 196 /* Obtain resource details describing I/O memory. */ 197 198 printf("Access CPM...\n"); 199 200 if (get_memory("jz4780-cpm", &cpm_base, &cpm_base_end) < 0) 201 return 1; 202 203 printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end); 204 205 printf("Access GPIO...\n"); 206 207 if (get_memory("jz4780-gpio", &gpio_base, &gpio_base_end) < 0) 208 return 1; 209 210 printf("GPIO at 0x%lx...0x%lx.\n", gpio_base, gpio_base_end); 211 212 printf("Access HDMI...\n"); 213 214 if (get_memory("jz4780-hdmi", &hdmi_base, &hdmi_base_end) < 0) 215 return 1; 216 217 printf("HDMI at 0x%lx...0x%lx.\n", hdmi_base, hdmi_base_end); 218 219 printf("Access LCD...\n"); 220 221 if (get_memory("jz4780-lcd", &lcd_base, &lcd_base_end) < 0) 222 return 1; 223 224 printf("LCD at 0x%lx...0x%lx.\n", lcd_base, lcd_base_end); 225 226 /* Obtain CPM object. */ 227 228 cpm = jz4780_cpm_init(cpm_base); 229 230 printf("VPLL frequency: %d\n", jz4780_cpm_get_vpll_frequency(cpm)); 231 printf("HDMI divider: %d\n", jz4780_cpm_get_hdmi_divider(cpm)); 232 printf("HDMI frequency: %d\n", jz4780_cpm_get_hdmi_frequency(cpm)); 233 234 jz4780_cpm_stop_hdmi(cpm); 235 jz4780_cpm_set_hdmi_frequency(cpm, 27000000); 236 237 printf("HDMI divider: %d\n", jz4780_cpm_get_hdmi_divider(cpm)); 238 printf("HDMI frequency: %d\n", jz4780_cpm_get_hdmi_frequency(cpm)); 239 240 jz4780_cpm_start_hdmi(cpm); 241 242 /* Configure pins. */ 243 244 port_f = gpio_base + 0x500; 245 port_f_end = port_f + 0x100; 246 247 printf("PORTF at 0x%lx...0x%lx.\n", port_f, port_f_end); 248 249 gpio_port_f = jz4780_gpio_init(port_f, port_f_end, 32, 0x7fa7f00f, 0x00580ff0); 250 251 printf("Set up GPIO pins...\n"); 252 253 jz4780_gpio_config_pad(gpio_port_f, DDCSCL, Function_alt, 0); 254 jz4780_gpio_config_pad(gpio_port_f, DDCSDA, Function_alt, 0); 255 256 /* Obtain HDMI reference. */ 257 258 printf("Set up HDMI...\n"); 259 260 hdmi = jz4780_hdmi_init(hdmi_base, hdmi_base_end, hdmi_irq); 261 262 printf("Read version...\n"); 263 264 jz4780_hdmi_get_version(hdmi, &hdmi_major, &hdmi_minor); 265 266 printf("HDMI version is %x.%03x\n", hdmi_major, hdmi_minor); 267 268 printf("Read EDID...\n"); 269 270 jz4780_hdmi_i2c_set_address(hdmi, 0x50); 271 length = jz4780_hdmi_i2c_read(hdmi, edid, 128); 272 273 if (length) 274 { 275 for (i = 0; i < length; i++) 276 printf("%02x%c", edid[i], ((i % 16) != 15) ? ' ' : '\n'); 277 } 278 279 show_timings(edid); 280 281 /* Obtain LCD reference. */ 282 283 printf("Set up LCD...\n"); 284 285 lcd = jz4740_lcd_init(lcd_base, &panel); 286 287 /* Test initialisation with a frequency appropriate for the test panel. */ 288 289 printf("LCD divider: %d\n", jz4780_cpm_get_lcd_pixel_divider(cpm)); 290 printf("LCD frequency: %d\n", jz4780_cpm_get_lcd_pixel_frequency(cpm)); 291 printf("Desired frequency: %d\n", jz4740_lcd_get_pixel_clock(lcd)); 292 293 jz4780_cpm_stop_lcd(cpm); 294 295 jz4780_cpm_set_lcd_frequencies(cpm, jz4740_lcd_get_pixel_clock(lcd), 3); 296 297 printf("LCD divider: %d\n", jz4780_cpm_get_lcd_pixel_divider(cpm)); 298 printf("LCD frequency: %d\n", jz4780_cpm_get_lcd_pixel_frequency(cpm)); 299 300 jz4780_cpm_start_lcd(cpm); 301 302 /* Detach from the interrupts. */ 303 304 err = l4_error(l4_irq_detach(hdmi_irq)) || 305 l4_error(l4_irq_detach(lcd_irq)); 306 307 if (err) 308 printf("Error detaching from IRQ: %ld\n", err); 309 310 printf("Done.\n"); 311 312 return 0; 313 }