Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: psgpam.c,v 1.2 2022/06/11 14:45:37 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.2 2022/06/11 14:45:37 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 	/* Only the first generation LUNA has YM2149 at XP */
    200 	if (machtype != LUNA_I)
    201 		return 0;
    202 
    203 	if (strcmp(xa->xa_name, psgpam_cd.cd_name))
    204 		return 0;
    205 
    206 	psgpam_matched = 1;
    207 	return 1;
    208 }
    209 
    210 static void
    211 psgpam_attach(device_t parent, device_t self, void *aux)
    212 {
    213 	struct psgpam_softc *sc;
    214 	const struct sysctlnode *node;
    215 
    216 	sc = device_private(self);
    217 	sc->sc_dev = self;
    218 
    219 	aprint_normal(": HD647180X I/O processor as PSG PAM\n");
    220 
    221 	sc->sc_shm_base = XP_SHM_BASE;
    222 	sc->sc_shm_size = XP_SHM_SIZE;
    223 
    224 	sc->sc_xp_enc = PAM_ENC_PAM2A;
    225 	sc->sc_sample_rate = 8000;
    226 	sc->sc_stride = 2;
    227 	sc->sc_dynamic = 1;
    228 
    229 	mutex_init(&sc->sc_thread_lock, MUTEX_DEFAULT, IPL_NONE);
    230 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
    231 
    232 	isrlink_autovec(psgpam_intr, sc, 5, ISRPRI_TTYNOBUF);
    233 
    234 	sysctl_createv(NULL, 0, NULL, &node,
    235 		0,
    236 		CTLTYPE_NODE, device_xname(sc->sc_dev),
    237 		SYSCTL_DESCR("psgpam"),
    238 		NULL, 0,
    239 		NULL, 0,
    240 		CTL_HW,
    241 		CTL_CREATE, CTL_EOL);
    242 	if (node != NULL) {
    243 #if defined(AUDIO_DEBUG)
    244 		sysctl_createv(NULL, 0, NULL, NULL,
    245 			CTLFLAG_READWRITE,
    246 			CTLTYPE_INT, "debug",
    247 			SYSCTL_DESCR("PSGPAM debug"),
    248 			psgpam_sysctl_debug, 0, (void *)sc, 0,
    249 			CTL_HW, node->sysctl_num,
    250 			CTL_CREATE, CTL_EOL);
    251 #endif
    252 		sysctl_createv(NULL, 0, NULL, NULL,
    253 			CTLFLAG_READWRITE,
    254 			CTLTYPE_INT, "enc",
    255 			SYSCTL_DESCR("PSGPAM encoding"),
    256 			psgpam_sysctl_enc, 0, (void *)sc, 0,
    257 			CTL_HW, node->sysctl_num,
    258 			CTL_CREATE, CTL_EOL);
    259 		sysctl_createv(NULL, 0, NULL, NULL,
    260 			CTLFLAG_READWRITE,
    261 			CTLTYPE_INT, "dynamic",
    262 			SYSCTL_DESCR("PSGPAM dynamic offset"),
    263 			psgpam_sysctl_dynamic, 0, (void *)sc, 0,
    264 			CTL_HW, node->sysctl_num,
    265 			CTL_CREATE, CTL_EOL);
    266 	}
    267 
    268 	audio_attach_mi(&psgpam_hw_if, sc, sc->sc_dev);
    269 }
    270 
    271 /* private functions */
    272 
    273 static void
    274 psgpam_xp_query(struct psgpam_softc *sc)
    275 {
    276 	u_int a;
    277 	int r;
    278 
    279 	if (!sc->sc_isopen) {
    280 		a = xp_acquire(DEVID_PAM, 0);
    281 		if (a == 0) {
    282 			sc->sc_xp_cycle_clk = 65535;
    283 			sc->sc_xp_rept_clk = 255;
    284 			sc->sc_xp_rept_max = 0;
    285 			DPRINTF(1, "XPLX BUSY!\n");
    286 			return;
    287 		}
    288 		xp_ensure_firmware();
    289 	}
    290 
    291 	xp_writemem8(PAM_ENC, sc->sc_xp_enc);
    292 	r = xp_cmd(DEVID_PAM, PAM_CMD_QUERY);
    293 	if (r != XPLX_R_OK) {
    294 		sc->sc_xp_cycle_clk = 65535;
    295 		sc->sc_xp_rept_clk = 255;
    296 		sc->sc_xp_rept_max = 0;
    297 		DPRINTF(1, "XPLX QUERY FAIL: %d\n", r);
    298 	} else {
    299 		sc->sc_xp_cycle_clk = xp_readmem16le(PAM_CYCLE_CLK);
    300 		sc->sc_xp_rept_clk = xp_readmem8(PAM_REPT_CLK);
    301 		sc->sc_xp_rept_max = xp_readmem8(PAM_REPT_MAX);
    302 		DPRINTF(1, "xp cycle_clk=%d rept_clk=%d rept_max=%d\n",
    303 		    sc->sc_xp_cycle_clk,
    304 		    sc->sc_xp_rept_clk,
    305 		    sc->sc_xp_rept_max);
    306 	}
    307 	if (!sc->sc_isopen) {
    308 		xp_release(DEVID_PAM);
    309 	}
    310 }
    311 
    312 static void
    313 psgpam_xp_start(struct psgpam_softc *sc)
    314 {
    315 
    316 	DPRINTF(3, "XP PAM starting..");
    317 	if (xp_readmem8(PAM_RUN) != 0) {
    318 		DPRINTF(1, "XP PAM already started???\n");
    319 	}
    320 
    321 	psgpam_xp_query(sc);
    322 
    323 	sc->sc_xp_rept = (XP_CPU_FREQ / sc->sc_sample_rate
    324 	    - sc->sc_xp_cycle_clk) / sc->sc_xp_rept_clk;
    325 	if (sc->sc_xp_rept < 0)
    326 		sc->sc_xp_rept = 0;
    327 	if (sc->sc_xp_rept > sc->sc_xp_rept_max)
    328 		sc->sc_xp_rept = sc->sc_xp_rept_max;
    329 	xp_writemem8(PAM_REPT, sc->sc_xp_rept);
    330 	DPRINTF(3, "ENC=%d REPT=%d\n", sc->sc_xp_enc, sc->sc_xp_rept);
    331 
    332 	xp_intr5_enable();
    333 	xp_cmd_nowait(DEVID_PAM, PAM_CMD_START);
    334 
    335 	DPRINTF(3, "XP PAM started\n");
    336 }
    337 
    338 /* MI MD API */
    339 
    340 static int
    341 psgpam_open(void *hdl, int flags)
    342 {
    343 	struct psgpam_softc *sc;
    344 	u_int a;
    345 
    346 	DPRINTF(1, "%s: flags=0x%x\n", __func__, flags);
    347 	sc = hdl;
    348 
    349 	a = xp_acquire(DEVID_PAM, 0);
    350 	if (a == 0)
    351 		return EBUSY;
    352 
    353 	/* firmware transfer */
    354 	xp_ensure_firmware();
    355 
    356 	sc->sc_xp_state = 0;
    357 	sc->sc_started = 0;
    358 	sc->sc_outcount = 0;
    359 	sc->sc_isopen = 1;
    360 
    361 	memset(xp_shmptr(PAM_BUF), XP_ATN_RESET, PAM_BUF_LEN);
    362 
    363 	return 0;
    364 }
    365 
    366 static void
    367 psgpam_close(void *hdl)
    368 {
    369 	struct psgpam_softc *sc;
    370 
    371 	sc = hdl;
    372 
    373 	xp_intr5_disable();
    374 
    375 	xp_release(DEVID_PAM);
    376 
    377 	sc->sc_isopen = 0;
    378 
    379 	DPRINTF(1, "%s\n", __func__);
    380 }
    381 
    382 static int
    383 psgpam_query_format(void *hdl, audio_format_query_t *afp)
    384 {
    385 	struct psgpam_softc *sc;
    386 	u_int freq;
    387 	uint8_t rept_max;
    388 	int clk;
    389 	int i, n;
    390 
    391 #define XP_FREQ_MAXCOUNT	40
    392 	int f[XP_FREQ_MAXCOUNT];
    393 
    394 	if (afp->index != 0)
    395 		return EINVAL;
    396 
    397 	sc = hdl;
    398 
    399 	psgpam_xp_query(sc);
    400 	switch (sc->sc_xp_enc) {
    401 	case PAM_ENC_PAM2A:
    402 	case PAM_ENC_PAM2B:
    403 		psgpam_format.validbits = 16;
    404 		psgpam_format.precision = 16;
    405 		break;
    406 	case PAM_ENC_PAM3A:
    407 	case PAM_ENC_PAM3B:
    408 		psgpam_format.validbits = 32;
    409 		psgpam_format.precision = 32;
    410 		break;
    411 	}
    412 
    413 	/* convert xp's max to AUFMT's max */
    414 	rept_max = sc->sc_xp_rept_max + 1;
    415 
    416 	if (rept_max <= AUFMT_MAX_FREQUENCIES) {
    417 		/* all choice */
    418 		for (i = 0; i < rept_max; i++) {
    419 			clk = sc->sc_xp_cycle_clk + i * sc->sc_xp_rept_clk;
    420 			freq = XP_CPU_FREQ / clk;
    421 			psgpam_format.frequency[i] = freq;
    422 		}
    423 		n = rept_max;
    424 	} else {
    425 		if (rept_max > XP_FREQ_MAXCOUNT)
    426 			rept_max = XP_FREQ_MAXCOUNT;
    427 
    428 		for (i = 0; i < rept_max; i++) {
    429 			clk = sc->sc_xp_cycle_clk + i * sc->sc_xp_rept_clk;
    430 			freq = XP_CPU_FREQ / clk;
    431 			if (freq < 4000) break;
    432 			f[i] = freq;
    433 		}
    434 		for (; i < XP_FREQ_MAXCOUNT; i++)
    435 			f[i] = 0;
    436 
    437 		/*
    438 		 * keep: first, last
    439 		 * remove: any unusable freq
    440 		 */
    441 		for (i = 1; i < rept_max - 1; i++) {
    442 			if (( 4000 <= f[i] && f[i] <  6000 &&
    443 			     f[i - 1] <  6000 && f[i + 1] >  4000) ||
    444 			    ( 6000 <= f[i]    && f[i] < 8000 &&
    445 			     f[i - 1] <  8000 && f[i + 1] >  6000) ||
    446 			    ( 8000 <= f[i] && f[i] < 12000 &&
    447 			     f[i - 1] < 12000 && f[i + 1] >  8000) ||
    448 			    (12000 <= f[i] && f[i] < 16000 &&
    449 			     f[i - 1] < 16000 && f[i + 1] > 12000)) {
    450 				f[i] = 0;
    451 			}
    452 		}
    453 		n = 0;
    454 		for (i = 0; i < rept_max; i++) {
    455 			if (f[i] != 0) {
    456 				psgpam_format.frequency[n] = f[i];
    457 				n++;
    458 				if (n == AUFMT_MAX_FREQUENCIES)
    459 					break;
    460 			}
    461 		}
    462 	}
    463 
    464 	psgpam_format.frequency_type = n;
    465 
    466 	afp->fmt = psgpam_format;
    467 	return 0;
    468 }
    469 
    470 static int
    471 psgpam_set_format(void *hdl, int setmode,
    472     const audio_params_t *play, const audio_params_t *rec,
    473     audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
    474 {
    475 	/* called before open */
    476 
    477 	struct psgpam_softc *sc;
    478 
    479 	sc = hdl;
    480 	DPRINTF(1, "%s: mode=%d %s/%dbit/%dch/%dHz\n", __func__,
    481 	    setmode, audio_encoding_name(play->encoding),
    482 	    play->precision, play->channels, play->sample_rate);
    483 
    484 	sc->sc_sample_rate = play->sample_rate;
    485 
    486 	/* set filter */
    487 	switch (sc->sc_xp_enc) {
    488 	case PAM_ENC_PAM2A:
    489 		if (sc->sc_dynamic) {
    490 			pfil->codec = psgpam_aint_to_pam2a_d;
    491 		} else {
    492 			pfil->codec = psgpam_aint_to_pam2a;
    493 		}
    494 		sc->sc_stride = 2;
    495 		break;
    496 	case PAM_ENC_PAM2B:
    497 		if (sc->sc_dynamic) {
    498 			pfil->codec = psgpam_aint_to_pam2b_d;
    499 		} else {
    500 			pfil->codec = psgpam_aint_to_pam2b;
    501 		}
    502 		sc->sc_stride = 2;
    503 		break;
    504 	case PAM_ENC_PAM3A:
    505 		if (sc->sc_dynamic) {
    506 			pfil->codec = psgpam_aint_to_pam3a_d;
    507 		} else {
    508 			pfil->codec = psgpam_aint_to_pam3a;
    509 		}
    510 		sc->sc_stride = 4;
    511 		break;
    512 	case PAM_ENC_PAM3B:
    513 		if (sc->sc_dynamic) {
    514 			pfil->codec = psgpam_aint_to_pam3b_d;
    515 		} else {
    516 			pfil->codec = psgpam_aint_to_pam3b;
    517 		}
    518 		sc->sc_stride = 4;
    519 		break;
    520 	}
    521 	psgpam_init_context(&sc->sc_psgpam_codecvar, sc->sc_sample_rate);
    522 	pfil->context = &sc->sc_psgpam_codecvar;
    523 
    524 	return 0;
    525 }
    526 
    527 /* marking block */
    528 static void
    529 psgpam_mark_blk(struct psgpam_softc *sc, int blk_id)
    530 {
    531 	int markoffset;
    532 	uint marker;
    533 
    534 	markoffset = sc->sc_blksize * (blk_id + 1);
    535 
    536 	if (blk_id == sc->sc_blkcount - 1) {
    537 		marker = XP_ATN_RELOAD;
    538 	} else {
    539 		marker = XP_ATN_STAT;
    540 	}
    541 
    542 	/* marking */
    543 	uint8_t *start = sc->sc_start_ptr;
    544 	if (sc->sc_stride == 2) {
    545 		uint16_t *markptr = (uint16_t*)(start + markoffset);
    546 		markptr -= 1;
    547 		*markptr |= marker;
    548 	} else {
    549 		/* stride == 4 */
    550 		uint32_t *markptr = (uint32_t*)(start + markoffset);
    551 		markptr -= 1;
    552 		*markptr |= marker;
    553 	}
    554 }
    555 
    556 static int
    557 psgpam_trigger_output(void *hdl, void *start, void *end, int blksize,
    558     void (*intr)(void *), void *intrarg, const audio_params_t *param)
    559 {
    560 	void *dp;
    561 	struct psgpam_softc *sc;
    562 
    563 	sc = hdl;
    564 
    565 	DPRINTF(2, "%s start=%p end=%p blksize=%d\n", __func__,
    566 		start, end, blksize);
    567 
    568 	sc->sc_outcount++;
    569 
    570 	sc->sc_intr = intr;
    571 	sc->sc_arg = intrarg;
    572 	sc->sc_blksize = blksize;
    573 
    574 	sc->sc_start_ptr = start;
    575 	sc->sc_end_ptr = end;
    576 	sc->sc_blkcount = (sc->sc_end_ptr - sc->sc_start_ptr) / sc->sc_blksize;
    577 	sc->sc_xp_addr = PAM_BUF;
    578 
    579 	psgpam_mark_blk(sc, 0);
    580 	psgpam_mark_blk(sc, 1);
    581 
    582 	/* transfer */
    583 	dp = xp_shmptr(sc->sc_xp_addr);
    584 	memcpy(dp, start, blksize * 2);
    585 
    586 	/* (preincrement variable in intr) */
    587 	sc->sc_cur_blk_id = 1;
    588 	sc->sc_xp_addr += blksize;
    589 
    590 	/* play start */
    591 	if (sc->sc_started == 0) {
    592 		/* set flag first */
    593 		sc->sc_started = 1;
    594 		psgpam_xp_start(sc);
    595 	}
    596 
    597 	return 0;
    598 }
    599 
    600 static int
    601 psgpam_halt_output(void *hdl)
    602 {
    603 	struct psgpam_softc *sc = hdl;
    604 
    605 	DPRINTF(2, "%s\n", __func__);
    606 
    607 	xp_intr5_disable();
    608 
    609 	memset(xp_shmptr(PAM_BUF), XP_ATN_RESET, PAM_BUF_LEN);
    610 
    611 	sc->sc_started = 0;
    612 	sc->sc_xp_state = 0;
    613 
    614 	return 0;
    615 }
    616 
    617 static int
    618 psgpam_getdev(void *hdl, struct audio_device *ret)
    619 {
    620 
    621 	*ret = psgpam_device;
    622 	return 0;
    623 }
    624 
    625 static int
    626 psgpam_set_port(void *hdl, mixer_ctrl_t *mc)
    627 {
    628 
    629 	DPRINTF(2, "%s\n", __func__);
    630 	return 0;
    631 }
    632 
    633 static int
    634 psgpam_get_port(void *hdl, mixer_ctrl_t *mc)
    635 {
    636 
    637 	DPRINTF(2, "%s\n", __func__);
    638 	return 0;
    639 }
    640 
    641 static int
    642 psgpam_query_devinfo(void *hdl, mixer_devinfo_t *di)
    643 {
    644 
    645 	DPRINTF(2, "%s %d\n", __func__, di->index);
    646 	switch (di->index) {
    647 	default:
    648 		return EINVAL;
    649 	}
    650 	return 0;
    651 }
    652 
    653 static int
    654 psgpam_get_props(void *hdl)
    655 {
    656 
    657 	return AUDIO_PROP_PLAYBACK;
    658 }
    659 
    660 static void
    661 psgpam_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread)
    662 {
    663 	struct psgpam_softc *sc = hdl;
    664 
    665 	*intr = &sc->sc_intr_lock;
    666 	*thread = &sc->sc_thread_lock;
    667 }
    668 
    669 static int
    670 psgpam_round_blocksize(void *hdl, int bs, int mode,
    671     const audio_params_t *param)
    672 {
    673 
    674 #if 0
    675 	if (bs < 16384) {
    676 		return (16384 / bs) * bs;
    677 	} else {
    678 		return 16384;
    679 	}
    680 #else
    681 	return bs;
    682 #endif
    683 }
    684 
    685 static size_t
    686 psgpam_round_buffersize(void *hdl, int direction, size_t bufsize)
    687 {
    688 
    689 	if (bufsize > 28 * 1024) {
    690 		bufsize = 28 * 1024;
    691 	}
    692 	return bufsize;
    693 }
    694 
    695 static int
    696 psgpam_intr(void *hdl)
    697 {
    698 	struct psgpam_softc *sc = hdl;
    699 
    700 	xp_intr5_acknowledge();
    701 	DPRINTF(4, "psgpam intr\n");
    702 
    703 	sc->sc_cur_blk_id++;
    704 	sc->sc_xp_addr += sc->sc_blksize;
    705 	if (sc->sc_cur_blk_id == sc->sc_blkcount) {
    706 		sc->sc_cur_blk_id = 0;
    707 		sc->sc_xp_addr = PAM_BUF;
    708 	}
    709 	psgpam_mark_blk(sc, sc->sc_cur_blk_id);
    710 	memcpy(xp_shmptr(sc->sc_xp_addr),
    711 	    sc->sc_start_ptr + sc->sc_cur_blk_id * sc->sc_blksize,
    712 	    sc->sc_blksize);
    713 
    714 	mutex_spin_enter(&sc->sc_intr_lock);
    715 
    716 	if (sc->sc_intr) {
    717 		sc->sc_intr(sc->sc_arg);
    718 	} else {
    719 		DPRINTF(1, "psgpam_intr: spurious interrupt\n");
    720 	}
    721 
    722 	mutex_spin_exit(&sc->sc_intr_lock);
    723 
    724 	/* handled */
    725 	return 1;
    726 }
    727 
    728 #if defined(AUDIO_DEBUG)
    729 /* sysctl */
    730 static int
    731 psgpam_sysctl_debug(SYSCTLFN_ARGS)
    732 {
    733 	struct sysctlnode node;
    734 	int t, error;
    735 
    736 	node = *rnode;
    737 
    738 	t = psgpamdebug;
    739 	node.sysctl_data = &t;
    740 
    741 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    742 	if (error || newp == NULL) {
    743 		return error;
    744 	}
    745 
    746 	if (t < 0)
    747 		return EINVAL;
    748 	if (t > 4)
    749 		return EINVAL;
    750 	psgpamdebug = t;
    751 	return 0;
    752 }
    753 #endif
    754 
    755 /* sysctl */
    756 static int
    757 psgpam_sysctl_enc(SYSCTLFN_ARGS)
    758 {
    759 	struct sysctlnode node;
    760 	struct psgpam_softc *sc;
    761 	int t, error;
    762 
    763 	node = *rnode;
    764 	sc = node.sysctl_data;
    765 
    766 	t = sc->sc_xp_enc;
    767 	node.sysctl_data = &t;
    768 
    769 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    770 	if (error || newp == NULL) {
    771 		return error;
    772 	}
    773 
    774 	if (t < PAM_ENC_PAM2A)
    775 		return EINVAL;
    776 	if (t > PAM_ENC_PAM3B)
    777 		return EINVAL;
    778 	sc->sc_xp_enc = t;
    779 	return 0;
    780 }
    781 
    782 static int
    783 psgpam_sysctl_dynamic(SYSCTLFN_ARGS)
    784 {
    785 	struct sysctlnode node;
    786 	struct psgpam_softc *sc;
    787 	int t, error;
    788 
    789 	node = *rnode;
    790 	sc = node.sysctl_data;
    791 
    792 	t = sc->sc_dynamic;
    793 	node.sysctl_data = &t;
    794 
    795 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    796 	if (error || newp == NULL) {
    797 		return error;
    798 	}
    799 
    800 	sc->sc_dynamic = t;
    801 	return 0;
    802 }
    803