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 #include <l4/devices/memory.h> 18 #include <l4/io/io.h> 19 #include <l4/libedid/edid.h> 20 #include <l4/re/env.h> 21 #include <l4/re/c/util/cap_alloc.h> 22 #include <l4/sys/factory.h> 23 #include <l4/sys/icu.h> 24 #include <l4/sys/ipc.h> 25 #include <l4/sys/irq.h> 26 #include <l4/sys/rcv_endpoint.h> 27 #include <l4/vbus/vbus.h> 28 #include <stdio.h> 29 #include <unistd.h> 30 #include <stdint.h> 31 #include <string.h> 32 33 34 35 enum { 36 DDCSCL = 24, /* via PORTF */ 37 DDCSDA = 25, /* via PORTF */ 38 }; 39 40 41 42 /* Device and resource discovery. */ 43 44 static long item_in_range(long start, long end, long index) 45 { 46 if (start < end) 47 return start + index; 48 else 49 return start - index; 50 } 51 52 static void show_timings(uint8_t *buf) 53 { 54 unsigned int width, height; 55 56 /* Attempt to decode EDID information. */ 57 58 libedid_prefered_resolution(buf, &width, &height); 59 printf("Preferred resolution: %d x %d\n", width, height); 60 61 libedid_dump_standard_timings(buf); 62 } 63 64 int main(void) 65 { 66 long err; 67 68 /* Buffer for EDID data. */ 69 70 uint8_t edid[128]; 71 unsigned int length = 0, i; 72 73 /* Version details. */ 74 75 uint8_t hdmi_major; 76 uint16_t hdmi_minor; 77 78 /* Peripheral memory. */ 79 80 l4_addr_t cpm_base = 0, cpm_base_end = 0; 81 l4_addr_t gpio_base = 0, gpio_base_end = 0; 82 l4_addr_t hdmi_base = 0, hdmi_base_end = 0; 83 l4_addr_t port_f, port_f_end; 84 85 /* Peripheral abstractions. */ 86 87 void *cpm; 88 void *gpio_port_f; 89 void *hdmi; 90 91 /* Access to IRQs. */ 92 93 l4_uint32_t hdmi_irq_start = 0, hdmi_irq_end = 0; 94 l4_cap_idx_t icucap, irqcap; 95 96 irqcap = l4re_util_cap_alloc(); 97 icucap = l4re_env_get_cap("icu"); 98 99 if (l4_is_invalid_cap(icucap)) 100 { 101 printf("No 'icu' capability available in the virtual bus.\n"); 102 return 1; 103 } 104 105 if (l4_is_invalid_cap(irqcap)) 106 { 107 printf("Capabilities not available for interrupts.\n"); 108 return 1; 109 } 110 111 /* Obtain resource details describing the interrupt for HDMI I2C. */ 112 113 printf("Access IRQ...\n"); 114 115 if (get_irq("jz4780-hdmi", &hdmi_irq_start, &hdmi_irq_end) < 0) 116 return 1; 117 118 printf("IRQ range at %d...%d.\n", hdmi_irq_start, hdmi_irq_end); 119 120 /* Create interrupt objects. */ 121 122 err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irqcap)); 123 124 if (err) 125 { 126 printf("Could not create IRQ object: %lx\n", err); 127 return 1; 128 } 129 130 /* Bind interrupt objects to IRQ numbers. Here, the first HDMI interrupt is 131 bound, this being the general HDMI interrupt. */ 132 133 err = l4_error(l4_icu_bind(icucap, 134 item_in_range(hdmi_irq_start, hdmi_irq_end, 0), 135 irqcap)); 136 137 if (err) 138 { 139 printf("Could not bind IRQ to the ICU: %ld\n", err); 140 return 1; 141 } 142 143 /* Attach ourselves to the interrupt handler. */ 144 145 err = l4_error(l4_rcv_ep_bind_thread(irqcap, l4re_env()->main_thread, 0)); 146 147 if (err) 148 { 149 printf("Could not attach to IRQs: %ld\n", err); 150 return 1; 151 } 152 153 /* Obtain resource details describing I/O memory. */ 154 155 printf("Access CPM...\n"); 156 157 if (get_memory("jz4780-cpm", &cpm_base, &cpm_base_end) < 0) 158 return 1; 159 160 printf("CPM at 0x%lx...0x%lx.\n", cpm_base, cpm_base_end); 161 162 printf("Access GPIO...\n"); 163 164 if (get_memory("jz4780-gpio", &gpio_base, &gpio_base_end) < 0) 165 return 1; 166 167 printf("GPIO at 0x%lx...0x%lx.\n", gpio_base, gpio_base_end); 168 169 printf("Access HDMI...\n"); 170 171 if (get_memory("jz4780-hdmi", &hdmi_base, &hdmi_base_end) < 0) 172 return 1; 173 174 printf("HDMI at 0x%lx...0x%lx.\n", hdmi_base, hdmi_base_end); 175 176 /* Obtain CPM object. */ 177 178 cpm = jz4780_cpm_init(cpm_base); 179 180 printf("VPLL frequency: %d\n", jz4780_cpm_get_vpll_frequency(cpm)); 181 printf("HDMI divider: %d\n", jz4780_cpm_get_hdmi_divider(cpm)); 182 printf("HDMI frequency: %d\n", jz4780_cpm_get_hdmi_frequency(cpm)); 183 184 jz4780_cpm_stop_hdmi(cpm); 185 jz4780_cpm_set_hdmi_frequency(cpm, 27000000); 186 187 printf("HDMI divider: %d\n", jz4780_cpm_get_hdmi_divider(cpm)); 188 printf("HDMI frequency: %d\n", jz4780_cpm_get_hdmi_frequency(cpm)); 189 190 jz4780_cpm_start_hdmi(cpm); 191 192 /* Configure pins. */ 193 194 port_f = gpio_base + 0x500; 195 port_f_end = port_f + 0x100; 196 197 printf("PORTF at 0x%lx...0x%lx.\n", port_f, port_f_end); 198 199 gpio_port_f = jz4780_gpio_init(port_f, port_f_end, 32, 0x7fa7f00f, 0x00580ff0); 200 201 printf("Set up GPIO pins...\n"); 202 203 jz4780_gpio_config_pad(gpio_port_f, DDCSCL, Function_alt, 0); 204 jz4780_gpio_config_pad(gpio_port_f, DDCSDA, Function_alt, 0); 205 206 /* Obtain HDMI reference. */ 207 208 printf("Set up HDMI...\n"); 209 210 hdmi = jz4780_hdmi_init(hdmi_base, hdmi_base_end, irqcap); 211 212 printf("Read version...\n"); 213 214 jz4780_hdmi_get_version(hdmi, &hdmi_major, &hdmi_minor); 215 216 printf("HDMI version is %x.%03x\n", hdmi_major, hdmi_minor); 217 218 printf("Read EDID...\n"); 219 220 jz4780_hdmi_i2c_set_address(hdmi, 0x50); 221 length = jz4780_hdmi_i2c_read(hdmi, edid, 128); 222 223 if (length) 224 { 225 for (i = 0; i < length; i++) 226 printf("%02x%c", edid[i], ((i % 16) != 15) ? ' ' : '\n'); 227 } 228 229 show_timings(edid); 230 231 /* Detach from the interrupt. */ 232 233 err = l4_error(l4_irq_detach(irqcap)); 234 235 if (err) 236 printf("Error detaching from IRQ: %ld\n", err); 237 238 printf("Done.\n"); 239 240 return 0; 241 }