audio_4231_apcdma.c revision 9484:fbd5ddc28e96
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * Platform specifc code for the APC DMA controller. The APC is an SBus
28 * IC that includes play and record DMA engines and an interface for
29 * the CS4231.
30 */
31
32#include <sys/systm.h>
33#include <sys/ddi.h>
34#include <sys/sunddi.h>
35#include <sys/note.h>
36#include <sys/audio/audio_driver.h>
37#include "audio_4231.h"
38
39/*
40 * Attribute structure for the APC, used to create DMA handles.
41 */
42static ddi_dma_attr_t apc_dma_attr = {
43	DMA_ATTR_V0,			/* version */
44	0x0000000000000000LL,		/* dlim_addr_lo */
45	0x00000000ffffffffLL,		/* dlim_addr_hi */
46	0x0000000000000fffLL,		/* DMA counter register */
47	0x0000000000000001LL,		/* DMA address alignment */
48	0x00000014,			/* 4 and 16 byte burst sizes */
49	0x00000001,			/* min effective DMA size */
50	0x0000000000000fffLL,		/* maximum transfer size, 8k */
51	0x000000000000ffffLL,		/* segment boundary, 32k */
52	0x00000001,			/* s/g list length, no s/g */
53	0x00000001,			/* granularity of device, don't care */
54	0				/* DMA flags */
55};
56
57static ddi_device_acc_attr_t acc_attr = {
58	DDI_DEVICE_ATTR_V0,
59	DDI_STRUCTURE_BE_ACC,
60	DDI_STRICTORDER_ACC
61};
62
63/*
64 * Local routines
65 */
66static uint_t apc_intr(caddr_t);
67static void apc_load_fragment(CS_engine_t *);
68
69/*
70 * DMA ops vector functions
71 */
72static int apc_map_regs(CS_state_t *);
73static void apc_unmap_regs(CS_state_t *);
74static void apc_reset(CS_state_t *);
75static int apc_add_intr(CS_state_t *);
76static void apc_rem_intr(CS_state_t *);
77static int apc_start_engine(CS_engine_t *);
78static void apc_stop_engine(CS_engine_t *);
79static void apc_power(CS_state_t *, int);
80
81cs4231_dma_ops_t cs4231_apcdma_ops = {
82	"APC DMA controller",
83	&apc_dma_attr,
84	apc_map_regs,
85	apc_unmap_regs,
86	apc_reset,
87	apc_add_intr,
88	apc_rem_intr,
89	apc_start_engine,
90	apc_stop_engine,
91	apc_power,
92};
93
94/*
95 * apc_map_regs()
96 *
97 * Description:
98 *	This routine allocates the DMA handles and the memory for the
99 *	DMA engines to use. It then binds each of the buffers to its
100 *	respective handle, getting a DMA cookie. Finally, the registers
101 *	are mapped in.
102 *
103 *	NOTE: All of the ddi_dma_... routines sleep if they cannot get
104 *		memory. This means these calls will almost always succeed.
105 *
106 * Arguments:
107 *	CS_state_t	*state		The device's state structure
108 *
109 * Returns:
110 *	AUDIO_SUCCESS		Registers successfully mapped
111 *	AUDIO_FAILURE		Registers not successfully mapped
112 */
113static int
114apc_map_regs(CS_state_t *state)
115{
116	ddi_acc_handle_t	*handle = &APC_HANDLE;
117	dev_info_t		*dip = state->cs_dip;
118
119	/* map in the registers, getting a handle */
120	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&state->cs_regs, 0,
121	    sizeof (cs4231_regs_t), &acc_attr, handle) != DDI_SUCCESS) {
122		audio_dev_warn(state->cs_adev, "ddi_regs_map_setup() failed");
123		return (DDI_FAILURE);
124	}
125
126	/* clear the CSR so we have all interrupts disabled */
127	ddi_put32(*handle, &APC_DMACSR, APC_CLEAR_RESET_VALUE);
128
129	return (DDI_SUCCESS);
130}	/* apc_map_regs() */
131
132/*
133 * apc_unmap_regs()
134 *
135 * Description:
136 *	This routine unmaps the Codec's and DMA engine's registers.
137 *	It must be idempotent.
138 *
139 * Arguments:
140 *	CS_state_t	*state	The device's state structure
141 *
142 * Returns:
143 *	void
144 */
145static void
146apc_unmap_regs(CS_state_t *state)
147{
148	if (APC_HANDLE)
149		ddi_regs_map_free(&APC_HANDLE);
150
151}	/* apc_unmap_regs() */
152
153/*
154 * apc_reset()
155 *
156 * Description:
157 *	Reset both the play and record DMA engines. The engines are left
158 *	with interrupts and the DMA engine disabled.
159 *
160 * Arguments:
161 *	dev_info_t	*dip	Pointer to the device's devinfo structure
162 *	CS_state_t	*state	The device's state structure
163 *
164 * Returns:
165 *	void
166 */
167static void
168apc_reset(CS_state_t *state)
169{
170	ddi_acc_handle_t	handle = APC_HANDLE;
171
172	/*
173	 * The APC has a bug where the reset is not done
174	 * until you do the next pio to the APC. This
175	 * next write to the CSR causes the posted reset to
176	 * happen.
177	 */
178
179	ddi_put32(handle, &APC_DMACSR, APC_RESET);
180	ddi_put32(handle, &APC_DMACSR, APC_CLEAR_RESET_VALUE);
181
182}	/* apc_reset() */
183
184/*
185 * apc_add_intr()
186 *
187 * Description:
188 *	Register the APC interrupts with the kernel.
189 *
190 *	NOTE: This does NOT turn on interrupts.
191 *
192 *	CAUTION: While the interrupts are added, the Codec interrupts are
193 *		not enabled.
194 *
195 * Arguments:
196 *	CS_state_t	*state	Pointer to the device's state structure
197 *
198 * Returns:
199 *	DDI_SUCCESS		Registers successfully mapped
200 *	DDI_FAILURE		Registers not successfully mapped
201 */
202static int
203apc_add_intr(CS_state_t *state)
204{
205	dev_info_t	*dip = state->cs_dip;
206
207	/* first we make sure this isn't a high level interrupt */
208	if (ddi_intr_hilevel(dip, 0) != 0) {
209		audio_dev_warn(state->cs_adev,
210		    "unsupported high level interrupt");
211		return (DDI_FAILURE);
212	}
213
214	/* okay to register the interrupt */
215	if (ddi_add_intr(dip, 0, NULL, NULL, apc_intr, (caddr_t)state) !=
216	    DDI_SUCCESS) {
217		audio_dev_warn(state->cs_adev, "bad interrupt specification");
218		return (DDI_FAILURE);
219	}
220
221	return (DDI_SUCCESS);
222
223}	/* apc_add_intr() */
224
225/*
226 * apc_rem_intr()
227 *
228 * Description:
229 *	Unregister the APC interrupts from the kernel.
230 *
231 *	CAUTION: While the interrupts are removed, the Codec interrupts are
232 *		not disabled, but then, they never should have been on in
233 *		the first place.
234 *
235 * Arguments:
236 *	CS_state_t	*state	Pointer to the device's state structure
237 *
238 * Returns:
239 *	void
240 */
241static void
242apc_rem_intr(CS_state_t *state)
243{
244	ddi_remove_intr(state->cs_dip, 0, NULL);
245}	/* apc_rem_intr() */
246
247/*
248 * apc_start_engine()
249 *
250 * Description:
251 *	This routine starts the DMA engine.
252 *
253 *	For hard starts the DMA engine is started by programming the
254 *	Next Virtual Address and then the Next Counter twice, and
255 *	finally enabling the DMA engine.
256 *
257 *	NOTE: The state structure must be locked before this routine is called.
258 *
259 *	CAUTION: ?!? This routine doesn't start the Codec because the first
260 *		interrupt causes a recursive mutex_enter.
261 *
262 * Arguments:
263 *	CS_engine_t	*eng	The engine to start
264 *
265 * Returns:
266 *	DDI_SUCCESS		The DMA engine was started
267 *	DDI_FAILURE		The DMA engine was not started
268 */
269static int
270apc_start_engine(CS_engine_t *eng)
271{
272	CS_state_t		*state = eng->ce_state;
273	ddi_acc_handle_t	handle = APC_HANDLE;
274	uint32_t		csr;
275	uint32_t		enable;
276	uint32_t		dirty;
277	int			x;
278
279	ASSERT(mutex_owned(&state->cs_lock));
280
281	if (eng->ce_num == CS4231_PLAY) {
282		enable = APC_PLAY_ENABLE;
283		dirty = APC_PD;
284	} else {
285		enable = APC_CAP_ENABLE;
286		dirty = APC_CD;
287	}
288
289	/* make sure it's okay to program the Next Address/Count registers */
290	csr = ddi_get32(handle, &APC_DMACSR);
291	for (x = 0; !(csr & dirty) && x < CS4231_TIMEOUT; x++) {
292		drv_usecwait(1);	/* no reason to beat on the bus */
293		csr = ddi_get32(handle, &APC_DMACSR);
294	}
295	if (x >= CS4231_TIMEOUT) {
296		audio_dev_warn(state->cs_adev,
297		    "timeout waiting for engine, not started!");
298		return (DDI_FAILURE);
299	}
300
301	/*
302	 * Program the first fragment.
303	 */
304	apc_load_fragment(eng);
305
306	/*
307	 * Start the DMA engine, including interrupts.
308	 */
309	OR_SET_WORD(handle, &APC_DMACSR, enable);
310
311	/*
312	 * Program the second fragment.
313	 */
314	apc_load_fragment(eng);
315
316	return (DDI_SUCCESS);
317}
318
319/*
320 * apc_stop_engine()
321 *
322 * Description:
323 *	This routine stops the engine.
324 *
325 *	The DMA engine is stopped by using the CAP_ABORT bit.
326 *
327 *	NOTE: The state structure must be locked before this routine is called.
328 *
329 * Arguments:
330 *	CS_engine_t	*eng	The engine to sotp
331 *
332 * Returns:
333 *	void
334 */
335static void
336apc_stop_engine(CS_engine_t *eng)
337{
338	CS_state_t		*state = eng->ce_state;
339	ddi_acc_handle_t	handle = APC_HANDLE;
340	uint32_t		reg;
341	uint32_t		intren;
342	uint32_t		abort;
343	uint32_t		drainbit;
344	uint32_t		disable;
345
346	ASSERT(mutex_owned(&state->cs_lock));
347
348	if (eng->ce_num == CS4231_PLAY) {
349		intren = APC_PINTR_ENABLE;
350		abort = APC_P_ABORT;
351		drainbit = APC_PM;
352		disable = APC_PLAY_DISABLE;
353	} else {
354		intren = APC_CINTR_ENABLE;
355		abort = APC_C_ABORT;
356		drainbit = APC_CX;
357		disable = APC_CAP_DISABLE;
358	}
359
360	/* clear the interrupts so the ISR doesn't get involved */
361	AND_SET_WORD(handle, &APC_DMACSR, ~intren);
362
363	/* first, abort the DMA engine */
364	OR_SET_WORD(handle, &APC_DMACSR, abort);
365
366	/* wait for the pipeline to empty */
367	reg = ddi_get32(handle, &APC_DMACSR);
368	for (int x = 0; (!(reg & drainbit)) && (x < CS4231_TIMEOUT); x++) {
369		drv_usecwait(1);	/* don't beat on bus */
370		reg = ddi_get32(handle, &APC_DMACSR);
371	}
372
373	/* now clear the enable and abort bits */
374	AND_SET_WORD(handle, &APC_DMACSR, ~(abort|disable));
375}
376
377
378/*
379 * apc_power()
380 *
381 * Description:
382 *	This routine turns the Codec off by using the COD_PDWN bit in the
383 *	apc chip. To turn power on we have to reset the APC, which clears
384 *	the COD_PDWN bit. However, this is a settling bug in the APC which
385 *	requires the driver to delay quite a while before we may continue.
386 *	Since this is the first time this feature has actually been used
387 *	it isn't too surprising that it has some problems.
388 *
389 *	NOTE: The state structure must be locked when this routine is called.
390 *
391 * Arguments:
392 *	CS_state_t	*state		Ptr to the device's state structure
393 *	int		level		Power level to set
394 *
395 * Returns:
396 *	void
397 */
398static void
399apc_power(CS_state_t *state, int level)
400{
401	ddi_acc_handle_t	handle = APC_HANDLE;
402
403	if (level == CS4231_PWR_ON) {	/* turn power on */
404		AND_SET_WORD(handle, &APC_DMACSR, ~APC_COD_PDWN);
405		OR_SET_WORD(handle, &APC_DMACSR, APC_RESET);
406		AND_SET_WORD(handle, &APC_DMACSR, ~APC_RESET);
407
408		/*
409		 * wait for state change,
410		 */
411		delay(drv_usectohz(CS4231_300MS));
412	} else {	/* turn power off */
413		ASSERT(level == CS4231_PWR_OFF);
414		OR_SET_WORD(handle, &APC_DMACSR, APC_COD_PDWN);
415	}
416
417}	/* apc_power() */
418
419
420/* *******  Local Routines ************************************************** */
421
422/*
423 * apc_intr()
424 *
425 * Description:
426 *	APC interrupt service routine, which services both play and capture
427 *	interrupts. First we find out why there was an interrupt, then we
428 *	take the appropriate action.
429 *
430 *	Because this ISR deals with both play and record interrupts we have
431 *	to be careful to not lose an interrupt. So we service the record
432 *	interrupt first and save the incoming data until later. This is all
433 *	done without releasing the lock, thus there can be no race conditions.
434 *	Then we process the play interrupt. While processing the play interrupt
435 *	we have to release the lock. When this happens we send recorded data
436 *	to the mixer and then get the next chunk of data to play. If there
437 *	wasn't a play interrupt then we finish by sending the recorded data,
438 *	if any.
439 *
440 * Arguments:
441 *	caddr_t		T	Pointer to the interrupting device's state
442 *				structure
443 *
444 * Returns:
445 *	DDI_INTR_CLAIMED	Interrupt claimed and processed
446 *	DDI_INTR_UNCLAIMED	Interrupt not claimed, and thus ignored
447 */
448static uint_t
449apc_intr(caddr_t T)
450{
451	CS_state_t		*state = (void *)T;
452	ddi_acc_handle_t	handle = APC_HANDLE;
453	uint_t			csr;
454	int			rc = DDI_INTR_UNCLAIMED;
455
456	/* the state must be protected */
457	mutex_enter(&state->cs_lock);
458
459	if (state->cs_suspended) {
460		mutex_exit(&state->cs_lock);
461		return (DDI_INTR_UNCLAIMED);
462	}
463
464	/* get the APC CSR */
465	csr = ddi_get32(handle, &APC_DMACSR);
466
467	/* make sure this device sent the interrupt */
468	if (!(csr & APC_IP)) {
469		if (csr & APC_PMI_EN) {
470			/*
471			 * Clear device generated interrupt while play is
472			 * active (Only seen while playing and insane mode
473			 * switching)
474			 */
475			mutex_exit(&state->cs_lock);
476			return (DDI_INTR_CLAIMED);
477		} else {
478			/* nope, this isn't our interrupt */
479			mutex_exit(&state->cs_lock);
480			return (DDI_INTR_UNCLAIMED);
481		}
482	}
483
484	/* clear all interrupts we captured this time */
485	ddi_put32(handle, &APC_DMACSR, csr);
486
487	if (csr & APC_CINTR_MASK) {
488		/* try to load the next record buffer */
489		apc_load_fragment(state->cs_engines[CS4231_REC]);
490		rc = DDI_INTR_CLAIMED;
491	}
492
493	if (csr & APC_PINTR_MASK) {
494		/* try to load the next play buffer */
495		apc_load_fragment(state->cs_engines[CS4231_PLAY]);
496		rc = DDI_INTR_CLAIMED;
497	}
498
499done:
500
501	/* APC error interrupt, not sure what to do here */
502	if (csr & APC_EI) {
503		audio_dev_warn(state->cs_adev, "error interrupt: 0x%x", csr);
504		rc = DDI_INTR_CLAIMED;
505	}
506
507	/* update the kernel interrupt statistics */
508	if (state->cs_ksp) {
509		if (rc == DDI_INTR_CLAIMED) {
510			KIOP(state)->intrs[KSTAT_INTR_HARD]++;
511		}
512	}
513
514	mutex_exit(&state->cs_lock);
515
516	if (csr & APC_CINTR_MASK) {
517		CS_engine_t	*eng = state->cs_engines[CS4231_REC];
518		if (eng->ce_started)
519			audio_engine_produce(eng->ce_engine);
520	}
521	if (csr & APC_PINTR_MASK) {
522		CS_engine_t	*eng = state->cs_engines[CS4231_PLAY];
523		if (eng->ce_started)
524			audio_engine_consume(eng->ce_engine);
525	}
526
527	return (rc);
528
529}	/* apc_intr() */
530
531static void
532apc_load_fragment(CS_engine_t *eng)
533{
534	CS_state_t		*state = eng->ce_state;
535	ddi_acc_handle_t	handle = APC_HANDLE;
536	uint32_t		dirty;
537	uint32_t		*nva;	/* next VA reg */
538	uint32_t		*nc;	/* next count reg */
539
540	if (eng->ce_num == CS4231_PLAY) {
541		dirty = APC_PD;
542		nva = &APC_DMAPNVA;
543		nc = &APC_DMAPNC;
544	} else {
545		dirty = APC_CD;
546		nva = &APC_DMACNVA;
547		nc = &APC_DMACNC;
548	}
549
550	/* if we can't load another address, then don't */
551	if ((ddi_get32(handle, &APC_DMACSR) & dirty) == 0) {
552		return;
553	}
554
555	/* read the NVA, as per APC document */
556	(void) ddi_get32(handle, nva);
557
558	/* write the address of the next fragment */
559	ddi_put32(handle, nva, eng->ce_paddr[eng->ce_cfrag]);
560
561	/* now program the NC reg., which enables the state machine */
562	ddi_put32(handle, nc, eng->ce_fragsz);
563
564	eng->ce_cfrag++;
565	eng->ce_cfrag %= CS4231_NFRAGS;
566	eng->ce_count += eng->ce_fragfr;
567}
568