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