summaryrefslogtreecommitdiff
path: root/minix/drivers/video/fb/fb_edid.c
blob: c671e40b6f4be2ccd7b45d6c8245efffaa0f524a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * 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 <minix/fb.h>
#include <minix/chardriver.h>
#include <minix/drivers.h>
#include <minix/ds.h>
#include <minix/rs.h>
#include <minix/log.h>
#include <minix/sysutil.h>
#include <minix/type.h>
#include <minix/vm.h>
#include <sys/ioc_fb.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <dev/videomode/videomode.h>
#include <dev/videomode/edidvar.h>
#include <dev/videomode/edidreg.h>

#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 minix-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.m_lbdev_lblockdriver_msg.access = BDEV_R_BIT;
	m.m_lbdev_lblockdriver_msg.id = 0;
	m.m_lbdev_lblockdriver_msg.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.m_lbdev_lblockdriver_msg.minor = 0;
	m.m_lbdev_lblockdriver_msg.count = bufsize;
	m.m_lbdev_lblockdriver_msg.grant = grant_nr;
	m.m_lbdev_lblockdriver_msg.flags = BDEV_NOPAGE; /* the EEPROMs used for EDID are pageless */
	m.m_lbdev_lblockdriver_msg.id = 0;
	m.m_lbdev_lblockdriver_msg.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.m_lbdev_lblockdriver_msg.minor = 0;
		m.m_lbdev_lblockdriver_msg.id = 0;
		ipc_sendrec(driver_endpt, &m);
		return r;
	}

	/* Close the device */
	memset(&m, '\0', sizeof(message));
	m.m_type = BDEV_CLOSE;
	m.m_lbdev_lblockdriver_msg.minor = 0;
	m.m_lbdev_lblockdriver_msg.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;
}