Home | History | Annotate | Line # | Download | only in vr
      1 /*	$NetBSD: vraiu.c,v 1.20 2021/01/13 06:39:46 skrll Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2001 HAMAJIMA Katsuomi. 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 AND CONTRIBUTORS ``AS IS'' AND
     16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     23  * LIABILITY, 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: vraiu.c,v 1.20 2021/01/13 06:39:46 skrll Exp $");
     30 
     31 #include <sys/param.h>
     32 #include <sys/systm.h>
     33 #include <sys/device.h>
     34 #include <sys/bswap.h>
     35 
     36 #include <machine/cpu.h>
     37 #include <machine/intr.h>
     38 #include <machine/bus.h>
     39 #include <machine/platid.h>
     40 #include <machine/platid_mask.h>
     41 #include <machine/config_hook.h>
     42 
     43 #include <sys/audioio.h>
     44 #include <dev/audio/audio_if.h>
     45 
     46 #include <hpcmips/vr/vr.h>
     47 #include <hpcmips/vr/vripif.h>
     48 #include <hpcmips/vr/icureg.h>
     49 #include <hpcmips/vr/cmureg.h>
     50 #include <hpcmips/vr/vraiureg.h>
     51 
     52 #ifdef VRAIU_DEBUG
     53 int vraiu_debug = VRAIU_DEBUG;
     54 #define DPRINTFN(n,x) if (vraiu_debug>(n)) printf x;
     55 #else
     56 #define DPRINTFN(n,x)
     57 #endif
     58 
     59 #define AUDIO_BUF_SIZE 2048
     60 
     61 struct vraiu_softc {
     62 	device_t		sc_dev;
     63 	kmutex_t		sc_lock;
     64 	kmutex_t		sc_intr_lock;
     65 	bus_space_tag_t		sc_iot;
     66 	bus_space_handle_t	sc_ioh;
     67 	bus_dma_tag_t		sc_dmat;
     68 	bus_dmamap_t		sc_dmap;
     69 	vrip_chipset_tag_t	sc_vrip;
     70 	vrdcu_chipset_tag_t	sc_dc;
     71 	vrdmaau_chipset_tag_t	sc_ac;
     72 	vrcmu_chipset_tag_t	sc_cc;
     73 	void			*sc_handler;
     74 	u_short	*sc_buf;	/* DMA buffer pointer */
     75 	u_int	sc_rate;	/* sampling rate */
     76 	u_char	sc_volume;	/* volume */
     77 	void	(*sc_intr)(void *);	/* interrupt routine */
     78 	void	*sc_intrdata;		/* interrupt data */
     79 };
     80 
     81 int vraiu_match(device_t, cfdata_t, void *);
     82 void vraiu_attach(device_t, device_t, void *);
     83 int vraiu_intr(void *);
     84 
     85 CFATTACH_DECL_NEW(vraiu, sizeof(struct vraiu_softc),
     86     vraiu_match, vraiu_attach, NULL, NULL);
     87 
     88 struct audio_device aiu_device = {
     89 	"VR4121 AIU",
     90 	"0.1",
     91 	"aiu"
     92 };
     93 
     94 const struct audio_format vraiu_formats = {
     95 	.mode		= AUMODE_PLAY,
     96 	.encoding	= AUDIO_ENCODING_SLINEAR_NE,
     97 	.validbits	= 10,
     98 	.precision	= 16,
     99 	.channels	= 1,
    100 	.channel_mask	= AUFMT_MONAURAL,
    101 	.frequency_type	= 4,
    102 	.frequency	= { 8000, 11025, 22050, 44100 },
    103 };
    104 
    105 /*
    106  * Define our interface to the higher level audio driver.
    107  */
    108 int vraiu_query_format(void *, audio_format_query_t *);
    109 int vraiu_round_blocksize(void *, int, int, const audio_params_t *);
    110 int vraiu_commit_settings(void *);
    111 int vraiu_init_output(void *, void*, int);
    112 int vraiu_start_output(void *, void *, int, void (*)(void *), void *);
    113 int vraiu_halt_output(void *);
    114 int vraiu_getdev(void *, struct audio_device *);
    115 int vraiu_set_port(void *, mixer_ctrl_t *);
    116 int vraiu_get_port(void *, mixer_ctrl_t *);
    117 int vraiu_query_devinfo(void *, mixer_devinfo_t *);
    118 int vraiu_set_format(void *, int,
    119     const audio_params_t *, const audio_params_t *,
    120     audio_filter_reg_t *, audio_filter_reg_t *);
    121 int vraiu_get_props(void *);
    122 void vraiu_get_locks(void *, kmutex_t **, kmutex_t **);
    123 
    124 const struct audio_hw_if vraiu_hw_if = {
    125 	.query_format		= vraiu_query_format,
    126 	.set_format		= vraiu_set_format,
    127 	.round_blocksize	= vraiu_round_blocksize,
    128 	.commit_settings	= vraiu_commit_settings,
    129 	.init_output		= vraiu_init_output,
    130 	.start_output		= vraiu_start_output,
    131 	.halt_output		= vraiu_halt_output,
    132 	.getdev			= vraiu_getdev,
    133 	.set_port		= vraiu_set_port,
    134 	.get_port		= vraiu_get_port,
    135 	.query_devinfo		= vraiu_query_devinfo,
    136 	.get_props		= vraiu_get_props,
    137 	.get_locks		= vraiu_get_locks,
    138 };
    139 
    140 /*
    141  * convert to 1ch 10bit unsigned PCM data.
    142  */
    143 static void vraiu_slinear16_1(struct vraiu_softc *, u_short *, void *, int);
    144 
    145 int
    146 vraiu_match(device_t parent, cfdata_t cf, void *aux)
    147 {
    148 	return 1;
    149 }
    150 
    151 void
    152 vraiu_attach(device_t parent, device_t self, void *aux)
    153 {
    154 	struct vrip_attach_args *va;
    155 	struct vraiu_softc *sc;
    156 	bus_dma_segment_t segs;
    157 	int rsegs;
    158 
    159 	va = aux;
    160 	sc = device_private(self);
    161 	sc->sc_dev = self;
    162 	sc->sc_intr = NULL;
    163 	sc->sc_iot = va->va_iot;
    164 	sc->sc_vrip = va->va_vc;
    165 	sc->sc_cc = va->va_cc;
    166 	sc->sc_dc = va->va_dc;
    167 	sc->sc_ac = va->va_ac;
    168 	sc->sc_dmat = &vrdcu_bus_dma_tag;
    169 	sc->sc_volume = 127;
    170 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
    171 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
    172 
    173 	if (!sc->sc_cc) {
    174 		printf(" not configured: cmu not found\n");
    175 		return;
    176 	}
    177 	if (!sc->sc_dc) {
    178 		printf(" not configured: dcu not found\n");
    179 		return;
    180 	}
    181 	if (!sc->sc_ac) {
    182 		printf(" not configured: dmaau not found\n");
    183 		return;
    184 	}
    185 	if (bus_space_map(sc->sc_iot, va->va_addr, va->va_size,
    186 			  0 /* no flags */, &sc->sc_ioh)) {
    187 		printf(": can't map i/o space\n");
    188 		return;
    189 	}
    190 
    191 	/* install interrupt handler and enable interrupt */
    192 	if (!(sc->sc_handler = vrip_intr_establish(va->va_vc, va->va_unit,
    193 	    0, IPL_AUDIO, vraiu_intr, sc))) {
    194 		printf(": can't map interrupt line.\n");
    195 		return;
    196 	}
    197 	vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, (AIUINT_INTMEND | \
    198 							 AIUINT_INTM | \
    199 							 AIUINT_INTMIDLE | \
    200 							 AIUINT_INTMST | \
    201 							 AIUINT_INTSEND | \
    202 							 AIUINT_INTS | \
    203 							 AIUINT_INTSIDLE), 0);
    204 
    205 	if (bus_dmamem_alloc(sc->sc_dmat, AUDIO_BUF_SIZE, 0, 0, &segs, 1,
    206 			     &rsegs, BUS_DMA_WAITOK)) {
    207 		printf(": can't allocate memory.\n");
    208 		return;
    209 	}
    210 	if (bus_dmamem_map(sc->sc_dmat, &segs, rsegs, AUDIO_BUF_SIZE,
    211 			   (void **)&sc->sc_buf,
    212 			   BUS_DMA_WAITOK | BUS_DMA_COHERENT)) {
    213 		printf(": can't map memory.\n");
    214 		bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
    215 		return;
    216 	}
    217 	if (bus_dmamap_create(sc->sc_dmat, AUDIO_BUF_SIZE, 1, AUDIO_BUF_SIZE,
    218 			      0, BUS_DMA_WAITOK, &sc->sc_dmap)) {
    219 		printf(": can't create DMA map.\n");
    220 		bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_buf,
    221 				 AUDIO_BUF_SIZE);
    222 		bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
    223 		return;
    224 	}
    225 	if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, sc->sc_buf,
    226 				   AUDIO_BUF_SIZE, NULL, BUS_DMA_WAITOK)) {
    227 		printf(": can't load DMA map.\n");
    228 		bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmap);
    229 		bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_buf,
    230 				 AUDIO_BUF_SIZE);
    231 		bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
    232 		return;
    233 	}
    234 	if (sc->sc_ac->ac_set_aiuout(sc->sc_ac, sc->sc_buf)) {
    235 		printf(": can't set DMA address.\n");
    236 		bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
    237 		bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmap);
    238 		bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_buf,
    239 				 AUDIO_BUF_SIZE);
    240 		bus_dmamem_free(sc->sc_dmat, &segs, rsegs);
    241 		return;
    242 	}
    243 	printf("\n");
    244 
    245 	sc->sc_rate = SPS8000;
    246 	DPRINTFN(1, ("vraiu_attach: reset AIU\n"))
    247 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, SEQ_REG_W, AIURST);
    248 	/* attach audio subsystem */
    249 	audio_attach_mi(&vraiu_hw_if, sc, self);
    250 }
    251 
    252 int
    253 vraiu_query_format(void *self, audio_format_query_t *afp)
    254 {
    255 
    256 	return audio_query_format(&vraiu_formats, 1, afp);
    257 }
    258 
    259 int
    260 vraiu_set_format(void *self, int setmode,
    261 		 const audio_params_t *play, const audio_params_t *rec,
    262 		 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
    263 {
    264 	struct vraiu_softc *sc;
    265 
    266 	DPRINTFN(1, ("%s: %ubit, %uch, %uHz, encoding %u\n", __func__,
    267 		     play->precision, play->channels, play->sample_rate,
    268 		     play->encoding));
    269 	sc = self;
    270 
    271 	switch (play->sample_rate) {
    272 	case 8000:
    273 		sc->sc_rate = SPS8000;
    274 		break;
    275 	case 11025:
    276 		sc->sc_rate = SPS11025;
    277 		break;
    278 	case 22050:
    279 		sc->sc_rate = SPS22050;
    280 		break;
    281 	case 44100:
    282 		sc->sc_rate = SPS44100;
    283 		break;
    284 	default:
    285 		/* NOTREACHED */
    286 		panic("%s: rate error (%d)\n", __func__, play->sample_rate);
    287 	}
    288 
    289 	return 0;
    290 }
    291 
    292 int
    293 vraiu_round_blocksize(void *self, int bs, int mode, const audio_params_t *param)
    294 {
    295 	return AUDIO_BUF_SIZE;
    296 }
    297 
    298 int
    299 vraiu_commit_settings(void *self)
    300 {
    301 	struct vraiu_softc *sc;
    302 	int err;
    303 
    304 	DPRINTFN(1, ("vraiu_commit_settings\n"));
    305 	sc = self;
    306 
    307 	DPRINTFN(1, ("vraiu_commit_settings: set conversion rate %d\n",
    308 		     sc->sc_rate))
    309 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCNVR_REG_W, sc->sc_rate);
    310 	DPRINTFN(1, ("vraiu_commit_settings: clock supply start\n"))
    311 	if ((err = sc->sc_cc->cc_clock(sc->sc_cc, VR4102_CMUMSKAIU, 1))) {
    312 		DPRINTFN(0, ("vraiu_commit_settings: clock supply error\n"));
    313 		return err;
    314 	}
    315 	DPRINTFN(1, ("vraiu_commit_settings: enable DMA\n"))
    316 	if ((err = sc->sc_dc->dc_enable_aiuout(sc->sc_dc))) {
    317 		sc->sc_cc->cc_clock(sc->sc_cc, VR4102_CMUMSKAIU, 0);
    318 		DPRINTFN(0, ("vraiu_commit_settings: enable DMA error\n"));
    319 		return err;
    320 	}
    321 	DPRINTFN(1, ("vraiu_commit_settings: Vref on\n"))
    322 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCNT_REG_W, DAENAIU);
    323 	return 0;
    324 }
    325 
    326 int
    327 vraiu_init_output(void *self, void *buffer, int size)
    328 {
    329 	struct vraiu_softc *sc;
    330 
    331 	DPRINTFN(1, ("vraiu_init_output: buffer %p, size %d\n", buffer, size));
    332 	sc = self;
    333 	sc->sc_intr = NULL;
    334 	DPRINTFN(1, ("vraiu_init_output: speaker power on\n"))
    335 	config_hook_call(CONFIG_HOOK_POWERCONTROL,
    336 			 CONFIG_HOOK_POWERCONTROL_SPEAKER, (void*)1);
    337 	DPRINTFN(1, ("vraiu_init_output: start output\n"))
    338 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, SEQ_REG_W, AIUSEN);
    339 	return 0;
    340 }
    341 
    342 int
    343 vraiu_start_output(void *self, void *block, int bsize,
    344 		   void (*intr)(void *), void *intrarg)
    345 {
    346 	struct vraiu_softc *sc;
    347 
    348 	DPRINTFN(2, ("vraiu_start_output: block %p, bsize %d\n",
    349 		     block, bsize));
    350 	sc = self;
    351 	vraiu_slinear16_1(sc, sc->sc_buf, block, bsize);
    352 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, AUDIO_BUF_SIZE,
    353 			BUS_DMASYNC_PREWRITE);
    354 	sc->sc_intr = intr;
    355 	sc->sc_intrdata = intrarg;
    356 	/* clear interrupt status */
    357 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, INT_REG_W,
    358 			  SENDINTR | SINTR | SIDLEINTR);
    359 	/* enable interrupt */
    360 	vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, AIUINT_INTSEND, 1);
    361 	return 0;
    362 }
    363 
    364 int
    365 vraiu_intr(void* self)
    366 {
    367 	struct vraiu_softc *sc;
    368 	uint32_t reg;
    369 
    370 	DPRINTFN(2, ("vraiu_intr"));
    371 	sc = self;
    372 
    373 	mutex_spin_enter(&sc->sc_intr_lock);
    374 
    375 	vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, AIUINT_INTSEND, 0);
    376 	vrip_intr_getstatus2(sc->sc_vrip, sc->sc_handler, &reg);
    377 	if (reg & AIUINT_INTSEND) {
    378 		DPRINTFN(2, (": AIUINT_INTSEND"));
    379 		if (sc->sc_intr) {
    380 			void (*intr)(void *);
    381 			intr = sc->sc_intr;
    382 			sc->sc_intr = NULL;
    383 			(*(intr))(sc->sc_intrdata);
    384 		}
    385 		bus_space_write_2(sc->sc_iot, sc->sc_ioh, INT_REG_W, SENDINTR);
    386 	}
    387 	DPRINTFN(2, ("\n"));
    388 
    389 	mutex_spin_exit(&sc->sc_intr_lock);
    390 
    391 	return 0;
    392 }
    393 
    394 int
    395 vraiu_halt_output(void *self)
    396 {
    397 	struct vraiu_softc *sc;
    398 
    399 	DPRINTFN(1, ("vraiu_halt_output\n"));
    400 	sc =self;
    401 	DPRINTFN(1, ("vraiu_halt_output: disable interrupt\n"))
    402 	vrip_intr_setmask2(sc->sc_vrip, sc->sc_handler, AIUINT_INTSEND, 0);
    403 	DPRINTFN(1, ("vraiu_halt_output: stop output\n"))
    404 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, SEQ_REG_W, 0);
    405 	DPRINTFN(1, ("vraiu_halt_output: speaker power off\n"))
    406 	config_hook_call(CONFIG_HOOK_POWERCONTROL,
    407 			 CONFIG_HOOK_POWERCONTROL_SPEAKER, (void*)0);
    408 	DPRINTFN(1, ("vraiu_halt_output: Vref off\n"))
    409 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, SCNT_REG_W, 0);
    410 	DPRINTFN(1, ("vraiu_halt_output: disable DMA\n"))
    411 	sc->sc_dc->dc_disable(sc->sc_dc);
    412 	DPRINTFN(1, ("vraiu_halt_output: clock supply stop\n"))
    413 	sc->sc_cc->cc_clock(sc->sc_cc, VR4102_CMUMSKAIU, 0);
    414 	sc->sc_intr = NULL;
    415 	return 0;
    416 }
    417 
    418 int
    419 vraiu_getdev(void *self, struct audio_device *ret)
    420 {
    421 
    422 	DPRINTFN(3, ("vraiu_getdev\n"));
    423 	*ret = aiu_device;
    424 	return 0;
    425 }
    426 
    427 int
    428 vraiu_set_port(void *self, mixer_ctrl_t *mc)
    429 {
    430 	struct vraiu_softc *sc;
    431 
    432 	DPRINTFN(3, ("vraiu_set_port\n"));
    433 	sc = self;
    434 	/* software mixer, 1ch */
    435 	if (mc->dev == 0) {
    436 		if (mc->type != AUDIO_MIXER_VALUE)
    437 			return EINVAL;
    438 		if (mc->un.value.num_channels != 1)
    439 			return EINVAL;
    440 		sc->sc_volume = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
    441 		return 0;
    442 	}
    443 
    444 	return EINVAL;
    445 }
    446 
    447 int
    448 vraiu_get_port(void *self, mixer_ctrl_t *mc)
    449 {
    450 	struct vraiu_softc *sc;
    451 
    452 	DPRINTFN(3, ("vraiu_get_port\n"));
    453 	sc = self;
    454 	/* software mixer, 1ch */
    455 	if (mc->dev == 0) {
    456 		if (mc->un.value.num_channels != 1)
    457 			return EINVAL;
    458 		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_volume;
    459 		return 0;
    460 	}
    461 
    462 	return EINVAL;
    463 }
    464 
    465 int
    466 vraiu_query_devinfo(void *self, mixer_devinfo_t *di)
    467 {
    468 
    469 	DPRINTFN(3, ("vraiu_query_devinfo\n"));
    470 	/* software mixer, 1ch */
    471 	switch (di->index) {
    472 	case 0: /* inputs.dac mixer value */
    473 		di->mixer_class = 1;
    474 		di->next = di->prev = AUDIO_MIXER_LAST;
    475 		strcpy(di->label.name, AudioNdac);
    476 		di->type = AUDIO_MIXER_VALUE;
    477 		di->un.v.num_channels = 1;
    478 		strcpy(di->un.v.units.name, AudioNvolume);
    479 		return 0;
    480 	case 1: /* outputs class */
    481 		di->mixer_class = 1;
    482 		di->next = di->prev = AUDIO_MIXER_LAST;
    483 		strcpy(di->label.name, AudioCinputs);
    484 		di->type = AUDIO_MIXER_CLASS;
    485 		return 0;
    486 	}
    487 
    488 	return ENXIO;
    489 }
    490 
    491 int
    492 vraiu_get_props(void *self)
    493 {
    494 	DPRINTFN(3, ("vraiu_get_props\n"));
    495 
    496 	return AUDIO_PROP_PLAYBACK;
    497 }
    498 
    499 void
    500 vraiu_get_locks(void *self, kmutex_t **intr, kmutex_t **thread)
    501 {
    502 	struct vraiu_softc *sc;
    503 
    504 	DPRINTFN(3, ("vraiu_get_locks\n"));
    505 	sc = self;
    506 
    507 	*intr = &sc->sc_intr_lock;
    508 	*thread = &sc->sc_lock;
    509 }
    510 
    511 /* slinear16/mono -> ulinear10/mono with volume */
    512 static void
    513 vraiu_slinear16_1(struct vraiu_softc *sc, u_short *dmap, void *p, int n)
    514 {
    515 	short *q;
    516 
    517 	DPRINTFN(3, ("vraiu_slinear16_1\n"));
    518 	q = p;
    519 #ifdef DIAGNOSTIC
    520 	if (n > AUDIO_BUF_SIZE) {
    521 		printf("%s: output data too large (%d > %d)\n",
    522 		       device_xname(sc->sc_dev), n, AUDIO_BUF_SIZE);
    523 		n = AUDIO_BUF_SIZE;
    524 	}
    525 #endif
    526 	n /= 2;
    527 	while (n--) {
    528 		int i = *q++;
    529 		i = i * sc->sc_volume / 255;
    530 		*dmap++ = (i >> 6) + 0x200;
    531 	}
    532 }
    533