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