/* $OpenBSD: apldrm.c,v 1.2 2024/01/29 14:52:25 kettenis Exp $ */ /* * Copyright (c) 2023 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include struct apldrm_softc { struct platform_device sc_dev; struct drm_device sc_ddev; int sc_node; struct rasops_info sc_ri; struct wsscreen_descr sc_wsd; struct wsscreen_list sc_wsl; struct wsscreen_descr *sc_scrlist[1]; void (*sc_switchcb)(void *, int, int); void *sc_switchcbarg; void *sc_switchcookie; struct task sc_switchtask; int sc_burner_fblank; struct task sc_burner_task; }; #include "apple_drv.c" int apldrm_match(struct device *, void *, void *); void apldrm_attach(struct device *, struct device *, void *); int apldrm_activate(struct device *, int); const struct cfattach apldrm_ca = { sizeof (struct apldrm_softc), apldrm_match, apldrm_attach, NULL, apldrm_activate }; struct cfdriver apldrm_cd = { NULL, "apldrm", DV_DULL }; void apldrm_attachhook(struct device *); int apldrm_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; return OF_is_compatible(faa->fa_node, "apple,display-subsystem"); } void apldrm_attach(struct device *parent, struct device *self, void *aux) { struct apldrm_softc *sc = (struct apldrm_softc *)self; struct fdt_attach_args *faa = aux; sc->sc_node = faa->fa_node; printf("\n"); sc->sc_dev.faa = faa; platform_device_register(&sc->sc_dev); drm_attach_platform((struct drm_driver *)&apple_drm_driver, faa->fa_iot, faa->fa_dmat, self, &sc->sc_ddev); config_mountroot(self, apldrm_attachhook); } int apldrm_activate(struct device *self, int act) { int rv; switch (act) { case DVACT_QUIESCE: rv = config_activate_children(self, act); apple_platform_suspend(self); break; case DVACT_WAKEUP: apple_platform_resume(self); rv = config_activate_children(self, act); break; default: rv = config_activate_children(self, act); break; } return rv; } int apldrm_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) { struct rasops_info *ri = v; struct apldrm_softc *sc = ri->ri_hw; struct wsdisplay_param *dp = (struct wsdisplay_param *)data; struct wsdisplay_fbinfo *wdf; struct backlight_device *bd; bd = backlight_device_get_by_name("apple-panel-bl"); switch (cmd) { case WSDISPLAYIO_GTYPE: *(u_int *)data = WSDISPLAY_TYPE_KMS; return 0; case WSDISPLAYIO_GINFO: wdf = (struct wsdisplay_fbinfo *)data; wdf->width = ri->ri_width; wdf->height = ri->ri_height; wdf->depth = ri->ri_depth; wdf->stride = ri->ri_stride; wdf->offset = 0; /* XXX */ wdf->cmsize = 0; return 0; case WSDISPLAYIO_GETPARAM: if (bd == NULL) return -1; switch (dp->param) { case WSDISPLAYIO_PARAM_BRIGHTNESS: dp->min = 0; dp->max = bd->props.max_brightness; dp->curval = bd->props.brightness; return (dp->max > dp->min) ? 0 : -1; } break; case WSDISPLAYIO_SETPARAM: if (bd == NULL) return -1; switch (dp->param) { case WSDISPLAYIO_PARAM_BRIGHTNESS: bd->props.brightness = dp->curval; backlight_update_status(bd); knote_locked(&sc->sc_ddev.note, NOTE_CHANGE); return 0; } break; case WSDISPLAYIO_SVIDEO: case WSDISPLAYIO_GVIDEO: return 0; } return (-1); } paddr_t apldrm_wsmmap(void *v, off_t off, int prot) { return (-1); } int apldrm_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, int *curxp, int *curyp, uint32_t *attrp) { return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp); } void apldrm_free_screen(void *v, void *cookie) { return rasops_free_screen(v, cookie); } void apldrm_doswitch(void *v) { struct rasops_info *ri = v; struct apldrm_softc *sc = ri->ri_hw; struct drm_fb_helper *fb_helper = sc->sc_ddev.fb_helper; rasops_show_screen(ri, sc->sc_switchcookie, 0, NULL, NULL); drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); if (sc->sc_switchcb) (sc->sc_switchcb)(sc->sc_switchcbarg, 0, 0); } int apldrm_show_screen(void *v, void *cookie, int waitok, void (*cb)(void *, int, int), void *cbarg) { struct rasops_info *ri = v; struct apldrm_softc *sc = ri->ri_hw; if (cookie == ri->ri_active) return (0); sc->sc_switchcb = cb; sc->sc_switchcbarg = cbarg; sc->sc_switchcookie = cookie; if (cb) { task_add(systq, &sc->sc_switchtask); return (EAGAIN); } apldrm_doswitch(v); return (0); } void apldrm_enter_ddb(void *v, void *cookie) { struct rasops_info *ri = v; struct apldrm_softc *sc = ri->ri_hw; struct drm_fb_helper *fb_helper = sc->sc_ddev.fb_helper; if (cookie == ri->ri_active) return; rasops_show_screen(ri, cookie, 0, NULL, NULL); drm_fb_helper_debug_enter(fb_helper->info); } void apldrm_burner(void *v, u_int on, u_int flags) { struct rasops_info *ri = v; struct apldrm_softc *sc = ri->ri_hw; task_del(systq, &sc->sc_burner_task); if (on) sc->sc_burner_fblank = FB_BLANK_UNBLANK; else { if (flags & WSDISPLAY_BURN_VBLANK) sc->sc_burner_fblank = FB_BLANK_VSYNC_SUSPEND; else sc->sc_burner_fblank = FB_BLANK_NORMAL; } /* * Setting the DPMS mode may sleep while waiting for vblank so * hand things off to a taskq. */ task_add(systq, &sc->sc_burner_task); } void apldrm_burner_cb(void *arg) { struct apldrm_softc *sc = arg; struct drm_fb_helper *fb_helper = sc->sc_ddev.fb_helper; drm_fb_helper_blank(sc->sc_burner_fblank, fb_helper->info); } struct wsdisplay_accessops apldrm_accessops = { .ioctl = apldrm_wsioctl, .mmap = apldrm_wsmmap, .alloc_screen = apldrm_alloc_screen, .free_screen = apldrm_free_screen, .show_screen = apldrm_show_screen, .enter_ddb = apldrm_enter_ddb, .getchar = rasops_getchar, .load_font = rasops_load_font, .list_font = rasops_list_font, .scrollback = rasops_scrollback, .burn_screen = apldrm_burner }; void apldrm_attachhook(struct device *self) { struct apldrm_softc *sc = (struct apldrm_softc *)self; struct drm_fb_helper *fb_helper; struct rasops_info *ri = &sc->sc_ri; struct wsemuldisplaydev_attach_args waa; int idx, len, console = 0; uint32_t defattr; int error; error = apple_platform_probe(&sc->sc_dev); if (error) return; /* * If no display coprocessors were registered with the * component framework, the call above will succeed without * setting up a framebuffer. Bail if we don't have one. */ fb_helper = sc->sc_ddev.fb_helper; if (fb_helper == NULL) return; /* Claim framebuffer to prevent attaching other drivers. */ len = OF_getproplen(sc->sc_node, "memory-region"); idx = OF_getindex(sc->sc_node, "framebuffer", "memory-region-names"); if (idx >= 0 && idx < len / sizeof(uint32_t)) { uint32_t *phandles; uint64_t reg[2]; int node; phandles = malloc(len, M_TEMP, M_WAITOK | M_ZERO); OF_getpropintarray(sc->sc_node, "memory-region", phandles, len); node = OF_getnodebyphandle(phandles[idx]); if (node) { if (OF_getpropint64array(node, "reg", reg, sizeof(reg)) == sizeof(reg)) rasops_claim_framebuffer(reg[0], reg[1], self); } free(phandles, M_TEMP, len); } /* * Update our understanding of the console output node if * we're using the framebuffer console. */ if (OF_is_compatible(stdout_node, "simple-framebuffer")) stdout_node = sc->sc_node; if (sc->sc_node == stdout_node) console = 1; ri->ri_hw = sc; ri->ri_bits = fb_helper->info->screen_buffer; ri->ri_flg = RI_CENTER | RI_VCONS | RI_WRONLY; ri->ri_depth = fb_helper->fb->format->cpp[0] * 8; ri->ri_stride = fb_helper->fb->pitches[0]; ri->ri_width = fb_helper->info->var.xres; ri->ri_height = fb_helper->info->var.yres; switch (fb_helper->fb->format->format) { case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: ri->ri_rnum = 8; ri->ri_rpos = 16; ri->ri_gnum = 8; ri->ri_gpos = 8; ri->ri_bnum = 8; ri->ri_bpos = 0; break; case DRM_FORMAT_XRGB2101010: ri->ri_rnum = 10; ri->ri_rpos = 20; ri->ri_gnum = 10; ri->ri_gpos = 10; ri->ri_bnum = 10; ri->ri_bpos = 0; break; } rasops_init(ri, 160, 160); strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name)); sc->sc_wsd.capabilities = ri->ri_caps; sc->sc_wsd.nrows = ri->ri_rows; sc->sc_wsd.ncols = ri->ri_cols; sc->sc_wsd.textops = &ri->ri_ops; sc->sc_wsd.fontwidth = ri->ri_font->fontwidth; sc->sc_wsd.fontheight = ri->ri_font->fontheight; sc->sc_scrlist[0] = &sc->sc_wsd; sc->sc_wsl.nscreens = 1; sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist; task_set(&sc->sc_switchtask, apldrm_doswitch, ri); task_set(&sc->sc_burner_task, apldrm_burner_cb, sc); if (console) { ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr); wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active, ri->ri_ccol, ri->ri_crow, defattr); } memset(&waa, 0, sizeof(waa)); waa.scrdata = &sc->sc_wsl; waa.accessops = &apldrm_accessops; waa.accesscookie = ri; waa.console = console; printf("%s: %dx%d, %dbpp\n", sc->sc_dev.dev.dv_xname, ri->ri_width, ri->ri_height, ri->ri_depth); config_found_sm(self, &waa, wsemuldisplaydevprint, wsemuldisplaydevsubmatch); }