1 /* 2 * Handle reading the EDID information, validating it, and parsing it into 3 * a struct edid_info. EDID reads are done using the Block Device Protocol 4 * as it's already supported by the cat24c256 driver and there is no need 5 * to add yet another message format/type. 6 */ 7 8 #include <minix/fb.h> 9 #include <minix/chardriver.h> 10 #include <minix/drivers.h> 11 #include <minix/ds.h> 12 #include <minix/rs.h> 13 #include <minix/log.h> 14 #include <minix/sysutil.h> 15 #include <minix/type.h> 16 #include <minix/vm.h> 17 #include <sys/ioc_fb.h> 18 #include <assert.h> 19 #include <sys/ioctl.h> 20 #include <sys/mman.h> 21 #include <errno.h> 22 #include <string.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <stdint.h> 26 #include <dev/videomode/videomode.h> 27 #include <dev/videomode/edidvar.h> 28 #include <dev/videomode/edidreg.h> 29 30 #include "fb_edid.h" 31 #include "fb.h" 32 33 static int do_read(endpoint_t endpt, uint8_t *buf, size_t bufsize); 34 35 /* logging - use with log_warn(), log_info(), log_debug(), log_trace() */ 36 static struct log log = { 37 .name = "edid", 38 .log_level = LEVEL_INFO, 39 .log_func = default_log 40 }; 41 42 /* 43 * Labels corresponding to drivers which provide EDID. 44 */ 45 static char edid_providers[FB_DEV_NR][RS_MAX_LABEL_LEN+1]; 46 47 /* 48 * Populate edid_providers from command line arguments. The minix-service 49 * command should get EDID providers like this: "-args edid.0=tda19988.1.3470" 50 * where 0 is the minor number of the frame buffer, tda19988 is the device 51 * driver, 1 is the i2c bus and 3470 is the slave address (the TDA19988 has 2 52 * slave addresses 0x34 and 0x70). 53 */ 54 int 55 fb_edid_args_parse(void) 56 { 57 int i; 58 int r; 59 char key[32]; 60 61 for (i = 0; i < FB_DEV_NR; i++) { 62 63 memset(key, '\0', 32); 64 snprintf(key, 32, "edid.%d", i); 65 66 memset(edid_providers[i], '\0', RS_MAX_LABEL_LEN); 67 r = env_get_param(key, edid_providers[i], RS_MAX_LABEL_LEN); 68 if (r == OK) { 69 log_debug(&log, "Found key:%s value:%s\n", key, edid_providers[i]); 70 } else { 71 /* not an error, user is allowed to omit EDID 72 * providers in order to skip EDID reading and use 73 * the default settings. 74 */ 75 log_debug(&log, "Couldn't find key:%s\n", key); 76 } 77 } 78 79 return OK; 80 } 81 82 /* 83 * Send a read request to the block driver at endpoint endpt. 84 */ 85 static int 86 do_read(endpoint_t driver_endpt, uint8_t *buf, size_t bufsize) 87 { 88 int r; 89 message m; 90 cp_grant_id_t grant_nr; 91 92 /* Open Device - required for drivers using libblockdriver */ 93 memset(&m, '\0', sizeof(message)); 94 m.m_type = BDEV_OPEN; 95 m.m_lbdev_lblockdriver_msg.access = BDEV_R_BIT; 96 m.m_lbdev_lblockdriver_msg.id = 0; 97 m.m_lbdev_lblockdriver_msg.minor = 0; 98 99 r = ipc_sendrec(driver_endpt, &m); 100 if (r != OK) { 101 log_debug(&log, "ipc_sendrec(BDEV_OPEN) failed (r=%d)\n", r); 102 return r; 103 } 104 105 grant_nr = cpf_grant_direct(driver_endpt, (vir_bytes) buf, 106 bufsize, CPF_READ | CPF_WRITE); 107 108 /* Perform the read */ 109 memset(&m, '\0', sizeof(message)); 110 m.m_type = BDEV_READ; 111 m.m_lbdev_lblockdriver_msg.minor = 0; 112 m.m_lbdev_lblockdriver_msg.count = bufsize; 113 m.m_lbdev_lblockdriver_msg.grant = grant_nr; 114 m.m_lbdev_lblockdriver_msg.flags = BDEV_NOPAGE; /* the EEPROMs used for EDID are pageless */ 115 m.m_lbdev_lblockdriver_msg.id = 0; 116 m.m_lbdev_lblockdriver_msg.pos = 0; 117 118 r = ipc_sendrec(driver_endpt, &m); 119 cpf_revoke(grant_nr); 120 if (r != OK) { 121 log_debug(&log, "ipc_sendrec(BDEV_READ) failed (r=%d)\n", r); 122 /* Clean-up: try to close the device */ 123 memset(&m, '\0', sizeof(message)); 124 m.m_type = BDEV_CLOSE; 125 m.m_lbdev_lblockdriver_msg.minor = 0; 126 m.m_lbdev_lblockdriver_msg.id = 0; 127 ipc_sendrec(driver_endpt, &m); 128 return r; 129 } 130 131 /* Close the device */ 132 memset(&m, '\0', sizeof(message)); 133 m.m_type = BDEV_CLOSE; 134 m.m_lbdev_lblockdriver_msg.minor = 0; 135 m.m_lbdev_lblockdriver_msg.id = 0; 136 r = ipc_sendrec(driver_endpt, &m); 137 if (r != OK) { 138 log_debug(&log, "ipc_sendrec(BDEV_CLOSE) failed (r=%d)\n", r); 139 return r; 140 } 141 142 return bufsize; 143 } 144 145 int 146 fb_edid_read(int minor, struct edid_info *info) 147 { 148 149 int r; 150 uint8_t buffer[128]; 151 endpoint_t endpt; 152 153 if (info == NULL || minor < 0 || minor >= FB_DEV_NR || 154 edid_providers[minor][0] == '\0') { 155 return EINVAL; 156 } 157 158 log_debug(&log, "Contacting %s to get EDID.\n", edid_providers[minor]); 159 160 /* Look up the endpoint that corresponds to the label */ 161 endpt = 0; 162 r = ds_retrieve_label_endpt(edid_providers[minor], &endpt); 163 if (r != 0 || endpt == 0) { 164 log_warn(&log, "Couldn't find endpoint for label '%s'\n", edid_providers[minor]); 165 return r; 166 } 167 168 /* Perform the request and put the resulting EDID into the buffer. */ 169 memset(buffer, 0x00, 128); 170 r = do_read(endpt, buffer, 128); 171 if (r < 0) { 172 log_debug(&log, "Failed to read EDID\n"); 173 return r; 174 } 175 176 /* parse and validate EDID */ 177 r = edid_parse(buffer, info); 178 if (r != 0) { 179 log_warn(&log, "Invalid EDID data in buffer.\n"); 180 return r; 181 } 182 183 log_debug(&log, "EDID Retrieved and Parsed OK\n"); 184 185 return OK; 186 } 187 188