/* * Handle reading the EDID information, validating it, and parsing it into * a struct edid_info. EDID reads are done using the Block Device Protocol * as it's already supported by the cat24c256 driver and there is no need * to add yet another message format/type. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fb_edid.h" #include "fb.h" static int do_read(endpoint_t endpt, uint8_t *buf, size_t bufsize); /* logging - use with log_warn(), log_info(), log_debug(), log_trace() */ static struct log log = { .name = "edid", .log_level = LEVEL_INFO, .log_func = default_log }; /* * Labels corresponding to drivers which provide EDID. */ static char edid_providers[FB_DEV_NR][RS_MAX_LABEL_LEN+1]; /* * Populate edid_providers from command line arguments. The service command * should get EDID providers like this: "-args edid.0=tda19988.1.3470" where * 0 is the minor number of the frame buffer, tda19988 is the device driver, * 1 is the i2c bus and 3470 is the slave address (the TDA19988 has 2 slave * addresses 0x34 and 0x70). */ int fb_edid_args_parse(void) { int i; int r; char key[32]; for (i = 0; i < FB_DEV_NR; i++) { memset(key, '\0', 32); snprintf(key, 32, "edid.%d", i); memset(edid_providers[i], '\0', RS_MAX_LABEL_LEN); r = env_get_param(key, edid_providers[i], RS_MAX_LABEL_LEN); if (r == OK) { log_debug(&log, "Found key:%s value:%s\n", key, edid_providers[i]); } else { /* not an error, user is allowed to omit EDID * providers in order to skip EDID reading and use * the default settings. */ log_debug(&log, "Couldn't find key:%s\n", key); } } return OK; } /* * Send a read request to the block driver at endpoint endpt. */ static int do_read(endpoint_t driver_endpt, uint8_t *buf, size_t bufsize) { int r; message m; cp_grant_id_t grant_nr; /* Open Device - required for drivers using libblockdriver */ memset(&m, '\0', sizeof(message)); m.m_type = BDEV_OPEN; m.BDEV_ACCESS = BDEV_R_BIT; m.BDEV_ID = 0; m.BDEV_MINOR = 0; r = ipc_sendrec(driver_endpt, &m); if (r != OK) { log_debug(&log, "ipc_sendrec(BDEV_OPEN) failed (r=%d)\n", r); return r; } grant_nr = cpf_grant_direct(driver_endpt, (vir_bytes) buf, bufsize, CPF_READ | CPF_WRITE); /* Perform the read */ memset(&m, '\0', sizeof(message)); m.m_type = BDEV_READ; m.BDEV_MINOR = 0; m.BDEV_COUNT = bufsize; m.BDEV_GRANT = grant_nr; m.BDEV_FLAGS = BDEV_NOPAGE; /* the EEPROMs used for EDID are pageless */ m.BDEV_ID = 0; m.BDEV_POS = 0; r = ipc_sendrec(driver_endpt, &m); cpf_revoke(grant_nr); if (r != OK) { log_debug(&log, "ipc_sendrec(BDEV_READ) failed (r=%d)\n", r); /* Clean-up: try to close the device */ memset(&m, '\0', sizeof(message)); m.m_type = BDEV_CLOSE; m.BDEV_MINOR = 0; m.BDEV_ID = 0; ipc_sendrec(driver_endpt, &m); return r; } /* Close the device */ memset(&m, '\0', sizeof(message)); m.m_type = BDEV_CLOSE; m.BDEV_MINOR = 0; m.BDEV_ID = 0; r = ipc_sendrec(driver_endpt, &m); if (r != OK) { log_debug(&log, "ipc_sendrec(BDEV_CLOSE) failed (r=%d)\n", r); return r; } return bufsize; } int fb_edid_read(int minor, struct edid_info *info) { int r; uint8_t buffer[128]; endpoint_t endpt; if (info == NULL || minor < 0 || minor >= FB_DEV_NR || edid_providers[minor][0] == '\0') { return EINVAL; } log_debug(&log, "Contacting %s to get EDID.\n", edid_providers[minor]); /* Look up the endpoint that corresponds to the label */ endpt = 0; r = ds_retrieve_label_endpt(edid_providers[minor], &endpt); if (r != 0 || endpt == 0) { log_warn(&log, "Couldn't find endpoint for label '%s'\n", edid_providers[minor]); return r; } /* Perform the request and put the resulting EDID into the buffer. */ memset(buffer, 0x00, 128); r = do_read(endpt, buffer, 128); if (r < 0) { log_debug(&log, "Failed to read EDID\n"); return r; } /* parse and validate EDID */ r = edid_parse(buffer, info); if (r != 0) { log_warn(&log, "Invalid EDID data in buffer.\n"); return r; } log_debug(&log, "EDID Retrieved and Parsed OK\n"); return OK; }