xref: /netbsd/sys/arch/hpcmips/vr/vraiu.c (revision dc217226)
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
vraiu_match(device_t parent,cfdata_t cf,void * aux)146 vraiu_match(device_t parent, cfdata_t cf, void *aux)
147 {
148 	return 1;
149 }
150 
151 void
vraiu_attach(device_t parent,device_t self,void * aux)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
vraiu_query_format(void * self,audio_format_query_t * afp)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
vraiu_set_format(void * self,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)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
vraiu_round_blocksize(void * self,int bs,int mode,const audio_params_t * param)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
vraiu_commit_settings(void * self)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
vraiu_init_output(void * self,void * buffer,int size)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
vraiu_start_output(void * self,void * block,int bsize,void (* intr)(void *),void * intrarg)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
vraiu_intr(void * self)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
vraiu_halt_output(void * self)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
vraiu_getdev(void * self,struct audio_device * ret)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
vraiu_set_port(void * self,mixer_ctrl_t * mc)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
vraiu_get_port(void * self,mixer_ctrl_t * mc)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
vraiu_query_devinfo(void * self,mixer_devinfo_t * di)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
vraiu_get_props(void * self)492 vraiu_get_props(void *self)
493 {
494 	DPRINTFN(3, ("vraiu_get_props\n"));
495 
496 	return AUDIO_PROP_PLAYBACK;
497 }
498 
499 void
vraiu_get_locks(void * self,kmutex_t ** intr,kmutex_t ** thread)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
vraiu_slinear16_1(struct vraiu_softc * sc,u_short * dmap,void * p,int n)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