xref: /minix3/minix/drivers/video/fb/fb_edid.c (revision c58da9fbc35f86051ff0a75e6dd91e937d83cfff)
1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc  * Handle reading the EDID information, validating it, and parsing it into
3433d6423SLionel Sambuc  * a struct edid_info. EDID reads are done using the Block Device Protocol
4433d6423SLionel Sambuc  * as it's already supported by the cat24c256 driver and there is no need
5433d6423SLionel Sambuc  * to add yet another message format/type.
6433d6423SLionel Sambuc  */
7433d6423SLionel Sambuc 
8433d6423SLionel Sambuc #include <minix/fb.h>
9433d6423SLionel Sambuc #include <minix/chardriver.h>
10433d6423SLionel Sambuc #include <minix/drivers.h>
11433d6423SLionel Sambuc #include <minix/ds.h>
12433d6423SLionel Sambuc #include <minix/rs.h>
13433d6423SLionel Sambuc #include <minix/log.h>
14433d6423SLionel Sambuc #include <minix/sysutil.h>
15433d6423SLionel Sambuc #include <minix/type.h>
16433d6423SLionel Sambuc #include <minix/vm.h>
17433d6423SLionel Sambuc #include <sys/ioc_fb.h>
18433d6423SLionel Sambuc #include <assert.h>
19433d6423SLionel Sambuc #include <sys/ioctl.h>
20433d6423SLionel Sambuc #include <sys/mman.h>
21433d6423SLionel Sambuc #include <errno.h>
22433d6423SLionel Sambuc #include <string.h>
23433d6423SLionel Sambuc #include <stdio.h>
24433d6423SLionel Sambuc #include <stdlib.h>
25433d6423SLionel Sambuc #include <stdint.h>
26433d6423SLionel Sambuc #include <dev/videomode/videomode.h>
27433d6423SLionel Sambuc #include <dev/videomode/edidvar.h>
28433d6423SLionel Sambuc #include <dev/videomode/edidreg.h>
29433d6423SLionel Sambuc 
30433d6423SLionel Sambuc #include "fb_edid.h"
31433d6423SLionel Sambuc #include "fb.h"
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc static int do_read(endpoint_t endpt, uint8_t *buf, size_t bufsize);
34433d6423SLionel Sambuc 
35433d6423SLionel Sambuc /* logging - use with log_warn(), log_info(), log_debug(), log_trace() */
36433d6423SLionel Sambuc static struct log log = {
37433d6423SLionel Sambuc 	.name = "edid",
38433d6423SLionel Sambuc 	.log_level = LEVEL_INFO,
39433d6423SLionel Sambuc 	.log_func = default_log
40433d6423SLionel Sambuc };
41433d6423SLionel Sambuc 
42433d6423SLionel Sambuc /*
43433d6423SLionel Sambuc  * Labels corresponding to drivers which provide EDID.
44433d6423SLionel Sambuc  */
45433d6423SLionel Sambuc static char edid_providers[FB_DEV_NR][RS_MAX_LABEL_LEN+1];
46433d6423SLionel Sambuc 
47433d6423SLionel Sambuc /*
48*c58da9fbSDavid van Moolenbroek  * Populate edid_providers from command line arguments. The minix-service
49*c58da9fbSDavid van Moolenbroek  * command should get EDID providers like this: "-args edid.0=tda19988.1.3470"
50*c58da9fbSDavid van Moolenbroek  * where 0 is the minor number of the frame buffer, tda19988 is the device
51*c58da9fbSDavid van Moolenbroek  * driver, 1 is the i2c bus and 3470 is the slave address (the TDA19988 has 2
52*c58da9fbSDavid van Moolenbroek  * slave addresses 0x34 and 0x70).
53433d6423SLionel Sambuc  */
54433d6423SLionel Sambuc int
fb_edid_args_parse(void)55433d6423SLionel Sambuc fb_edid_args_parse(void)
56433d6423SLionel Sambuc {
57433d6423SLionel Sambuc 	int i;
58433d6423SLionel Sambuc 	int r;
59433d6423SLionel Sambuc 	char key[32];
60433d6423SLionel Sambuc 
61433d6423SLionel Sambuc 	for (i = 0; i < FB_DEV_NR; i++) {
62433d6423SLionel Sambuc 
63433d6423SLionel Sambuc 		memset(key, '\0', 32);
64433d6423SLionel Sambuc 		snprintf(key, 32, "edid.%d", i);
65433d6423SLionel Sambuc 
66433d6423SLionel Sambuc 		memset(edid_providers[i], '\0', RS_MAX_LABEL_LEN);
67433d6423SLionel Sambuc 		r = env_get_param(key, edid_providers[i], RS_MAX_LABEL_LEN);
68433d6423SLionel Sambuc 		if (r == OK) {
69433d6423SLionel Sambuc 			log_debug(&log, "Found key:%s value:%s\n", key, edid_providers[i]);
70433d6423SLionel Sambuc 		} else {
71433d6423SLionel Sambuc 			/* not an error, user is allowed to omit EDID
72433d6423SLionel Sambuc 			 * providers in order to skip EDID reading and use
73433d6423SLionel Sambuc 			 * the default settings.
74433d6423SLionel Sambuc 			 */
75433d6423SLionel Sambuc 			log_debug(&log, "Couldn't find key:%s\n", key);
76433d6423SLionel Sambuc 		}
77433d6423SLionel Sambuc 	}
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc 	return OK;
80433d6423SLionel Sambuc }
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc /*
83433d6423SLionel Sambuc  * Send a read request to the block driver at endpoint endpt.
84433d6423SLionel Sambuc  */
85433d6423SLionel Sambuc static int
do_read(endpoint_t driver_endpt,uint8_t * buf,size_t bufsize)86433d6423SLionel Sambuc do_read(endpoint_t driver_endpt, uint8_t *buf, size_t bufsize)
87433d6423SLionel Sambuc {
88433d6423SLionel Sambuc 	int r;
89433d6423SLionel Sambuc 	message m;
90433d6423SLionel Sambuc 	cp_grant_id_t grant_nr;
91433d6423SLionel Sambuc 
92433d6423SLionel Sambuc 	/* Open Device - required for drivers using libblockdriver */
93433d6423SLionel Sambuc 	memset(&m, '\0', sizeof(message));
94433d6423SLionel Sambuc 	m.m_type = BDEV_OPEN;
95433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.access = BDEV_R_BIT;
96433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.id = 0;
97433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.minor = 0;
98433d6423SLionel Sambuc 
99433d6423SLionel Sambuc 	r = ipc_sendrec(driver_endpt, &m);
100433d6423SLionel Sambuc 	if (r != OK) {
101433d6423SLionel Sambuc 		log_debug(&log, "ipc_sendrec(BDEV_OPEN) failed (r=%d)\n", r);
102433d6423SLionel Sambuc 		return r;
103433d6423SLionel Sambuc 	}
104433d6423SLionel Sambuc 
105433d6423SLionel Sambuc 	grant_nr = cpf_grant_direct(driver_endpt, (vir_bytes) buf,
106433d6423SLionel Sambuc 		bufsize, CPF_READ | CPF_WRITE);
107433d6423SLionel Sambuc 
108433d6423SLionel Sambuc 	/* Perform the read */
109433d6423SLionel Sambuc 	memset(&m, '\0', sizeof(message));
110433d6423SLionel Sambuc 	m.m_type = BDEV_READ;
111433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.minor = 0;
112433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.count = bufsize;
113433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.grant = grant_nr;
114433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.flags = BDEV_NOPAGE; /* the EEPROMs used for EDID are pageless */
115433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.id = 0;
116433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.pos = 0;
117433d6423SLionel Sambuc 
118433d6423SLionel Sambuc 	r = ipc_sendrec(driver_endpt, &m);
119433d6423SLionel Sambuc 	cpf_revoke(grant_nr);
120433d6423SLionel Sambuc 	if (r != OK) {
121433d6423SLionel Sambuc 		log_debug(&log, "ipc_sendrec(BDEV_READ) failed (r=%d)\n", r);
122433d6423SLionel Sambuc 		/* Clean-up: try to close the device */
123433d6423SLionel Sambuc 		memset(&m, '\0', sizeof(message));
124433d6423SLionel Sambuc 		m.m_type = BDEV_CLOSE;
125433d6423SLionel Sambuc 		m.m_lbdev_lblockdriver_msg.minor = 0;
126433d6423SLionel Sambuc 		m.m_lbdev_lblockdriver_msg.id = 0;
127433d6423SLionel Sambuc 		ipc_sendrec(driver_endpt, &m);
128433d6423SLionel Sambuc 		return r;
129433d6423SLionel Sambuc 	}
130433d6423SLionel Sambuc 
131433d6423SLionel Sambuc 	/* Close the device */
132433d6423SLionel Sambuc 	memset(&m, '\0', sizeof(message));
133433d6423SLionel Sambuc 	m.m_type = BDEV_CLOSE;
134433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.minor = 0;
135433d6423SLionel Sambuc 	m.m_lbdev_lblockdriver_msg.id = 0;
136433d6423SLionel Sambuc 	r = ipc_sendrec(driver_endpt, &m);
137433d6423SLionel Sambuc 	if (r != OK) {
138433d6423SLionel Sambuc 		log_debug(&log, "ipc_sendrec(BDEV_CLOSE) failed (r=%d)\n", r);
139433d6423SLionel Sambuc 		return r;
140433d6423SLionel Sambuc 	}
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc 	return bufsize;
143433d6423SLionel Sambuc }
144433d6423SLionel Sambuc 
145433d6423SLionel Sambuc int
fb_edid_read(int minor,struct edid_info * info)146433d6423SLionel Sambuc fb_edid_read(int minor, struct edid_info *info)
147433d6423SLionel Sambuc {
148433d6423SLionel Sambuc 
149433d6423SLionel Sambuc 	int r;
150433d6423SLionel Sambuc 	uint8_t buffer[128];
151433d6423SLionel Sambuc 	endpoint_t endpt;
152433d6423SLionel Sambuc 
153433d6423SLionel Sambuc 	if (info == NULL || minor < 0 || minor >= FB_DEV_NR ||
154433d6423SLionel Sambuc 					edid_providers[minor][0] == '\0') {
155433d6423SLionel Sambuc 		return EINVAL;
156433d6423SLionel Sambuc 	}
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc 	log_debug(&log, "Contacting %s to get EDID.\n", edid_providers[minor]);
159433d6423SLionel Sambuc 
160433d6423SLionel Sambuc 	/* Look up the endpoint that corresponds to the label */
161433d6423SLionel Sambuc 	endpt = 0;
162433d6423SLionel Sambuc 	r = ds_retrieve_label_endpt(edid_providers[minor], &endpt);
163433d6423SLionel Sambuc 	if (r != 0 || endpt == 0) {
164433d6423SLionel Sambuc 		log_warn(&log, "Couldn't find endpoint for label '%s'\n", edid_providers[minor]);
165433d6423SLionel Sambuc 		return r;
166433d6423SLionel Sambuc 	}
167433d6423SLionel Sambuc 
168433d6423SLionel Sambuc 	/* Perform the request and put the resulting EDID into the buffer. */
169433d6423SLionel Sambuc 	memset(buffer, 0x00, 128);
170433d6423SLionel Sambuc 	r = do_read(endpt, buffer, 128);
171433d6423SLionel Sambuc 	if (r < 0) {
172433d6423SLionel Sambuc 		log_debug(&log, "Failed to read EDID\n");
173433d6423SLionel Sambuc 		return r;
174433d6423SLionel Sambuc 	}
175433d6423SLionel Sambuc 
176433d6423SLionel Sambuc 	/* parse and validate EDID */
177433d6423SLionel Sambuc 	r = edid_parse(buffer, info);
178433d6423SLionel Sambuc 	if (r != 0) {
179433d6423SLionel Sambuc 		log_warn(&log, "Invalid EDID data in buffer.\n");
180433d6423SLionel Sambuc 		return r;
181433d6423SLionel Sambuc 	}
182433d6423SLionel Sambuc 
183433d6423SLionel Sambuc 	log_debug(&log, "EDID Retrieved and Parsed OK\n");
184433d6423SLionel Sambuc 
185433d6423SLionel Sambuc 	return OK;
186433d6423SLionel Sambuc }
187433d6423SLionel Sambuc 
188