Home | History | Annotate | Line # | Download | only in dev
psgpam.c revision 1.1
      1 /*	$NetBSD: psgpam.c,v 1.1 2022/06/10 21:42:23 tsutsui Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2018 Yosuke Sugahara. All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     25  * SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: psgpam.c,v 1.1 2022/06/10 21:42:23 tsutsui Exp $");
     30 
     31 #include <sys/param.h>
     32 #include <sys/systm.h>
     33 #include <sys/device.h>
     34 #include <sys/endian.h>
     35 #include <sys/kmem.h>
     36 #include <sys/sysctl.h>
     37 
     38 #include <sys/cpu.h>
     39 #include <sys/audioio.h>
     40 #include <dev/audio/audio_if.h>
     41 
     42 #include <machine/autoconf.h>
     43 
     44 #include <luna68k/dev/xpbusvar.h>
     45 #include <luna68k/dev/psgpam_enc.h>
     46 #include <luna68k/dev/xpcmd.h>
     47 #include <luna68k/dev/xplx/xplxdefs.h>
     48 
     49 #include <luna68k/luna68k/isr.h>
     50 
     51 #include "ioconf.h"
     52 
     53 /*
     54  * Debug level:
     55  * 0: No debug logs
     56  * 1: action changes like open/close/set_format...
     57  * 2: + normal operations like read/write/ioctl...
     58  * 3: + TRACEs except interrupt
     59  * 4: + TRACEs including interrupt
     60  */
     61 /* Note AUDIO_DEBUG should be sync'ed with src/sys/dev/audio/audio.c */
     62 /* #define AUDIO_DEBUG	1 */
     63 
     64 #if defined(AUDIO_DEBUG)
     65 #define DPRINTF(n, fmt...)	do {					\
     66 	if (psgpamdebug >= (n)) {					\
     67 		if (cpu_intr_p()) {					\
     68 			audio_mlog_printf(fmt);				\
     69 		} else {						\
     70 			audio_mlog_flush();				\
     71 			printf(fmt);					\
     72 		}							\
     73 	}								\
     74 } while (0)
     75 
     76 /* XXX Parasitic to audio.c... */
     77 extern void audio_mlog_flush(void);
     78 extern void audio_mlog_printf(const char *, ...);
     79 
     80 static int	psgpamdebug = AUDIO_DEBUG;
     81 #else
     82 #define DPRINTF(n, fmt...)	__nothing
     83 #endif
     84 
     85 struct psgpam_softc {
     86 	device_t sc_dev;
     87 	vaddr_t sc_shm_base;
     88 	vsize_t sc_shm_size;
     89 
     90 	void (*sc_intr)(void *);
     91 	void *sc_arg;
     92 
     93 	kmutex_t sc_intr_lock;
     94 	kmutex_t sc_thread_lock;
     95 
     96 	int      sc_isopen;
     97 
     98 	int      sc_started;
     99 	int      sc_outcount;
    100 	int      sc_xp_state;
    101 	uint16_t sc_xp_addr;	/* XP buffer addr */
    102 
    103 	int      sc_xp_enc;
    104 	int      sc_xp_rept;
    105 	int      sc_xp_cycle_clk;
    106 	int      sc_xp_rept_clk;
    107 	int      sc_xp_rept_max;
    108 
    109 	u_int    sc_sample_rate;
    110 	int      sc_stride;
    111 	int      sc_dynamic;
    112 
    113 	uint8_t *sc_start_ptr;
    114 	uint8_t *sc_end_ptr;
    115 	int      sc_blksize;
    116 	int      sc_blkcount;
    117 	int      sc_cur_blk_id;
    118 
    119 	struct psgpam_codecvar sc_psgpam_codecvar;
    120 };
    121 
    122 static int  psgpam_match(device_t, cfdata_t, void *);
    123 static void psgpam_attach(device_t, device_t, void *);
    124 
    125 /* MI audio layer interface */
    126 static int  psgpam_open(void *, int);
    127 static void psgpam_close(void *);
    128 static int  psgpam_query_format(void *, audio_format_query_t *);
    129 static int  psgpam_set_format(void *, int,
    130     const audio_params_t *, const audio_params_t *,
    131     audio_filter_reg_t *, audio_filter_reg_t *);
    132 static int  psgpam_trigger_output(void *, void *, void *, int,
    133     void (*)(void *), void *, const audio_params_t *);
    134 static int  psgpam_halt_output(void *);
    135 static int  psgpam_getdev(void *, struct audio_device *);
    136 static int  psgpam_set_port(void *, mixer_ctrl_t *);
    137 static int  psgpam_get_port(void *, mixer_ctrl_t *);
    138 static int  psgpam_query_devinfo(void *, mixer_devinfo_t *);
    139 static int  psgpam_get_props(void *);
    140 static void psgpam_get_locks(void *, kmutex_t **, kmutex_t **);
    141 static int  psgpam_round_blocksize(void *, int, int, const audio_params_t *);
    142 static size_t psgpam_round_buffersize(void *, int, size_t);
    143 
    144 static int  psgpam_intr(void *);
    145 
    146 #if defined(AUDIO_DEBUG)
    147 static int  psgpam_sysctl_debug(SYSCTLFN_PROTO);
    148 #endif
    149 static int  psgpam_sysctl_enc(SYSCTLFN_PROTO);
    150 static int  psgpam_sysctl_dynamic(SYSCTLFN_PROTO);
    151 
    152 CFATTACH_DECL_NEW(psgpam, sizeof(struct psgpam_softc),
    153     psgpam_match, psgpam_attach, NULL, NULL);
    154 
    155 static int psgpam_matched;
    156 
    157 static const struct audio_hw_if psgpam_hw_if = {
    158 	.open			= psgpam_open,
    159 	.close			= psgpam_close,
    160 	.query_format		= psgpam_query_format,
    161 	.set_format		= psgpam_set_format,
    162 	.trigger_output		= psgpam_trigger_output,
    163 	.halt_output		= psgpam_halt_output,
    164 	.getdev			= psgpam_getdev,
    165 	.set_port		= psgpam_set_port,
    166 	.get_port		= psgpam_get_port,
    167 	.query_devinfo		= psgpam_query_devinfo,
    168 	.get_props		= psgpam_get_props,
    169 	.get_locks		= psgpam_get_locks,
    170 	.round_blocksize        = psgpam_round_blocksize,
    171 	.round_buffersize	= psgpam_round_buffersize,
    172 };
    173 
    174 static struct audio_device psgpam_device = {
    175 	"PSG PAM",
    176 	"0.2",
    177 	"",
    178 };
    179 
    180 static struct audio_format psgpam_format = {
    181 	.mode		= AUMODE_PLAY,
    182 	.encoding	= AUDIO_ENCODING_NONE,
    183 	.validbits	= 0,		/* filled by query_format */
    184 	.precision	= 0,		/* filled by query_format */
    185 	.channels	= 1,
    186 	.channel_mask	= AUFMT_MONAURAL,
    187 	.frequency_type	= 0,		/* filled by query_format */
    188 	.frequency	= { 0 },	/* filled by query_format */
    189 };
    190 
    191 static int
    192 psgpam_match(device_t parent, cfdata_t cf, void *aux)
    193 {
    194 	struct xpbus_attach_args *xa = aux;
    195 
    196 	if (psgpam_matched)
    197 		return 0;
    198 
    199 	if (strcmp(xa->xa_name, psgpam_cd.cd_name))
    200 		return 0;
    201 
    202 	psgpam_matched = 1;
    203 	return 1;
    204 }
    205 
    206 static void
    207 psgpam_attach(device_t parent, device_t self, void *aux)
    208 {
    209 	struct psgpam_softc *sc;
    210 	const struct sysctlnode *node;
    211 
    212 	sc = device_private(self);
    213 	sc->sc_dev = self;
    214 
    215 	aprint_normal(": HD647180X I/O processor as PSG PAM\n");
    216 
    217 	sc->sc_shm_base = XP_SHM_BASE;
    218 	sc->sc_shm_size = XP_SHM_SIZE;
    219 
    220 	sc->sc_xp_enc = PAM_ENC_PAM2A;
    221 	sc->sc_sample_rate = 8000;
    222 	sc->sc_stride = 2;
    223 	sc->sc_dynamic = 1;
    224 
    225 	mutex_init(&sc->sc_thread_lock, MUTEX_DEFAULT, IPL_NONE);
    226 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
    227 
    228 	isrlink_autovec(psgpam_intr, sc, 5, ISRPRI_TTYNOBUF);
    229 
    230 	sysctl_createv(NULL, 0, NULL, &node,
    231 		0,
    232 		CTLTYPE_NODE, device_xname(sc->sc_dev),
    233 		SYSCTL_DESCR("psgpam"),
    234 		NULL, 0,
    235 		NULL, 0,
    236 		CTL_HW,
    237 		CTL_CREATE, CTL_EOL);
    238 	if (node != NULL) {
    239 #if defined(AUDIO_DEBUG)
    240 		sysctl_createv(NULL, 0, NULL, NULL,
    241 			CTLFLAG_READWRITE,
    242 			CTLTYPE_INT, "debug",
    243 			SYSCTL_DESCR("PSGPAM debug"),
    244 			psgpam_sysctl_debug, 0, (void *)sc, 0,
    245 			CTL_HW, node->sysctl_num,
    246 			CTL_CREATE, CTL_EOL);
    247 #endif
    248 		sysctl_createv(NULL, 0, NULL, NULL,
    249 			CTLFLAG_READWRITE,
    250 			CTLTYPE_INT, "enc",
    251 			SYSCTL_DESCR("PSGPAM encoding"),
    252 			psgpam_sysctl_enc, 0, (void *)sc, 0,
    253 			CTL_HW, node->sysctl_num,
    254 			CTL_CREATE, CTL_EOL);
    255 		sysctl_createv(NULL, 0, NULL, NULL,
    256 			CTLFLAG_READWRITE,
    257 			CTLTYPE_INT, "dynamic",
    258 			SYSCTL_DESCR("PSGPAM dynamic offset"),
    259 			psgpam_sysctl_dynamic, 0, (void *)sc, 0,
    260 			CTL_HW, node->sysctl_num,
    261 			CTL_CREATE, CTL_EOL);
    262 	}
    263 
    264 	audio_attach_mi(&psgpam_hw_if, sc, sc->sc_dev);
    265 }
    266 
    267 /* private functions */
    268 
    269 static void
    270 psgpam_xp_query(struct psgpam_softc *sc)
    271 {
    272 	u_int a;
    273 	int r;
    274 
    275 	if (!sc->sc_isopen) {
    276 		a = xp_acquire(DEVID_PAM, 0);
    277 		if (a == 0) {
    278 			sc->sc_xp_cycle_clk = 65535;
    279 			sc->sc_xp_rept_clk = 255;
    280 			sc->sc_xp_rept_max = 0;
    281 			DPRINTF(1, "XPLX BUSY!\n");
    282 			return;
    283 		}
    284 		xp_ensure_firmware();
    285 	}
    286 
    287 	xp_writemem8(PAM_ENC, sc->sc_xp_enc);
    288 	r = xp_cmd(DEVID_PAM, PAM_CMD_QUERY);
    289 	if (r != XPLX_R_OK) {
    290 		sc->sc_xp_cycle_clk = 65535;
    291 		sc->sc_xp_rept_clk = 255;
    292 		sc->sc_xp_rept_max = 0;
    293 		DPRINTF(1, "XPLX QUERY FAIL: %d\n", r);
    294 	} else {
    295 		sc->sc_xp_cycle_clk = xp_readmem16le(PAM_CYCLE_CLK);
    296 		sc->sc_xp_rept_clk = xp_readmem8(PAM_REPT_CLK);
    297 		sc->sc_xp_rept_max = xp_readmem8(PAM_REPT_MAX);
    298 		DPRINTF(1, "xp cycle_clk=%d rept_clk=%d rept_max=%d\n",
    299 		    sc->sc_xp_cycle_clk,
    300 		    sc->sc_xp_rept_clk,
    301 		    sc->sc_xp_rept_max);
    302 	}
    303 	if (!sc->sc_isopen) {
    304 		xp_release(DEVID_PAM);
    305 	}
    306 }
    307 
    308 static void
    309 psgpam_xp_start(struct psgpam_softc *sc)
    310 {
    311 
    312 	DPRINTF(3, "XP PAM starting..");
    313 	if (xp_readmem8(PAM_RUN) != 0) {
    314 		DPRINTF(1, "XP PAM already started???\n");
    315 	}
    316 
    317 	psgpam_xp_query(sc);
    318 
    319 	sc->sc_xp_rept = (XP_CPU_FREQ / sc->sc_sample_rate
    320 	    - sc->sc_xp_cycle_clk) / sc->sc_xp_rept_clk;
    321 	if (sc->sc_xp_rept < 0)
    322 		sc->sc_xp_rept = 0;
    323 	if (sc->sc_xp_rept > sc->sc_xp_rept_max)
    324 		sc->sc_xp_rept = sc->sc_xp_rept_max;
    325 	xp_writemem8(PAM_REPT, sc->sc_xp_rept);
    326 	DPRINTF(3, "ENC=%d REPT=%d\n", sc->sc_xp_enc, sc->sc_xp_rept);
    327 
    328 	xp_intr5_enable();
    329 	xp_cmd_nowait(DEVID_PAM, PAM_CMD_START);
    330 
    331 	DPRINTF(3, "XP PAM started\n");
    332 }
    333 
    334 /* MI MD API */
    335 
    336 static int
    337 psgpam_open(void *hdl, int flags)
    338 {
    339 	struct psgpam_softc *sc;
    340 	u_int a;
    341 
    342 	DPRINTF(1, "%s: flags=0x%x\n", __func__, flags);
    343 	sc = hdl;
    344 
    345 	a = xp_acquire(DEVID_PAM, 0);
    346 	if (a == 0)
    347 		return EBUSY;
    348 
    349 	/* firmware transfer */
    350 	xp_ensure_firmware();
    351 
    352 	sc->sc_xp_state = 0;
    353 	sc->sc_started = 0;
    354 	sc->sc_outcount = 0;
    355 	sc->sc_isopen = 1;
    356 
    357 	memset(xp_shmptr(PAM_BUF), XP_ATN_RESET, PAM_BUF_LEN);
    358 
    359 	return 0;
    360 }
    361 
    362 static void
    363 psgpam_close(void *hdl)
    364 {
    365 	struct psgpam_softc *sc;
    366 
    367 	sc = hdl;
    368 
    369 	xp_intr5_disable();
    370 
    371 	xp_release(DEVID_PAM);
    372 
    373 	sc->sc_isopen = 0;
    374 
    375 	DPRINTF(1, "%s\n", __func__);
    376 }
    377 
    378 static int
    379 psgpam_query_format(void *hdl, audio_format_query_t *afp)
    380 {
    381 	struct psgpam_softc *sc;
    382 	u_int freq;
    383 	uint8_t rept_max;
    384 	int clk;
    385 	int i, n;
    386 
    387 #define XP_FREQ_MAXCOUNT	40
    388 	int f[XP_FREQ_MAXCOUNT];
    389 
    390 	if (afp->index != 0)
    391 		return EINVAL;
    392 
    393 	sc = hdl;
    394 
    395 	psgpam_xp_query(sc);
    396 	switch (sc->sc_xp_enc) {
    397 	case PAM_ENC_PAM2A:
    398 	case PAM_ENC_PAM2B:
    399 		psgpam_format.validbits = 16;
    400 		psgpam_format.precision = 16;
    401 		break;
    402 	case PAM_ENC_PAM3A:
    403 	case PAM_ENC_PAM3B:
    404 		psgpam_format.validbits = 32;
    405 		psgpam_format.precision = 32;
    406 		break;
    407 	}
    408 
    409 	/* convert xp's max to AUFMT's max */
    410 	rept_max = sc->sc_xp_rept_max + 1;
    411 
    412 	if (rept_max <= AUFMT_MAX_FREQUENCIES) {
    413 		/* all choice */
    414 		for (i = 0; i < rept_max; i++) {
    415 			clk = sc->sc_xp_cycle_clk + i * sc->sc_xp_rept_clk;
    416 			freq = XP_CPU_FREQ / clk;
    417 			psgpam_format.frequency[i] = freq;
    418 		}
    419 		n = rept_max;
    420 	} else {
    421 		if (rept_max > XP_FREQ_MAXCOUNT)
    422 			rept_max = XP_FREQ_MAXCOUNT;
    423 
    424 		for (i = 0; i < rept_max; i++) {
    425 			clk = sc->sc_xp_cycle_clk + i * sc->sc_xp_rept_clk;
    426 			freq = XP_CPU_FREQ / clk;
    427 			if (freq < 4000) break;
    428 			f[i] = freq;
    429 		}
    430 		for (; i < XP_FREQ_MAXCOUNT; i++)
    431 			f[i] = 0;
    432 
    433 		/*
    434 		 * keep: first, last
    435 		 * remove: any unusable freq
    436 		 */
    437 		for (i = 1; i < rept_max - 1; i++) {
    438 			if (( 4000 <= f[i] && f[i] <  6000 &&
    439 			     f[i - 1] <  6000 && f[i + 1] >  4000) ||
    440 			    ( 6000 <= f[i]    && f[i] < 8000 &&
    441 			     f[i - 1] <  8000 && f[i + 1] >  6000) ||
    442 			    ( 8000 <= f[i] && f[i] < 12000 &&
    443 			     f[i - 1] < 12000 && f[i + 1] >  8000) ||
    444 			    (12000 <= f[i] && f[i] < 16000 &&
    445 			     f[i - 1] < 16000 && f[i + 1] > 12000)) {
    446 				f[i] = 0;
    447 			}
    448 		}
    449 		n = 0;
    450 		for (i = 0; i < rept_max; i++) {
    451 			if (f[i] != 0) {
    452 				psgpam_format.frequency[n] = f[i];
    453 				n++;
    454 				if (n == AUFMT_MAX_FREQUENCIES)
    455 					break;
    456 			}
    457 		}
    458 	}
    459 
    460 	psgpam_format.frequency_type = n;
    461 
    462 	afp->fmt = psgpam_format;
    463 	return 0;
    464 }
    465 
    466 static int
    467 psgpam_set_format(void *hdl, int setmode,
    468     const audio_params_t *play, const audio_params_t *rec,
    469     audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
    470 {
    471 	/* called before open */
    472 
    473 	struct psgpam_softc *sc;
    474 
    475 	sc = hdl;
    476 	DPRINTF(1, "%s: mode=%d %s/%dbit/%dch/%dHz\n", __func__,
    477 	    setmode, audio_encoding_name(play->encoding),
    478 	    play->precision, play->channels, play->sample_rate);
    479 
    480 	sc->sc_sample_rate = play->sample_rate;
    481 
    482 	/* set filter */
    483 	switch (sc->sc_xp_enc) {
    484 	case PAM_ENC_PAM2A:
    485 		if (sc->sc_dynamic) {
    486 			pfil->codec = psgpam_aint_to_pam2a_d;
    487 		} else {
    488 			pfil->codec = psgpam_aint_to_pam2a;
    489 		}
    490 		sc->sc_stride = 2;
    491 		break;
    492 	case PAM_ENC_PAM2B:
    493 		if (sc->sc_dynamic) {
    494 			pfil->codec = psgpam_aint_to_pam2b_d;
    495 		} else {
    496 			pfil->codec = psgpam_aint_to_pam2b;
    497 		}
    498 		sc->sc_stride = 2;
    499 		break;
    500 	case PAM_ENC_PAM3A:
    501 		if (sc->sc_dynamic) {
    502 			pfil->codec = psgpam_aint_to_pam3a_d;
    503 		} else {
    504 			pfil->codec = psgpam_aint_to_pam3a;
    505 		}
    506 		sc->sc_stride = 4;
    507 		break;
    508 	case PAM_ENC_PAM3B:
    509 		if (sc->sc_dynamic) {
    510 			pfil->codec = psgpam_aint_to_pam3b_d;
    511 		} else {
    512 			pfil->codec = psgpam_aint_to_pam3b;
    513 		}
    514 		sc->sc_stride = 4;
    515 		break;
    516 	}
    517 	psgpam_init_context(&sc->sc_psgpam_codecvar, sc->sc_sample_rate);
    518 	pfil->context = &sc->sc_psgpam_codecvar;
    519 
    520 	return 0;
    521 }
    522 
    523 /* marking block */
    524 static void
    525 psgpam_mark_blk(struct psgpam_softc *sc, int blk_id)
    526 {
    527 	int markoffset;
    528 	uint marker;
    529 
    530 	markoffset = sc->sc_blksize * (blk_id + 1);
    531 
    532 	if (blk_id == sc->sc_blkcount - 1) {
    533 		marker = XP_ATN_RELOAD;
    534 	} else {
    535 		marker = XP_ATN_STAT;
    536 	}
    537 
    538 	/* marking */
    539 	uint8_t *start = sc->sc_start_ptr;
    540 	if (sc->sc_stride == 2) {
    541 		uint16_t *markptr = (uint16_t*)(start + markoffset);
    542 		markptr -= 1;
    543 		*markptr |= marker;
    544 	} else {
    545 		/* stride == 4 */
    546 		uint32_t *markptr = (uint32_t*)(start + markoffset);
    547 		markptr -= 1;
    548 		*markptr |= marker;
    549 	}
    550 }
    551 
    552 static int
    553 psgpam_trigger_output(void *hdl, void *start, void *end, int blksize,
    554     void (*intr)(void *), void *intrarg, const audio_params_t *param)
    555 {
    556 	void *dp;
    557 	struct psgpam_softc *sc;
    558 
    559 	sc = hdl;
    560 
    561 	DPRINTF(2, "%s start=%p end=%p blksize=%d\n", __func__,
    562 		start, end, blksize);
    563 
    564 	sc->sc_outcount++;
    565 
    566 	sc->sc_intr = intr;
    567 	sc->sc_arg = intrarg;
    568 	sc->sc_blksize = blksize;
    569 
    570 	sc->sc_start_ptr = start;
    571 	sc->sc_end_ptr = end;
    572 	sc->sc_blkcount = (sc->sc_end_ptr - sc->sc_start_ptr) / sc->sc_blksize;
    573 	sc->sc_xp_addr = PAM_BUF;
    574 
    575 	psgpam_mark_blk(sc, 0);
    576 	psgpam_mark_blk(sc, 1);
    577 
    578 	/* transfer */
    579 	dp = xp_shmptr(sc->sc_xp_addr);
    580 	memcpy(dp, start, blksize * 2);
    581 
    582 	/* (preincrement variable in intr) */
    583 	sc->sc_cur_blk_id = 1;
    584 	sc->sc_xp_addr += blksize;
    585 
    586 	/* play start */
    587 	if (sc->sc_started == 0) {
    588 		/* set flag first */
    589 		sc->sc_started = 1;
    590 		psgpam_xp_start(sc);
    591 	}
    592 
    593 	return 0;
    594 }
    595 
    596 static int
    597 psgpam_halt_output(void *hdl)
    598 {
    599 	struct psgpam_softc *sc = hdl;
    600 
    601 	DPRINTF(2, "%s\n", __func__);
    602 
    603 	xp_intr5_disable();
    604 
    605 	memset(xp_shmptr(PAM_BUF), XP_ATN_RESET, PAM_BUF_LEN);
    606 
    607 	sc->sc_started = 0;
    608 	sc->sc_xp_state = 0;
    609 
    610 	return 0;
    611 }
    612 
    613 static int
    614 psgpam_getdev(void *hdl, struct audio_device *ret)
    615 {
    616 
    617 	*ret = psgpam_device;
    618 	return 0;
    619 }
    620 
    621 static int
    622 psgpam_set_port(void *hdl, mixer_ctrl_t *mc)
    623 {
    624 
    625 	DPRINTF(2, "%s\n", __func__);
    626 	return 0;
    627 }
    628 
    629 static int
    630 psgpam_get_port(void *hdl, mixer_ctrl_t *mc)
    631 {
    632 
    633 	DPRINTF(2, "%s\n", __func__);
    634 	return 0;
    635 }
    636 
    637 static int
    638 psgpam_query_devinfo(void *hdl, mixer_devinfo_t *di)
    639 {
    640 
    641 	DPRINTF(2, "%s %d\n", __func__, di->index);
    642 	switch (di->index) {
    643 	default:
    644 		return EINVAL;
    645 	}
    646 	return 0;
    647 }
    648 
    649 static int
    650 psgpam_get_props(void *hdl)
    651 {
    652 
    653 	return AUDIO_PROP_PLAYBACK;
    654 }
    655 
    656 static void
    657 psgpam_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread)
    658 {
    659 	struct psgpam_softc *sc = hdl;
    660 
    661 	*intr = &sc->sc_intr_lock;
    662 	*thread = &sc->sc_thread_lock;
    663 }
    664 
    665 static int
    666 psgpam_round_blocksize(void *hdl, int bs, int mode,
    667     const audio_params_t *param)
    668 {
    669 
    670 #if 0
    671 	if (bs < 16384) {
    672 		return (16384 / bs) * bs;
    673 	} else {
    674 		return 16384;
    675 	}
    676 #else
    677 	return bs;
    678 #endif
    679 }
    680 
    681 static size_t
    682 psgpam_round_buffersize(void *hdl, int direction, size_t bufsize)
    683 {
    684 
    685 	if (bufsize > 28 * 1024) {
    686 		bufsize = 28 * 1024;
    687 	}
    688 	return bufsize;
    689 }
    690 
    691 static int
    692 psgpam_intr(void *hdl)
    693 {
    694 	struct psgpam_softc *sc = hdl;
    695 
    696 	xp_intr5_acknowledge();
    697 	DPRINTF(4, "psgpam intr\n");
    698 
    699 	sc->sc_cur_blk_id++;
    700 	sc->sc_xp_addr += sc->sc_blksize;
    701 	if (sc->sc_cur_blk_id == sc->sc_blkcount) {
    702 		sc->sc_cur_blk_id = 0;
    703 		sc->sc_xp_addr = PAM_BUF;
    704 	}
    705 	psgpam_mark_blk(sc, sc->sc_cur_blk_id);
    706 	memcpy(xp_shmptr(sc->sc_xp_addr),
    707 	    sc->sc_start_ptr + sc->sc_cur_blk_id * sc->sc_blksize,
    708 	    sc->sc_blksize);
    709 
    710 	mutex_spin_enter(&sc->sc_intr_lock);
    711 
    712 	if (sc->sc_intr) {
    713 		sc->sc_intr(sc->sc_arg);
    714 	} else {
    715 		DPRINTF(1, "psgpam_intr: spurious interrupt\n");
    716 	}
    717 
    718 	mutex_spin_exit(&sc->sc_intr_lock);
    719 
    720 	/* handled */
    721 	return 1;
    722 }
    723 
    724 #if defined(AUDIO_DEBUG)
    725 /* sysctl */
    726 static int
    727 psgpam_sysctl_debug(SYSCTLFN_ARGS)
    728 {
    729 	struct sysctlnode node;
    730 	int t, error;
    731 
    732 	node = *rnode;
    733 
    734 	t = psgpamdebug;
    735 	node.sysctl_data = &t;
    736 
    737 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    738 	if (error || newp == NULL) {
    739 		return error;
    740 	}
    741 
    742 	if (t < 0)
    743 		return EINVAL;
    744 	if (t > 4)
    745 		return EINVAL;
    746 	psgpamdebug = t;
    747 	return 0;
    748 }
    749 #endif
    750 
    751 /* sysctl */
    752 static int
    753 psgpam_sysctl_enc(SYSCTLFN_ARGS)
    754 {
    755 	struct sysctlnode node;
    756 	struct psgpam_softc *sc;
    757 	int t, error;
    758 
    759 	node = *rnode;
    760 	sc = node.sysctl_data;
    761 
    762 	t = sc->sc_xp_enc;
    763 	node.sysctl_data = &t;
    764 
    765 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    766 	if (error || newp == NULL) {
    767 		return error;
    768 	}
    769 
    770 	if (t < PAM_ENC_PAM2A)
    771 		return EINVAL;
    772 	if (t > PAM_ENC_PAM3B)
    773 		return EINVAL;
    774 	sc->sc_xp_enc = t;
    775 	return 0;
    776 }
    777 
    778 static int
    779 psgpam_sysctl_dynamic(SYSCTLFN_ARGS)
    780 {
    781 	struct sysctlnode node;
    782 	struct psgpam_softc *sc;
    783 	int t, error;
    784 
    785 	node = *rnode;
    786 	sc = node.sysctl_data;
    787 
    788 	t = sc->sc_dynamic;
    789 	node.sysctl_data = &t;
    790 
    791 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    792 	if (error || newp == NULL) {
    793 		return error;
    794 	}
    795 
    796 	sc->sc_dynamic = t;
    797 	return 0;
    798 }
    799