xref: /openbsd/sys/dev/pci/drm/apple/apldrm.c (revision 5dea098c)
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