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
is_pci_bridge(dev_info_t * dip)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
gfxp_check_for_console(dev_info_t * devi,struct gfxp_fb_softc * softc,int pci_pcie_bus)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
gfxp_fb_softc_alloc(void)164 gfxp_fb_softc_alloc(void)
165 {
166 return (kmem_zalloc(sizeof (struct gfxp_fb_softc), KM_SLEEP));
167 }
168
169 void
gfxp_fb_softc_free(gfxp_fb_softc_ptr_t ptr)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
gfxp_fb_resume(struct gfxp_fb_softc * softc)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
gfxp_fb_suspend(struct gfxp_fb_softc * softc)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
gfxp_fb_attach(dev_info_t * devi,ddi_attach_cmd_t cmd,gfxp_fb_softc_ptr_t ptr)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
gfxp_fb_detach(dev_info_t * devi,ddi_detach_cmd_t cmd,gfxp_fb_softc_ptr_t ptr)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
gfxp_fb_open(dev_t * devp,int flag,int otyp,cred_t * cred,gfxp_fb_softc_ptr_t ptr)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
gfxp_fb_close(dev_t devp,int flag,int otyp,cred_t * cred,gfxp_fb_softc_ptr_t ptr)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
do_gfx_ioctl(int cmd,intptr_t data,int mode,struct gfxp_fb_softc * softc)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
gfxp_fb_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * cred,int * rval,gfxp_fb_softc_ptr_t ptr)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
gfxp_fb_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model,void * ptr)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