xref: /illumos-gate/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231_apcdma.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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  */
42 static 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 
57 static 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  */
66 static uint_t apc_intr(caddr_t);
67 static void apc_load_fragment(CS_engine_t *);
68 
69 /*
70  * DMA ops vector functions
71  */
72 static int apc_map_regs(CS_state_t *);
73 static void apc_unmap_regs(CS_state_t *);
74 static void apc_reset(CS_state_t *);
75 static int apc_add_intr(CS_state_t *);
76 static void apc_rem_intr(CS_state_t *);
77 static int apc_start_engine(CS_engine_t *);
78 static void apc_stop_engine(CS_engine_t *);
79 static void apc_power(CS_state_t *, int);
80 
81 cs4231_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  */
113 static int
114 apc_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  */
145 static void
146 apc_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  */
167 static void
168 apc_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  */
202 static int
203 apc_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  */
241 static void
242 apc_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  */
269 static int
270 apc_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  */
335 static void
336 apc_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  */
398 static void
399 apc_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  */
448 static uint_t
449 apc_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 
499 done:
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 
531 static void
532 apc_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