1/* $NetBSD: sunxi_debe.c,v 1.17 2024/02/02 22:14:04 andvar Exp $ */
2
3/*-
4 * Copyright (c) 2018 Manuel Bouyer <bouyer@antioche.eu.org>
5 * All rights reserved.
6 *
7 * Copyright (c) 2014 Jared D. McNeill <jmcneill@invisible.ca>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include "genfb.h"
33
34#ifndef SUNXI_DEBE_VIDEOMEM
35#define SUNXI_DEBE_VIDEOMEM	(16 * 1024 * 1024)
36#endif
37
38#define SUNXI_DEBE_CURMAX	64
39
40#include <sys/cdefs.h>
41__KERNEL_RCSID(0, "$NetBSD: sunxi_debe.c,v 1.17 2024/02/02 22:14:04 andvar Exp $");
42
43#include <sys/param.h>
44#include <sys/bus.h>
45#include <sys/device.h>
46#include <sys/intr.h>
47#include <sys/systm.h>
48#include <sys/kernel.h>
49#include <sys/mutex.h>
50#include <sys/condvar.h>
51
52#include <dev/fdt/fdtvar.h>
53#include <dev/fdt/fdt_port.h>
54
55#include <dev/videomode/videomode.h>
56#include <dev/wscons/wsconsio.h>
57#include <dev/wsfb/genfbvar.h>
58
59#include <arm/sunxi/sunxi_debereg.h>
60#include <arm/sunxi/sunxi_display.h>
61#include <arm/sunxi/sunxi_platform.h>
62#include <machine/bootconfig.h>
63
64enum sunxi_debe_type {
65	DEBE_A10 = 1,
66};
67
68struct sunxi_debe_softc {
69	device_t sc_dev;
70	device_t sc_fbdev;
71	enum sunxi_debe_type sc_type;
72	bus_space_tag_t sc_bst;
73	bus_space_handle_t sc_bsh;
74	bus_dma_tag_t sc_dmat;
75
76	struct clk *sc_clk_ahb;
77	struct clk *sc_clk_mod;
78	struct clk *sc_clk_ram;
79
80	struct fdtbus_reset *sc_rst;
81
82	bus_dma_segment_t sc_dmasegs[1];
83	bus_size_t sc_dmasize;
84	bus_dmamap_t sc_dmamap;
85	void *sc_dmap;
86
87	bool sc_cursor_enable;
88	int sc_cursor_x, sc_cursor_y;
89	int sc_hot_x, sc_hot_y;
90	uint8_t sc_cursor_bitmap[8 * SUNXI_DEBE_CURMAX];
91	uint8_t sc_cursor_mask[8 * SUNXI_DEBE_CURMAX];
92
93	int	sc_phandle;
94	struct fdt_device_ports sc_ports;
95	struct fdt_endpoint *sc_out_ep;
96	int sc_unit; /* debe0 or debe1 */
97};
98
99#define DEBE_READ(sc, reg) \
100    bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
101#define DEBE_WRITE(sc, reg, val) \
102    bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
103
104static const struct device_compatible_entry compat_data[] = {
105	{ .compat = "allwinner,sun4i-a10-display-backend", .value = DEBE_A10 },
106	{ .compat = "allwinner,sun7i-a20-display-backend", .value = DEBE_A10 },
107	DEVICE_COMPAT_EOL
108};
109
110struct sunxifb_attach_args {
111	void *afb_fb;
112	uint32_t afb_width;
113	uint32_t afb_height;
114	bus_dma_tag_t afb_dmat;
115	bus_dma_segment_t *afb_dmasegs;
116	int afb_ndmasegs;
117};
118
119static void	sunxi_debe_ep_connect(device_t, struct fdt_endpoint *, bool);
120static int	sunxi_debe_ep_enable(device_t, struct fdt_endpoint *, bool);
121static int	sunxi_debe_match(device_t, cfdata_t, void *);
122static void	sunxi_debe_attach(device_t, device_t, void *);
123
124static int	sunxi_debe_alloc_videomem(struct sunxi_debe_softc *);
125static void	sunxi_debe_setup_fbdev(struct sunxi_debe_softc *,
126				      const struct videomode *);
127
128static int	sunxi_debe_set_curpos(struct sunxi_debe_softc *, int, int);
129static int	sunxi_debe_set_cursor(struct sunxi_debe_softc *,
130				     struct wsdisplay_cursor *);
131static int	sunxi_debe_ioctl(device_t, u_long, void *);
132static void	sunxi_befb_set_videomode(device_t, u_int, u_int);
133void sunxi_debe_dump_regs(int);
134
135static struct sunxi_debe_softc *debe_console_sc;
136static int sunxi_simplefb_phandle = -1;
137
138CFATTACH_DECL_NEW(sunxi_debe, sizeof(struct sunxi_debe_softc),
139	sunxi_debe_match, sunxi_debe_attach, NULL, NULL);
140
141static int
142sunxi_debe_match(device_t parent, cfdata_t cf, void *aux)
143{
144	struct fdt_attach_args * const faa = aux;
145
146	return of_compatible_match(faa->faa_phandle, compat_data);
147}
148
149static void
150sunxi_debe_attach(device_t parent, device_t self, void *aux)
151{
152	struct sunxi_debe_softc *sc = device_private(self);
153	struct fdt_attach_args * const faa = aux;
154	const int phandle = faa->faa_phandle;
155	bus_addr_t addr;
156	bus_size_t size;
157	int error;
158
159	sc->sc_dev = self;
160	sc->sc_phandle = phandle;
161	sc->sc_bst = faa->faa_bst;
162	sc->sc_dmat = faa->faa_dmat;
163	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
164		aprint_error(": couldn't get registers\n");
165	}
166	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
167		aprint_error(": couldn't map registers\n");
168		return;
169	}
170
171	sc->sc_clk_ahb = fdtbus_clock_get(phandle, "ahb");
172	sc->sc_clk_mod = fdtbus_clock_get(phandle, "mod");
173	sc->sc_clk_ram = fdtbus_clock_get(phandle, "ram");
174
175	if (sc->sc_clk_ahb == NULL || sc->sc_clk_mod == NULL
176	    || sc->sc_clk_ram == NULL) {
177		aprint_error(": couldn't get clocks\n");
178		aprint_debug_dev(self, "clk ahb %s mod %s ram %s\n",
179		    sc->sc_clk_ahb == NULL ? "missing" : "present",
180		    sc->sc_clk_mod == NULL ? "missing" : "present",
181		    sc->sc_clk_ram == NULL ? "missing" : "present");
182		return;
183	}
184
185	sc->sc_rst = fdtbus_reset_get_index(phandle, 0);
186	if (sc->sc_rst == NULL) {
187		aprint_error(": couldn't get reset\n");
188		return;
189	}
190
191	sc->sc_type =
192	    of_compatible_lookup(faa->faa_phandle, compat_data)->value;
193
194	aprint_naive("\n");
195	aprint_normal(": Display Engine Backend (%s)\n",
196	    fdtbus_get_string(phandle, "name"));
197
198
199	sc->sc_dmasize = SUNXI_DEBE_VIDEOMEM;
200
201	error = sunxi_debe_alloc_videomem(sc);
202	if (error) {
203		aprint_error_dev(sc->sc_dev,
204		    "couldn't allocate video memory, error = %d\n", error);
205		return;
206	}
207
208	sc->sc_unit = -1;
209	sc->sc_ports.dp_ep_connect = sunxi_debe_ep_connect;
210	sc->sc_ports.dp_ep_enable = sunxi_debe_ep_enable;
211	fdt_ports_register(&sc->sc_ports, self, phandle, EP_OTHER);
212}
213
214static void
215sunxi_debe_doreset(void)
216{
217	device_t dev;
218	struct sunxi_debe_softc *sc;
219	int error;
220
221	for (int i = 0;;i++) {
222		dev = device_find_by_driver_unit("sunxidebe", i);
223		if (dev == NULL)
224			return;
225		sc = device_private(dev);
226
227		if (fdtbus_reset_assert(sc->sc_rst) != 0) {
228			aprint_error_dev(dev, ": couldn't assert reset\n");
229			return;
230		}
231		delay(1);
232		if (fdtbus_reset_deassert(sc->sc_rst) != 0) {
233			aprint_error_dev(dev, ": couldn't de-assert reset\n");
234			return;
235		}
236
237
238		error = clk_set_rate(sc->sc_clk_mod, 300000000);
239		if (error) {
240			aprint_error_dev(dev,
241			    "couldn't set mod clock rate (%d)\n", error);
242			return;
243		}
244
245		if (clk_enable(sc->sc_clk_ahb) != 0 ||
246		    clk_enable(sc->sc_clk_mod) != 0) {
247			aprint_error_dev(dev, ": couldn't enable clocks\n");
248			return;
249		}
250		if (clk_disable(sc->sc_clk_ram) != 0) {
251			aprint_error_dev(dev, ": couldn't disable ram clock\n");
252		}
253
254		for (unsigned int reg = 0x800; reg < 0x1000; reg += 4) {
255			DEBE_WRITE(sc, reg, 0);
256		}
257
258		DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, SUNXI_DEBE_MODCTL_EN);
259
260		DEBE_WRITE(sc, SUNXI_DEBE_HWC_PALETTE_TABLE, 0);
261
262		if (clk_disable(sc->sc_clk_ahb) != 0 ||
263		    clk_disable(sc->sc_clk_mod) != 0) {
264			aprint_error_dev(sc->sc_dev,
265			    ": couldn't disable clocks\n");
266		}
267	}
268}
269
270static void
271sunxi_debe_ep_connect(device_t self, struct fdt_endpoint *ep, bool connect)
272{
273	struct sunxi_debe_softc *sc = device_private(self);
274	struct fdt_endpoint *rep = fdt_endpoint_remote(ep);
275	int rep_idx = fdt_endpoint_index(rep);
276
277	KASSERT(device_is_a(self, "sunxidebe"));
278	if (!connect) {
279		aprint_error_dev(self, "endpoint disconnect not supported\n");
280		return;
281	}
282
283	if (fdt_endpoint_port_index(ep) == 1) {
284		bool do_print = (sc->sc_unit == -1);
285		/*
286		 * one of our output endpoints has been connected.
287		 * the remote id is our unit number
288		 */
289		if (sc->sc_unit != -1 && rep_idx != -1 &&
290		    sc->sc_unit != rep_idx) {
291			aprint_error_dev(self, ": remote id %d doesn't match"
292			    " discovered unit number %d\n",
293			    rep_idx, sc->sc_unit);
294			return;
295		}
296		if (!device_is_a(fdt_endpoint_device(rep), "sunxitcon")) {
297			aprint_error_dev(self,
298			    ": output %d connected to unknown device\n",
299			    fdt_endpoint_index(ep));
300			return;
301		}
302		if (rep_idx != -1)
303			sc->sc_unit = rep_idx;
304		else {
305			/* assume only one debe */
306			sc->sc_unit = 0;
307		}
308		if (do_print)
309			aprint_verbose_dev(self, "debe unit %d\n", sc->sc_unit);
310	}
311}
312
313static int
314sunxi_debe_alloc_videomem(struct sunxi_debe_softc *sc)
315{
316	int error, nsegs;
317
318	error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmasize, 0x1000, 0,
319	    sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK);
320	if (error)
321		return error;
322	error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs,
323	    sc->sc_dmasize, &sc->sc_dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
324	if (error)
325		goto free;
326	error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmasize, 1,
327	    sc->sc_dmasize, 0, BUS_DMA_WAITOK, &sc->sc_dmamap);
328	if (error)
329		goto unmap;
330	error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmap,
331	    sc->sc_dmasize, NULL, BUS_DMA_WAITOK);
332	if (error)
333		goto destroy;
334
335	memset(sc->sc_dmap, 0, sc->sc_dmasize);
336
337	return 0;
338
339destroy:
340	bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
341unmap:
342	bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmap, sc->sc_dmasize);
343free:
344	bus_dmamem_free(sc->sc_dmat, sc->sc_dmasegs, nsegs);
345
346	sc->sc_dmasize = 0;
347	sc->sc_dmap = NULL;
348
349	return error;
350}
351
352static void
353sunxi_debe_setup_fbdev(struct sunxi_debe_softc *sc, const struct videomode *mode)
354{
355	if (mode == NULL)
356		return;
357
358	const u_int interlace_p = !!(mode->flags & VID_INTERLACE);
359	const u_int fb_width = mode->hdisplay;
360	const u_int fb_height = (mode->vdisplay << interlace_p);
361
362	if (mode && sc->sc_fbdev == NULL) {
363		/* see if we are the console */
364		if (sunxi_simplefb_phandle >= 0) {
365			const char *cons_pipeline =
366			    fdtbus_get_string(sunxi_simplefb_phandle,
367			        "allwinner,pipeline");
368			struct fdt_endpoint *ep = fdt_endpoint_get_from_index(
369			    &sc->sc_ports, SUNXI_PORT_OUTPUT, sc->sc_unit);
370			struct fdt_endpoint *rep = fdt_endpoint_remote(ep);
371			if (sunxi_tcon_is_console(
372			    fdt_endpoint_device(rep), cons_pipeline))
373				debe_console_sc = sc;
374		} else if (debe_console_sc == NULL) {
375			if (match_bootconf_option(boot_args,
376			    "console", "fb0")) {
377				if (sc->sc_unit == 0)
378					debe_console_sc = sc;
379			} else if (match_bootconf_option(boot_args,
380			    "console", "fb1")) {
381				if (sc->sc_unit == 1)
382					debe_console_sc = sc;
383			} else if (match_bootconf_option(boot_args,
384			    "console", "fb")) {
385				/* match first activated */
386				debe_console_sc = sc;
387			}
388		}
389		struct sunxifb_attach_args afb = {
390			.afb_fb = sc->sc_dmap,
391			.afb_width = fb_width,
392			.afb_height = fb_height,
393			.afb_dmat = sc->sc_dmat,
394			.afb_dmasegs = sc->sc_dmasegs,
395			.afb_ndmasegs = 1
396		};
397		sc->sc_fbdev = config_found(sc->sc_dev, &afb, NULL, CFARGS_NONE);
398	} else if (sc->sc_fbdev != NULL) {
399		sunxi_befb_set_videomode(sc->sc_fbdev, fb_width, fb_height);
400	}
401}
402
403static int
404sunxi_debe_set_curpos(struct sunxi_debe_softc *sc, int x, int y)
405{
406	int xx, yy;
407	u_int yoff, xoff;
408
409	xoff = yoff = 0;
410	xx = x - sc->sc_hot_x;
411	yy = y - sc->sc_hot_y;
412	if (xx < 0) {
413		xoff -= xx;
414		xx = 0;
415	}
416	if (yy < 0) {
417		yoff -= yy;
418		yy = 0;
419	}
420
421	DEBE_WRITE(sc, SUNXI_DEBE_HWCCTL_REG,
422	    __SHIFTIN(yy, SUNXI_DEBE_HWCCTL_YCOOR) |
423	    __SHIFTIN(xx, SUNXI_DEBE_HWCCTL_XCOOR));
424	DEBE_WRITE(sc, SUNXI_DEBE_HWCFBCTL_REG,
425#if SUNXI_DEBE_CURMAX == 32
426	    __SHIFTIN(SUNXI_DEBE_HWCFBCTL_YSIZE_32, SUNXI_DEBE_HWCFBCTL_YSIZE) |
427	    __SHIFTIN(SUNXI_DEBE_HWCFBCTL_XSIZE_32, SUNXI_DEBE_HWCFBCTL_XSIZE) |
428#else
429	    __SHIFTIN(SUNXI_DEBE_HWCFBCTL_YSIZE_64, SUNXI_DEBE_HWCFBCTL_YSIZE) |
430	    __SHIFTIN(SUNXI_DEBE_HWCFBCTL_XSIZE_64, SUNXI_DEBE_HWCFBCTL_XSIZE) |
431#endif
432	    __SHIFTIN(SUNXI_DEBE_HWCFBCTL_FBFMT_2BPP, SUNXI_DEBE_HWCFBCTL_FBFMT) |
433	    __SHIFTIN(yoff, SUNXI_DEBE_HWCFBCTL_YCOOROFF) |
434	    __SHIFTIN(xoff, SUNXI_DEBE_HWCFBCTL_XCOOROFF));
435
436	return 0;
437}
438
439static int
440sunxi_debe_set_cursor(struct sunxi_debe_softc *sc, struct wsdisplay_cursor *cur)
441{
442	uint32_t val;
443	uint8_t r[4], g[4], b[4];
444	u_int index, count, shift, off, pcnt;
445	int i, j, idx, error;
446	uint8_t mask;
447
448	if (cur->which & WSDISPLAY_CURSOR_DOCUR) {
449		val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG);
450		if (cur->enable)
451			val |= SUNXI_DEBE_MODCTL_HWC_EN;
452		else
453			val &= ~SUNXI_DEBE_MODCTL_HWC_EN;
454		DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val);
455
456		sc->sc_cursor_enable = cur->enable;
457	}
458
459	if (cur->which & WSDISPLAY_CURSOR_DOHOT) {
460		sc->sc_hot_x = cur->hot.x;
461		sc->sc_hot_y = cur->hot.y;
462		cur->which |= WSDISPLAY_CURSOR_DOPOS;
463	}
464
465	if (cur->which & WSDISPLAY_CURSOR_DOPOS) {
466		sunxi_debe_set_curpos(sc, cur->pos.x, cur->pos.y);
467	}
468
469	if (cur->which & WSDISPLAY_CURSOR_DOCMAP) {
470		index = cur->cmap.index;
471		count = cur->cmap.count;
472		if (index >= 2 || count > 2 - index)
473			return EINVAL;
474		error = copyin(cur->cmap.red, &r[index], count);
475		if (error)
476			return error;
477		error = copyin(cur->cmap.green, &g[index], count);
478		if (error)
479			return error;
480		error = copyin(cur->cmap.blue, &b[index], count);
481		if (error)
482			return error;
483
484		for (i = index; i < (index + count); i++) {
485			DEBE_WRITE(sc,
486			    SUNXI_DEBE_HWC_PALETTE_TABLE + (4 * (i + 2)),
487			    (r[i] << 16) | (g[i] << 8) | b[i] | 0xff000000);
488		}
489	}
490
491	if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) {
492		error = copyin(cur->mask, sc->sc_cursor_mask,
493		    SUNXI_DEBE_CURMAX * 8);
494		if (error)
495			return error;
496		error = copyin(cur->image, sc->sc_cursor_bitmap,
497		    SUNXI_DEBE_CURMAX * 8);
498		if (error)
499			return error;
500	}
501
502	if (cur->which & (WSDISPLAY_CURSOR_DOCMAP|WSDISPLAY_CURSOR_DOSHAPE)) {
503		for (i = 0, pcnt = 0; i < SUNXI_DEBE_CURMAX * 8; i++) {
504			for (j = 0, mask = 1; j < 8; j++, mask <<= 1, pcnt++) {
505				idx = ((sc->sc_cursor_mask[i] & mask) ? 2 : 0) |
506				    ((sc->sc_cursor_bitmap[i] & mask) ? 1 : 0);
507				off = (pcnt >> 4) * 4;
508				shift = (pcnt & 0xf) * 2;
509				val = DEBE_READ(sc,
510				    SUNXI_DEBE_HWC_PATTERN_BLOCK + off);
511				val &= ~(3 << shift);
512				val |= (idx << shift);
513				DEBE_WRITE(sc,
514				    SUNXI_DEBE_HWC_PATTERN_BLOCK + off, val);
515			}
516		}
517	}
518
519	return 0;
520}
521
522static int
523sunxi_debe_ep_enable(device_t dev, struct fdt_endpoint *ep, bool enable)
524{
525	struct sunxi_debe_softc *sc;
526	uint32_t val;
527
528	KASSERT(device_is_a(dev, "sunxidebe"));
529	sc = device_private(dev);
530
531	if (enable) {
532		if (clk_enable(sc->sc_clk_ram) != 0) {
533			device_printf(dev,
534			    ": warning: failed to enable ram clock\n");
535		}
536		val = DEBE_READ(sc, SUNXI_DEBE_REGBUFFCTL_REG);
537		val |= SUNXI_DEBE_REGBUFFCTL_REGLOADCTL;
538		DEBE_WRITE(sc, SUNXI_DEBE_REGBUFFCTL_REG, val);
539
540		val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG);
541		val |= SUNXI_DEBE_MODCTL_START_CTL;
542		DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val);
543#ifdef SUNXI_DEBE_DEBUG
544		sunxi_debe_dump_regs(sc->sc_unit);
545#endif
546	} else {
547		val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG);
548		val &= ~SUNXI_DEBE_MODCTL_START_CTL;
549		DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val);
550		if (clk_disable(sc->sc_clk_ram) != 0) {
551			device_printf(dev,
552			    ": warning: failed to disable ram clock\n");
553		}
554	}
555#if 0
556	for (int i = 0; i < 0x1000; i += 4) {
557		printf("DEBE 0x%04x: 0x%08x\n", i, DEBE_READ(sc, i));
558	}
559#endif
560	return 0;
561}
562
563/*
564 * FIXME 2020/10/19
565 * This function is not called actually at the moment.
566 */
567void
568sunxi_debe_set_videomode(device_t dev, const struct videomode *mode)
569{
570	struct sunxi_debe_softc *sc;
571	uint32_t val;
572
573	KASSERT(device_is_a(dev, "sunxidebe"));
574	sc = device_private(dev);
575
576	if (mode) {
577		const u_int interlace_p = !!(mode->flags & VID_INTERLACE);
578		const u_int width = mode->hdisplay;
579		const u_int height = (mode->vdisplay << interlace_p);
580		const u_int fb_width = width;
581		const u_int fb_height = height;
582		uint32_t vmem = width * height * 4;
583
584		if (vmem > sc->sc_dmasize) {
585			device_printf(sc->sc_dev,
586			    "not enough memory for %ux%u fb (req %u have %u)\n",
587			    width, height, vmem, (unsigned int)sc->sc_dmasize);
588			return;
589		}
590
591		paddr_t pa = sc->sc_dmamap->dm_segs[0].ds_addr;
592#if !defined(ALLWINNER_A80) && 0
593#define SUNXI_SDRAM_PBASE-0 0x40000000
594		/*
595		 * On 2GB systems, we need to subtract AWIN_SDRAM_PBASE from
596		 * the phys addr.
597		 */
598		if (pa >= SUNXI_SDRAM_PBASE)
599			pa -= SUNXI_SDRAM_PBASE;
600#endif
601
602		/* notify fb */
603		sunxi_debe_setup_fbdev(sc, mode);
604
605		DEBE_WRITE(sc, SUNXI_DEBE_DISSIZE_REG,
606		    ((height - 1) << 16) | (width - 1));
607		DEBE_WRITE(sc, SUNXI_DEBE_LAYSIZE_REG,
608		    ((fb_height - 1) << 16) | (fb_width - 1));
609		DEBE_WRITE(sc, SUNXI_DEBE_LAYCOOR_REG, 0);
610		DEBE_WRITE(sc, SUNXI_DEBE_LAYLINEWIDTH_REG, (fb_width << 5));
611		DEBE_WRITE(sc, SUNXI_DEBE_LAYFB_L32ADD_REG, pa << 3);
612		DEBE_WRITE(sc, SUNXI_DEBE_LAYFB_H4ADD_REG, pa >> 29);
613
614		val = DEBE_READ(sc, SUNXI_DEBE_ATTCTL1_REG);
615		val &= ~SUNXI_DEBE_ATTCTL1_LAY_FBFMT;
616		val |= __SHIFTIN(SUNXI_DEBE_ATTCTL1_LAY_FBFMT_XRGB8888,
617				 SUNXI_DEBE_ATTCTL1_LAY_FBFMT);
618		val &= ~SUNXI_DEBE_ATTCTL1_LAY_BRSWAPEN;
619		val &= ~SUNXI_DEBE_ATTCTL1_LAY_FBPS;
620#if 0 /* __ARMEB__ */
621		/*
622		 * For big endian, we dynamically override FDT to let
623		 * genfb(4) know that framebuffer is byte-swapped.
624		 * See fdt_update_fb_format() in fdt_machdep.c.
625		 */
626		val |= __SHIFTIN(SUNXI_DEBE_ATTCTL1_LAY_FBPS_32BPP_BGRA,
627				 SUNXI_DEBE_ATTCTL1_LAY_FBPS);
628#else
629		val |= __SHIFTIN(SUNXI_DEBE_ATTCTL1_LAY_FBPS_32BPP_ARGB,
630				 SUNXI_DEBE_ATTCTL1_LAY_FBPS);
631#endif
632		DEBE_WRITE(sc, SUNXI_DEBE_ATTCTL1_REG, val);
633
634		val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG);
635		val |= SUNXI_DEBE_MODCTL_LAY0_EN;
636		if (interlace_p) {
637			val |= SUNXI_DEBE_MODCTL_ITLMOD_EN;
638		} else {
639			val &= ~SUNXI_DEBE_MODCTL_ITLMOD_EN;
640		}
641		val &= ~SUNXI_DEBE_MODCTL_OUT_SEL;
642		if (sc->sc_unit == 1) {
643			val |= __SHIFTIN(SUNXI_DEBE_MODCTL_OUT_SEL_LCD1,
644			    SUNXI_DEBE_MODCTL_OUT_SEL);
645		}
646		DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val);
647	} else {
648		/* disable */
649		val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG);
650		val &= ~SUNXI_DEBE_MODCTL_LAY0_EN;
651		val &= ~SUNXI_DEBE_MODCTL_START_CTL;
652		DEBE_WRITE(sc, SUNXI_DEBE_MODCTL_REG, val);
653
654		/* notify fb */
655		sunxi_debe_setup_fbdev(sc, mode);
656	}
657}
658
659static int
660sunxi_debe_ioctl(device_t self, u_long cmd, void *data)
661{
662	struct sunxi_debe_softc *sc = device_private(self);
663	struct wsdisplay_curpos *cp;
664	uint32_t val;
665	int enable;
666
667	switch (cmd) {
668	case WSDISPLAYIO_SVIDEO:
669		enable = *(int *)data;
670		val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG);
671		if (enable) {
672			if (val & SUNXI_DEBE_MODCTL_START_CTL) {
673				/* already enabled */
674				return 0;
675			}
676		} else {
677			if ((val & SUNXI_DEBE_MODCTL_START_CTL) == 0) {
678				/* already disabled */
679				return 0;
680			}
681		}
682		return fdt_endpoint_enable(sc->sc_out_ep, enable);
683	case WSDISPLAYIO_GVIDEO:
684		val = DEBE_READ(sc, SUNXI_DEBE_MODCTL_REG);
685		*(int *)data = !!(val & SUNXI_DEBE_MODCTL_LAY0_EN);
686		return 0;
687	case WSDISPLAYIO_GCURPOS:
688		cp = data;
689		cp->x = sc->sc_cursor_x;
690		cp->y = sc->sc_cursor_y;
691		return 0;
692	case WSDISPLAYIO_SCURPOS:
693		cp = data;
694		return sunxi_debe_set_curpos(sc, cp->x, cp->y);
695	case WSDISPLAYIO_GCURMAX:
696		cp = data;
697		cp->x = SUNXI_DEBE_CURMAX;
698		cp->y = SUNXI_DEBE_CURMAX;
699		return 0;
700	case WSDISPLAYIO_SCURSOR:
701		return sunxi_debe_set_cursor(sc, data);
702	}
703
704	return EPASSTHROUGH;
705}
706
707/* genfb attachment */
708
709struct sunxi_befb_softc {
710	struct genfb_softc sc_gen;
711	device_t sc_debedev;
712
713	bus_dma_tag_t sc_dmat;
714	bus_dma_segment_t *sc_dmasegs;
715	int sc_ndmasegs;
716};
717
718static device_t	sunxi_befb_consoledev = NULL;
719
720static int	sunxi_befb_match(device_t, cfdata_t, void *);
721static void	sunxi_befb_attach(device_t, device_t, void *);
722
723static int	sunxi_befb_ioctl(void *, void *, u_long, void *, int, lwp_t *);
724static paddr_t	sunxi_befb_mmap(void *, void *, off_t, int);
725static bool	sunxi_befb_shutdown(device_t, int);
726
727CFATTACH_DECL_NEW(sunxi_befb, sizeof(struct sunxi_befb_softc),
728	sunxi_befb_match, sunxi_befb_attach, NULL, NULL);
729
730static int
731sunxi_befb_match(device_t parent, cfdata_t cf, void *aux)
732{
733	return 1;
734}
735
736static void
737sunxi_befb_attach(device_t parent, device_t self, void *aux)
738{
739	struct sunxi_befb_softc *sc = device_private(self);
740	struct sunxifb_attach_args * const afb = aux;
741	prop_dictionary_t cfg = device_properties(self);
742	struct genfb_ops ops;
743
744	sc->sc_gen.sc_dev = self;
745	sc->sc_debedev = parent;
746	sc->sc_dmat = afb->afb_dmat;
747	sc->sc_dmasegs = afb->afb_dmasegs;
748	sc->sc_ndmasegs = afb->afb_ndmasegs;
749
750	prop_dictionary_set_uint32(cfg, "width", afb->afb_width);
751	prop_dictionary_set_uint32(cfg, "height", afb->afb_height);
752	prop_dictionary_set_uint8(cfg, "depth", 32);
753	prop_dictionary_set_uint16(cfg, "linebytes", afb->afb_width * 4);
754	prop_dictionary_set_uint32(cfg, "address", 0);
755	prop_dictionary_set_uint32(cfg, "virtual_address",
756	    (uintptr_t)afb->afb_fb);
757
758	genfb_init(&sc->sc_gen);
759
760	if (sc->sc_gen.sc_width == 0 || sc->sc_gen.sc_fbsize == 0) {
761		aprint_normal(": disabled\n");
762		return;
763	}
764
765	pmf_device_register1(self, NULL, NULL, sunxi_befb_shutdown);
766
767	memset(&ops, 0, sizeof(ops));
768	ops.genfb_ioctl = sunxi_befb_ioctl;
769	ops.genfb_mmap = sunxi_befb_mmap;
770
771	aprint_naive("\n");
772
773	bool is_console = (debe_console_sc == device_private(parent));
774	if (is_console)
775		aprint_normal(": switching to framebuffer console\n");
776	else
777		aprint_normal("\n");
778
779#ifdef WSDISPLAY_MULTICONS
780	/*
781	 * if we support multicons, only the first framebuffer is console,
782	 * unless we already know which framebuffer will be the console
783	 */
784	if (!is_console && debe_console_sc == NULL &&
785	    sunxi_befb_consoledev == NULL)
786		is_console = true;
787#endif
788	prop_dictionary_set_bool(cfg, "is_console", is_console);
789	if (is_console) {
790		KASSERT(sunxi_befb_consoledev == NULL);
791		sunxi_befb_consoledev = self;
792	}
793
794	genfb_attach(&sc->sc_gen, &ops);
795}
796
797static int
798sunxi_befb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l)
799{
800	struct sunxi_befb_softc *sc = v;
801	struct wsdisplayio_bus_id *busid;
802	struct wsdisplayio_fbinfo *fbi;
803	struct rasops_info *ri;
804	int error;
805
806	switch (cmd) {
807	case WSDISPLAYIO_GTYPE:
808		*(u_int *)data = WSDISPLAY_TYPE_ALLWINNER;
809		return 0;
810	case WSDISPLAYIO_GET_BUSID:
811		busid = data;
812		busid->bus_type = WSDISPLAYIO_BUS_SOC;
813		return 0;
814	case WSDISPLAYIO_GET_FBINFO:
815		fbi = data;
816		ri = &sc->sc_gen.vd.active->scr_ri;
817		error = wsdisplayio_get_fbinfo(ri, fbi);
818		if (error == 0) {
819			fbi->fbi_flags |= WSFB_VRAM_IS_RAM;
820			fbi->fbi_fbsize = sc->sc_dmasegs[0].ds_len;
821		}
822		return error;
823	case WSDISPLAYIO_SVIDEO:
824	case WSDISPLAYIO_GVIDEO:
825	case WSDISPLAYIO_GCURPOS:
826	case WSDISPLAYIO_SCURPOS:
827	case WSDISPLAYIO_GCURMAX:
828	case WSDISPLAYIO_SCURSOR:
829		return sunxi_debe_ioctl(sc->sc_debedev, cmd, data);
830	default:
831		return EPASSTHROUGH;
832	}
833}
834
835static paddr_t
836sunxi_befb_mmap(void *v, void *vs, off_t off, int prot)
837{
838	struct sunxi_befb_softc *sc = v;
839
840	if (off < 0 || off >= sc->sc_dmasegs[0].ds_len)
841		return -1;
842
843	return bus_dmamem_mmap(sc->sc_dmat, sc->sc_dmasegs, sc->sc_ndmasegs,
844	    off, prot, BUS_DMA_PREFETCHABLE);
845}
846
847static bool
848sunxi_befb_shutdown(device_t self, int flags)
849{
850	genfb_enable_polling(self);
851	return true;
852}
853
854static void
855sunxi_befb_set_videomode(device_t dev, u_int width, u_int height)
856{
857	struct sunxi_befb_softc *sc = device_private(dev);
858
859	if (sc->sc_gen.sc_width != width || sc->sc_gen.sc_height != height) {
860		device_printf(sc->sc_gen.sc_dev,
861		    "mode switching not yet supported\n");
862	}
863}
864
865int
866sunxi_debe_pipeline(int phandle, bool active)
867{
868	device_t dev;
869	struct sunxi_debe_softc *sc;
870	struct fdt_endpoint *ep;
871	int i, error;
872	static bool reset_done = false;
873
874	if (!active)
875		return EOPNOTSUPP;
876
877	for (i = 0;;i++) {
878		dev = device_find_by_driver_unit("sunxidebe", i);
879		if (dev == NULL)
880			return ENODEV;
881		sc = device_private(dev);
882		if (sc->sc_phandle == phandle)
883			break;
884	}
885	if (!reset_done) {
886		sunxi_debe_doreset();
887		sunxi_tcon_doreset();
888		sunxi_hdmi_doreset();
889		reset_done = true;
890	}
891
892	aprint_normal("activate %s\n", device_xname(dev));
893	if (clk_enable(sc->sc_clk_ahb) != 0 ||
894	    clk_enable(sc->sc_clk_mod) != 0) {
895		aprint_error_dev(dev, "couldn't enable clocks\n");
896		return EIO;
897	}
898	/* connect debd0 to tcon0, debe1 to tcon1 */
899	ep = fdt_endpoint_get_from_index(&sc->sc_ports, SUNXI_PORT_OUTPUT,
900	    sc->sc_unit);
901	if (ep == NULL) {
902		aprint_error_dev(dev, "no output endpoint for %d\n",
903		    sc->sc_unit);
904		return ENODEV;
905	}
906	error = fdt_endpoint_activate(ep, true);
907	if (error)
908		return error;
909
910	sc->sc_out_ep = ep;
911	error = fdt_endpoint_enable(ep, true);
912	return error;
913}
914
915/*
916 * we don't want to take over console at this time - simplefb will
917 * do a better job than us. We will take over later.
918 * But we want to record the /chose/framebuffer phandle if there is one
919 */
920
921static const struct device_compatible_entry simplefb_compat_data[] = {
922	{ .compat = "allwinner,simple-framebuffer" },
923	DEVICE_COMPAT_EOL
924};
925
926static int
927sunxidebe_console_match(int phandle)
928{
929	if (of_compatible_match(phandle, simplefb_compat_data)) {
930		sunxi_simplefb_phandle = phandle;
931	}
932	return 0;
933}
934
935static void
936sunxidebe_console_consinit(struct fdt_attach_args *faa, u_int uart_freq)
937{
938	panic("sunxidebe_console_consinit");
939}
940
941static const struct fdt_console sunxidebe_fdt_console = {
942	.match = sunxidebe_console_match,
943	.consinit = sunxidebe_console_consinit
944};
945
946FDT_CONSOLE(sunxidebe, &sunxidebe_fdt_console);
947
948#if defined(SUNXI_DEBE_DEBUG)
949void
950sunxi_debe_dump_regs(int u)
951{
952	static const struct {
953		const char *name;
954		uint16_t reg;
955	} regs[] = {
956		{ "SUNXI_DEBE_MODCTL_REG", SUNXI_DEBE_MODCTL_REG},
957		{ "SUNXI_DEBE_BACKCOLOR_REG", SUNXI_DEBE_BACKCOLOR_REG},
958		{ "SUNXI_DEBE_DISSIZE_REG", SUNXI_DEBE_DISSIZE_REG},
959		{ "SUNXI_DEBE_LAYSIZE_REG", SUNXI_DEBE_LAYSIZE_REG},
960		{ "SUNXI_DEBE_LAYCOOR_REG", SUNXI_DEBE_LAYCOOR_REG},
961		{ "SUNXI_DEBE_LAYLINEWIDTH_REG", SUNXI_DEBE_LAYLINEWIDTH_REG},
962		{ "SUNXI_DEBE_LAYFB_L32ADD_REG", SUNXI_DEBE_LAYFB_L32ADD_REG},
963		{ "SUNXI_DEBE_LAYFB_H4ADD_REG", SUNXI_DEBE_LAYFB_H4ADD_REG},
964		{ "SUNXI_DEBE_REGBUFFCTL_REG", SUNXI_DEBE_REGBUFFCTL_REG},
965		{ "SUNXI_DEBE_CKMAX_REG", SUNXI_DEBE_CKMAX_REG},
966		{ "SUNXI_DEBE_CKMIN_REG", SUNXI_DEBE_CKMIN_REG},
967		{ "SUNXI_DEBE_CKCFG_REG", SUNXI_DEBE_CKCFG_REG},
968		{ "SUNXI_DEBE_ATTCTL0_REG", SUNXI_DEBE_ATTCTL0_REG},
969		{ "SUNXI_DEBE_ATTCTL1_REG", SUNXI_DEBE_ATTCTL1_REG},
970		{ "SUNXI_DEBE_HWCCTL_REG", SUNXI_DEBE_HWCCTL_REG},
971		{ "SUNXI_DEBE_HWCFBCTL_REG", SUNXI_DEBE_HWCFBCTL_REG},
972		{ "SUNXI_DEBE_WBCTL_REG", SUNXI_DEBE_WBCTL_REG},
973		{ "SUNXI_DEBE_WBADD_REG", SUNXI_DEBE_WBADD_REG},
974		{ "SUNXI_DEBE_WBLINEWIDTH_REG", SUNXI_DEBE_WBLINEWIDTH_REG},
975		{ "SUNXI_DEBE_IYUVCTL_REG", SUNXI_DEBE_IYUVCTL_REG},
976		{ "SUNXI_DEBE_IYUVADD_REG", SUNXI_DEBE_IYUVADD_REG},
977		{ "SUNXI_DEBE_IYUVLINEWIDTH_REG", SUNXI_DEBE_IYUVLINEWIDTH_REG},
978		{ "SUNXI_DEBE_YGCOEF_REG", SUNXI_DEBE_YGCOEF_REG},
979		{ "SUNXI_DEBE_YGCONS_REG", SUNXI_DEBE_YGCONS_REG},
980		{ "SUNXI_DEBE_URCOEF_REG", SUNXI_DEBE_URCOEF_REG},
981		{ "SUNXI_DEBE_URCONS_REG", SUNXI_DEBE_URCONS_REG},
982		{ "SUNXI_DEBE_VBCOEF_REG", SUNXI_DEBE_VBCOEF_REG},
983		{ "SUNXI_DEBE_VBCONS_REG", SUNXI_DEBE_VBCONS_REG},
984		{ "SUNXI_DEBE_OCCTL_REG", SUNXI_DEBE_OCCTL_REG},
985		{ "SUNXI_DEBE_OCRCOEF_REG", SUNXI_DEBE_OCRCOEF_REG},
986		{ "SUNXI_DEBE_OCRCONS_REG", SUNXI_DEBE_OCRCONS_REG},
987		{ "SUNXI_DEBE_OCGCOEF_REG", SUNXI_DEBE_OCGCOEF_REG},
988		{ "SUNXI_DEBE_OCGCONS_REG", SUNXI_DEBE_OCGCONS_REG},
989		{ "SUNXI_DEBE_OCBCOEF_REG", SUNXI_DEBE_OCBCOEF_REG},
990		{ "SUNXI_DEBE_OCBCONS_REG", SUNXI_DEBE_OCBCONS_REG},
991	};
992	struct sunxi_debe_softc *sc;
993	device_t dev;
994
995	dev = device_find_by_driver_unit("sunxidebe", u);
996	if (dev == NULL)
997		return;
998	sc = device_private(dev);
999
1000	for (int i = 0; i < __arraycount(regs); i++) {
1001		printf("%s: 0x%08x\n", regs[i].name,
1002		    DEBE_READ(sc, regs[i].reg));
1003	}
1004}
1005#endif
1006