Module Name:    src
Committed By:   phx
Date:           Wed May  3 13:49:30 UTC 2023

Modified Files:
        src/distrib/sets/lists/man: mi
        src/share/man/man4/man4.amiga: Makefile
        src/sys/arch/amiga/amiga: conf.c
        src/sys/arch/amiga/conf: WSCONS files.amiga
        src/sys/arch/amiga/dev: zbus.c
Added Files:
        src/share/man/man4/man4.amiga: zz9k.4
        src/sys/arch/amiga/dev: zz9k.c zz9k_ax.c zz9k_fb.c zz9k_if.c zz9k_usb.c
            zz9kreg.h zz9kvar.h

Log Message:
Driver for the MNT ZZ9000 Zorro board. Submitted by Alain Runa.

It was formerly known as mntzz, which Alain released three years ago.
Since then, the ZZ9000 had several firmware updates which changed some
hardware interface details rendering the former driver non functional in some
aspects. Also the audio card plug-in ZZ9000AX became available from MNT
Research. Considering the major rewrite of the driver in some areas he
decided to rename it to zz9k(9). The driver consists of several sub-drivers
each addressing different functionality of the MNT ZZ9000 & ZZ9000AX combo
card.

zz9k* is the main card driver and need to be enabled if any of the sub-driver
is enabled. zz9k on its own is not very useful, it only provides a common
zz9kbus for the other sub-drivers to connect to, so only enable it if one of
the zz9k sub-drivers are enabled.
zzfb* represents the graphics driver for the boot console and the dumb
framebuffer for X11 based on WSCONS. ZZFB_CONSOLE option enables the ZZ9000
to become the boot console.
zz* represents the ethernet interface of the ZZ9000. It basically works but
is considered experimental.
zzax* represents the ZZ9000AX audio card driver, audio* attaches to it to
provide audio output and input functionality. The driver is not functional
yet.
zzusb* represents the ZZ9000AX usb driver. It was not implemented yet and
probably never will.


To generate a diff of this commit:
cvs rdiff -u -r1.1758 -r1.1759 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.33 -r1.34 src/share/man/man4/man4.amiga/Makefile
cvs rdiff -u -r0 -r1.1 src/share/man/man4/man4.amiga/zz9k.4
cvs rdiff -u -r1.74 -r1.75 src/sys/arch/amiga/amiga/conf.c
cvs rdiff -u -r1.72 -r1.73 src/sys/arch/amiga/conf/WSCONS
cvs rdiff -u -r1.184 -r1.185 src/sys/arch/amiga/conf/files.amiga
cvs rdiff -u -r1.77 -r1.78 src/sys/arch/amiga/dev/zbus.c
cvs rdiff -u -r0 -r1.1 src/sys/arch/amiga/dev/zz9k.c \
    src/sys/arch/amiga/dev/zz9k_ax.c src/sys/arch/amiga/dev/zz9k_fb.c \
    src/sys/arch/amiga/dev/zz9k_if.c src/sys/arch/amiga/dev/zz9k_usb.c \
    src/sys/arch/amiga/dev/zz9kreg.h src/sys/arch/amiga/dev/zz9kvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/man/mi
diff -u src/distrib/sets/lists/man/mi:1.1758 src/distrib/sets/lists/man/mi:1.1759
--- src/distrib/sets/lists/man/mi:1.1758	Thu Dec 22 11:05:54 2022
+++ src/distrib/sets/lists/man/mi	Wed May  3 13:49:29 2023
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1758 2022/12/22 11:05:54 nat Exp $
+# $NetBSD: mi,v 1.1759 2023/05/03 13:49:29 phx Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -870,6 +870,7 @@
 ./usr/share/man/cat4/amiga/xsurf.0		man-sys-catman		.cat
 ./usr/share/man/cat4/amiga/z3rambd.0		man-sys-catman		.cat
 ./usr/share/man/cat4/amiga/zssc.0		man-sys-catman		.cat
+./usr/share/man/cat4/amiga/zz9k.0		man-sys-catman		.cat
 ./usr/share/man/cat4/amr.0			man-sys-catman		.cat
 ./usr/share/man/cat4/ams.0			man-sys-catman		.cat
 ./usr/share/man/cat4/an.0			man-sys-catman		.cat
@@ -4180,6 +4181,7 @@
 ./usr/share/man/html4/amiga/xsurf.html		man-sys-htmlman		html
 ./usr/share/man/html4/amiga/z3rambd.html	man-sys-htmlman		html
 ./usr/share/man/html4/amiga/zssc.html		man-sys-htmlman		html
+./usr/share/man/html4/amiga/zz9k.html		man-sys-htmlman		html
 ./usr/share/man/html4/amr.html			man-sys-htmlman		html
 ./usr/share/man/html4/ams.html			man-sys-htmlman		html
 ./usr/share/man/html4/an.html			man-sys-htmlman		html
@@ -7186,6 +7188,7 @@
 ./usr/share/man/man4/amiga/xsurf.4		man-sys-man		.man
 ./usr/share/man/man4/amiga/z3rambd.4		man-sys-man		.man
 ./usr/share/man/man4/amiga/zssc.4		man-sys-man		.man
+./usr/share/man/man4/amiga/zz9k.4		man-sys-man		.man
 ./usr/share/man/man4/amr.4			man-sys-man		.man
 ./usr/share/man/man4/ams.4			man-sys-man		.man
 ./usr/share/man/man4/an.4			man-sys-man		.man

Index: src/share/man/man4/man4.amiga/Makefile
diff -u src/share/man/man4/man4.amiga/Makefile:1.33 src/share/man/man4/man4.amiga/Makefile:1.34
--- src/share/man/man4/man4.amiga/Makefile:1.33	Mon Sep 21 08:46:07 2020
+++ src/share/man/man4/man4.amiga/Makefile	Wed May  3 13:49:30 2023
@@ -1,12 +1,12 @@
 # 	from: @(#)Makefile	8.2 (Berkeley) 2/16/94
-#	$NetBSD: Makefile,v 1.33 2020/09/21 08:46:07 kim Exp $
+#	$NetBSD: Makefile,v 1.34 2023/05/03 13:49:30 phx Exp $
 
 MAN=    a1k2cp.4 a2kbbc.4 a34kbbc.4 acafh.4 afsc.4 ahsc.4 amidisplaycc.4 \
 	atzsc.4 autoconf.4 console.4 bah.4 bppcsc.4 clockport.4 cv3dpb.4 \
 	drbbc.4 ed.4 efa.4 em4k.4 empb.4 es.4 fdc.4 grf.4 \
 	grfcl.4 grfcv.4 grfcv3d.4 grfet.4 grfrh.4 grfrt.4 grful.4 \
 	gtsc.4 intro.4 ite.4 mem.4 mfcs.4 mgnsc.4 mntva.4 mppb.4 p5membar.4 \
-	p5pb.4 qn.4 ser.4 wesc.4 xsh.4 xsurf.4 z3rambd.4 zssc.4
+	p5pb.4 qn.4 ser.4 wesc.4 xsh.4 xsurf.4 z3rambd.4 zssc.4 zz9k.4
 MLINKS=	empb.4 emmem.4 \
 	empb.4 empm.4 \
 	mem.4 kmem.4

Index: src/sys/arch/amiga/amiga/conf.c
diff -u src/sys/arch/amiga/amiga/conf.c:1.74 src/sys/arch/amiga/amiga/conf.c:1.75
--- src/sys/arch/amiga/amiga/conf.c:1.74	Mon Oct  3 14:26:02 2016
+++ src/sys/arch/amiga/amiga/conf.c	Wed May  3 13:49:30 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: conf.c,v 1.74 2016/10/03 14:26:02 rkujawa Exp $	*/
+/*	$NetBSD: conf.c,v 1.75 2023/05/03 13:49:30 phx Exp $	*/
 
 /*-
  * Copyright (c) 1991 The Regents of the University of California.
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: conf.c,v 1.74 2016/10/03 14:26:02 rkujawa Exp $");
+__KERNEL_RCSID(0, "$NetBSD: conf.c,v 1.75 2023/05/03 13:49:30 phx Exp $");
 
 #include <sys/param.h>
 #include <sys/conf.h>
@@ -41,8 +41,10 @@ __KERNEL_RCSID(0, "$NetBSD: conf.c,v 1.7
 
 #include "ser.h"
 #include "ite.h"
+#include "grf.h"
 #include "amidisplaycc.h"
 #include "mntva.h"
+#include "zz9k_fb.h"
 #include "wsdisplay.h"
 
 /*
@@ -53,6 +55,7 @@ __KERNEL_RCSID(0, "$NetBSD: conf.c,v 1.7
  */
 cons_decl(ser);
 cons_decl(mntva);
+cons_decl(zzfb_);
 cons_decl(grf);
 cons_decl(amidisplaycc_);
 cons_decl(ite);
@@ -64,7 +67,10 @@ struct	consdev constab[] = {
 #if NMNTVA > 0
 	cons_init(mntva),
 #endif
-#if NWSDISPLAY > 0
+#if NZZ9K_FB > 0
+	cons_init(zzfb_),
+#endif
+#if (NWSDISPLAY > 0) && (NGRF > 0)
 	{ dev_init(grf,cnprobe), dev_init(grf,cninit) },
 #endif
 #if NAMIDISPLAYCC > 0
@@ -73,5 +79,5 @@ struct	consdev constab[] = {
 #if NITE > 0
 	cons_init(ite),
 #endif
-	{ 0 },
+	{ 0 }
 };

Index: src/sys/arch/amiga/conf/WSCONS
diff -u src/sys/arch/amiga/conf/WSCONS:1.72 src/sys/arch/amiga/conf/WSCONS:1.73
--- src/sys/arch/amiga/conf/WSCONS:1.72	Sat Mar  7 20:27:54 2020
+++ src/sys/arch/amiga/conf/WSCONS	Wed May  3 13:49:30 2023
@@ -1,4 +1,4 @@
-# $NetBSD: WSCONS,v 1.72 2020/03/07 20:27:54 is Exp $
+# $NetBSD: WSCONS,v 1.73 2023/05/03 13:49:30 phx Exp $
 
 # GENERIC with wscons(4)
 #
@@ -46,6 +46,14 @@ no ite7		at grf7
 mntva*		at zbus?		# MNTMN VA2000
 #options	MNTVA_CONSOLE
 
+zz9k*		at zbus?		# MNT ZZ9000
+zzfb*		at zz9k?		# Framebuffer
+options		ZZFB_CONSOLE		# Console on ZZ9000
+#zz*		at zz9k?		# Ethernet (experimental)
+#zzax*		at zz9k?		# MNT ZZ9000AX (not functional yet)
+#audio*		at zzax?		# Audio on ZZ9000AX
+#zzusb*		at zz9k?		# USB (not implemented yet)
+
 # PCI framebuffers
 #genfb*		at pci?			# CyberVisionPPC/BlizzardVisionPPC only
 voodoofb*	at pci?			# 3Dfx Voodoo 3 in G-REX 

Index: src/sys/arch/amiga/conf/files.amiga
diff -u src/sys/arch/amiga/conf/files.amiga:1.184 src/sys/arch/amiga/conf/files.amiga:1.185
--- src/sys/arch/amiga/conf/files.amiga:1.184	Sat May  7 04:40:01 2022
+++ src/sys/arch/amiga/conf/files.amiga	Wed May  3 13:49:30 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: files.amiga,v 1.184 2022/05/07 04:40:01 msaitoh Exp $
+#	$NetBSD: files.amiga,v 1.185 2023/05/03 13:49:30 phx Exp $
 
 # maxpartitions must be first item in files.${ARCH}.newconf
 maxpartitions 16			# NOTE THAT AMIGA IS SPECIAL!
@@ -22,6 +22,8 @@ defflag	opt_amigacons.h		SERCONSOLE
 
 defflag	opt_mntva.h		MNTVA_CONSOLE
 
+defflag opt_zz9k_fb.h		ZZFB_CONSOLE
+
 defflag	opt_kfont.h		KFONT_CONS_ISO8859_1
 defflag	opt_kfont.h		KFONT_CONS_ISO8859_2
 
@@ -225,6 +227,28 @@ device	mntva: wsemuldisplaydev, rasops16
 attach	mntva at zbus
 file	arch/amiga/dev/mntva.c		mntva needs-flag
 
+# MNT ZZ9000
+define	zz9kbus {}
+device	zz9k: zz9kbus
+attach	zz9k at zbus
+file	arch/amiga/dev/zz9k.c		zz9k needs-flag
+
+device	zzfb: wsemuldisplaydev, rasops8, rasops15, rasops16, rasops32, vcons
+attach	zzfb at zz9kbus with zz9k_fb
+file	arch/amiga/dev/zz9k_fb.c	zz9k_fb needs-flag
+
+device	zz: ifnet, ether, arp
+attach	zz at zz9kbus with zz9k_if
+file	arch/amiga/dev/zz9k_if.c	zz9k_if needs-flag
+
+device	zzax: audiobus
+attach	zzax at zz9kbus with zz9k_ax
+file	arch/amiga/dev/zz9k_ax.c	zz9k_ax needs-flag
+
+device	zzusb
+attach	zzusb at zz9kbus with zz9k_usb
+file	arch/amiga/dev/zz9k_usb.c	zz9k_usb needs-flag
+
 # device defined in sys/conf/files
 # A2065, Ameristar, Ariadne ethernet cards
 attach	le at zbus with le_zbus: le24

Index: src/sys/arch/amiga/dev/zbus.c
diff -u src/sys/arch/amiga/dev/zbus.c:1.77 src/sys/arch/amiga/dev/zbus.c:1.78
--- src/sys/arch/amiga/dev/zbus.c:1.77	Sat Aug  7 16:18:42 2021
+++ src/sys/arch/amiga/dev/zbus.c	Wed May  3 13:49:30 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: zbus.c,v 1.77 2021/08/07 16:18:42 thorpej Exp $ */
+/*	$NetBSD: zbus.c,v 1.78 2023/05/03 13:49:30 phx Exp $ */
 
 /*
  * Copyright (c) 1994 Christian E. Hopps
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: zbus.c,v 1.77 2021/08/07 16:18:42 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: zbus.c,v 1.78 2023/05/03 13:49:30 phx Exp $");
 
 #include <sys/param.h>
 #include <sys/device.h>
@@ -194,7 +194,10 @@ static const struct aconfdata aconftab[]
 	/* Matay Grzegorz Kraszewski */
 	{ "mppb",	44359,	1},	/* Prometheus PCI bridge */
 	/* MNT */
-	{ "mntva",	28014,	1}	/* MNT VA2000 */
+	{ "mntva",	28014,	1},	/* MNT VA2000 */
+	{ "zz9k",	28014, 	3},	/* MNT ZZ9000 Z2 */
+	{ "zz9k",	28014, 	4},	/* MNT ZZ9000 Z3 */
+	{ "zz9k",	28014, 	5}	/* MNT ZZ9000 Z3 256MB */
 };
 static int naconfent = sizeof(aconftab) / sizeof(struct aconfdata);
 
@@ -227,8 +230,10 @@ static struct preconfdata preconftab[] =
 	{2167,	2, 0},	/* Domino regs */
 	{2167,	3, 0},	/* Domino regs (proto 16M) */
 	{2181,	0, 0},	/* oMniBus mem or regs */
-	{8512,	67, 0}	/* Cybervison 64/3D */		/* grf7 */
-/*	{28014,	1, 0}	// MNTMN VA2000 */
+	{8512,	67, 0},	/* Cybervison 64/3D */		/* grf7 */
+/*	{28014,	1, 0},	// MNTMN VA2000 */
+	{28014,	3, 0},	/* MNT ZZ9000 Z2 */
+	{28014,	4, 0}	/* MNT ZZ9000 Z3 */
 };
 static int npreconfent = sizeof(preconftab) / sizeof(struct preconfdata);
 

Added files:

Index: src/share/man/man4/man4.amiga/zz9k.4
diff -u /dev/null src/share/man/man4/man4.amiga/zz9k.4:1.1
--- /dev/null	Wed May  3 13:49:30 2023
+++ src/share/man/man4/man4.amiga/zz9k.4	Wed May  3 13:49:30 2023
@@ -0,0 +1,99 @@
+.\" $NetBSD: zz9k.4,v 1.1 2023/05/03 13:49:30 phx Exp $
+.\"
+.\" Copyright (c) 2016 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Alain Runa.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd April 2, 2023
+.Dt ZZ9K 4 amiga
+.Os
+.Sh NAME
+.Nm zz9k
+.Nd MNT ZZ9000 graphics card driver
+.Sh SYNOPSIS
+.Cd "zz9k*	at zbus?"
+.Cd "zzfb*	at zz9k?"
+.Cd "options	ZZFB_CONSOLE"
+.Cd "zz*	at zz9k?"
+.Cd "zzax*	at zz9k?"
+.Cd "zzusb*	at zz9k?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the MNT ZZ9000 graphics card and ethernet interface.
+Each driver can be individually enabled or disabled. However all ZZ9000 drivers
+depend on the zz9k* as the root to work.
+.Pp
+The zzfb*
+.Xr wscons 4
+driver implements blitter accelerated support for the boot console. Support
+for an unaccelerated X11 framebuffer is also available via the
+.Xr wsfb 4
+driver.
+.Pp
+The zz* driver implements the ZZ9000 ethernet interface.
+.Pp
+There is also provision for for adding ZZ9000AX audio card support as zzax* and
+the USB port support as zzusb* at a later time. None of these are functional
+yet.
+.Sh SEE ALSO
+.Xr wscons 4 ,
+.Xr wsdisplay 4
+.Sh HISTORY
+The
+.Nm
+device first appeared in
+.Nx 10.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Alain Runa Aq Mt alai...@gmx.net
+and was inspired by the original ZZ9000 drivers for AmigaOS. 
+.Sh CAVEATS
+For the ZZ9000 to assume the boot console, the user has to explicitly set the
+.Dv FBZZ_CONSOLE
+option in the kernel config file.
+.Pp
+Currently, the video mode is hard-coded to 1280x720 at 8 bpp for the console
+emulation and 16 bpp for the framebuffer used by X11. Resolutions and color
+depths can be individually changed in the zz9k_fb.c source code. Please follow
+the source code comments.
+.Pp
+zz* ethernet driver is considered experimental. As the MAC address is not
+stored permanently in the card, it will use it's default address. This should
+not impose any problem unless two ZZ9000 will be used in the same LAN. Please
+configure the desired link address for the zz0 interface in rc.conf or in the
+/etc/ifconfig.zz0 file, to avoid a MAC address collision.
+.Pp
+zzax* audio card driver is not functional yet. Only a basic skeleton source code
+is available for a possible later implementation.
+.Pp
+zzusb* USB driver is not functional yet. Only a basic skeleton source code is
+available for a possible later implementation.
+.Sh BUGS
+None known.

Index: src/sys/arch/amiga/dev/zz9k.c
diff -u /dev/null src/sys/arch/amiga/dev/zz9k.c:1.1
--- /dev/null	Wed May  3 13:49:30 2023
+++ src/sys/arch/amiga/dev/zz9k.c	Wed May  3 13:49:30 2023
@@ -0,0 +1,179 @@
+/*	$NetBSD: zz9k.c,v 1.1 2023/05/03 13:49:30 phx Exp $ */
+
+/*
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alain Runa.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: zz9k.c,v 1.1 2023/05/03 13:49:30 phx Exp $");
+
+/* miscellaneous */
+#include <sys/types.h>			/* size_t */
+#include <sys/stdint.h>			/* uintXX_t */
+#include <sys/stdbool.h>		/* bool */
+
+/* driver(9) includes */
+#include <sys/param.h>			/* NODEV */
+#include <sys/device.h>			/* CFATTACH_DECL_NEW(), device_priv() */
+#include <sys/errno.h>			/* . */
+
+/* bus_space(9) and zorro bus includes */
+#include <sys/bus.h>			/* bus_space_xxx(), bus_space_xxx */
+#include <sys/cpu.h>			/* kvtop() */
+#include <sys/systm.h>			/* aprint_xxx() */
+#include <amiga/dev/zbusvar.h>		/* zbus_args */
+
+/* zz9k and amiga related */
+#include <amiga/amiga/device.h>		/* amiga_realconfig */
+#include <amiga/dev/zz9kvar.h>		/* zz9k_softc */
+#include <amiga/dev/zz9kreg.h>		/* ZZ9000 registers */
+#include "zz9k.h"			/* NZZ9K */
+
+
+/* helper functions */
+static int zz9k_print(void *aux, const char *pnp);
+
+/* driver(9) essentials */
+static int zz9k_match(device_t parent, cfdata_t match, void *aux);
+static void zz9k_attach(device_t parent, device_t self, void *aux);
+CFATTACH_DECL_NEW(
+    zz9k, sizeof(struct zz9k_softc), zz9k_match, zz9k_attach, NULL, NULL);
+
+bool zz9k_exists = false;		/* required by zz9k_fb ZZFB_CONSOLE */
+
+
+/* It's the year 2020. And so it begins... */
+
+static int
+zz9k_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct zbus_args *zap = aux;
+	bool found = false;
+
+	if (zap->manid == ZZ9K_MANID) {
+		switch (zap->prodid) {
+		case ZZ9K_PRODID_Z2:
+		case ZZ9K_PRODID_Z3:
+			found = true;
+			break;
+		}
+	}
+
+	if (amiga_realconfig == 0) { /* pre-config, just flag if zz9k exists. */
+		zz9k_exists = found;
+		return 0;    /* 0, as we don't need zz9k_attach() this round. */
+	}
+
+	return found;
+}
+
+static void
+zz9k_attach(device_t parent, device_t self, void *aux)
+{
+	struct zbus_args *zap = aux;
+	struct zz9k_softc *sc = device_private(self);
+	struct zz9kbus_attach_args zz9k_fb;	/* Graphics */
+	struct zz9kbus_attach_args zz9k_if;	/* Ethernet */
+	struct zz9kbus_attach_args zz9k_ax;	/* Audio */
+	struct zz9kbus_attach_args zz9k_usb;	/* USB */
+
+	const bus_addr_t regBase = ZZ9K_REG_BASE;
+	const bus_size_t regSize = ZZ9K_REG_SIZE;
+
+	sc->sc_dev = self;
+	sc->sc_bst.base = (bus_addr_t)zap->va;
+	sc->sc_bst.absm = &amiga_bus_stride_1;
+	sc->sc_iot = &sc->sc_bst;
+	sc->sc_zsize = zap->size;
+
+	if (bus_space_map(sc->sc_iot, regBase, regSize, 0, &sc->sc_regh)) {
+		aprint_error("Failed to map MNT ZZ9000 registers.\n");
+		return;
+	}
+
+	uint16_t hwVer = ZZREG_R(ZZ9K_HW_VERSION);
+	uint16_t fwVer = ZZREG_R(ZZ9K_FW_VERSION);
+
+	aprint_normal(": MNT ZZ9000 Zorro %s (HW: %i.%i, FW: %i.%i)\n",
+	    (zap->prodid == ZZ9K_PRODID_Z3) ? "III" : "II",
+	    hwVer >> 8, hwVer & 0x00ff, fwVer >> 8, fwVer & 0x00ff);
+
+	if (fwVer < ZZ9K_FW_VER_MIN) {
+		aprint_error("Firmware version is not supported. "
+		    "Please update to newer firmware from:\n");
+		aprint_error(
+		    "https://source.mnt.re/amiga/zz9000-firmware/-/releases\n";);
+		return;
+	}
+
+	uint16_t tcore = ZZREG_R(ZZ9K_TEMPERATURE);
+	uint16_t vcore = ZZREG_R(ZZ9K_VOLTAGE_CORE);
+	uint16_t vaux  = ZZREG_R(ZZ9K_VOLTAGE_AUX);
+	
+	aprint_normal_dev(sc->sc_dev, "Hardware status "
+	    "<Tcore: %i.%i C, Vcore: %i.%i V, Vaux: %i.%i V>\n",
+	    tcore/10, tcore%10, vcore/100, vcore%100, vaux/100, vaux%100);
+
+	aprint_debug_dev(sc->sc_dev, "[DEBUG] registers at %p/%p (pa/va), "
+	    "MNT ZZ9000 is mapped with %i MB in Zorro %s space.\n",
+	    (void *)kvtop((void *)sc->sc_regh),
+	    bus_space_vaddr(sc->sc_iot, sc->sc_regh),
+	    zap->size / (1024 * 1024),
+	    (zap->prodid == ZZ9K_PRODID_Z3) ? "III" : "II");
+	
+	if (fwVer > ZZ9K_FW_VER) {
+		aprint_normal_dev(sc->sc_dev, "The firmware is newer than "
+		    "%i.%i and may not be supported yet.\n",
+		    ZZ9K_FW_VER >> 8, ZZ9K_FW_VER & 0x00ff);
+	}
+
+	/* Add framebuffer */
+	zz9k_fb.zzaa_base = (bus_addr_t)zap->va;
+	strcpy(zz9k_fb.zzaa_name, "zz9k_fb");
+	config_found(sc->sc_dev, &zz9k_fb, zz9k_print, CFARGS_NONE);
+
+	/* Add zz* ethernet interface */
+	zz9k_if.zzaa_base = (bus_addr_t)zap->va;
+	strcpy(zz9k_if.zzaa_name, "zz9k_if");
+	config_found(sc->sc_dev, &zz9k_if, zz9k_print, CFARGS_NONE);
+
+	/* Add AX audio interface */
+	zz9k_ax.zzaa_base = (bus_addr_t)zap->va;
+	strcpy(zz9k_ax.zzaa_name, "zz9k_ax");
+	config_found(sc->sc_dev, &zz9k_ax, zz9k_print, CFARGS_NONE);
+
+	/* Add USB interface */
+	zz9k_usb.zzaa_base = (bus_addr_t)zap->va;
+	strcpy(zz9k_usb.zzaa_name, "zz9k_usb");
+	config_found(sc->sc_dev, &zz9k_usb, zz9k_print, CFARGS_NONE);
+}
+
+static int
+zz9k_print(void *aux, const char *pnp)
+{
+	return 0;
+}
Index: src/sys/arch/amiga/dev/zz9k_ax.c
diff -u /dev/null src/sys/arch/amiga/dev/zz9k_ax.c:1.1
--- /dev/null	Wed May  3 13:49:30 2023
+++ src/sys/arch/amiga/dev/zz9k_ax.c	Wed May  3 13:49:30 2023
@@ -0,0 +1,524 @@
+/*	$NetBSD: zz9k_ax.c,v 1.1 2023/05/03 13:49:30 phx Exp $ */
+
+/*
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alain Runa.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: zz9k_ax.c,v 1.1 2023/05/03 13:49:30 phx Exp $");
+
+/* miscellaneous */
+#include <sys/types.h>			/* size_t */
+#include <sys/stdint.h>			/* uintXX_t */
+#include <sys/stdbool.h>		/* bool */
+
+/* driver(9) */
+#include <sys/param.h>			/* NODEV */
+#include <sys/device.h>			/* CFATTACH_DECL_NEW(), device_priv() */
+#include <sys/errno.h>			/* . */
+
+/* bus_space(9) and zorro bus */
+#include <sys/bus.h>			/* bus_space_xxx(), bus_space_xxx_t */
+#include <sys/cpu.h>			/* kvtop() */
+#include <sys/systm.h>			/* aprint_xxx() */
+
+/* mutex(9) */
+#include <sys/mutex.h>
+
+/* Interrupt related */
+#include <sys/intr.h>
+#include <amiga/amiga/isr.h>		/* isr */
+
+/* audio(9) */
+#include <sys/audioio.h>
+#include <dev/audio/audio_if.h>
+
+/* zz9k related */
+#include <amiga/dev/zz9kvar.h>		/* zz9kbus_attach_args */
+#include <amiga/dev/zz9kreg.h>		/* ZZ9000 registers */
+#include "zz9k_ax.h"			/* NZZ9K_AX */
+
+
+/* The allmighty softc structure */
+struct zzax_softc {
+	device_t sc_dev;
+	struct bus_space_tag sc_bst;
+	bus_space_tag_t sc_iot;
+	bus_space_handle_t sc_regh;
+	bus_space_handle_t sc_txbufh;
+	size_t sc_txbufsize;
+
+	struct isr sc_isr;
+
+	kmutex_t  sc_lock;
+	kmutex_t  sc_intr_lock;
+};
+
+static int zzax_intr(void *arg);
+static void zzax_set_param(struct zzax_softc *sc, uint16_t param, uint16_t val);
+
+/* audio_hw_if */
+static int zzax_open(void *hdl, int flags);
+static void zzax_close(void *hdl);
+static int zzax_query_format(void *hdl, audio_format_query_t *afp);
+static int zzax_set_format(void *hdl, int setmode,
+    const audio_params_t *play, const audio_params_t *rec,
+    audio_filter_reg_t *pfil, audio_filter_reg_t *rfil);
+static int zzax_round_blocksize(void *hdl, int bs, int mode,
+    const audio_params_t *param);
+static int zzax_commit_settings(void *hdl);
+static int zzax_init_output(void *hdl, void *buffer, int size);
+static int zzax_init_input(void *hdl, void *buffer, int size);
+static int zzax_start_output(void *hdl, void *block, int blksize,
+    void (*intr)(void*), void *intrarg);
+static int zzax_start_input(void *hdl, void *block, int blksize,
+    void (*intr)(void*), void *intrarg);
+static int zzax_halt_output(void *hdl);
+static int zzax_halt_input(void *hdl);
+static int zzax_speaker_ctl(void *hdl, int on);
+static int zzax_getdev(void *hdl, struct audio_device *ret);
+static int zzax_set_port(void *hdl, mixer_ctrl_t *mc);
+static int zzax_get_port(void *hdl, mixer_ctrl_t *mc);
+static int zzax_query_devinfo(void *hdl, mixer_devinfo_t *di);
+#if 0
+static void *zzax_allocm(void *hdl, int direction, size_t size);
+static void zzax_freem(void *hdl, void *addr, size_t size);
+#endif
+static size_t zzax_round_buffersize(void *hdl, int direction, size_t bufsize);
+static int zzax_get_props(void *hdl);
+static int zzax_trigger_output(void *hdl, void *start, void *end, int blksize,
+    void (*intr)(void*), void *intrarg, const audio_params_t *param);
+static int zzax_trigger_input(void *hdl, void *start, void *end, int blksize,
+    void (*intr)(void*), void *intrarg, const audio_params_t *param);
+static int zzax_dev_ioctl(void *hdl, u_long cmd, void *addr, int flag,
+    struct lwp *l);
+static void zzax_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread);
+
+static const struct audio_hw_if zzax_hw_if = {
+	.open			= zzax_open,
+	.close			= zzax_close,
+	.query_format		= zzax_query_format,
+	.set_format		= zzax_set_format,
+	.round_blocksize	= zzax_round_blocksize,
+	.commit_settings	= zzax_commit_settings,
+	.init_output		= zzax_init_output,
+	.init_input		= zzax_init_input,
+	.start_output		= zzax_start_output,
+	.start_input		= zzax_start_input,
+	.halt_output		= zzax_halt_output,
+	.halt_input		= zzax_halt_input,
+	.speaker_ctl		= zzax_speaker_ctl,
+	.getdev			= zzax_getdev,
+	.set_port		= zzax_set_port,
+	.get_port		= zzax_get_port,
+	.query_devinfo		= zzax_query_devinfo,
+#if 0
+	.allocm			= zzax_allocm,
+	.freem			= zzax_freem,
+#endif
+	.round_buffersize	= zzax_round_buffersize,
+	.get_props		= zzax_get_props,
+	.trigger_output		= zzax_trigger_output,
+	.trigger_input		= zzax_trigger_input,
+	.dev_ioctl		= zzax_dev_ioctl,
+	.get_locks		= zzax_get_locks
+};
+
+static const struct audio_format zzax_format = {
+	.mode		= AUMODE_PLAY,
+	.encoding	= AUDIO_ENCODING_SLINEAR_BE,
+	.validbits	= 16,
+	.precision	= 16,
+	.channels	= 2,
+	.channel_mask	= AUFMT_STEREO,
+	.frequency_type	= 0,
+	.frequency	= {8000, 12000, 24000, 32000, 44100, 48000},
+	.priority	= 0
+};
+
+static const struct audio_device zzax_device = {
+	.name		= "ZZ9000AX",
+	.version	= "1.13",
+	.config		= "zzax"
+};
+
+/* mixer sets */
+#define ZZAX_CHANNELS 0
+
+/* mixer values */
+#define ZZAX_VOLUME 1
+#define ZZAX_OUTPUT_CLASS 2
+
+
+/* driver(9) essentials */
+static int zzax_match(device_t parent, cfdata_t match, void *aux);
+static void zzax_attach(device_t parent, device_t self, void *aux);
+CFATTACH_DECL_NEW(zz9k_ax, sizeof(struct zzax_softc),
+    zzax_match, zzax_attach, NULL, NULL);
+
+
+/* Go ahead, make my day. */
+
+static int
+zzax_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct zz9kbus_attach_args *bap = aux;
+
+	if (strcmp(bap->zzaa_name, "zz9k_ax") != 0)
+		return 0;
+
+	return 1;
+}
+
+static void
+zzax_attach(device_t parent, device_t self, void *aux)
+{
+	struct zz9kbus_attach_args *bap = aux;
+	struct zzax_softc *sc = device_private(self);
+	struct zz9k_softc *psc = device_private(parent);
+
+	sc->sc_dev = self;
+	sc->sc_bst.base = bap->zzaa_base;
+	sc->sc_bst.absm = &amiga_bus_stride_1;
+	sc->sc_iot = &sc->sc_bst;
+	sc->sc_regh = psc->sc_regh;
+
+	uint16_t config = ZZREG_R(ZZ9K_AUDIO_CONFIG);
+	aprint_normal(": ZZ9000AX %sdetected.\n",
+	    (config == 0) ? "not " : "");
+	aprint_normal_dev(sc->sc_dev,
+	    "MNT ZZ9000AX driver is not functional yet.\n");
+
+	aprint_debug_dev(sc->sc_dev, "[DEBUG] registers at %p/%p (pa/va)\n",
+	    (void *)kvtop((void *)sc->sc_regh),
+	    bus_space_vaddr(sc->sc_iot, sc->sc_regh));
+
+	sc->sc_txbufsize = 0x10000;
+	uint32_t tx_buffer = psc->sc_zsize - sc->sc_txbufsize;
+	if (bus_space_map(sc->sc_iot, tx_buffer, sc->sc_txbufsize,
+	    BUS_SPACE_MAP_LINEAR, &sc->sc_txbufh)) {
+		aprint_error(": Failed to map MNT ZZ9000AX audio tx buffer.\n");
+		return;
+	}
+
+	aprint_normal_dev(sc->sc_dev, "base: %p/%p size: %i\n",
+	    (void *)kvtop((void *)sc->sc_txbufh),
+	    bus_space_vaddr(sc->sc_iot, sc->sc_txbufh),
+	    sc->sc_txbufsize);
+
+	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
+	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
+
+	sc->sc_isr.isr_intr = zzax_intr;
+	sc->sc_isr.isr_arg  = sc;
+	sc->sc_isr.isr_ipl  = 6;
+	add_isr(&sc->sc_isr);
+
+	audio_attach_mi(&zzax_hw_if, sc, self);
+
+	uint16_t conf = ZZREG_R(ZZ9K_AUDIO_CONFIG); /* enable interrupt */
+	ZZREG_W(ZZ9K_AUDIO_CONFIG, conf | ZZ9K_AUDIO_CONFIG_INT_AUDIO);
+}
+
+static int
+zzax_intr(void *arg)
+{
+	struct zzax_softc *sc = arg;
+
+	uint16_t conf = ZZREG_R(ZZ9K_CONFIG);
+	if ((conf & ZZ9K_CONFIG_INT_AUDIO) == 0)
+		return 0;
+	
+#if 0
+	uint16_t conf = ZZREG_R(ZZ9K_AUDIO_CONFIG); /* disable interrupt */
+	ZZREG_W(ZZ9K_AUDIO_CONFIG, conf & ~ZZ9K_AUDIO_CONFIG_INT_AUDIO);
+#endif
+	ZZREG_W(ZZ9K_CONFIG, ZZ9K_CONFIG_INT_ACK | ZZ9K_CONFIG_INT_ACK_AUDIO);
+
+	mutex_spin_enter(&sc->sc_intr_lock);
+	/* do stuff */
+	mutex_spin_exit(&sc->sc_intr_lock);
+
+#if 0
+	uint16_t conf = ZZREG_R(ZZ9K_AUDIO_CONFIG); /* enable interrupt */
+	ZZREG_W(ZZ9K_AUDIO_CONFIG, conf | ZZ9K_AUDIO_CONFIG_INT_AUDIO);
+#endif
+
+	return 1;
+}
+
+static void
+zzax_set_param(struct zzax_softc *sc, uint16_t param, uint16_t val)
+{
+	ZZREG_W(ZZ9K_AUDIO_PARAM, param);
+	ZZREG_W(ZZ9K_AUDIO_VAL, val);
+	ZZREG_W(ZZ9K_AUDIO_PARAM, 0);
+}
+
+int
+zzax_open(void *hdl, int flags)
+{
+	struct zzax_softc *sc = hdl;
+	uint32_t buffer = (uint32_t)bus_space_vaddr(sc->sc_iot, sc->sc_txbufh);
+	zzax_set_param(sc, ZZ9K_AP_TX_BUF_OFFS_HI, buffer >> 16);
+	zzax_set_param(sc, ZZ9K_AP_TX_BUF_OFFS_HI, buffer & 0xFFFF);
+	printf("zzax_open: %X\n", buffer);
+	return 0;
+}
+
+static void
+zzax_close(void *hdl)
+{
+	printf("zzax_close:\n");
+}
+
+static int
+zzax_query_format(void *hdl, audio_format_query_t *afp)
+{
+	printf("zzax_query_format:\n");
+	return audio_query_format(&zzax_format, 1, afp);
+}
+
+static int
+zzax_set_format(void *hdl, int setmode,
+    const audio_params_t *play, const audio_params_t *rec,
+    audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
+{
+	struct zzax_softc *sc = hdl;
+
+	printf("zzax_set_format:\n");
+	if (setmode & AUMODE_PLAY) {
+		zzax_set_param(sc, ZZ9K_AP_DSP_SET_LOWPASS, 23900);
+		printf("::play->sample_rate: %i\n", play->sample_rate);
+		printf("::play->encoding: %i\n", play->encoding);
+		printf("::play->precision: %i\n", play->precision);
+		printf("::play->validbits: %i\n", play->validbits);
+		printf("::play->channels: %i\n", play->channels);
+	}
+
+	if (setmode & AUMODE_RECORD) {
+		printf("::rec->sample_rate: %i\n", rec->sample_rate);
+		printf("::rec->encoding: %i\n", rec->encoding);
+		printf("::rec->precision: %i\n", rec->precision);
+		printf("::rec->validbits: %i\n", rec->validbits);
+		printf("::rec->channels: %i\n", rec->channels);
+	}
+
+	return 0;
+}
+
+static int
+zzax_round_blocksize(void *hdl, int bs, int mode, const audio_params_t *param)
+{
+	printf("zzax_round_blocksize:\n");
+	printf("::bs: %i\n", bs);
+	printf("::mode: %i\n", mode);
+	printf("::param->sample_rate: %i\n", param->sample_rate);
+	printf("::param->encoding: %i\n", param->encoding);
+	printf("::param->precision: %i\n", param->precision);
+	printf("::param->validbits: %i\n", param->validbits);
+	printf("::param->channels: %i\n", param->channels);
+
+	return bs;
+}
+
+static int
+zzax_commit_settings(void *hdl)
+{
+	printf("zzax_commit_settings:\n");
+	return 0;
+}
+
+static int
+zzax_init_output(void *hdl, void *buffer, int size)
+{
+	printf("zzax_init_output:\n");
+	return 0;
+}
+
+static int
+zzax_init_input(void *hdl, void *buffer, int size)
+{
+	printf("zzax_init_input:\n");
+	return 0;
+}
+
+static int
+zzax_start_output(void *hdl, void *block, int blksize,
+    void (*intr)(void*), void *intrarg)
+{
+	printf("zzax_start_output:\n");
+	return 0;
+}
+
+static int
+zzax_start_input(void *hdl, void *block, int blksize,
+    void (*intr)(void*), void *intrarg)
+{
+	printf("zzax_start_input:\n");
+	return ENXIO;
+}
+
+static int
+zzax_halt_output(void *hdl)
+{
+	printf("zzax_halt_output:\n");
+	return 0;
+}
+
+static int
+zzax_halt_input(void *hdl)
+{
+	printf("zzax_halt_input:\n");
+	return ENXIO;
+}
+
+static int
+zzax_speaker_ctl(void *hdl, int on)
+{
+	printf("zzax_speaker_ctl:\n");
+	return 0;
+}
+
+static int
+zzax_getdev(void *hdl, struct audio_device *ret)
+{
+	*ret = zzax_device;
+	printf("zzax_getdev: %p\n", ret);
+	return 0;
+}
+
+static int
+zzax_set_port(void *hdl, mixer_ctrl_t *mc)
+{
+	printf("zzax_set_port:\n");
+	return 0;
+}
+
+static int
+zzax_get_port(void *hdl, mixer_ctrl_t *mc)
+{
+	printf("zzax_get_port:\n");
+	return 0;
+}
+
+static int
+zzax_query_devinfo(void *hdl, mixer_devinfo_t *di)
+{
+	switch(di->index) {
+	case ZZAX_CHANNELS:
+		strcpy(di->label.name, "speaker");
+		di->type = AUDIO_MIXER_SET;
+		di->mixer_class = ZZAX_OUTPUT_CLASS;
+		di->prev = AUDIO_MIXER_LAST;
+		di->next = AUDIO_MIXER_LAST;
+		di->un.s.num_mem = 1;
+		strcpy(di->un.s.member[0].label.name, "channel0");
+		di->un.s.member[0].mask = 0;
+	case ZZAX_VOLUME:
+		strcpy(di->label.name, "master");
+		di->type = AUDIO_MIXER_VALUE;
+		di->mixer_class = ZZAX_OUTPUT_CLASS;
+		di->prev = AUDIO_MIXER_LAST;
+		di->next = AUDIO_MIXER_LAST;
+		di->un.v.num_channels = 1;
+		strcpy(di->un.v.units.name, "volume");
+		break;
+	case ZZAX_OUTPUT_CLASS:
+		strcpy(di->label.name, "outputs");
+		di->type = AUDIO_MIXER_CLASS;
+		di->mixer_class = ZZAX_OUTPUT_CLASS;
+		di->prev = AUDIO_MIXER_LAST;
+		di->next = AUDIO_MIXER_LAST;
+		break;
+	default:
+		return ENXIO; 
+	}
+
+	printf("zzax_query_devinfo: %s\n", di->label.name);
+
+	return 0;
+}
+
+#if 0
+static void*
+zzax_allocm(void *hdl, int direction, size_t size)
+{
+
+}
+
+static void
+zzax_freem(void *hdl, void *addr, size_t size)
+{
+
+}
+#endif
+
+static size_t
+zzax_round_buffersize(void *hdl, int direction, size_t bufsize)
+{
+	printf("zzax_round_buffersize:\n");
+	printf("::direction: %i\n", direction);
+	printf("::bufsize: %zu\n", bufsize);
+	return bufsize;
+}
+
+static int
+zzax_get_props(void *hdl)
+{
+	return AUDIO_PROP_PLAYBACK;
+}
+
+static int
+zzax_trigger_output(void *hdl, void *start, void *end, int blksize,
+    void (*intr)(void*), void *intrarg, const audio_params_t *param)
+{
+	printf("zzax_trigger_output:\n");
+	return 0;
+}
+
+static int
+zzax_trigger_input(void *hdl, void *start, void *end, int blksize,
+    void (*intr)(void*), void *intrarg, const audio_params_t *param)
+{
+	return 0;
+}
+
+static int
+zzax_dev_ioctl(void *hdl, u_long cmd, void *addr, int flag, struct lwp *l)
+{
+	printf("zzax_dev_ioctl: %lu\n", cmd);
+	return 0;
+}
+
+static void
+zzax_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread)
+{
+	struct zzax_softc *sc = hdl;
+	*intr = &sc->sc_intr_lock;
+	*thread = &sc->sc_lock;
+}
Index: src/sys/arch/amiga/dev/zz9k_fb.c
diff -u /dev/null src/sys/arch/amiga/dev/zz9k_fb.c:1.1
--- /dev/null	Wed May  3 13:49:30 2023
+++ src/sys/arch/amiga/dev/zz9k_fb.c	Wed May  3 13:49:30 2023
@@ -0,0 +1,932 @@
+/*	$NetBSD: zz9k_fb.c,v 1.1 2023/05/03 13:49:30 phx Exp $ */
+
+/*
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alain Runa.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: zz9k_fb.c,v 1.1 2023/05/03 13:49:30 phx Exp $");
+
+/* miscellaneous */
+#include <sys/errno.h>			/* EPASSTHROUGH */
+#include <sys/types.h>			/* size_t */
+#include <sys/stdint.h>			/* uintXX_t */
+#include <sys/stdbool.h>		/* bool */
+
+/* driver(9) */
+#include <sys/param.h>      		/* NODEV */
+#include <sys/device.h>     		/* CFATTACH_DECL_NEW(), device_priv() */
+#include <sys/errno.h>			/* EINVAL, ENODEV, EPASSTHROUGH */
+
+/* bus_space(9) and zorro bus */
+#include <sys/bus.h>			/* bus_space_xxx(), bus_space_xxx_t */
+#include <sys/cpu.h>			/* kvtop() */
+#include <sys/systm.h>			/* aprint_xxx() */
+
+/* wsdisplay(9) */
+#include <dev/wscons/wsconsio.h>	/* WSDISPLAYIO_XXX, wsdisplayio_xxx */
+#include <dev/wscons/wsdisplayvar.h>	/* wsscreen_xxx, wsdisplay_xxx */
+#include <dev/wscons/wsemulvar.h>	/* ? */
+#include <dev/wscons/wsemul_vt100var.h>	/* ? */
+
+/* rasops(9) */
+/* #include <dev/wscons/wsdisplayvar.h> */
+#include <dev/rasops/rasops.h>		/* rasops_unpack_attr(), rasops_info */
+
+/* wsfont(9) */
+/* #include <dev/wscons/wsconsio.h> */	/* WSDISPLAYIO_XXX, wsdisplayio_xxx */
+#include <dev/wsfont/wsfont.h>		/* wsfont_init() */
+
+/* vcons(9) */
+#include <dev/wscons/wsdisplay_vconsvar.h>	/* vcons_xxx(), vcons_data,
+						vcons_screen */
+/* cons(9) */
+#include <dev/cons.h>			/* consdev, CN_INTERNAL */
+
+/* zz9k and amiga related */
+#include <amiga/dev/kbdvar.h>		/* kbd_cnattach() */
+#include <amiga/dev/zz9kvar.h>		/* zz9kbus_attach_args */
+#include <amiga/dev/zz9kreg.h>		/* ZZ9000 registers */
+#include "opt_zz9k_fb.h"		/* ZZFB_CONSOLE */
+#include "zz9k_fb.h"			/* NZZ9K_FB */
+#include "kbd.h"			/* NKBD */
+
+
+/*
+ * One can choose different graphics modes and color depths for the different
+ * wsdisplay modes emul (console), mapped (raw) and dumbfb (e.g. X11) here.
+ * Please consult zz9kreg.h for available graphics modes and color depths
+ * supported by the ZZ9000.
+ */
+#define ZZFB_SET_CON_MODE	ZZ9K_MODE_1280x720	/* Console */
+#define ZZFB_SET_CON_BPP	ZZ9K_COLOR_8BIT
+#define ZZFB_SET_GFX_MODE	ZZ9K_MODE_1280x720	/* raw FB */
+#define ZZFB_SET_GFX_BPP	ZZ9K_COLOR_16BIT
+#define ZZFB_SET_DFB_MODE	ZZ9K_MODE_1280x720	/* X11 */
+#define ZZFB_SET_DFB_BPP	ZZ9K_COLOR_16BIT
+
+/*
+ * This defines ZZ9000 scandoubler's capture mode and it is used only in case
+ * the ZZ9000 does not have the early console, but also in X11 video off mode.
+ * NetBSD defaults to NTSC amiga screen, so the scandoubler default is NTSC too.
+ * On a custom configured kernel for a PAL screen, one should consider to change
+ * the capture mode to PAL to get the best result. If the attached monitor does
+ * not support a scandoubled PAL signal in 50Hz, consider using the VGA 800x600
+ * capture mode which is compatible with most monitors and works fine with NTSC
+ * and PAL captures at 60Hz.
+ * Valid values for ZZFB_CAP_MODE: 0: NTSC, 1: PAL, 2:VGA (PAL60)
+ */
+#define ZZFB_CAP_MODE 0
+
+#if   ZZFB_CAP_MODE == 0
+#define ZZFB_CAPTURE_MODE	ZZ9K_MODE_720x480
+#define ZZFB_DISPLAY_MODE	ZZ9K_MODE_720x480
+#elif ZZFB_CAP_MODE == 1
+#define ZZFB_CAPTURE_MODE	ZZ9K_MODE_720x576p50
+#define ZZFB_DISPLAY_MODE	ZZ9K_MODE_720x576p50
+#elif ZZFB_CAP_MODE == 2
+#define ZZFB_CAPTURE_MODE	ZZ9K_MODE_800x600
+#define ZZFB_DISPLAY_MODE	ZZ9K_MODE_800x600
+#endif
+
+/* The allmighty softc structure */
+struct zzfb_softc {
+	device_t sc_dev;
+	struct bus_space_tag sc_bst;
+	bus_space_tag_t sc_iot;
+	bus_space_handle_t sc_regh;
+	bus_space_handle_t sc_fbh;
+	size_t sc_fbsize;
+
+	struct vcons_screen sc_console_screen;
+	struct vcons_data sc_vd;
+	struct wsscreen_descr sc_defaultscreen;
+	struct wsscreen_list sc_screenlist;
+	const struct wsscreen_descr *sc_screens[1];
+	u_int sc_wsmode;
+
+	uint16_t sc_displaymode;
+	uint16_t sc_colormode;
+	uint16_t sc_width;
+	uint16_t sc_height;
+	uint16_t sc_bpp;
+	uint16_t sc_stride;
+
+	u_char red[ZZ9K_PALETTE_SIZE];
+	u_char green[ZZ9K_PALETTE_SIZE];
+	u_char blue[ZZ9K_PALETTE_SIZE];
+
+	bool sc_isconsole;
+	bool sc_isrtg;
+};
+
+static const struct {
+	const char* name;
+	uint16_t width;
+	uint16_t height;
+	uint16_t scale;
+} zzfb_modes[] = {				/* Hardcoded in firmware */
+	{       "1280x720p60", 1280,  720, ZZ9K_MODE_SCALE_0},
+	{        "800x600p60",  800,  600, ZZ9K_MODE_SCALE_0},
+	{        "640x480p60",  640,  480, ZZ9K_MODE_SCALE_0},
+	{       "1024x768p60", 1024,  768, ZZ9K_MODE_SCALE_0},
+	{      "1280x1024p60", 1280, 1024, ZZ9K_MODE_SCALE_0},
+	{      "1920x1080p60", 1920, 1080, ZZ9K_MODE_SCALE_0},
+	{        "720x576p50",  720,  576, ZZ9K_MODE_SCALE_0},	/* 50 Hz */
+	{      "1920x1080p50", 1920, 1080, ZZ9K_MODE_SCALE_0},	/* 50 Hz */
+	{        "720x480p60",  720,  480, ZZ9K_MODE_SCALE_0},
+	{        "640x512p60",  640,  512, ZZ9K_MODE_SCALE_0},
+	{      "1600x1200p60", 1600, 1200, ZZ9K_MODE_SCALE_0},
+	{      "2560x1444p30", 2560, 1444, ZZ9K_MODE_SCALE_0},	/* 30 Hz */
+	{ "720x576p50-NS-PAL",  720,  576, ZZ9K_MODE_SCALE_0},	/* 50 Hz */
+	{ "720x480p60-NS-PAL",  720,  480, ZZ9K_MODE_SCALE_0},
+	{"720x576p50-NS-NTSC",  720,  576, ZZ9K_MODE_SCALE_0},	/* 50 Hz */
+	{"720x480p60-NS-NTSC",  720,  480, ZZ9K_MODE_SCALE_0},
+	{        "640x400p60",  640,  400, ZZ9K_MODE_SCALE_0},
+	{       "1920x800p60",  640,  400, ZZ9K_MODE_SCALE_0}
+};
+
+static const struct {
+	const char* name;
+	uint16_t bpp;
+	uint16_t mode;
+	uint16_t stride;
+} zzfb_colors[] = {				/* Hardcoded in firmware */
+	{      "8-bit LUT",  8, ZZ9K_MODE_COLOR_8BIT , 1},
+	{  "16-bit RGB565", 16, ZZ9K_MODE_COLOR_16BIT, 2},
+	{"32-bit BGRA8888", 32, ZZ9K_MODE_COLOR_32BIT, 4},
+	{"16-bit ARGB1555", 15, ZZ9K_MODE_COLOR_15BIT, 2}
+};
+
+#define ZZFB_MODES_SIZE		(sizeof zzfb_modes / sizeof zzfb_modes[0])
+#define ZZFB_COLORS_SIZE	(sizeof zzfb_colors / sizeof zzfb_colors[0])
+
+/* functions to set gfx mode, palette and misc stuff to make life easier. */
+static void zzfb_set_capture(struct zzfb_softc *sc, uint16_t display_mode,
+    uint16_t capture_mode);
+static void zzfb_set_mode(struct zzfb_softc *sc, uint16_t display_mode,
+    uint16_t color_mode);
+static void zzfb_wait_vblank(struct zzfb_softc *sc);
+static void zzfb_init_palette(struct zzfb_softc *sc);
+static void zzfb_set_palette(struct zzfb_softc *sc);
+static void zzfb_clearbg(struct zzfb_softc *sc, uint8_t color_index);
+static int zzfb_get_fbinfo(struct zzfb_softc *sc,
+    struct wsdisplayio_fbinfo *fbi);
+
+/* vcons_data init_screen function */
+static void zzfb_init_screen(void *cookie, struct vcons_screen *scr,
+    int existing, long *defattr);
+
+/* accelerated raster ops functions */
+static void zzfb_eraserows(void *cookie, int row, int nrows, long fillattr);
+static void zzfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows);
+static void zzfb_copycols(void *cookie, int row, int srccol, int dstcol,
+    int ncols);
+static void zzfb_erasecols(void *cookie, int row, int startcol, int ncols,
+    long fillattr);
+
+/* blitter support functions*/
+static void zzfb_rectfill(struct zzfb_softc *sc, uint16_t x, uint16_t y,
+    uint16_t w, uint16_t h, uint32_t color);
+static void zzfb_bitblt(struct zzfb_softc *sc, uint16_t x, uint16_t y,
+    uint16_t w, uint16_t h, uint16_t xs, uint16_t ys);
+
+/* wsdisplay_accessops stuff */
+static paddr_t zzfb_mmap(void *v, void *vs, off_t offset, int prot);
+static int zzfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
+    struct lwp *l);
+static int zzfb_getcmap(struct zzfb_softc *sc, struct wsdisplay_cmap *cm);
+static int zzfb_putcmap(struct zzfb_softc *sc, struct wsdisplay_cmap *cm);
+struct wsdisplay_accessops zzfb_accessops =
+    {zzfb_ioctl, zzfb_mmap, NULL, NULL, NULL, NULL, NULL, NULL};
+
+/* driver(9) essentials */
+static int zzfb_match(device_t parent, cfdata_t match, void *aux);
+static void zzfb_attach(device_t parent, device_t self, void *aux);
+CFATTACH_DECL_NEW(
+    zz9k_fb, sizeof(struct zzfb_softc), zzfb_match, zzfb_attach, NULL, NULL );
+
+#ifdef ZZFB_CONSOLE
+extern bool zz9k_exists;
+#endif /* ZZFB_CONSOLE */
+
+
+/* If you build it, they will come... */
+
+static int
+zzfb_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct zz9kbus_attach_args *bap = aux;
+	
+	if (strcmp(bap->zzaa_name, "zz9k_fb") != 0) {
+		return 0;
+	}
+
+	return 1;
+}
+
+static void
+zzfb_attach(device_t parent, device_t self, void *aux)
+{
+	struct zz9kbus_attach_args *bap = aux;
+	struct zzfb_softc *sc = device_private(self);
+	struct zz9k_softc *psc = device_private(parent);
+	struct rasops_info *ri;
+	struct wsemuldisplaydev_attach_args ws_aa;
+	long defattr;
+
+	sc->sc_dev = self;
+	sc->sc_bst.base = bap->zzaa_base;
+	sc->sc_bst.absm = &amiga_bus_stride_1;
+	sc->sc_iot = &sc->sc_bst;
+	sc->sc_regh = psc->sc_regh;
+
+	if (psc->sc_zsize >= (ZZ9K_FB_BASE + ZZ9K_FB_SIZE)) {
+		sc->sc_fbsize = ZZ9K_FB_SIZE;
+	} else {
+		sc->sc_fbsize = psc->sc_zsize - ZZ9K_FB_BASE;
+	}
+
+	if (bus_space_map(sc->sc_iot, ZZ9K_FB_BASE, sc->sc_fbsize,
+	    BUS_SPACE_MAP_LINEAR, &sc->sc_fbh)) {
+		aprint_error(": Failed to map MNT ZZ9000 framebuffer.\n");
+		return;
+	}
+
+	zzfb_set_mode(sc, ZZFB_SET_CON_MODE, ZZFB_SET_CON_BPP);
+    	zzfb_init_palette(sc);
+	zzfb_clearbg(sc, WS_DEFAULT_BG);
+
+	aprint_normal(": Framebuffer resolution: %s, "
+	    "depth: %i bpp (%s)\n", zzfb_modes[sc->sc_displaymode].name,
+	    sc->sc_bpp, zzfb_colors[sc->sc_colormode].name);
+
+	aprint_debug_dev(sc->sc_dev, "[DEBUG] registers at %p/%p (pa/va), "
+	    "framebuffer at %p/%p (pa/va) with %i MB\n",
+	    (void *)kvtop((void *)sc->sc_regh),
+	    bus_space_vaddr(sc->sc_iot, sc->sc_regh),
+	    (void *)kvtop((void *)sc->sc_fbh),
+	    bus_space_vaddr(sc->sc_iot, sc->sc_fbh),
+	    sc->sc_fbsize / (1024 * 1024));
+
+	sc->sc_defaultscreen = (struct wsscreen_descr) {"default", 0, 0, NULL,
+	    8, 16, WSSCREEN_WSCOLORS | WSSCREEN_HILIT, NULL};
+	sc->sc_screens[0] = &sc->sc_defaultscreen;
+	sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens};
+	sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
+
+	vcons_init(&sc->sc_vd, sc, &sc->sc_defaultscreen, &zzfb_accessops);
+	sc->sc_vd.init_screen = zzfb_init_screen;
+
+	ri = &sc->sc_console_screen.scr_ri;
+
+#ifdef ZZFB_CONSOLE
+	sc->sc_isconsole = true;
+	vcons_init_screen(&sc->sc_vd, &sc->sc_console_screen, 1,
+	    &defattr);
+	sc->sc_console_screen.scr_flags = VCONS_SCREEN_IS_STATIC;
+	vcons_redraw_screen(&sc->sc_console_screen);
+		
+	sc->sc_defaultscreen.textops = &ri->ri_ops;
+	sc->sc_defaultscreen.capabilities = ri->ri_caps;
+	sc->sc_defaultscreen.nrows = ri->ri_rows;
+	sc->sc_defaultscreen.ncols = ri->ri_cols;
+		
+	wsdisplay_cnattach(&sc->sc_defaultscreen, ri, 0, 0, defattr);
+	vcons_replay_msgbuf(&sc->sc_console_screen);
+#else
+	sc->sc_isconsole = false;
+	if (sc->sc_console_screen.scr_ri.ri_rows == 0) {
+		vcons_init_screen(&sc->sc_vd, &sc->sc_console_screen, 1,
+		    &defattr);
+	} else {
+		(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
+	}
+	zzfb_set_capture(sc, ZZFB_DISPLAY_MODE, ZZFB_CAPTURE_MODE);
+	aprint_normal_dev (sc->sc_dev, "Scandoubler capture: %s, "
+	    "display: %s in %s.\n",
+	    zzfb_modes[ZZFB_CAPTURE_MODE].name,
+	    zzfb_modes[ZZFB_DISPLAY_MODE].name,
+	    zzfb_colors[ZZ9K_COLOR_32BIT].name);
+#endif /* ZZFB_CONSOLE */
+
+	ws_aa.console = sc->sc_isconsole;
+	ws_aa.scrdata = &sc->sc_screenlist;
+	ws_aa.accessops = &zzfb_accessops;
+	ws_aa.accesscookie = &sc->sc_vd;
+
+	config_found(sc->sc_dev, &ws_aa, wsemuldisplaydevprint, CFARGS_NONE);
+}
+
+static void
+zzfb_init_palette(struct zzfb_softc *sc)
+{
+	for (int index = 0; index < ZZ9K_PALETTE_SIZE; index++) {
+		sc->red[index] = rasops_cmap[index * 3 + 0];
+		sc->green[index] = rasops_cmap[index * 3 + 1];
+		sc->blue[index] = rasops_cmap[index * 3 + 2];
+	}
+
+	zzfb_set_palette(sc);
+}
+
+static void
+zzfb_set_palette(struct zzfb_softc *sc)
+{
+	uint32_t palette;
+	uint8_t rVal;
+	uint8_t gVal;
+	uint8_t bVal;
+
+	for (int index = 0; index < ZZ9K_PALETTE_SIZE; index++) {
+		rVal = sc->red[index];
+		gVal = sc->green[index];
+		bVal = sc->blue[index];
+		palette = ((index << 24) | (rVal << 16) | (gVal << 8) | bVal);
+		ZZREG_W(ZZ9K_VIDEO_CTRL_DATA_HI, palette >> 16);
+		ZZREG_W(ZZ9K_VIDEO_CTRL_DATA_LO, palette & 0xFFFF);
+		ZZREG_W(ZZ9K_VIDEO_CTRL_OP, ZZ9K_OP_PALETTE);
+		ZZREG_W(ZZ9K_VIDEO_CTRL_OP, ZZ9K_OP_NOP);
+	}
+}
+
+static void
+zzfb_init_screen(void *cookie, struct vcons_screen *scr, int existing,
+    long *defattr)
+{
+	struct zzfb_softc *sc = cookie;
+	struct rasops_info *ri = &scr->scr_ri;
+
+	scr->scr_flags = VCONS_SCREEN_IS_STATIC;
+
+	wsfont_init();
+
+	ri->ri_bits = (u_char *)bus_space_vaddr(sc->sc_iot, sc->sc_fbh);
+	ri->ri_depth = sc->sc_bpp;
+	ri->ri_width = sc->sc_width;
+	ri->ri_height = sc->sc_height;
+	ri->ri_stride = sc->sc_stride;
+	ri->ri_flg = 0;
+
+	if (ri->ri_depth == 32) {	/* adjust for BGRA8888 */
+		ri->ri_rnum = 8;	/* for other depths default is OK */
+		ri->ri_gnum = 8;
+		ri->ri_bnum = 8;
+		ri->ri_rpos = 8;	/* skip over alpha channel */
+		ri->ri_gpos = 8 + ri->ri_rnum;
+		ri->ri_bpos = 8 + ri->ri_rnum + ri->ri_gnum;
+	}
+
+	rasops_init(ri, 0, 0);
+	ri->ri_caps = WSSCREEN_WSCOLORS;
+	rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
+	    ri->ri_width / ri->ri_font->fontwidth);
+	ri->ri_hw = scr;
+
+	ri->ri_ops.eraserows = zzfb_eraserows;
+	ri->ri_ops.copyrows = zzfb_copyrows;
+	ri->ri_ops.erasecols = zzfb_erasecols;
+	ri->ri_ops.copycols = zzfb_copycols;
+}
+
+static void
+zzfb_set_capture(struct zzfb_softc *sc, uint16_t display_mode,
+    uint16_t capture_mode)
+{
+	uint16_t panPtrHi;
+	uint16_t panPtrLo;
+	uint16_t new_mode;
+
+	switch (display_mode) {
+	case ZZ9K_MODE_720x480:		/* NTSC */
+		panPtrHi = ZZ9K_CAPTURE_PAN_NTSC >> 16;
+		panPtrLo = ZZ9K_CAPTURE_PAN_NTSC & 0xFFFF;
+		break;
+	case ZZ9K_MODE_720x576p50:	/* PAL */
+		panPtrHi = ZZ9K_CAPTURE_PAN_PAL >> 16;
+		panPtrLo = ZZ9K_CAPTURE_PAN_PAL & 0xFFFF;
+		break;
+	case ZZ9K_MODE_800x600:		/* VGA */
+		panPtrHi = ZZ9K_CAPTURE_PAN_VGA >> 16;
+		panPtrLo = ZZ9K_CAPTURE_PAN_VGA & 0xFFFF;
+		break;
+	default:
+		aprint_error_dev(sc->sc_dev, "Unsupported scandoubler "
+		    "capture and display mode combination.\n");
+		return;
+	}
+	new_mode = ZZ9K_MODE_SCALE_2 | ZZ9K_MODE_COLOR_32BIT | display_mode;
+	zzfb_wait_vblank(sc);
+	ZZREG_W(ZZ9K_VIDEOCAP_VMODE, capture_mode);
+	ZZREG_W(ZZ9K_BLITTER_USER1, ZZ9K_FEATURE_NONSTANDARD_VSYNC);
+	ZZREG_W(ZZ9K_SET_FEATURE, 0x0000);
+	ZZREG_W(ZZ9K_VIDEO_CAPTURE_MODE, ZZ9K_CAPTURE_ON);
+	ZZREG_W(ZZ9K_BLITTER_X1, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_Y1, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_X2, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_COLORMODE, ZZ9K_COLOR_32BIT);
+	ZZREG_W(ZZ9K_PAN_PTR_HI, panPtrHi);
+	ZZREG_W(ZZ9K_PAN_PTR_LO, panPtrLo);
+	ZZREG_W(ZZ9K_MODE, new_mode);
+	sc->sc_isrtg = false;
+}
+
+void
+zzfb_set_mode(struct zzfb_softc *sc, uint16_t display_mode,
+    uint16_t color_mode)
+{
+	uint16_t new_mode;
+
+	if ((display_mode < 0) || (display_mode >= ZZFB_MODES_SIZE))
+		display_mode = ZZ9K_MODE_1280x720;
+
+	sc->sc_width	= zzfb_modes[display_mode].width;
+	sc->sc_height	= zzfb_modes[display_mode].height;
+	new_mode	= zzfb_modes[display_mode].scale;
+	
+	if ((color_mode < 0) || (color_mode >= ZZFB_COLORS_SIZE))
+		color_mode = ZZ9K_COLOR_8BIT;
+		
+	if ((color_mode == ZZ9K_COLOR_32BIT) && (sc->sc_width > 1920))
+		color_mode = ZZ9K_COLOR_16BIT;
+
+	sc->sc_bpp    = zzfb_colors[color_mode].bpp;
+	sc->sc_stride = sc->sc_width * zzfb_colors[color_mode].stride;
+	new_mode      = new_mode | zzfb_colors[color_mode].mode | display_mode;
+
+	sc->sc_displaymode = display_mode;
+	sc->sc_colormode   = color_mode;
+
+	zzfb_wait_vblank(sc);
+	ZZREG_W(ZZ9K_VIDEO_CAPTURE_MODE, ZZ9K_CAPTURE_OFF);
+	ZZREG_W(ZZ9K_BLITTER_X1, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_Y1, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_X2, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_COLORMODE, color_mode);
+	ZZREG_W(ZZ9K_BLITTER_SRC_HI, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_SRC_LO, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_DST_HI, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_DST_LO, 0x0000);
+	ZZREG_W(ZZ9K_BLITTER_SPLIT_POS, 0x0000);
+	ZZREG_W(ZZ9K_PAN_PTR_HI, 0x0000);
+	ZZREG_W(ZZ9K_PAN_PTR_LO, 0x0000);
+	ZZREG_W(ZZ9K_MODE, new_mode);
+	sc->sc_isrtg = true;
+}
+
+static void
+zzfb_wait_vblank(struct zzfb_softc *sc)
+{
+	uint16_t vb_status = ZZREG_R(ZZ9K_VIDEO_BLANK_STATUS);
+	while (vb_status != 0)
+		vb_status = ZZREG_R(ZZ9K_VIDEO_BLANK_STATUS);
+	while (vb_status == 0)
+		vb_status = ZZREG_R(ZZ9K_VIDEO_BLANK_STATUS);
+}
+
+static void
+zzfb_clearbg(struct zzfb_softc *sc, uint8_t color_index)
+{
+	if (color_index >= ZZ9K_PALETTE_SIZE)
+		color_index = 0;
+	
+	uint32_t palette = 0;
+	uint8_t rVal = rasops_cmap[color_index * 3 + 0];
+	uint8_t gVal = rasops_cmap[color_index * 3 + 1];
+	uint8_t bVal = rasops_cmap[color_index * 3 + 2];
+
+	switch (sc->sc_colormode) {
+	case ZZ9K_COLOR_32BIT:	/* BGRA8888 */
+		palette = ((bVal << 24) | (gVal << 16) | (rVal << 8) | 0xFF);
+		break;
+	case ZZ9K_COLOR_16BIT: /* RGB565 at high word, don't ask why. */
+		palette = (((rVal & 0xF8) << 8) |
+			   ((gVal & 0xFC) << 3) |
+			   ((bVal >> 3) & 0x1F) ) << 16;
+		break;
+	case ZZ9K_COLOR_15BIT: /* ARGB1555 at high word, don't ask why. */
+		palette = ((0x8000) |
+			   ((rVal & 0xF8) << 7) |
+			   ((gVal & 0xF8) << 2) |
+			   ((bVal >> 3) & 0x1F) ) << 16;
+		break;
+	case ZZ9K_COLOR_8BIT: /* 256 LUT */
+	default:
+		palette = color_index;
+		break;
+	}
+
+	zzfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height, palette);
+}
+
+static void
+zzfb_rectfill(struct zzfb_softc *sc, uint16_t x, uint16_t y,
+    uint16_t w, uint16_t h, uint32_t color)
+{
+	ZZREG_W(ZZ9K_BLITTER_X1, x);
+	ZZREG_W(ZZ9K_BLITTER_Y1, y);
+	ZZREG_W(ZZ9K_BLITTER_X2, w);
+	ZZREG_W(ZZ9K_BLITTER_Y2, h);
+	ZZREG_W(ZZ9K_BLITTER_ROW_PITCH, sc->sc_stride >> 2);
+	ZZREG_W(ZZ9K_BLITTER_COLORMODE, sc->sc_colormode);
+	ZZREG_W(ZZ9K_BLITTER_RGB_HI, color >> 16);
+	ZZREG_W(ZZ9K_BLITTER_RGB_LO, color & 0xFFFF);
+	ZZREG_W(ZZ9K_BLITTER_OP_FILLRECT, 0x00FF);
+}
+
+static void
+zzfb_bitblt(struct zzfb_softc *sc, uint16_t x, uint16_t y, uint16_t w,
+    uint16_t h, uint16_t xs, uint16_t ys)
+{
+	ZZREG_W(ZZ9K_BLITTER_X1, x);
+	ZZREG_W(ZZ9K_BLITTER_Y1, y);
+	ZZREG_W(ZZ9K_BLITTER_X2, w);
+	ZZREG_W(ZZ9K_BLITTER_Y2, h);
+	ZZREG_W(ZZ9K_BLITTER_X3, xs);
+	ZZREG_W(ZZ9K_BLITTER_Y3, ys);
+	ZZREG_W(ZZ9K_BLITTER_ROW_PITCH, sc->sc_stride >> 2);
+	ZZREG_W(ZZ9K_BLITTER_COLORMODE, (0xFF << 8) | sc->sc_colormode);
+	ZZREG_W(ZZ9K_BLITTER_OP_COPYRECT, ZZ9K_OPT_REGULAR);
+}
+
+static void
+zzfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
+{
+	struct rasops_info *ri = cookie;
+	struct vcons_screen *scr = ri->ri_hw;
+	struct zzfb_softc *sc = scr->scr_cookie;
+	int x, y, w, h, ys;
+
+	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) {
+		x = ri->ri_xorigin;
+		ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow;
+		y = ri->ri_yorigin + ri->ri_font->fontheight * dstrow;
+		w = ri->ri_emuwidth;
+		h = ri->ri_font->fontheight * nrows;
+		zzfb_bitblt(sc, x, y, w, h, x, ys);
+	}
+}
+
+static void
+zzfb_eraserows(void *cookie, int row, int nrows, long fillattr)
+{
+	struct rasops_info *ri = cookie;
+	struct vcons_screen *scr = ri->ri_hw;
+	struct zzfb_softc *sc = scr->scr_cookie;
+	int x, y, w, h, fg, bg, ul;
+
+	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) {
+		x = ri->ri_xorigin;
+		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
+		w = ri->ri_emuwidth;
+		h = ri->ri_font->fontheight * nrows;
+		rasops_unpack_attr(fillattr, &fg, &bg, &ul);
+		zzfb_rectfill(sc, x, y, w, h, ri->ri_devcmap[bg]);
+	}
+}
+
+static void
+zzfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
+{
+	struct rasops_info *ri = cookie;
+	struct vcons_screen *scr = ri->ri_hw;
+	struct zzfb_softc *sc = scr->scr_cookie;
+	int x, y, w, h, xs;
+
+	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) {
+		xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol;
+		x = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol;
+		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
+		w = ri->ri_font->fontwidth * ncols;
+		h = ri->ri_font->fontheight;
+		zzfb_bitblt(sc, x, y, w, h, xs, y);
+	}
+}
+
+static void
+zzfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
+{
+	struct rasops_info *ri = cookie;
+	struct vcons_screen *scr = ri->ri_hw;
+	struct zzfb_softc *sc = scr->scr_cookie;
+	int x, y, w, h, fg, bg, ul;
+
+	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) {
+		x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol;
+		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
+		w = ri->ri_font->fontwidth * ncols;
+		h = ri->ri_font->fontheight;
+		rasops_unpack_attr(fillattr, &fg, &bg, &ul);
+		zzfb_rectfill(sc, x, y, w, h, ri->ri_devcmap[bg & 0xf]);
+	}
+}
+
+static int
+zzfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
+{
+	int retval = 0;
+	u_int new_wsmode;
+	struct vcons_data *vd = v;
+	struct zzfb_softc *sc = vd->cookie;
+	struct vcons_screen *scr = vd->active;
+	struct wsdisplayio_bus_id *busid;
+
+	switch (cmd) {
+	case WSDISPLAYIO_GTYPE:
+		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
+		break;
+	case WSDISPLAYIO_GET_FBINFO:
+		retval = zzfb_get_fbinfo(sc, (struct wsdisplayio_fbinfo *)data);
+		break;
+	case WSDISPLAYIO_GINFO:
+		((struct wsdisplay_fbinfo *)data)->width  = sc->sc_width;
+		((struct wsdisplay_fbinfo *)data)->height = sc->sc_height;
+		((struct wsdisplay_fbinfo *)data)->depth  = sc->sc_bpp;
+		((struct wsdisplay_fbinfo *)data)->cmsize = ZZ9K_PALETTE_SIZE;
+		break;
+	case WSDISPLAYIO_GETCMAP:
+		retval = zzfb_getcmap(sc, (struct wsdisplay_cmap *)data);
+		break;
+	case WSDISPLAYIO_PUTCMAP:
+		retval = zzfb_putcmap(sc, (struct wsdisplay_cmap *)data);
+		break;
+	case WSDISPLAYIO_GVIDEO:
+		*(int *)data = (sc->sc_isrtg == true) ?
+		    WSDISPLAYIO_VIDEO_ON : WSDISPLAYIO_VIDEO_OFF;
+		break;
+	case WSDISPLAYIO_SVIDEO:
+		if (*(int *)data == WSDISPLAYIO_VIDEO_ON) {
+			zzfb_set_mode(sc, sc->sc_displaymode, sc->sc_colormode);
+		} else {
+			zzfb_set_capture(sc, ZZFB_DISPLAY_MODE,
+			    ZZFB_CAPTURE_MODE);
+		}
+		break;	
+	case WSDISPLAYIO_GET_BUSID:
+		busid = data;
+		busid->bus_type = WSDISPLAYIO_BUS_SOC;
+		break;
+	case WSDISPLAYIO_GCURPOS:
+	case WSDISPLAYIO_SCURPOS:
+	case WSDISPLAYIO_GCURMAX:
+	case WSDISPLAYIO_GCURSOR:
+	case WSDISPLAYIO_SCURSOR:
+		retval = EPASSTHROUGH;
+		break;	
+	case WSDISPLAYIO_GMODE:
+		*(u_int *)data = sc->sc_wsmode;
+		break;	
+	case WSDISPLAYIO_SMODE:
+		new_wsmode = *(u_int *)data;
+		if (new_wsmode != sc->sc_wsmode) {
+			switch (new_wsmode) {
+			case WSDISPLAYIO_MODE_EMUL:
+				zzfb_set_mode(sc,
+				    ZZFB_SET_CON_MODE, ZZFB_SET_CON_BPP);
+				zzfb_init_palette(sc);
+				zzfb_clearbg(sc, WS_DEFAULT_BG);
+				vcons_redraw_screen(scr);
+				sc->sc_wsmode = new_wsmode;
+				break;
+			case WSDISPLAYIO_MODE_MAPPED:
+				zzfb_set_mode(sc,
+				    ZZFB_SET_GFX_MODE, ZZFB_SET_GFX_BPP);
+				    zzfb_clearbg(sc, WSCOL_BLACK);
+				sc->sc_wsmode = new_wsmode;
+				break;
+			case WSDISPLAYIO_MODE_DUMBFB:
+				zzfb_set_mode(sc,
+				    ZZFB_SET_DFB_MODE, ZZFB_SET_DFB_BPP);
+				    zzfb_clearbg(sc, WSCOL_BLACK);
+				sc->sc_wsmode = new_wsmode;
+				break;
+			default:
+				retval = EINVAL;
+				break;
+			}
+		} else {
+			retval = EINVAL;
+		}
+		break;
+	case WSDISPLAYIO_LINEBYTES:
+		*(u_int *)data = sc->sc_stride;
+		break;
+	case WSDISPLAYIO_GMSGATTRS:
+	case WSDISPLAYIO_SMSGATTRS:
+	case WSDISPLAYIO_GBORDER:
+	case WSDISPLAYIO_SBORDER:
+	case WSDISPLAYIO_GETWSCHAR:
+	case WSDISPLAYIO_PUTWSCHAR:
+	case WSDISPLAYIO_SSPLASH:
+	case WSDISPLAYIO_GET_EDID:
+	case WSDISPLAYIO_SETVERSION:	
+	default:
+		retval = EPASSTHROUGH;
+		break;
+	}
+
+	return retval;
+}
+
+static paddr_t
+zzfb_mmap(void *v, void *vs, off_t offset, int prot)
+{
+	struct vcons_data *vd = v;
+	struct zzfb_softc *sc = vd->cookie;
+
+	if ( (offset >= 0) && (offset < sc->sc_fbsize) ) {
+		return bus_space_mmap( sc->sc_iot,
+		    (bus_addr_t)kvtop((void *)sc->sc_fbh), offset, prot,
+		    BUS_SPACE_MAP_LINEAR);
+	} else {
+		return -1;
+	}
+}
+
+static int
+zzfb_get_fbinfo(struct zzfb_softc *sc, struct wsdisplayio_fbinfo *fbi)
+{
+	uint32_t bpA, bpR, bpG, bpB;
+
+	switch (sc->sc_bpp) {
+	case 8:
+		bpA = 0; bpR = 0; bpG = 0; bpB = 0;
+		break;
+	case 15:
+		bpA = 1; bpR = 5; bpG = 5; bpB = 5;
+		break;
+	case 16:
+		bpA = 0; bpR = 5; bpG = 6; bpB = 5;
+		break;
+	case 32:
+		bpA = 8; bpR = 8; bpG = 8; bpB = 8;
+		break;
+	default:
+		return EINVAL;
+	}
+
+	fbi->fbi_flags		= 0;
+	fbi->fbi_fboffset	= 0;
+	fbi->fbi_fbsize		= sc->sc_stride * sc->sc_height;
+	fbi->fbi_width 		= sc->sc_width;
+	fbi->fbi_height		= sc->sc_height;
+	fbi->fbi_stride		= sc->sc_stride;
+	fbi->fbi_bitsperpixel	= (sc->sc_bpp == 15) ? 16 : sc->sc_bpp;
+
+	switch (sc->sc_bpp) {
+	case 8:
+		fbi->fbi_pixeltype			    = WSFB_CI;	
+		fbi->fbi_subtype.fbi_cmapinfo.cmap_entries  = ZZ9K_PALETTE_SIZE;
+		return 0;
+	case 15: /* ZZ9000 uses ARGB1555 format for 15 bpp */
+	case 16: /* ZZ9000 uses RGB565 format for 16 bpp */
+		fbi->fbi_subtype.fbi_rgbmasks.alpha_offset	= bpB+bpG+bpR;
+		fbi->fbi_subtype.fbi_rgbmasks.red_offset	= bpB+bpG;
+		fbi->fbi_subtype.fbi_rgbmasks.green_offset	= bpB;
+		fbi->fbi_subtype.fbi_rgbmasks.blue_offset	= 0;
+		break;
+	case 32: /* ZZ9000 uses BGRA8888 format for 32 bpp */
+		fbi->fbi_subtype.fbi_rgbmasks.alpha_offset	= 0;
+		fbi->fbi_subtype.fbi_rgbmasks.red_offset	= bpA;
+		fbi->fbi_subtype.fbi_rgbmasks.green_offset	= bpA+bpR;
+		fbi->fbi_subtype.fbi_rgbmasks.blue_offset	= bpA+bpR+bpG;
+		break;
+	default:
+		return EINVAL;
+	}
+
+	fbi->fbi_pixeltype				= WSFB_RGB;
+	fbi->fbi_subtype.fbi_rgbmasks.alpha_size	= bpA;
+	fbi->fbi_subtype.fbi_rgbmasks.red_size		= bpR;
+	fbi->fbi_subtype.fbi_rgbmasks.green_size	= bpG;
+	fbi->fbi_subtype.fbi_rgbmasks.blue_size		= bpB;
+
+	return 0;
+}
+
+static int
+zzfb_getcmap(struct zzfb_softc *sc, struct wsdisplay_cmap *cm)
+{
+	int retval = 0;
+	u_int index = cm->index;
+	u_int count = cm->count;
+
+	if (index >= ZZ9K_PALETTE_SIZE || index + count > ZZ9K_PALETTE_SIZE)
+		return EINVAL;
+
+	retval = copyout(&sc->red[index], cm->red, count);
+	if (retval != 0)
+		return retval;
+	
+	retval = copyout(&sc->green[index], cm->green, count);
+	if (retval != 0)
+		return retval;
+	
+	retval = copyout(&sc->blue[index], cm->blue, count);
+	if (retval != 0)
+		return retval;
+
+	return retval;
+}
+
+static int
+zzfb_putcmap(struct zzfb_softc *sc, struct wsdisplay_cmap *cm)
+{
+	int retval = 0;
+	u_int index = cm->index;
+	u_int count = cm->count;
+	
+	if (index >= ZZ9K_PALETTE_SIZE || index + count > ZZ9K_PALETTE_SIZE)
+		return EINVAL;
+
+	retval = copyin(cm->red, &sc->red[index], count);
+	if (retval != 0)
+		return retval;
+
+	retval = copyin(cm->green, &sc->green[index], count);
+	if (retval != 0)
+		return retval;
+
+	retval = copyin(cm->blue, &sc->blue[index], count);
+	if (retval != 0)
+		return retval;
+	
+	zzfb_set_palette(sc);
+	return retval;
+}
+
+/*
+ * Early console handling, associated with amiga/conf.c file which holds a
+ * table of all console devices. The below functions ensures that ZZ9000 becomes
+ * wsdisplay0 and wskbd0 gets attached to it.
+ */
+
+/* early console handling */
+void zzfb_cnprobe(struct consdev *cd);
+void zzfb_cninit(struct consdev *cd);
+void zzfb_cnpollc(dev_t cd, int on);
+void zzfb_cnputc(dev_t cd, int ch);
+int zzfb_cngetc(dev_t cd);
+
+void 
+zzfb_cnprobe(struct consdev *cd)
+{
+#ifdef ZZFB_CONSOLE
+	if (zz9k_exists == true) {
+		cd->cn_pri = CN_INTERNAL;
+	} else {
+		cd->cn_pri = CN_DEAD;
+	}
+	cd->cn_dev = NODEV;
+#endif /* ZZFB_CONSOLE */
+}
+
+void
+zzfb_cninit(struct consdev *cd)
+{
+#if defined ZZFB_CONSOLE && NKBD > 0
+	/* tell kbd device it is used as console keyboard */
+	if (zz9k_exists == true)
+		kbd_cnattach();
+#endif /* ZZFB_CONSOLE && NKBD > 0 */
+}
+
+void
+zzfb_cnpollc(dev_t cd, int on)
+{
+}
+
+void
+zzfb_cnputc(dev_t cd, int ch)
+{
+}
+
+int
+zzfb_cngetc(dev_t cd)
+{
+	return 0;
+}
Index: src/sys/arch/amiga/dev/zz9k_if.c
diff -u /dev/null src/sys/arch/amiga/dev/zz9k_if.c:1.1
--- /dev/null	Wed May  3 13:49:30 2023
+++ src/sys/arch/amiga/dev/zz9k_if.c	Wed May  3 13:49:30 2023
@@ -0,0 +1,395 @@
+/*	$NetBSD: zz9k_if.c,v 1.1 2023/05/03 13:49:30 phx Exp $ */
+
+/*
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alain Runa.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: zz9k_if.c,v 1.1 2023/05/03 13:49:30 phx Exp $");
+
+/* miscellaneous */
+#include <sys/types.h>			/* size_t */
+#include <sys/stdint.h>			/* uintXX_t */
+#include <sys/stdbool.h>		/* bool */
+#include <sys/syslog.h>			/* log(), LOG_ERR */
+
+/* driver(9) */
+#include <sys/param.h>			/* NODEV */
+#include <sys/device.h>			/* CFATTACH_DECL_NEW(), device_priv() */
+#include <sys/errno.h>			/* EINVAL, ENODEV, EPASSTHROUGH */
+
+/* bus_space(9) and zorro bus */
+#include <sys/bus.h>			/* bus_space_xxx(), bus_space_xxx_t */
+#include <sys/cpu.h>			/* kvtop() */
+#include <sys/systm.h>			/* aprint_xxx(), memcpy() */
+#include <amiga/dev/zbusvar.h>		/* zbus_args */
+
+/* arp(9) and mbuf(9) */
+#include <net/if.h>			/* if_oerrors */
+#include <net/if_ether.h>		/* ethercom, ifnet, ether_ifattach() */
+#include <net/if_dl.h>			/* satosdl(), sockaddr_dl, dl_addr */
+#include <net/bpf.h>			/* bpf_mtap(), BPF_D_OUT */
+#include <netinet/if_inarp.h>		/* arp_ifinit() */
+#include <sys/mbuf.h>			/* mbuf_xxx */
+#include <sys/sockio.h>			/* SIOXXX */
+
+/* Interrupt related */
+#include <sys/intr.h>			/* splvm(), splx() */ 
+#include <amiga/amiga/isr.h>		/* isr */
+
+/* zz9k related */
+#include <amiga/dev/zz9kvar.h>		/* zz9kbus_attach_args */
+#include <amiga/dev/zz9kreg.h>		/* ZZ9000 registers */
+#include "zz9k_if.h"			/* NZZ9K_IF */
+
+
+/* The allmighty softc structure */
+struct zzif_softc {
+	device_t sc_dev;
+	struct bus_space_tag sc_bst;
+	bus_space_tag_t sc_iot;
+	bus_space_handle_t sc_regh;
+	bus_space_handle_t sc_rxh;
+	bus_space_handle_t sc_txh;
+	
+	struct ethercom sc_ethercom;
+	struct isr sc_isr;
+	void* sc_txbuffer;
+	void* sc_rxbuffer;
+	uint16_t sc_sequence;
+};
+
+/* rx buffer contents */
+struct zzif_frame {
+	uint16_t size;
+	uint16_t serial;
+	struct ether_header header;
+	uint8_t payload[ETHER_MAX_LEN - ETHER_HDR_LEN];
+};
+
+
+/* ifnet related callbacks */
+static void zzif_init(struct zzif_softc *sc);
+static void zzif_stop(struct zzif_softc *sc);
+static void zzif_start(struct ifnet *ifp);
+static int zzif_intr(void *arg);
+static int zzif_ioctl(struct ifnet *ifp, u_long cmd, void *data);
+
+/* driver(9) essentials */
+static int zzif_match(device_t parent, cfdata_t match, void *aux);
+static void zzif_attach(device_t parent, device_t self, void *aux);
+CFATTACH_DECL_NEW(
+    zz9k_if, sizeof(struct zzif_softc), zzif_match, zzif_attach, NULL, NULL);
+
+
+/* Oh my God, it's full of stars! */
+
+static int
+zzif_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct zz9kbus_attach_args *bap = aux;
+
+	if (strcmp(bap->zzaa_name, "zz9k_if") != 0)
+		return 0;
+
+	return 1;
+}
+
+static void
+zzif_attach(device_t parent, device_t self, void *aux)
+{
+	struct zz9kbus_attach_args *bap = aux;
+	struct zzif_softc *sc = device_private(self);
+	struct zz9k_softc *psc = device_private(parent);
+	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+	uint8_t lla[ETHER_ADDR_LEN];
+
+	sc->sc_dev = self;
+	sc->sc_bst.base = bap->zzaa_base;
+	sc->sc_bst.absm = &amiga_bus_stride_1;
+	sc->sc_iot = &sc->sc_bst;
+	sc->sc_regh = psc->sc_regh;
+
+	if (bus_space_map(sc->sc_iot, ZZ9K_RX_BASE, ZZ9K_RX_SIZE,
+	    BUS_SPACE_MAP_LINEAR, &sc->sc_rxh)) {
+		aprint_error(": Failed to map MNT ZZ9000 eth rx buffer.\n");
+		return;
+	}
+
+	if (bus_space_map(sc->sc_iot, ZZ9K_TX_BASE, ZZ9K_TX_SIZE,
+	    BUS_SPACE_MAP_LINEAR, &sc->sc_txh)) {
+		aprint_error(": Failed to map MNT ZZ9000 eth tx buffer.\n");
+		return;
+	}
+
+	/* get MAC from NIC */
+	uint16_t macHI = ZZREG_R(ZZ9K_ETH_MAC_HI);
+	uint16_t macMD = ZZREG_R(ZZ9K_ETH_MAC_MD);
+	uint16_t macLO = ZZREG_R(ZZ9K_ETH_MAC_LO);
+	lla[0] = macHI >> 8;
+	lla[1] = macHI & 0xff;
+	lla[2] = macMD >> 8;
+	lla[3] = macMD & 0xff;
+	lla[4] = macLO >> 8;
+	lla[5] = macLO & 0xff;
+
+#if 0
+	aprint_normal(": Ethernet address %s  "
+	    "(10BASE-T, 100BASE-TX, AUTO)\n", ether_sprintf(lla));
+#endif
+
+	aprint_debug_dev(sc->sc_dev, "[DEBUG] registers at %p/%p (pa/va), "
+	    "rx buffer at %p/%p (pa/va), tx buffer at %p/%p (pa/va)\n",
+	    (void *)kvtop((void *)sc->sc_regh),
+	    bus_space_vaddr(sc->sc_iot, sc->sc_regh),
+	    (void *)kvtop((void *)sc->sc_rxh),
+	    bus_space_vaddr(sc->sc_iot, sc->sc_rxh),
+	    (void *)kvtop((void *)sc->sc_txh),
+	    bus_space_vaddr(sc->sc_iot, sc->sc_txh));
+
+	/* set rx/tx buffers */
+	sc->sc_txbuffer = bus_space_vaddr(sc->sc_iot, sc->sc_txh);
+	sc->sc_rxbuffer = bus_space_vaddr(sc->sc_iot, sc->sc_rxh);
+
+	/* configure the network interface */
+	memcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
+	ifp->if_softc = sc;
+	ifp->if_ioctl = zzif_ioctl;
+	ifp->if_start = zzif_start;
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+	ifp->if_mtu   = ETHERMTU;
+
+	/* attach the network interface. */
+	if_attach(ifp);
+	if_deferred_start_init(ifp, NULL);
+	ether_ifattach(ifp, lla);
+
+	aprint_normal(": Ethernet address %s\n", ether_sprintf(lla));
+
+	/* setup the (receiving) interrupt service routine on int6 (default) */
+	sc->sc_isr.isr_intr = zzif_intr;
+	sc->sc_isr.isr_arg  = sc;
+	sc->sc_isr.isr_ipl  = 6;
+	add_isr(&sc->sc_isr);
+}
+
+static void
+zzif_init(struct zzif_softc *sc)
+{
+	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+	int s = splvm();
+
+	ZZREG_W(ZZ9K_CONFIG, ZZREG_R(ZZ9K_CONFIG) | ZZ9K_CONFIG_INT_ETH);
+	ifp->if_flags |= IFF_RUNNING;
+	ifp->if_flags &= ~IFF_OACTIVE;
+	if_schedule_deferred_start(ifp);
+	splx(s);
+}
+
+static void
+zzif_stop(struct zzif_softc *sc)
+{
+	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+	int s = splvm();
+
+	ZZREG_W(ZZ9K_CONFIG, ZZREG_R(ZZ9K_CONFIG) & ~ZZ9K_CONFIG_INT_ETH);
+	ifp->if_flags &= ~IFF_RUNNING;
+	splx(s);
+}
+
+static void
+zzif_start(struct ifnet *ifp)
+{
+	struct zzif_softc *sc = ifp->if_softc;
+	uint8_t *frame= (uint8_t *)sc->sc_txbuffer;
+	struct mbuf *m;
+	int size;
+
+	if ((ifp->if_flags & IFF_RUNNING) == 0)
+		return;
+
+	ifp->if_flags |= IFF_OACTIVE;
+
+	for (;;) {
+		IF_DEQUEUE(&ifp->if_snd, m);
+		if (m == NULL)
+			break;
+
+		/* this should never happen, otherwise there's no help */
+		if ((m->m_flags & M_PKTHDR) == 0)
+			panic("zzif_start: no packet header mbuf");
+		
+		size = m->m_pkthdr.len;
+		if (size == 0)
+			panic("zzif_start: header mbuf with 0 len");
+
+		if ((size < (ETHER_HDR_LEN)) ||
+		    (size > (ETHER_MAX_LEN-ETHER_CRC_LEN))) {
+			aprint_error_dev(sc->sc_dev,
+			    ": abnormal tx frame size of %i bytes\n", size);
+			m_freem(m);
+			break;
+		}
+
+		/* make bpf happy */
+		bpf_mtap(ifp, m, BPF_D_OUT);
+		
+		/* copy dequeued mbuf data to tranmit buffer of the ZZ9000 */
+		for (struct mbuf *n = m; n != NULL; n = n->m_next) {
+			memcpy(frame, n->m_data, n->m_len);
+			frame += n->m_len;
+		}
+
+		m_freem(m);	/* we don't need it anymore */
+
+		/* tell ZZ9000 to transmit packet with size and check result */ 
+		ZZREG_W(ZZ9K_ETH_TX, size);
+		size = ZZREG_R(ZZ9K_ETH_TX);
+		if (size == 0) {
+			if_statinc(ifp, if_opackets);
+		} else {
+			if_statinc(ifp, if_oerrors);
+		}
+	}
+
+	ifp->if_flags &= ~IFF_OACTIVE;
+}
+
+static int
+zzif_intr(void *arg)
+{
+	struct zzif_softc *sc = arg;
+
+	uint16_t conf = ZZREG_R(ZZ9K_CONFIG);
+	if ((conf & ZZ9K_CONFIG_INT_ETH) == 0)
+		return 0;
+
+	ZZREG_W(ZZ9K_CONFIG, conf & ~ZZ9K_CONFIG_INT_ETH);  /* disable INT */
+	ZZREG_W(ZZ9K_CONFIG, ZZ9K_CONFIG_INT_ACK | ZZ9K_CONFIG_INT_ACK_ETH);
+
+	struct zzif_frame *frame = (struct zzif_frame *)sc->sc_rxbuffer;
+	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+	struct mbuf *m;
+
+	/* check if interrupt was due to new packet arrival */
+	if (frame->serial == sc->sc_sequence) {
+		ZZREG_W(ZZ9K_ETH_RX, 1);	/* discard packet */
+		ZZREG_W(ZZ9K_CONFIG, conf);	/* restore interrupt */
+		return 0;	/* nope, something else triggered it */
+	}
+
+	sc->sc_sequence = frame->serial;
+
+	if ((frame->size < (ETHER_MIN_LEN-ETHER_CRC_LEN)) ||
+	    (frame->size > ETHER_MAX_LEN)) {
+		aprint_error_dev(sc->sc_dev,
+		    ": abnormal rx frame size of %i bytes\n", frame->size);
+		ZZREG_W(ZZ9K_ETH_RX, 1);	/* discard packet */
+		ZZREG_W(ZZ9K_CONFIG, conf);	/* restore interrupt */
+		return 0;
+	}
+
+	int s = splvm();
+	m = m_devget((char *)&frame->header, frame->size, 0, ifp);
+	if_percpuq_enqueue(ifp->if_percpuq, m);
+	splx(s);
+	ZZREG_W(ZZ9K_ETH_RX, 1);	/* packet served */
+	ZZREG_W(ZZ9K_CONFIG, conf);	/* restore interrupt */
+
+	return 1;
+}
+
+static int
+zzif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+{
+	struct zzif_softc *sc = ifp->if_softc;
+	struct ifaddr *ifa;
+	struct if_laddrreq *lar;
+	const struct sockaddr_dl *sdl;
+	int retval = 0;
+
+	int s = splvm();
+
+	switch (cmd) {
+	case SIOCINITIFADDR:
+		ifa = (struct ifaddr *)data;
+		zzif_stop(sc);
+		zzif_init(sc);
+		switch (ifa->ifa_addr->sa_family) {
+		case AF_INET:
+			arp_ifinit(ifp, ifa);
+			break;
+		default:
+			break;
+		}
+		break;
+	case SIOCSIFFLAGS:
+		retval = ifioctl_common(ifp, cmd, data);
+		if (retval != 0)
+			break;
+
+		switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+		case IFF_RUNNING: /* and not UP */
+			zzif_stop(sc);
+			break;
+		case IFF_UP: /* and not RUNNING */
+			zzif_init(sc);
+			break;
+		case (IFF_UP | IFF_RUNNING):
+			zzif_stop(sc);
+			zzif_init(sc);
+			break;
+		default:
+			break;
+		}
+		break;
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		retval = EINVAL;
+		break;
+	case SIOCALIFADDR:
+		lar = (struct if_laddrreq *)data;
+		sdl = satocsdl(&lar->addr);
+		uint8_t *mac = (uint8_t *)&sdl->sdl_addr.dl_data;
+
+		if ((lar->flags==IFLR_ACTIVE) && (sdl->sdl_family==AF_LINK)) {
+			ZZREG_W(ZZ9K_ETH_MAC_HI, (mac[0] << 8) | mac[1]);
+			ZZREG_W(ZZ9K_ETH_MAC_MD, (mac[2] << 8) | mac[3]);
+			ZZREG_W(ZZ9K_ETH_MAC_LO, (mac[4] << 8) | mac[5]);
+		}
+		aprint_normal(": Ethernet address %s", ether_sprintf(mac));
+		retval = ether_ioctl(ifp, cmd, data);
+		break;
+	default:
+		retval = ether_ioctl(ifp, cmd, data);
+		break;
+	}
+
+	splx(s);
+	
+	return retval;
+}
Index: src/sys/arch/amiga/dev/zz9k_usb.c
diff -u /dev/null src/sys/arch/amiga/dev/zz9k_usb.c:1.1
--- /dev/null	Wed May  3 13:49:30 2023
+++ src/sys/arch/amiga/dev/zz9k_usb.c	Wed May  3 13:49:30 2023
@@ -0,0 +1,107 @@
+/*	$NetBSD: zz9k_usb.c,v 1.1 2023/05/03 13:49:30 phx Exp $ */
+
+/*
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alain Runa.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: zz9k_usb.c,v 1.1 2023/05/03 13:49:30 phx Exp $");
+
+/* miscellaneous */
+#include <sys/types.h>			/* size_t */
+#include <sys/stdint.h>			/* uintXX_t */
+#include <sys/stdbool.h>		/* bool */
+
+/* driver(9) */
+#include <sys/param.h>			/* NODEV */
+#include <sys/device.h>			/* CFATTACH_DECL_NEW(), device_priv() */
+#include <sys/errno.h>			/* . */
+
+/* bus_space(9) and zorro bus */
+#include <sys/bus.h>			/* bus_space_xxx(), bus_space_xxx_t */
+#include <sys/cpu.h>			/* kvtop() */
+#include <sys/systm.h>			/* aprint_xxx() */
+
+/* zz9k related */
+#include <amiga/dev/zz9kvar.h>		/* zz9kbus_attach_args */
+#include <amiga/dev/zz9kreg.h>		/* ZZ9000 registers */
+#include "zz9k_usb.h"			/* NZZ9K_USB */
+
+
+/* The allmighty softc structure */
+struct zzusb_softc {
+	device_t sc_dev;
+	struct bus_space_tag sc_bst;
+	bus_space_tag_t sc_iot;
+	bus_space_handle_t sc_regh;
+};
+
+/* driver(9) essentials */
+static int zzusb_match(device_t parent, cfdata_t match, void *aux);
+static void zzusb_attach(device_t parent, device_t self, void *aux);
+CFATTACH_DECL_NEW(zz9k_usb, sizeof(struct zzusb_softc),
+    zzusb_match, zzusb_attach, NULL, NULL);
+
+
+/* Go ahead, make my day. */
+
+static int
+zzusb_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct zz9kbus_attach_args *bap = aux;
+
+	if (strcmp(bap->zzaa_name, "zz9k_usb") != 0)
+		return 0;
+
+	return 1;
+}
+
+static void
+zzusb_attach(device_t parent, device_t self, void *aux)
+{
+	struct zz9kbus_attach_args *bap = aux;
+	struct zzusb_softc *sc = device_private(self);
+	struct zzusb_softc *psc = device_private(parent);
+
+	sc->sc_dev = self;
+	sc->sc_bst.base = bap->zzaa_base;
+	sc->sc_bst.absm = &amiga_bus_stride_1;
+	sc->sc_iot = &sc->sc_bst;
+	sc->sc_regh = psc->sc_regh;
+
+	uint16_t capacity  = ZZREG_R(ZZ9K_USB_CAPACITY);
+	aprint_normal(": USB device %sdetected.\n",
+	    (capacity == 0) ? "not " : "");
+	aprint_normal_dev (sc->sc_dev,
+	    "MNT ZZ9000 USB driver is not implemented yet.\n");
+
+	aprint_debug_dev(sc->sc_dev, "[DEBUG] registers at %p/%p (pa/va)\n",
+	    (void *)kvtop((void *)sc->sc_regh),
+	    bus_space_vaddr(sc->sc_iot, sc->sc_regh));
+
+	/* to be implemented */
+}
Index: src/sys/arch/amiga/dev/zz9kreg.h
diff -u /dev/null src/sys/arch/amiga/dev/zz9kreg.h:1.1
--- /dev/null	Wed May  3 13:49:30 2023
+++ src/sys/arch/amiga/dev/zz9kreg.h	Wed May  3 13:49:30 2023
@@ -0,0 +1,288 @@
+/*	$NetBSD: zz9kreg.h,v 1.1 2023/05/03 13:49:30 phx Exp $ */
+
+/*
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alain Runa. Register names derived from original drivers for AmigaOS.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ZZ9KREG_H
+#define ZZ9KREG_H
+
+/* Expected hardware and firmware versions */
+#define ZZ9K_HW_VER			(0x0000)
+#define ZZ9K_FW_VER			(0x010D)        /* v1.13 */
+#define ZZ9K_FW_VER_MIN			(0x010D)        /* v1.13 */
+
+/* Zorro IDs */
+#define ZZ9K_MANID			(0x6D6E)
+#define ZZ9K_PRODID_Z2			(0x0003)
+#define ZZ9K_PRODID_Z3			(0x0004)
+
+/* Address space */
+#define ZZ9K_REG_BASE			(0x00000000)
+#define ZZ9K_REG_SIZE			(0x00002000)	/* 8 KB */
+#define ZZ9K_FB_BASE			(0x00010000)
+#define ZZ9K_FB_SIZE			(0x02FF0000)	/* 48 MB - 64 KB */
+#define ZZ9K_RX_BASE			(0x00002000)
+#define ZZ9K_RX_SIZE			(0x00002000)	/* 8 KB */
+#define ZZ9K_TX_BASE			(0x00008000)
+#define ZZ9K_TX_SIZE			(0x00002000)	/* 8 KB */
+
+/* Color mode */
+#define ZZ9K_COLOR_8BIT			(0)
+#define ZZ9K_COLOR_16BIT		(1)
+#define ZZ9K_COLOR_32BIT		(2)
+#define ZZ9K_COLOR_15BIT		(3)
+
+/* Modes of ZZ9K_MODE */
+#define ZZ9K_MODE_1280x720		(0)
+#define ZZ9K_MODE_800x600		(1)
+#define ZZ9K_MODE_640x480		(2)
+#define ZZ9K_MODE_1024x768		(3)
+#define ZZ9K_MODE_1280x1024		(4)
+#define ZZ9K_MODE_1920x1080		(5)
+#define ZZ9K_MODE_720x576p50		(6)
+#define ZZ9K_MODE_1920x1080p50		(7)
+#define ZZ9K_MODE_720x480		(8)
+#define ZZ9K_MODE_640x512		(9)
+#define ZZ9K_MODE_1600x1200		(10)
+#define ZZ9K_MODE_2560x1440p30		(11)
+#define ZZ9K_MODE_720x576p50_NS_PAL     (12)
+#define ZZ9K_MODE_720x480_NS_PAL        (13)
+#define ZZ9K_MODE_720x576p50_NS_NTSC    (14)
+#define ZZ9K_MODE_720x480_NS_NTSC       (15)
+#define ZZ9K_MODE_640x400               (16)
+#define ZZ9K_MODE_1920x800              (17)
+
+/* Some registers expect the modes and scale factors shifted */
+#define ZZ9K_MODE_COLOR_8BIT		(ZZ9K_COLOR_8BIT  << 8)
+#define ZZ9K_MODE_COLOR_16BIT		(ZZ9K_COLOR_16BIT << 8)
+#define ZZ9K_MODE_COLOR_32BIT		(ZZ9K_COLOR_32BIT << 8)
+#define ZZ9K_MODE_COLOR_15BIT		(ZZ9K_COLOR_15BIT << 8)
+#define ZZ9K_MODE_SCALE_0		(0 << 12)
+#define ZZ9K_MODE_SCALE_1		(1 << 12)
+#define ZZ9K_MODE_SCALE_2		(2 << 12)
+#define ZZ9K_MODE_SCALE_3		(3 << 12)
+
+/* Feature of ZZ9K_BLITTER_USER1 for ZZ9K_FEATURE */
+#define	ZZ9K_FEATURE_NONE		(0)
+#define	ZZ9K_FEATURE_SECONDARY_PALETTE	(1)
+#define	ZZ9K_FEATURE_NONSTANDARD_VSYNC	(2)
+
+/* Video capture pan */
+#define ZZ9K_CAPTURE_PAN_NTSC		(0x00E00000)
+#define ZZ9K_CAPTURE_PAN_PAL		(0x00E00000)
+#define ZZ9K_CAPTURE_PAN_VGA		(0x00DFF2F8)
+
+/* Modes of ZZ9K_VIDEO_CAPTURE_MODE */
+#define ZZ9K_CAPTURE_OFF		(0)
+#define ZZ9K_CAPTURE_ON			(1)
+
+/* Operations of MNTZ_VIDEO_CONTROL_OP */
+#define ZZ9K_OP_NOP			(0)
+#define ZZ9K_OP_PALETTE			(3)
+#define ZZ9K_OP_VSYNC			(5)
+
+#define ZZ9K_PALETTE_SIZE		(256)
+
+/* Options of ZZ9K_BLITTER_OP_COPYRECT */
+#define ZZ9K_OPT_REGULAR                (1)
+#define ZZ90_OPT_NOMASK                 (2)
+
+/* Video Control */
+#define ZZ9K_VIDEO_CTRL_DATA_HI		(0x1000)
+#define ZZ9K_VIDEO_CTRL_DATA_LO		(0x1002)
+#define ZZ9K_VIDEO_CTRL_OP		(0x1004)
+#define ZZ9K_VIDEO_CAPTURE_MODE		(0x1006)
+#define ZZ9K_VIDEO_BLANK_STATUS		(0x1600)
+
+/* Bits of ZZ9K_CONFIG */
+#define ZZ9K_CONFIG_INT_ETH		(1 << 0)
+#define ZZ9K_CONFIG_INT_AUDIO		(1 << 1)
+#define ZZ9K_CONFIG_INT_ACK		(1 << 3)
+#define ZZ9K_CONFIG_INT_ACK_ETH		(1 << 4)
+#define ZZ9K_CONFIG_INT_ACK_AUDIO	(1 << 5)
+
+/* Bits of ZZ9K_AUDIO_CONFIG */
+#define ZZ9K_AUDIO_CONFIG_INT_AUDIO	(1 << 0)
+
+/* Parameters of ZZ9K_AUDIO_PARAM */
+#define ZZ9K_AP_TX_BUF_OFFS_HI		(0)
+#define ZZ9K_AP_TX_BUF_OFFS_LO		(1)
+#define ZZ9K_AP_RX_BUF_OFFS_HI		(2)
+#define ZZ9K_AP_RX_BUF_OFFS_LO		(3)
+#define ZZ9K_AP_DSP_PROG_OFFS_HI	(4)
+#define ZZ9K_AP_DSP_PROG_OFFS_LO	(5)
+#define ZZ9K_AP_DSP_PARAM_OFFS_HI	(6)
+#define ZZ9K_AP_DSP_PARAM_OFFS_LO	(7)
+#define ZZ9K_AP_DSP_UPLOAD		(8)
+#define ZZ9K_AP_DSP_SET_LOWPASS		(9)
+#define ZZ9K_AP_DSP_SET_VOLUMES		(10)
+#define ZZ9K_AP_DSP_SET_PREFACTOR	(11)
+#define ZZ9K_AP_DSP_SET_EQ_BAND1	(12)
+#define ZZ9K_AP_DSP_SET_EQ_BAND2	(13)
+#define ZZ9K_AP_DSP_SET_EQ_BAND3	(14)
+#define ZZ9K_AP_DSP_SET_EQ_BAND4	(15)
+#define ZZ9K_AP_DSP_SET_EQ_BAND5	(16)
+#define ZZ9K_AP_DSP_SET_EQ_BAND6	(17)
+#define ZZ9K_AP_DSP_SET_EQ_BAND7	(18)
+#define ZZ9K_AP_DSP_SET_EQ_BAND8	(29)
+#define ZZ9K_AP_DSP_SET_EQ_BAND9	(20)
+#define ZZ9K_AP_DSP_SET_EQ_BAND10	(21)
+#define ZZ9K_AP_DSP_SET_STEREO_VOLUME	(22)
+
+/* Parameters of ZZ9K_DECODER_PARAM */
+#define ZZ9K_DP_DECODE_CLEAR		(0)
+#define ZZ9K_DP_DECODE_INIT		(1)
+#define ZZ9K_DP_DECODE_RUN		(2)
+
+
+/* REGISTERS */
+
+/* Config and Video */
+#define ZZ9K_HW_VERSION			0x00
+#define ZZ9K_MODE			0x02
+#define ZZ9K_CONFIG			0x04
+
+#define ZZ9K_SPRITE_X			0x06
+#define ZZ9K_SPRITE_Y			0x08
+
+#define ZZ9K_PAN_PTR_HI			0x0A
+#define ZZ9K_PAN_PTR_LO			0x0C
+#define ZZ9K_VIDEOCAP_VMODE		0x0E
+
+/* Blitter */
+#define ZZ9K_BLITTER_X1			0x10
+#define ZZ9K_BLITTER_Y1			0x12
+#define ZZ9K_BLITTER_X2			0x14
+#define ZZ9K_BLITTER_Y2			0x16
+#define ZZ9K_BLITTER_ROW_PITCH		0x18
+#define ZZ9K_BLITTER_X3			0x1A
+#define ZZ9K_BLITTER_Y3			0x1C
+#define ZZ9K_BLITTER_RGB_HI		0x1E
+#define ZZ9K_BLITTER_RGB_LO		0x20
+#define ZZ9K_BLITTER_OP_FILLRECT	0x22
+#define ZZ9K_BLITTER_OP_COPYRECT	0x24
+#define ZZ9K_BLITTER_OP_FILLTEMPLATE	0x26
+#define ZZ9K_BLITTER_SRC_HI		0x28
+#define ZZ9K_BLITTER_SRC_LO		0x2A
+#define ZZ9K_BLITTER_DST_HI		0x2C
+#define ZZ9K_BLITTER_DST_LO		0x2E
+#define ZZ9K_BLITTER_COLORMODE		0x30
+#define ZZ9K_BLITTER_SRC_PITCH		0x32
+#define ZZ9K_BLITTER_RGB2_HI		0x34
+#define ZZ9K_BLITTER_RGB2_LO		0x36
+#define ZZ9K_BLITTER_OP_P2C		0x38
+#define ZZ9K_BLITTER_OP_DRAW_LINE	0x3A
+#define ZZ9K_BLITTER_OP_P2D		0x3C
+#define ZZ9K_BLITTER_OP_INVERTRECT	0x3E
+
+#define ZZ9K_BLITTER_USER1		0x40
+#define ZZ9K_BLITTER_USER2		0x42
+#define ZZ9K_BLITTER_USER3		0x44
+#define ZZ9K_BLITTER_USER4		0x46
+
+/* Sprite cursor */
+#define ZZ9K_SPRITE_BITMAP		0x48
+#define ZZ9K_SPRITE_COLORS		0x4A
+
+#define ZZ9K_VBLANK_STATUS		0x4C
+
+/* ? */
+#define ZZ9K_SCRATCH_COPY		0x50
+#define ZZ9K_CVMODE_PARAM		0x52
+#define ZZ9K_CVMODE_VAL			0x54
+#define ZZ9K_CVMODE_SEL			0x56
+#define ZZ9K_CVMODE			0x58
+
+/* Blitter */
+#define ZZ9K_BLITTER_OP_DMA		0x5A
+#define ZZ9K_BLITTER_OP_ACC		0x5C
+#define ZZ9K_BLITTER_SPLIT_POS		0x5E
+
+/* ? */
+#define ZZ9K_SET_FEATURE		0x60
+
+/* Audio */
+#define ZZ9K_AUDIO_SWAB			0x70
+#define ZZ9K_DECODER_FIFO		0x72
+#define ZZ9K_AUDIO_SCALE 		0x74
+#define ZZ9K_AUDIO_PARAM		0x76
+#define ZZ9K_AUDIO_VAL			0x78
+#define ZZ9K_DECODER_PARAM		0x7A
+#define ZZ9K_DECODER_VAL		0x7C
+#define ZZ9K_DECODE			0x7E
+
+/* Network */
+#define ZZ9K_ETH_TX			0x80
+#define ZZ9K_ETH_RX			0x82
+#define ZZ9K_ETH_MAC_HI			0x84
+#define ZZ9K_ETH_MAC_MD			0x86
+#define ZZ9K_ETH_MAC_LO			0x88
+
+/* ARM processing */
+#define ZZ9K_ARM_RUN_HI			0x90
+#define ZZ9K_ARM_RUN_LO			0x92
+#define ZZ9K_ARM_ARGC			0x94
+#define ZZ9K_ARM_ARGV0			0x96
+#define ZZ9K_ARM_ARGV1			0x98
+#define ZZ9K_ARM_ARGV2			0x9A
+#define ZZ9K_ARM_ARGV3			0x9C
+#define ZZ9K_ARM_ARGV4			0x9E
+#define ZZ9K_ARM_ARGV5			0xA0
+#define ZZ9K_ARM_ARGV6			0xA2
+#define ZZ9K_ARM_ARGV7			0xA4
+
+#define ZZ9K_ARM_EVENT_SERIAL		0xB0
+#define ZZ9K_ARM_EVENT_CODE		0xB2
+
+/* Board hardware */
+#define ZZ9K_FW_VERSION			0xC0
+
+/* USB */
+#define ZZ9K_USB_TX_HI			0xD0
+#define ZZ9K_USB_TX_LO			0xD2
+#define ZZ9K_USB_RX_HI			0xD4
+#define ZZ9K_USB_RX_LO			0xD6
+#define ZZ9K_USB_STATUS			0xD8
+#define ZZ9K_USB_BUFSEL			0xDA
+#define ZZ9K_USB_CAPACITY		0xDC
+
+/* Hardware Status */
+#define ZZ9K_TEMPERATURE		0xE0
+#define ZZ9K_VOLTAGE_AUX		0xE2
+#define ZZ9K_VOLTAGE_CORE		0xE4
+
+/* Misccellaneous */
+#define ZZ9K_PRINT_CHR			0xF0
+#define ZZ9K_PRINT_HEX			0xF2
+#define ZZ9K_AUDIO_CONFIG		0xF4
+
+#define ZZ9K_DEBUG			0xFC
+#define ZZ9K_DEBUG_TIMER		0xFE
+
+
+#endif /* ZZ9KREG_H */
Index: src/sys/arch/amiga/dev/zz9kvar.h
diff -u /dev/null src/sys/arch/amiga/dev/zz9kvar.h:1.1
--- /dev/null	Wed May  3 13:49:30 2023
+++ src/sys/arch/amiga/dev/zz9kvar.h	Wed May  3 13:49:30 2023
@@ -0,0 +1,56 @@
+/*	$NetBSD: zz9kvar.h,v 1.1 2023/05/03 13:49:30 phx Exp $ */
+
+/*
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alain Runa.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *	notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *	notice, this list of conditions and the following disclaimer in the
+ *	documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ZZ9KVAR_H
+#define ZZ9KVAR_H
+
+#include <sys/types.h>				/* size_t, char */
+#include <sys/bus.h>				/* bus_space_xxx, bus_addr_t */
+#include <sys/device.h>				/* device_t */
+#include <amiga/dev/zbusvar.h>			/* zbus_args */
+
+#define ZZREG_R(r)	bus_space_read_2(sc->sc_iot, sc->sc_regh, (r))
+#define ZZREG_W(r, v)	bus_space_write_2(sc->sc_iot, sc->sc_regh, (r), (v))
+
+struct zz9k_softc {
+	device_t sc_dev;
+	struct bus_space_tag sc_bst;
+	bus_space_tag_t sc_iot;
+	bus_space_handle_t sc_regh;
+
+	size_t sc_zsize;
+};
+
+struct zz9kbus_attach_args {
+	char		zzaa_name[32];
+	bus_addr_t	zzaa_base;
+};
+
+#endif /* ZZ9KVAR_H */

Reply via email to