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, &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
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