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