diff options
Diffstat (limited to 'sys/dev/pci/drm/apple/apldrm.c')
-rw-r--r-- | sys/dev/pci/drm/apple/apldrm.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/sys/dev/pci/drm/apple/apldrm.c b/sys/dev/pci/drm/apple/apldrm.c new file mode 100644 index 00000000000..b860463380b --- /dev/null +++ b/sys/dev/pci/drm/apple/apldrm.c @@ -0,0 +1,401 @@ +/* $OpenBSD: apldrm.c,v 1.1 2024/01/22 18:54:01 kettenis Exp $ */ +/* + * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org> + * + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/fdt.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/fdt.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsdisplayvar.h> +#include <dev/rasops/rasops.h> + +#include <linux/platform_device.h> + +#include <drm/drm_drv.h> +#include <drm/drm_framebuffer.h> + +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; + int idx, len, node; + uint32_t *phandles; + uint64_t reg[2]; + + sc->sc_node = faa->fa_node; + + /* Claim framebuffer to prevent attaching other drivers. */ + len = OF_getproplen(faa->fa_node, "memory-region"); + idx = OF_getindex(faa->fa_node, "framebuffer", "memory-region-names"); + if (idx >= 0 && idx < len / sizeof(uint32_t)) { + phandles = malloc(len, M_TEMP, M_WAITOK | M_ZERO); + OF_getpropintarray(faa->fa_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; + + 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 console = 0; + uint32_t defattr; + int error; + + error = apple_platform_probe(&sc->sc_dev); + if (error) + return; + + if (sc->sc_node == stdout_node) + console = 1; + + fb_helper = sc->sc_ddev.fb_helper; + 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); +} |