cs4231_ebus.c revision 1.2
1/*	$NetBSD: cs4231_ebus.c,v 1.2 2002/03/12 06:00:42 uwe Exp $ */
2
3/*
4 * Copyright (c) 2002 Valeriy E. Ushakov
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/errno.h>
33#include <sys/device.h>
34#include <sys/malloc.h>
35
36#include <machine/autoconf.h>
37#include <machine/cpu.h>
38#include <dev/ebus/ebusreg.h>
39#include <dev/ebus/ebusvar.h>
40
41#include <sys/audioio.h>
42#include <dev/audio_if.h>
43
44#include <dev/ic/ad1848reg.h>
45#include <dev/ic/cs4231reg.h>
46#include <dev/ic/ad1848var.h>
47#include <dev/ic/cs4231var.h>
48
49#ifdef AUDIO_DEBUG
50int cs4231_ebus_debug = 0;
51#define DPRINTF(x)	if (cs4231_ebus_debug) printf x
52#else
53#define DPRINTF(x)
54#endif
55
56
57struct cs4231_ebus_softc {
58	struct cs4231_softc sc_cs4231;
59
60	volatile struct	ebus_dmac_reg	*sc_pdmareg; /* playback DMA */
61	volatile struct	ebus_dmac_reg	*sc_rdmareg; /* record DMA */
62};
63
64
65void	cs4231_ebus_attach(struct device *, struct device *, void *);
66int	cs4231_ebus_match(struct device *, struct cfdata *, void *);
67
68struct cfattach audiocs_ebus_ca = {
69	sizeof(struct cs4231_ebus_softc), cs4231_ebus_match, cs4231_ebus_attach
70};
71
72
73/* audio_hw_if methods specific to ebus dma */
74static int	cs4231_ebus_trigger_output(void *, void *, void *, int,
75					   void (*)(void *), void *,
76					   struct audio_params *);
77static int	cs4231_ebus_trigger_input(void *, void *, void *, int,
78					  void (*)(void *), void *,
79					  struct audio_params *);
80static int	cs4231_ebus_halt_output(void *);
81static int	cs4231_ebus_halt_input(void *);
82
83struct audio_hw_if audiocs_ebus_hw_if = {
84	cs4231_open,
85	cs4231_close,
86	NULL,			/* drain */
87	ad1848_query_encoding,
88	ad1848_set_params,
89	cs4231_round_blocksize,
90	ad1848_commit_settings,
91	NULL,			/* init_output */
92	NULL,			/* init_input */
93	NULL,			/* start_output */
94	NULL,			/* start_input */
95	cs4231_ebus_halt_output,
96	cs4231_ebus_halt_input,
97	NULL,			/* speaker_ctl */
98	cs4231_getdev,
99	NULL,			/* setfd */
100	cs4231_set_port,
101	cs4231_get_port,
102	cs4231_query_devinfo,
103	cs4231_malloc,
104	cs4231_free,
105	cs4231_round_buffersize,
106	NULL,			/* mappage */
107	cs4231_get_props,
108	cs4231_ebus_trigger_output,
109	cs4231_ebus_trigger_input,
110	NULL,			/* dev_ioctl */
111};
112
113#ifdef AUDIO_DEBUG
114static void	cs4231_ebus_regdump(char *, struct cs4231_ebus_softc *);
115#endif
116
117static int	cs4231_ebus_dma_reset(volatile struct ebus_dmac_reg *);
118static int	cs4231_ebus_trigger_transfer(struct cs4231_softc *,
119			struct cs_transfer *, volatile struct ebus_dmac_reg *,
120			int, void *, void *, int, void (*)(void *), void *,
121			struct audio_params *);
122static void	cs4231_ebus_dma_advance(struct cs_transfer *,
123					volatile struct ebus_dmac_reg *);
124static int	cs4231_ebus_dma_intr(struct cs_transfer *,
125				     volatile struct ebus_dmac_reg *);
126static int	cs4231_ebus_intr(void *);
127
128
129int
130cs4231_ebus_match(parent, cf, aux)
131	struct device *parent;
132	struct cfdata *cf;
133	void *aux;
134{
135	struct ebus_attach_args *ea = aux;
136
137	if (strcmp(ea->ea_name, AUDIOCS_PROM_NAME) == 0)
138		return (1);
139#ifdef __sparc__		/* XXX: Krups */
140	if (strcmp(ea->ea_name, "sound") == 0)
141		return (1);
142#endif
143
144	return (0);
145}
146
147
148void
149cs4231_ebus_attach(parent, self, aux)
150	struct device *parent, *self;
151	void *aux;
152{
153	struct cs4231_ebus_softc *ebsc = (struct cs4231_ebus_softc *)self;
154	struct cs4231_softc *sc = &ebsc->sc_cs4231;
155	struct ebus_attach_args *ea = aux;
156	bus_space_handle_t bh;
157	int i;
158
159	sc->sc_bustag = ea->ea_bustag;
160	sc->sc_dmatag = ea->ea_dmatag;
161
162	/*
163	 * These are the register we get from the prom:
164	 *	- CS4231 registers
165	 *	- Playback EBus DMA controller
166	 *	- Capture EBus DMA controller
167	 *	- AUXIO audio register (codec powerdown)
168	 *
169	 * Map my registers in, if they aren't already in virtual
170	 * address space.
171	 */
172	if (ea->ea_nvaddr) {
173		bh = (bus_space_handle_t)ea->ea_vaddr[0];
174	} else {
175		if (bus_space_map(ea->ea_bustag,
176				  EBUS_ADDR_FROM_REG(&ea->ea_reg[0]),
177				  ea->ea_reg[0].size,
178				  BUS_SPACE_MAP_LINEAR,
179				  &bh) != 0)
180		{
181			printf("%s: unable to map registers\n",
182			       self->dv_xname);
183			return;
184		}
185	}
186
187	/* XXX: map playback DMA registers (we just know where they are) */
188	if (bus_space_map(ea->ea_bustag,
189			  BUS_ADDR(0x14, 0x702000), /* XXX: magic num */
190			  sizeof(struct ebus_dmac_reg),
191			  BUS_SPACE_MAP_LINEAR,
192			  (bus_space_handle_t *)&ebsc->sc_pdmareg) != 0)
193	{
194		printf("%s: unable to map playback DMA registers\n",
195		       self->dv_xname);
196		return;
197	}
198
199	/* XXX: map capture DMA registers (we just know where they are) */
200	if (bus_space_map(ea->ea_bustag,
201			  BUS_ADDR(0x14, 0x704000), /* XXX: magic num */
202			  sizeof(struct ebus_dmac_reg),
203			  BUS_SPACE_MAP_LINEAR,
204			  (bus_space_handle_t *)&ebsc->sc_rdmareg) != 0)
205	{
206		printf("%s: unable to map capture DMA registers\n",
207		       self->dv_xname);
208		return;
209	}
210
211	/* establish interrupt channels */
212	for (i = 0; i < ea->ea_nintr; ++i)
213		bus_intr_establish(ea->ea_bustag,
214				   ea->ea_intr[i], IPL_AUDIO, 0,
215				   cs4231_ebus_intr, ebsc);
216
217	cs4231_common_attach(sc, bh);
218	printf("\n");
219
220	/* XXX: todo: move to cs4231_common_attach, pass hw_if as arg? */
221	audio_attach_mi(&audiocs_ebus_hw_if, sc, &sc->sc_ad1848.sc_dev);
222}
223
224
225#ifdef AUDIO_DEBUG
226static void
227cs4231_ebus_regdump(label, ebsc)
228	char *label;
229	struct cs4231_ebus_softc *ebsc;
230{
231	/* char bits[128]; */
232
233	printf("cs4231regdump(%s): regs:", label);
234	/* XXX: dump ebus dma and aux registers */
235	ad1848_dump_regs(&ebsc->sc_cs4231.sc_ad1848);
236}
237#endif /* AUDIO_DEBUG */
238
239
240/* XXX: nothing CS4231-specific in this code... */
241static int
242cs4231_ebus_dma_reset(dmac)
243	volatile struct ebus_dmac_reg *dmac;
244{
245	int timo;
246
247	dmac->dcsr = EBDMA_RESET | EBDMA_TC; /* also clear TC, just in case */
248
249	for (timo = 50000; timo != 0; --timo)
250		if ((dmac->dcsr & (EBDMA_CYC_PEND | EBDMA_DRAIN)) == 0)
251			break;
252
253	if (timo == 0) {
254		printf("cs4231_ebus_dma_reset: dcsr = %x, reset timed out\n",
255		       dmac->dcsr);
256		return (ETIMEDOUT);
257	}
258
259	dmac->dcsr &= ~EBDMA_RESET;
260	return (0);
261}
262
263
264static void
265cs4231_ebus_dma_advance(t, dmac)
266	struct cs_transfer *t;
267	volatile struct ebus_dmac_reg *dmac;
268{
269	bus_addr_t dmaaddr;
270	bus_size_t dmasize;
271
272	cs4231_transfer_advance(t, &dmaaddr, &dmasize);
273	dmac->dbcr = (u_int32_t)dmasize;
274	dmac->dacr = (u_int32_t)dmaaddr;
275}
276
277
278/*
279 * Trigger transfer "t" using DMA controller "dmac".
280 * "iswrite" defines direction of the transfer.
281 */
282static int
283cs4231_ebus_trigger_transfer(sc, t, dmac, iswrite,
284			     start, end, blksize,
285			     intr, arg, param)
286	struct cs4231_softc *sc;
287	struct cs_transfer *t;
288	volatile struct ebus_dmac_reg *dmac;
289	int iswrite;
290	void *start, *end;
291	int blksize;
292	void (*intr)(void *);
293	void *arg;
294	struct audio_params *param;
295{
296	bus_addr_t dmaaddr;
297	bus_size_t dmasize;
298	int ret;
299
300	ret = cs4231_transfer_init(sc, t, &dmaaddr, &dmasize,
301				   start, end, blksize, intr, arg);
302	if (ret != 0)
303		return (ret);
304
305	ret = cs4231_ebus_dma_reset(dmac);
306	if (ret != 0)
307		return (ret);
308
309	dmac->dcsr |= EBDMA_EN_NEXT | (iswrite ? EBDMA_WRITE : 0)
310		| EBDMA_EN_DMA | EBDMA_EN_CNT | EBDMA_INT_EN;
311
312	/* first load: goes to DACR/DBCR */
313	dmac->dbcr = (u_int32_t)dmasize;
314	dmac->dacr = (u_int32_t)dmaaddr;
315
316	/* next load: goes to DNAR/DNBR */
317	cs4231_ebus_dma_advance(t, dmac);
318
319	return (0);
320}
321
322
323static int
324cs4231_ebus_trigger_output(addr, start, end, blksize, intr, arg, param)
325	void *addr;
326	void *start, *end;
327	int blksize;
328	void (*intr)(void *);
329	void *arg;
330	struct audio_params *param;
331{
332	struct cs4231_ebus_softc *ebsc = addr;
333	struct cs4231_softc *sc = &ebsc->sc_cs4231;
334	struct cs_transfer *t = &sc->sc_playback;
335	volatile struct ebus_dmac_reg *dma = ebsc->sc_pdmareg;
336	int cfg;
337	int ret;
338
339	ret = cs4231_ebus_trigger_transfer(sc, t, dma, 0,
340					   start, end, blksize,
341					   intr, arg,
342					   param);
343	if (ret != 0)
344		return (ret);
345
346	ad_write(&sc->sc_ad1848, SP_LOWER_BASE_COUNT, 0xff);
347	ad_write(&sc->sc_ad1848, SP_UPPER_BASE_COUNT, 0xff);
348
349	cfg = ad_read(&sc->sc_ad1848, SP_INTERFACE_CONFIG);
350	ad_write(&sc->sc_ad1848, SP_INTERFACE_CONFIG, (cfg | PLAYBACK_ENABLE));
351
352	return (0);
353}
354
355
356static int
357cs4231_ebus_trigger_input(addr, start, end, blksize, intr, arg, param)
358	void *addr;
359	void *start, *end;
360	int blksize;
361	void (*intr)(void *);
362	void *arg;
363	struct audio_params *param;
364{
365	struct cs4231_ebus_softc *ebsc = addr;
366	struct cs4231_softc *sc = &ebsc->sc_cs4231;
367	struct cs_transfer *t = &sc->sc_capture;
368	volatile struct ebus_dmac_reg *dmac = ebsc->sc_rdmareg;
369	int cfg;
370	int ret;
371
372	ret = cs4231_ebus_trigger_transfer(sc, t, dmac, 1,
373					   start, end, blksize,
374					   intr, arg,
375					   param);
376	if (ret != 0)
377		return (ret);
378
379	ad_write(&sc->sc_ad1848, CS_LOWER_REC_CNT, 0xff);
380	ad_write(&sc->sc_ad1848, CS_UPPER_REC_CNT, 0xff);
381
382	cfg = ad_read(&sc->sc_ad1848, SP_INTERFACE_CONFIG);
383	ad_write(&sc->sc_ad1848, SP_INTERFACE_CONFIG, (cfg | CAPTURE_ENABLE));
384
385	return (0);
386}
387
388
389static int
390cs4231_ebus_halt_output(addr)
391	void *addr;
392{
393	struct cs4231_ebus_softc *ebsc = (struct cs4231_ebus_softc *)addr;
394	struct cs4231_softc *sc = &ebsc->sc_cs4231;
395	int cfg;
396
397	sc->sc_playback.t_active = 0;
398	ebsc->sc_pdmareg->dcsr &= ~EBDMA_EN_DMA;
399
400	cfg = ad_read(&sc->sc_ad1848, SP_INTERFACE_CONFIG);
401	ad_write(&sc->sc_ad1848, SP_INTERFACE_CONFIG,(cfg & ~PLAYBACK_ENABLE));
402
403	return (0);
404}
405
406
407static int
408cs4231_ebus_halt_input(addr)
409	void *addr;
410{
411	struct cs4231_ebus_softc *ebsc = (struct cs4231_ebus_softc *)addr;
412	struct cs4231_softc *sc = &ebsc->sc_cs4231;
413	int cfg;
414
415	sc->sc_capture.t_active = 0;
416	ebsc->sc_pdmareg->dcsr &= ~EBDMA_EN_DMA;
417
418	cfg = ad_read(&sc->sc_ad1848, SP_INTERFACE_CONFIG);
419	ad_write(&sc->sc_ad1848, SP_INTERFACE_CONFIG, (cfg & ~CAPTURE_ENABLE));
420
421	return (0);
422}
423
424
425static int
426cs4231_ebus_dma_intr(t, dmac)
427	struct cs_transfer *t;
428	volatile struct ebus_dmac_reg *dmac;
429{
430	u_int32_t csr;
431#ifdef AUDIO_DEBUG
432	char bits[128];
433#endif
434
435	/* read DMA status, clear TC bit by writing it back */
436	csr = dmac->dcsr;
437	dmac->dcsr = csr;
438	DPRINTF(("audiocs: %s dcsr=%s\n", t->t_name,
439		 bitmask_snprintf(csr, EBUS_DCSR_BITS, bits, sizeof(bits))));
440
441	if (csr & EBDMA_ERR_PEND) {
442		++t->t_ierrcnt.ev_count;
443		printf("audiocs: %s DMA error, resetting\n", t->t_name);
444		cs4231_ebus_dma_reset(dmac);
445		/* how to notify audio(9)??? */
446		return (1);
447	}
448
449	if ((csr & EBDMA_INT_PEND) == 0)
450		return (0);
451
452	++t->t_intrcnt.ev_count;
453
454	if ((csr & EBDMA_TC) == 0) { /* can this happen? */
455		printf("audiocs: %s INT_PEND but !TC\n", t->t_name);
456		return (1);
457	}
458
459	if (!t->t_active)
460		return (1);
461
462	cs4231_ebus_dma_advance(t, dmac);
463
464	/* call audio(9) framework while dma is chugging along */
465	if (t->t_intr != NULL)
466		(*t->t_intr)(t->t_arg);
467	return (1);
468}
469
470
471static int
472cs4231_ebus_intr(arg)
473	void *arg;
474{
475	struct cs4231_ebus_softc *ebsc = (struct cs4231_ebus_softc *)arg;
476	struct cs4231_softc *sc = &ebsc->sc_cs4231;
477	int status;
478	int ret;
479#ifdef AUDIO_DEBUG
480	char bits[128];
481#endif
482
483	status = ADREAD(&sc->sc_ad1848, AD1848_STATUS);
484
485#ifdef AUDIO_DEBUG
486	if (cs4231_ebus_debug > 1)
487		cs4231_ebus_regdump("audiointr", ebsc);
488
489	DPRINTF(("%s: status: %s\n", sc->sc_ad1848.sc_dev.dv_xname,
490		 bitmask_snprintf(status, AD_R2_BITS, bits, sizeof(bits))));
491#endif
492
493	if (status & INTERRUPT_STATUS) {
494#ifdef AUDIO_DEBUG
495		int reason;
496
497		reason = ad_read(&sc->sc_ad1848, CS_IRQ_STATUS);
498		DPRINTF(("%s: i24: %s\n", sc->sc_ad1848.sc_dev.dv_xname,
499		  bitmask_snprintf(reason, CS_I24_BITS, bits, sizeof(bits))));
500#endif
501		/* clear interrupt from ad1848 */
502		ADWRITE(&sc->sc_ad1848, AD1848_STATUS, 0);
503	}
504
505	ret = 0;
506
507	if (cs4231_ebus_dma_intr(&sc->sc_capture, ebsc->sc_rdmareg)) {
508		++sc->sc_intrcnt.ev_count;
509		ret = 1;
510	}
511
512	if (cs4231_ebus_dma_intr(&sc->sc_playback, ebsc->sc_pdmareg)) {
513		++sc->sc_intrcnt.ev_count;
514		ret = 1;
515	}
516
517	return (ret);
518}
519