1 /* $OpenBSD: apldrm.c,v 1.2 2024/01/29 14:52:25 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 22 #include <machine/fdt.h> 23 24 #include <dev/ofw/openfirm.h> 25 #include <dev/ofw/fdt.h> 26 27 #include <dev/wscons/wsconsio.h> 28 #include <dev/wscons/wsdisplayvar.h> 29 #include <dev/rasops/rasops.h> 30 31 #include <linux/platform_device.h> 32 33 #include <drm/drm_drv.h> 34 #include <drm/drm_framebuffer.h> 35 36 struct apldrm_softc { 37 struct platform_device sc_dev; 38 struct drm_device sc_ddev; 39 40 int sc_node; 41 42 struct rasops_info sc_ri; 43 struct wsscreen_descr sc_wsd; 44 struct wsscreen_list sc_wsl; 45 struct wsscreen_descr *sc_scrlist[1]; 46 47 void (*sc_switchcb)(void *, int, int); 48 void *sc_switchcbarg; 49 void *sc_switchcookie; 50 struct task sc_switchtask; 51 52 int sc_burner_fblank; 53 struct task sc_burner_task; 54 }; 55 56 #include "apple_drv.c" 57 58 int apldrm_match(struct device *, void *, void *); 59 void apldrm_attach(struct device *, struct device *, void *); 60 int apldrm_activate(struct device *, int); 61 62 const struct cfattach apldrm_ca = { 63 sizeof (struct apldrm_softc), apldrm_match, apldrm_attach, 64 NULL, apldrm_activate 65 }; 66 67 struct cfdriver apldrm_cd = { 68 NULL, "apldrm", DV_DULL 69 }; 70 71 void apldrm_attachhook(struct device *); 72 73 int 74 apldrm_match(struct device *parent, void *match, void *aux) 75 { 76 struct fdt_attach_args *faa = aux; 77 78 return OF_is_compatible(faa->fa_node, "apple,display-subsystem"); 79 } 80 81 void 82 apldrm_attach(struct device *parent, struct device *self, void *aux) 83 { 84 struct apldrm_softc *sc = (struct apldrm_softc *)self; 85 struct fdt_attach_args *faa = aux; 86 87 sc->sc_node = faa->fa_node; 88 89 printf("\n"); 90 91 sc->sc_dev.faa = faa; 92 platform_device_register(&sc->sc_dev); 93 94 drm_attach_platform((struct drm_driver *)&apple_drm_driver, 95 faa->fa_iot, faa->fa_dmat, self, &sc->sc_ddev); 96 config_mountroot(self, apldrm_attachhook); 97 } 98 99 int 100 apldrm_activate(struct device *self, int act) 101 { 102 int rv; 103 104 switch (act) { 105 case DVACT_QUIESCE: 106 rv = config_activate_children(self, act); 107 apple_platform_suspend(self); 108 break; 109 case DVACT_WAKEUP: 110 apple_platform_resume(self); 111 rv = config_activate_children(self, act); 112 break; 113 default: 114 rv = config_activate_children(self, act); 115 break; 116 } 117 118 return rv; 119 } 120 121 int 122 apldrm_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 123 { 124 struct rasops_info *ri = v; 125 struct apldrm_softc *sc = ri->ri_hw; 126 struct wsdisplay_param *dp = (struct wsdisplay_param *)data; 127 struct wsdisplay_fbinfo *wdf; 128 struct backlight_device *bd; 129 130 bd = backlight_device_get_by_name("apple-panel-bl"); 131 132 switch (cmd) { 133 case WSDISPLAYIO_GTYPE: 134 *(u_int *)data = WSDISPLAY_TYPE_KMS; 135 return 0; 136 case WSDISPLAYIO_GINFO: 137 wdf = (struct wsdisplay_fbinfo *)data; 138 wdf->width = ri->ri_width; 139 wdf->height = ri->ri_height; 140 wdf->depth = ri->ri_depth; 141 wdf->stride = ri->ri_stride; 142 wdf->offset = 0; /* XXX */ 143 wdf->cmsize = 0; 144 return 0; 145 case WSDISPLAYIO_GETPARAM: 146 if (bd == NULL) 147 return -1; 148 149 switch (dp->param) { 150 case WSDISPLAYIO_PARAM_BRIGHTNESS: 151 dp->min = 0; 152 dp->max = bd->props.max_brightness; 153 dp->curval = bd->props.brightness; 154 return (dp->max > dp->min) ? 0 : -1; 155 } 156 break; 157 case WSDISPLAYIO_SETPARAM: 158 if (bd == NULL) 159 return -1; 160 161 switch (dp->param) { 162 case WSDISPLAYIO_PARAM_BRIGHTNESS: 163 bd->props.brightness = dp->curval; 164 backlight_update_status(bd); 165 knote_locked(&sc->sc_ddev.note, NOTE_CHANGE); 166 return 0; 167 } 168 break; 169 case WSDISPLAYIO_SVIDEO: 170 case WSDISPLAYIO_GVIDEO: 171 return 0; 172 } 173 174 return (-1); 175 } 176 177 paddr_t 178 apldrm_wsmmap(void *v, off_t off, int prot) 179 { 180 return (-1); 181 } 182 183 int 184 apldrm_alloc_screen(void *v, const struct wsscreen_descr *type, 185 void **cookiep, int *curxp, int *curyp, uint32_t *attrp) 186 { 187 return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp); 188 } 189 190 void 191 apldrm_free_screen(void *v, void *cookie) 192 { 193 return rasops_free_screen(v, cookie); 194 } 195 196 void 197 apldrm_doswitch(void *v) 198 { 199 struct rasops_info *ri = v; 200 struct apldrm_softc *sc = ri->ri_hw; 201 struct drm_fb_helper *fb_helper = sc->sc_ddev.fb_helper; 202 203 rasops_show_screen(ri, sc->sc_switchcookie, 0, NULL, NULL); 204 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); 205 206 if (sc->sc_switchcb) 207 (sc->sc_switchcb)(sc->sc_switchcbarg, 0, 0); 208 } 209 210 int 211 apldrm_show_screen(void *v, void *cookie, int waitok, 212 void (*cb)(void *, int, int), void *cbarg) 213 { 214 struct rasops_info *ri = v; 215 struct apldrm_softc *sc = ri->ri_hw; 216 217 if (cookie == ri->ri_active) 218 return (0); 219 220 sc->sc_switchcb = cb; 221 sc->sc_switchcbarg = cbarg; 222 sc->sc_switchcookie = cookie; 223 if (cb) { 224 task_add(systq, &sc->sc_switchtask); 225 return (EAGAIN); 226 } 227 228 apldrm_doswitch(v); 229 230 return (0); 231 } 232 233 void 234 apldrm_enter_ddb(void *v, void *cookie) 235 { 236 struct rasops_info *ri = v; 237 struct apldrm_softc *sc = ri->ri_hw; 238 struct drm_fb_helper *fb_helper = sc->sc_ddev.fb_helper; 239 240 if (cookie == ri->ri_active) 241 return; 242 243 rasops_show_screen(ri, cookie, 0, NULL, NULL); 244 drm_fb_helper_debug_enter(fb_helper->info); 245 } 246 247 void 248 apldrm_burner(void *v, u_int on, u_int flags) 249 { 250 struct rasops_info *ri = v; 251 struct apldrm_softc *sc = ri->ri_hw; 252 253 task_del(systq, &sc->sc_burner_task); 254 255 if (on) 256 sc->sc_burner_fblank = FB_BLANK_UNBLANK; 257 else { 258 if (flags & WSDISPLAY_BURN_VBLANK) 259 sc->sc_burner_fblank = FB_BLANK_VSYNC_SUSPEND; 260 else 261 sc->sc_burner_fblank = FB_BLANK_NORMAL; 262 } 263 264 /* 265 * Setting the DPMS mode may sleep while waiting for vblank so 266 * hand things off to a taskq. 267 */ 268 task_add(systq, &sc->sc_burner_task); 269 } 270 271 void 272 apldrm_burner_cb(void *arg) 273 { 274 struct apldrm_softc *sc = arg; 275 struct drm_fb_helper *fb_helper = sc->sc_ddev.fb_helper; 276 277 drm_fb_helper_blank(sc->sc_burner_fblank, fb_helper->info); 278 } 279 280 struct wsdisplay_accessops apldrm_accessops = { 281 .ioctl = apldrm_wsioctl, 282 .mmap = apldrm_wsmmap, 283 .alloc_screen = apldrm_alloc_screen, 284 .free_screen = apldrm_free_screen, 285 .show_screen = apldrm_show_screen, 286 .enter_ddb = apldrm_enter_ddb, 287 .getchar = rasops_getchar, 288 .load_font = rasops_load_font, 289 .list_font = rasops_list_font, 290 .scrollback = rasops_scrollback, 291 .burn_screen = apldrm_burner 292 }; 293 294 void 295 apldrm_attachhook(struct device *self) 296 { 297 struct apldrm_softc *sc = (struct apldrm_softc *)self; 298 struct drm_fb_helper *fb_helper; 299 struct rasops_info *ri = &sc->sc_ri; 300 struct wsemuldisplaydev_attach_args waa; 301 int idx, len, console = 0; 302 uint32_t defattr; 303 int error; 304 305 error = apple_platform_probe(&sc->sc_dev); 306 if (error) 307 return; 308 309 /* 310 * If no display coprocessors were registered with the 311 * component framework, the call above will succeed without 312 * setting up a framebuffer. Bail if we don't have one. 313 */ 314 fb_helper = sc->sc_ddev.fb_helper; 315 if (fb_helper == NULL) 316 return; 317 318 /* Claim framebuffer to prevent attaching other drivers. */ 319 len = OF_getproplen(sc->sc_node, "memory-region"); 320 idx = OF_getindex(sc->sc_node, "framebuffer", "memory-region-names"); 321 if (idx >= 0 && idx < len / sizeof(uint32_t)) { 322 uint32_t *phandles; 323 uint64_t reg[2]; 324 int node; 325 326 phandles = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 327 OF_getpropintarray(sc->sc_node, "memory-region", 328 phandles, len); 329 node = OF_getnodebyphandle(phandles[idx]); 330 if (node) { 331 if (OF_getpropint64array(node, "reg", reg, 332 sizeof(reg)) == sizeof(reg)) 333 rasops_claim_framebuffer(reg[0], reg[1], self); 334 } 335 free(phandles, M_TEMP, len); 336 } 337 338 /* 339 * Update our understanding of the console output node if 340 * we're using the framebuffer console. 341 */ 342 if (OF_is_compatible(stdout_node, "simple-framebuffer")) 343 stdout_node = sc->sc_node; 344 345 if (sc->sc_node == stdout_node) 346 console = 1; 347 348 ri->ri_hw = sc; 349 ri->ri_bits = fb_helper->info->screen_buffer; 350 ri->ri_flg = RI_CENTER | RI_VCONS | RI_WRONLY; 351 ri->ri_depth = fb_helper->fb->format->cpp[0] * 8; 352 ri->ri_stride = fb_helper->fb->pitches[0]; 353 ri->ri_width = fb_helper->info->var.xres; 354 ri->ri_height = fb_helper->info->var.yres; 355 356 switch (fb_helper->fb->format->format) { 357 case DRM_FORMAT_XRGB8888: 358 case DRM_FORMAT_ARGB8888: 359 ri->ri_rnum = 8; 360 ri->ri_rpos = 16; 361 ri->ri_gnum = 8; 362 ri->ri_gpos = 8; 363 ri->ri_bnum = 8; 364 ri->ri_bpos = 0; 365 break; 366 case DRM_FORMAT_XRGB2101010: 367 ri->ri_rnum = 10; 368 ri->ri_rpos = 20; 369 ri->ri_gnum = 10; 370 ri->ri_gpos = 10; 371 ri->ri_bnum = 10; 372 ri->ri_bpos = 0; 373 break; 374 } 375 376 rasops_init(ri, 160, 160); 377 378 strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name)); 379 sc->sc_wsd.capabilities = ri->ri_caps; 380 sc->sc_wsd.nrows = ri->ri_rows; 381 sc->sc_wsd.ncols = ri->ri_cols; 382 sc->sc_wsd.textops = &ri->ri_ops; 383 sc->sc_wsd.fontwidth = ri->ri_font->fontwidth; 384 sc->sc_wsd.fontheight = ri->ri_font->fontheight; 385 386 sc->sc_scrlist[0] = &sc->sc_wsd; 387 sc->sc_wsl.nscreens = 1; 388 sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist; 389 390 task_set(&sc->sc_switchtask, apldrm_doswitch, ri); 391 task_set(&sc->sc_burner_task, apldrm_burner_cb, sc); 392 393 if (console) { 394 ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr); 395 wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active, 396 ri->ri_ccol, ri->ri_crow, defattr); 397 } 398 399 memset(&waa, 0, sizeof(waa)); 400 waa.scrdata = &sc->sc_wsl; 401 waa.accessops = &apldrm_accessops; 402 waa.accesscookie = ri; 403 waa.console = console; 404 405 printf("%s: %dx%d, %dbpp\n", sc->sc_dev.dev.dv_xname, 406 ri->ri_width, ri->ri_height, ri->ri_depth); 407 408 config_found_sm(self, &waa, wsemuldisplaydevprint, 409 wsemuldisplaydevsubmatch); 410 } 411