xref: /minix/minix/drivers/video/fb/fb_edid.c (revision 27852ebe)
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