xref: /illumos-gate/usr/src/uts/i86pc/io/gfx_private/gfxp_fb.c (revision 0bead3cac461a1ad4d49bae1dc8a3be05110aa74)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2016 Toomas Soome <tsoome@me.com>
14  */
15 
16 /*
17  * Generic framebuffer interface. Implementing common interfaces
18  * for bitmapped frame buffer and vgatext.
19  */
20 #include <sys/types.h>
21 #include <sys/ddi.h>
22 #include <sys/sunddi.h>
23 #include <sys/file.h>
24 #include <sys/visual_io.h>
25 #include <sys/vgareg.h>
26 #include <sys/vgasubr.h>
27 #include <sys/pci.h>
28 #include <sys/boot_console.h>
29 #include <sys/kd.h>
30 #include <sys/fbio.h>
31 #include <sys/gfx_private.h>
32 #include "gfxp_fb.h"
33 
34 #define	MYNAME	"gfxp_fb"
35 
36 /* need to keep vgatext symbols for compatibility */
37 #pragma weak gfxp_vgatext_softc_alloc = gfxp_fb_softc_alloc
38 #pragma weak gfxp_vgatext_softc_free = gfxp_fb_softc_free
39 #pragma weak gfxp_vgatext_attach = gfxp_fb_attach
40 #pragma weak gfxp_vgatext_detach = gfxp_fb_detach
41 #pragma weak gfxp_vgatext_open = gfxp_fb_open
42 #pragma weak gfxp_vgatext_close = gfxp_fb_close
43 #pragma weak gfxp_vgatext_ioctl = gfxp_fb_ioctl
44 #pragma weak gfxp_vgatext_devmap = gfxp_fb_devmap
45 
46 text_cmap_t cmap_rgb16 = {
47 /* BEGIN CSTYLED */
48 /*             0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
49               Wh+  Bk   Bl   Gr   Cy   Rd   Mg   Br   Wh   Bk+  Bl+  Gr+  Cy+  Rd+  Mg+  Yw */
50   .red =   {0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff},
51   .green = {0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff},
52   .blue =  {0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00}
53 /* END CSTYLED */
54 };
55 
56 /*
57  * NOTE: this function is duplicated here and in consplat/vgatext while
58  *       we work on a set of commitable interfaces to sunpci.c.
59  *
60  * Use the class code to determine if the device is a PCI-to-PCI bridge.
61  * Returns:  B_TRUE  if the device is a bridge.
62  *           B_FALSE if the device is not a bridge or the property cannot be
63  *		     retrieved.
64  */
65 static boolean_t
66 is_pci_bridge(dev_info_t *dip)
67 {
68 	uint32_t class_code;
69 
70 	class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
71 	    DDI_PROP_DONTPASS, "class-code", 0xffffffff);
72 
73 	if (class_code == 0xffffffff || class_code == DDI_PROP_NOT_FOUND)
74 		return (B_FALSE);
75 
76 	class_code &= 0x00ffff00;
77 	if (class_code == ((PCI_CLASS_BRIDGE << 16) | (PCI_BRIDGE_PCI << 8)))
78 		return (B_TRUE);
79 
80 	return (B_FALSE);
81 }
82 
83 #define	STREQ(a, b)	(strcmp((a), (b)) == 0)
84 
85 static void
86 gfxp_check_for_console(dev_info_t *devi, struct gfxp_fb_softc *softc,
87     int pci_pcie_bus)
88 {
89 	ddi_acc_handle_t pci_conf;
90 	dev_info_t *pdevi;
91 	uint16_t data16;
92 
93 	/*
94 	 * Based on Section 11.3, "PCI Display Subsystem Initialization",
95 	 * of the 1.1 PCI-to-PCI Bridge Architecture Specification
96 	 * determine if this is the boot console device.  First, see
97 	 * if the SBIOS has turned on PCI I/O for this device.  Then if
98 	 * this is PCI/PCI-E, verify the parent bridge has VGAEnable set.
99 	 */
100 
101 	if (pci_config_setup(devi, &pci_conf) != DDI_SUCCESS) {
102 		cmn_err(CE_WARN, MYNAME ": can't get PCI conf handle");
103 		return;
104 	}
105 
106 	data16 = pci_config_get16(pci_conf, PCI_CONF_COMM);
107 	if (data16 & PCI_COMM_IO)
108 		softc->flags |= GFXP_FLAG_CONSOLE;
109 
110 	pci_config_teardown(&pci_conf);
111 
112 	/* If IO not enabled or ISA/EISA, just return */
113 	if (!(softc->flags & GFXP_FLAG_CONSOLE) || !pci_pcie_bus)
114 		return;
115 
116 	/*
117 	 * Check for VGA Enable in the Bridge Control register for all
118 	 * PCI/PCIEX parents.  If not set all the way up the chain,
119 	 * this cannot be the boot console.
120 	 */
121 
122 	pdevi = devi;
123 	while (pdevi = ddi_get_parent(pdevi)) {
124 		int	error;
125 		ddi_acc_handle_t ppci_conf;
126 		char	*parent_type = NULL;
127 
128 		error = ddi_prop_lookup_string(DDI_DEV_T_ANY, pdevi,
129 		    DDI_PROP_DONTPASS, "device_type", &parent_type);
130 		if (error != DDI_SUCCESS) {
131 			return;
132 		}
133 
134 		/* Verify still on the PCI/PCIEX parent tree */
135 		if (!STREQ(parent_type, "pci") &&
136 		    !STREQ(parent_type, "pciex")) {
137 			ddi_prop_free(parent_type);
138 			return;
139 		}
140 
141 		ddi_prop_free(parent_type);
142 		parent_type = NULL;
143 
144 		/* VGAEnable is set only for PCI-to-PCI bridges. */
145 		if (is_pci_bridge(pdevi) == B_FALSE)
146 			continue;
147 
148 		if (pci_config_setup(pdevi, &ppci_conf) != DDI_SUCCESS)
149 			continue;
150 
151 		data16 = pci_config_get16(ppci_conf, PCI_BCNF_BCNTRL);
152 		pci_config_teardown(&ppci_conf);
153 
154 		if (!(data16 & PCI_BCNF_BCNTRL_VGA_ENABLE)) {
155 			softc->flags &= ~GFXP_FLAG_CONSOLE;
156 			return;
157 		}
158 	}
159 }
160 
161 gfxp_fb_softc_ptr_t
162 gfxp_fb_softc_alloc(void)
163 {
164 	return (kmem_zalloc(sizeof (struct gfxp_fb_softc), KM_SLEEP));
165 }
166 
167 void
168 gfxp_fb_softc_free(gfxp_fb_softc_ptr_t ptr)
169 {
170 	kmem_free(ptr, sizeof (struct gfxp_fb_softc));
171 }
172 
173 void
174 gfxp_fb_resume(struct gfxp_fb_softc *softc)
175 {
176 	if (softc->gfxp_ops->resume != NULL)
177 		softc->gfxp_ops->resume(softc);
178 }
179 
180 int
181 gfxp_fb_suspend(struct gfxp_fb_softc *softc)
182 {
183 	if (softc->gfxp_ops->suspend != NULL)
184 		return (softc->gfxp_ops->suspend(softc));
185 	return (DDI_FAILURE);
186 }
187 
188 int
189 gfxp_fb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd, gfxp_fb_softc_ptr_t ptr)
190 {
191 	struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr;
192 	int	error;
193 	char	*parent_type = NULL;
194 	int pci_pcie_bus = 0;
195 	int value;
196 
197 	if (softc == NULL)
198 		return (DDI_FAILURE);
199 
200 	switch (cmd) {
201 	case DDI_ATTACH:
202 		break;
203 
204 	case DDI_RESUME:
205 		gfxp_fb_resume(softc);
206 		return (DDI_SUCCESS);
207 
208 	default:
209 		return (DDI_FAILURE);
210 	}
211 
212 	/* DDI_ATTACH */
213 	softc->devi = devi; /* Copy and init DEVI */
214 	softc->polledio.arg = (struct vis_polledio_arg *)softc;
215 	softc->mode = -1;	/* the actual value will be set by tem */
216 	mutex_init(&(softc->lock), NULL, MUTEX_DRIVER, NULL);
217 
218 	error = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(devi),
219 	    DDI_PROP_DONTPASS, "device_type", &parent_type);
220 	if (error != DDI_SUCCESS) {
221 		cmn_err(CE_WARN, MYNAME ": can't determine parent type.");
222 		goto fail;
223 	}
224 
225 	if (STREQ(parent_type, "pci") || STREQ(parent_type, "pciex")) {
226 		pci_pcie_bus = 1;
227 	}
228 	ddi_prop_free(parent_type);
229 	gfxp_check_for_console(devi, softc, pci_pcie_bus);
230 
231 	value = GFXP_IS_CONSOLE(softc) ? 1 : 0;
232 	if (ddi_prop_update_int(DDI_DEV_T_NONE, devi,
233 	    "primary-controller", value) != DDI_SUCCESS) {
234 		cmn_err(CE_WARN,
235 		    "Cannot %s primary-controller "
236 		    "property for driver", value ? "set" : "clear");
237 	}
238 
239 	switch (fb_info.fb_type) {
240 	case FB_TYPE_UNINITIALIZED:
241 		/*
242 		 * While booting from MB1, we do not have FB.
243 		 * Fall through.
244 		 */
245 	case FB_TYPE_EGA_TEXT:
246 		softc->fb_type = GFXP_VGATEXT;
247 		error = gfxp_vga_attach(devi, softc);
248 		break;
249 
250 	case FB_TYPE_INDEXED:	/* FB types */
251 	case FB_TYPE_RGB:
252 		softc->fb_type = GFXP_BITMAP;
253 		error = gfxp_bm_attach(devi, softc);
254 		break;
255 
256 	default:
257 		error = DDI_FAILURE;
258 	}
259 
260 	if (error == DDI_SUCCESS)
261 		return (error);
262 
263 	(void) ddi_prop_remove(DDI_DEV_T_ANY, devi, "primary-controller");
264 fail:
265 	(void) gfxp_fb_detach(devi, DDI_DETACH, (void *)softc);
266 	return (error);
267 }
268 
269 int
270 gfxp_fb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd, gfxp_fb_softc_ptr_t ptr)
271 {
272 	struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr;
273 	int error;
274 
275 	if (softc == NULL)
276 		return (DDI_FAILURE);
277 
278 	switch (cmd) {
279 	case DDI_SUSPEND:
280 		return (gfxp_fb_suspend(softc));
281 
282 	case DDI_DETACH:
283 		(void) ddi_prop_remove(DDI_DEV_T_ANY, devi,
284 		    "primary-controller");
285 
286 		switch (softc->fb_type) {
287 		case GFXP_BITMAP:
288 			error = gfxp_bm_detach(devi, softc);
289 			break;
290 		case GFXP_VGATEXT:
291 			error = gfxp_vga_detach(devi, softc);
292 			break;
293 		}
294 		mutex_destroy(&(softc->lock));
295 		return (error);
296 
297 	default:
298 		cmn_err(CE_WARN, "gfxp_fb_detach: unknown cmd 0x%x\n",
299 		    cmd);
300 		return (DDI_FAILURE);
301 	}
302 }
303 
304 /*ARGSUSED*/
305 int
306 gfxp_fb_open(dev_t *devp, int flag, int otyp, cred_t *cred,
307     gfxp_fb_softc_ptr_t ptr)
308 {
309 	struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr;
310 
311 	if (softc == NULL || otyp == OTYP_BLK)
312 		return (ENXIO);
313 
314 	return (0);
315 }
316 
317 /*ARGSUSED*/
318 int
319 gfxp_fb_close(dev_t devp, int flag, int otyp, cred_t *cred,
320     gfxp_fb_softc_ptr_t ptr)
321 {
322 	return (0);
323 }
324 
325 static int
326 do_gfx_ioctl(int cmd, intptr_t data, int mode, struct gfxp_fb_softc *softc)
327 {
328 	static char kernel_only[] =
329 	    "gfxp_fb_ioctl: %s is a kernel only ioctl";
330 	int err;
331 	int kd_mode;
332 
333 	switch (cmd) {
334 	case KDSETMODE:
335 		kd_mode = (int)data;
336 		if ((kd_mode == softc->mode) || (!GFXP_IS_CONSOLE(softc)))
337 			break;
338 		return (softc->gfxp_ops->kdsetmode(softc, kd_mode));
339 
340 	case KDGETMODE:
341 		kd_mode = softc->mode;
342 		if (ddi_copyout(&kd_mode, (void *)data, sizeof (int), mode))
343 			return (EFAULT);
344 		break;
345 
346 	case VIS_GETIDENTIFIER:
347 		if (ddi_copyout(softc->gfxp_ops->ident, (void *)data,
348 		    sizeof (struct vis_identifier), mode))
349 			return (EFAULT);
350 		break;
351 
352 	case VIS_DEVINIT:
353 
354 		if (!(mode & FKIOCTL)) {
355 			cmn_err(CE_CONT, kernel_only, "VIS_DEVINIT");
356 			return (ENXIO);
357 		}
358 
359 		err = softc->gfxp_ops->devinit(softc,
360 		    (struct vis_devinit *)data);
361 		if (err != 0) {
362 			cmn_err(CE_WARN,
363 			    "gfxp_fb_ioctl:  could not initialize console");
364 			return (err);
365 		}
366 		break;
367 
368 	case VIS_CONSCLEAR:	/* clear screen */
369 	{
370 		struct vis_consclear pma;
371 
372 		if (ddi_copyin((void *)data, &pma,
373 		    sizeof (struct vis_consclear), mode))
374 			return (EFAULT);
375 
376 		return (softc->gfxp_ops->cons_clear(softc, &pma));
377 	}
378 
379 	case VIS_CONSCOPY:	/* move */
380 	{
381 		struct vis_conscopy pma;
382 
383 		if (ddi_copyin((void *)data, &pma,
384 		    sizeof (struct vis_conscopy), mode))
385 			return (EFAULT);
386 
387 		softc->gfxp_ops->cons_copy(softc, &pma);
388 		break;
389 	}
390 
391 	case VIS_CONSDISPLAY:	/* display */
392 	{
393 		struct vis_consdisplay display_request;
394 
395 		if (ddi_copyin((void *)data, &display_request,
396 		    sizeof (display_request), mode))
397 			return (EFAULT);
398 
399 		softc->gfxp_ops->cons_display(softc, &display_request);
400 		break;
401 	}
402 
403 	case VIS_CONSCURSOR:
404 	{
405 		struct vis_conscursor cursor_request;
406 
407 		if (ddi_copyin((void *)data, &cursor_request,
408 		    sizeof (cursor_request), mode))
409 			return (EFAULT);
410 
411 		softc->gfxp_ops->cons_cursor(softc, &cursor_request);
412 
413 		if (cursor_request.action == VIS_GET_CURSOR &&
414 		    ddi_copyout(&cursor_request, (void *)data,
415 		    sizeof (cursor_request), mode))
416 			return (EFAULT);
417 		break;
418 	}
419 
420 	case VIS_GETCMAP:
421 	case VIS_PUTCMAP:
422 	case FBIOPUTCMAP:
423 	case FBIOGETCMAP:
424 		/*
425 		 * At the moment, text mode is not considered to have
426 		 * a color map.
427 		 */
428 		return (EINVAL);
429 
430 	case FBIOGATTR:
431 		if (copyout(softc->fbgattr, (void *)data,
432 		    sizeof (struct fbgattr)))
433 			return (EFAULT);
434 		break;
435 
436 	case FBIOGTYPE:
437 		if (copyout(&softc->fbgattr->fbtype, (void *)data,
438 		    sizeof (struct fbtype)))
439 			return (EFAULT);
440 		break;
441 
442 	default:
443 		cmn_err(CE_CONT, "!unimplemented cmd: 0x%x\n", cmd);
444 		return (ENXIO);
445 	}
446 	return (0);
447 }
448 
449 /*ARGSUSED*/
450 int
451 gfxp_fb_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
452     cred_t *cred, int *rval, gfxp_fb_softc_ptr_t ptr)
453 {
454 	struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr;
455 	int error = DDI_FAILURE;
456 
457 	if (softc == NULL)
458 		return (error);
459 	mutex_enter(&(softc->lock));
460 	error = do_gfx_ioctl(cmd, data, mode, softc);
461 	mutex_exit(&(softc->lock));
462 	return (error);
463 }
464 
465 int
466 gfxp_fb_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off,
467     size_t len, size_t *maplen, uint_t model, void *ptr)
468 {
469 	struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr;
470 
471 	if (softc == NULL)
472 		return (DDI_FAILURE);
473 
474 	return (softc->gfxp_ops->devmap(dev, dhp, off, len, maplen,
475 	    model, ptr));
476 }
477