xref: /minix/minix/drivers/video/fb/fb.c (revision 83133719)
1 #include <minix/fb.h>
2 #include <minix/chardriver.h>
3 #include <minix/drivers.h>
4 #include <minix/ds.h>
5 #include <minix/sysutil.h>
6 #include <minix/type.h>
7 #include <minix/vm.h>
8 #include <sys/ioc_fb.h>
9 #include <assert.h>
10 #include <sys/ioctl.h>
11 #include <sys/mman.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdint.h>
15 #include <dev/videomode/videomode.h>
16 #include <dev/videomode/edidvar.h>
17 #include <dev/videomode/edidreg.h>
18 
19 #include "logos.h"
20 #include "fb_edid.h"
21 #include "fb.h"
22 
23 /*
24  * Function prototypes for the fb driver.
25  */
26 static int fb_open(devminor_t minor, int access, endpoint_t user_endpt);
27 static int fb_close(devminor_t minor);
28 static ssize_t fb_read(devminor_t minor, u64_t pos, endpoint_t ep,
29 	cp_grant_id_t gid, size_t size, int flags, cdev_id_t id);
30 static ssize_t fb_write(devminor_t minor, u64_t pos, endpoint_t ep,
31 	cp_grant_id_t gid, size_t size, int flags, cdev_id_t id);
32 static int fb_ioctl(devminor_t minor, unsigned long request, endpoint_t ep,
33 	cp_grant_id_t gid, int flags, endpoint_t user_ep, cdev_id_t id);
34 static void paint_bootlogo(int minor);
35 static void paint_restartlogo(int minor);
36 static void paint_centered(int minor, char *data, int width, int height);
37 static int do_get_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid);
38 static int do_put_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid);
39 static int do_get_fixscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid);
40 static int do_pan_display(int minor, endpoint_t ep, cp_grant_id_t gid);
41 static int keep_displaying_restarted(void);
42 
43 /* SEF functions and variables. */
44 static void sef_local_startup(void);
45 static int sef_cb_init(int type, sef_init_info_t *info);
46 static int sef_cb_lu_state_save(int);
47 static int lu_state_restore(void);
48 
49 /* Entry points to the fb driver. */
50 static struct chardriver fb_tab =
51 {
52 	.cdr_open	= fb_open,
53 	.cdr_close	= fb_close,
54 	.cdr_read	= fb_read,
55 	.cdr_write	= fb_write,
56 	.cdr_ioctl	= fb_ioctl
57 };
58 
59 /** Represents the /dev/fb device. */
60 static int has_restarted = 0;
61 static u64_t has_restarted_t1, has_restarted_t2;
62 
63 static int open_counter[FB_DEV_NR];		/* Open count */
64 
65 static int
66 fb_open(devminor_t minor, int UNUSED(access), endpoint_t UNUSED(user_endpt))
67 {
68 	int r;
69 	static int initialized = 0;
70 	static struct edid_info info;
71 	static struct edid_info *infop = NULL;
72 
73 	if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
74 
75 	if (!initialized) {
76 		r = fb_edid_read(minor, &info);
77 		infop = (r == 0) ? &info : NULL;
78 	}
79 
80 	if (arch_fb_init(minor, infop) == OK) {
81 		open_counter[minor]++;
82 		if (!initialized) {
83 			if (has_restarted) {
84 				read_frclock_64(&has_restarted_t1);
85 				paint_restartlogo(minor);
86 			} else {
87 				paint_bootlogo(minor);
88 			}
89 			initialized = 1;
90 		}
91 		return OK;
92 	}
93 	return ENXIO;
94 }
95 
96 static int
97 fb_close(devminor_t minor)
98 {
99 	if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
100 	assert(open_counter[minor] > 0);
101 	open_counter[minor]--;
102 	return OK;
103 }
104 
105 static ssize_t
106 fb_read(devminor_t minor, u64_t pos, endpoint_t ep, cp_grant_id_t gid,
107 	size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
108 {
109 	struct device dev;
110 	int r;
111 
112 	if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
113 	assert(open_counter[minor] > 0);
114 
115 	arch_get_device(minor, &dev);
116 
117 	if (size == 0 || pos >= dev.dv_size) return 0;
118 	if (pos + size > dev.dv_size)
119 		size = (size_t)(dev.dv_size - pos);
120 
121 	r = sys_safecopyto(ep, gid, 0, (vir_bytes)(dev.dv_base + (size_t)pos),
122 	    size);
123 
124 	return (r != OK) ? r : size;
125 }
126 
127 static int
128 fb_ioctl(devminor_t minor, unsigned long request, endpoint_t ep,
129 	cp_grant_id_t gid, int UNUSED(flags), endpoint_t UNUSED(user_ep),
130 	cdev_id_t UNUSED(id))
131 {
132 /* Process I/O control requests */
133 	int r;
134 
135 	if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
136 
137 	switch(request) {
138 	case FBIOGET_VSCREENINFO:
139 		r = do_get_varscreeninfo(minor, ep, gid);
140 		return r;
141 	case FBIOPUT_VSCREENINFO:
142 		r = do_put_varscreeninfo(minor, ep, gid);
143 		return r;
144 	case FBIOGET_FSCREENINFO:
145 		r = do_get_fixscreeninfo(minor, ep, gid);
146 		return r;
147 	case FBIOPAN_DISPLAY:
148 		r = do_pan_display(minor, ep, gid);
149 		return r;
150 	}
151 
152 	return ENOTTY;
153 }
154 
155 static int
156 do_get_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
157 {
158 	int r;
159 	struct fb_var_screeninfo fbvs;
160 
161 	if ((r = arch_get_varscreeninfo(minor, &fbvs)) == OK) {
162 		r = sys_safecopyto(ep, gid, 0, (vir_bytes) &fbvs, sizeof(fbvs));
163 	}
164 
165 	return r;
166 }
167 
168 static int
169 do_put_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
170 {
171 	int r;
172 	struct fb_var_screeninfo fbvs_copy;
173 
174 	if (has_restarted && keep_displaying_restarted()) {
175 		return EAGAIN;
176 	}
177 
178 	if ((r = sys_safecopyfrom(ep, gid, 0, (vir_bytes) &fbvs_copy,
179 	    sizeof(fbvs_copy))) != OK) {
180 		return r;
181 	}
182 
183 	return arch_put_varscreeninfo(minor, &fbvs_copy);
184 }
185 
186 static int
187 do_pan_display(int minor, endpoint_t ep, cp_grant_id_t gid)
188 {
189 	int r;
190         struct fb_var_screeninfo fbvs_copy;
191 
192 	if (has_restarted && keep_displaying_restarted()) {
193 		return EAGAIN;
194 	}
195 
196         if ((r = sys_safecopyfrom(ep, gid, 0, (vir_bytes) &fbvs_copy,
197             sizeof(fbvs_copy))) != OK) {
198                 return r;
199         }
200 
201         return arch_pan_display(minor, &fbvs_copy);
202 }
203 
204 static int
205 do_get_fixscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
206 {
207         int r;
208         struct fb_fix_screeninfo fbfs;
209 
210         if ((r = arch_get_fixscreeninfo(minor, &fbfs)) == OK) {
211                 r = sys_safecopyto(ep, gid, 0, (vir_bytes) &fbfs, sizeof(fbfs));
212         }
213 
214         return r;
215 }
216 
217 static ssize_t
218 fb_write(devminor_t minor, u64_t pos, endpoint_t ep, cp_grant_id_t gid,
219 	size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
220 {
221 	struct device dev;
222 	int r;
223 
224 	if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
225 	assert(open_counter[minor] > 0);
226 
227 	if (has_restarted && keep_displaying_restarted())
228 		return EAGAIN;
229 
230 	arch_get_device(minor, &dev);
231 
232 	if (size == 0 || pos >= dev.dv_size) return 0;
233 	if (pos + size > dev.dv_size)
234 		size = (size_t)(dev.dv_size - pos);
235 
236 	r = sys_safecopyfrom(ep, gid, 0,
237 	    (vir_bytes)(dev.dv_base + (size_t)pos), size);
238 
239 	return (r != OK) ? r : size;
240 }
241 
242 static int
243 sef_cb_lu_state_save(int UNUSED(state)) {
244 /* Save the state. */
245 	ds_publish_u32("open_counter", open_counter[0], DSF_OVERWRITE);
246 
247 	return OK;
248 }
249 
250 static int
251 lu_state_restore() {
252 /* Restore the state. */
253 	u32_t value;
254 
255 	ds_retrieve_u32("open_counter", &value);
256 	ds_delete_u32("open_counter");
257 	open_counter[0] = (int) value;
258 
259 	return OK;
260 }
261 
262 static void
263 sef_local_startup()
264 {
265 	/* Register init callbacks. Use the same function for all event types */
266 	sef_setcb_init_fresh(sef_cb_init);
267 	sef_setcb_init_lu(sef_cb_init);
268 	sef_setcb_init_restart(sef_cb_init);
269 
270 	/* Register live update callbacks  */
271 	/* - Agree to update immediately when LU is requested in a valid state*/
272 	sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
273 	/* - Support live update starting from any standard state */
274 	sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
275 	/* - Register a custom routine to save the state. */
276 	sef_setcb_lu_state_save(sef_cb_lu_state_save);
277 
278 	/* Let SEF perform startup. */
279 	sef_startup();
280 }
281 
282 static int
283 sef_cb_init(int type, sef_init_info_t *UNUSED(info))
284 {
285 /* Initialize the fb driver. */
286 	int do_announce_driver = TRUE;
287 
288 	open_counter[0] = 0;
289 	switch(type) {
290 	case SEF_INIT_FRESH:
291 	    printf("framebuffer fresh: pid %d\n", getpid());
292 	    break;
293 
294 	case SEF_INIT_LU:
295 	    /* Restore the state. */
296 	    lu_state_restore();
297 	    do_announce_driver = FALSE;
298 
299 	    printf("framebuffer: I'm a new version!\n");
300 	    break;
301 
302 	case SEF_INIT_RESTART:
303 	    printf("framebuffer restarted: pid %d\n", getpid());
304 	    has_restarted = 1;
305 	    break;
306 	}
307 
308 	/* Announce we are up when necessary. */
309 	if (do_announce_driver) {
310 		chardriver_announce();
311 	}
312 
313 	/* Initialization completed successfully. */
314 	return OK;
315 }
316 
317 int
318 main(int argc, char *argv[])
319 {
320 	env_setargs(argc, argv);
321 	fb_edid_args_parse();
322 
323 	sef_local_startup();
324 	chardriver_task(&fb_tab);
325 	return OK;
326 }
327 
328 static int
329 keep_displaying_restarted()
330 {
331 	u64_t delta;
332 	u32_t micro_delta;
333 
334 	read_frclock_64(&has_restarted_t2);
335 	delta = delta_frclock_64(has_restarted_t1, has_restarted_t2);
336 	micro_delta = frclock_64_to_micros(delta);
337 
338 #define DISPLAY_1SEC 1000000	/* 1 second in microseconds */
339 	if (micro_delta < DISPLAY_1SEC) {
340 		return 1;
341 	}
342 
343 	has_restarted = 0;
344 	return 0;
345 }
346 
347 static void
348 paint_bootlogo(int minor)
349 {
350 	paint_centered(minor, bootlogo_data, bootlogo_width, bootlogo_height);
351 }
352 
353 static void
354 paint_restartlogo(int minor)
355 {
356 	paint_centered(minor, restartlogo_data, restartlogo_width,
357 			restartlogo_height);
358 }
359 
360 static void
361 paint_centered(int minor, char *data, int width, int height)
362 {
363 	u8_t pixel[3];
364 	u32_t i, min_x, min_y, max_x, max_y, x_painted = 0, rows = 0;
365 	int r, bytespp;
366 	struct device dev;
367 	struct fb_var_screeninfo fbvs;
368 
369 	/* Put display in a known state to simplify positioning code below */
370 	if ((r = arch_get_varscreeninfo(minor, &fbvs)) != OK) {
371 		printf("fb: unable to get screen info: %d\n", r);
372 	}
373 	fbvs.yoffset = 0;
374 	if ((r = arch_pan_display(minor, &fbvs)) != OK) {
375 		printf("fb: unable to pan display: %d\n", r);
376 	}
377 
378 	arch_get_device(minor, &dev);
379 
380 	/* Paint on a white canvas */
381 	bytespp = fbvs.bits_per_pixel / 8;
382 	for (i = 0; i < fbvs.xres * fbvs.yres * bytespp; i+= bytespp)
383 		*((u32_t *)((u32_t) dev.dv_base + i)) = 0x00FFFFFF;
384 
385 	/* First seek to start */
386 	min_x = fbvs.xres / 2 - width / 2;
387 	max_x = fbvs.xres / 2 + width / 2;
388 	min_y = fbvs.yres / 2 - height / 2;
389 	max_y = fbvs.yres / 2 + height / 2;
390 	i = min_x * fbvs.xres + min_y;
391 
392 	/* Add the image data */
393 	for (i = ((min_y * fbvs.xres) + min_x) * bytespp; rows < height;) {
394 		GET_PIXEL(data, pixel);
395 
396 		((unsigned char *)((u32_t) dev.dv_base + i))[0] = pixel[2];
397 		((unsigned char *)((u32_t) dev.dv_base + i))[1] = pixel[1];
398                 ((unsigned char *)((u32_t) dev.dv_base + i))[2] = pixel[0];
399 		((unsigned char *)((u32_t) dev.dv_base + i))[3] = 0;
400 
401 		x_painted++;/* Keep tab of how many row pixels we've painted */
402 		if (x_painted == width) {
403 			/* We've reached the end of the row, carriage return
404 			 * and go to next line.
405 			 */
406 			x_painted = 0;
407 			rows++;
408 			i = (((min_y + rows) * fbvs.xres) + min_x) * 4;
409 		} else {
410 			i += 4;
411 		}
412 	}
413 }
414 
415