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 53int 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 61struct 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 81int vraiu_match(device_t, cfdata_t, void *); 82void vraiu_attach(device_t, device_t, void *); 83int vraiu_intr(void *); 84 85CFATTACH_DECL_NEW(vraiu, sizeof(struct vraiu_softc), 86 vraiu_match, vraiu_attach, NULL, NULL); 87 88struct audio_device aiu_device = { 89 "VR4121 AIU", 90 "0.1", 91 "aiu" 92}; 93 94const 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 */ 108int vraiu_query_format(void *, audio_format_query_t *); 109int vraiu_round_blocksize(void *, int, int, const audio_params_t *); 110int vraiu_commit_settings(void *); 111int vraiu_init_output(void *, void*, int); 112int vraiu_start_output(void *, void *, int, void (*)(void *), void *); 113int vraiu_halt_output(void *); 114int vraiu_getdev(void *, struct audio_device *); 115int vraiu_set_port(void *, mixer_ctrl_t *); 116int vraiu_get_port(void *, mixer_ctrl_t *); 117int vraiu_query_devinfo(void *, mixer_devinfo_t *); 118int vraiu_set_format(void *, int, 119 const audio_params_t *, const audio_params_t *, 120 audio_filter_reg_t *, audio_filter_reg_t *); 121int vraiu_get_props(void *); 122void vraiu_get_locks(void *, kmutex_t **, kmutex_t **); 123 124const 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 */ 143static void vraiu_slinear16_1(struct vraiu_softc *, u_short *, void *, int); 144 145int 146vraiu_match(device_t parent, cfdata_t cf, void *aux) 147{ 148 return 1; 149} 150 151void 152vraiu_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 252int 253vraiu_query_format(void *self, audio_format_query_t *afp) 254{ 255 256 return audio_query_format(&vraiu_formats, 1, afp); 257} 258 259int 260vraiu_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 292int 293vraiu_round_blocksize(void *self, int bs, int mode, const audio_params_t *param) 294{ 295 return AUDIO_BUF_SIZE; 296} 297 298int 299vraiu_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 326int 327vraiu_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 342int 343vraiu_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 364int 365vraiu_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, ®); 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 394int 395vraiu_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 418int 419vraiu_getdev(void *self, struct audio_device *ret) 420{ 421 422 DPRINTFN(3, ("vraiu_getdev\n")); 423 *ret = aiu_device; 424 return 0; 425} 426 427int 428vraiu_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 447int 448vraiu_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 465int 466vraiu_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 491int 492vraiu_get_props(void *self) 493{ 494 DPRINTFN(3, ("vraiu_get_props\n")); 495 496 return AUDIO_PROP_PLAYBACK; 497} 498 499void 500vraiu_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 */ 512static void 513vraiu_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