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
apldrm_match(struct device * parent,void * match,void * aux)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
apldrm_attach(struct device * parent,struct device * self,void * aux)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
apldrm_activate(struct device * self,int act)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
apldrm_wsioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)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
apldrm_wsmmap(void * v,off_t off,int prot)178 apldrm_wsmmap(void *v, off_t off, int prot)
179 {
180 return (-1);
181 }
182
183 int
apldrm_alloc_screen(void * v,const struct wsscreen_descr * type,void ** cookiep,int * curxp,int * curyp,uint32_t * attrp)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
apldrm_free_screen(void * v,void * cookie)191 apldrm_free_screen(void *v, void *cookie)
192 {
193 return rasops_free_screen(v, cookie);
194 }
195
196 void
apldrm_doswitch(void * v)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
apldrm_show_screen(void * v,void * cookie,int waitok,void (* cb)(void *,int,int),void * cbarg)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
apldrm_enter_ddb(void * v,void * cookie)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
apldrm_burner(void * v,u_int on,u_int flags)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
apldrm_burner_cb(void * arg)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
apldrm_attachhook(struct device * self)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