#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;
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;
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;
}
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;
fb_helper = sc->sc_ddev.fb_helper;
if (fb_helper == NULL)
return;
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);
}
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);
}