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