1 /* $NetBSD: pxa2x0_i2s.c,v 1.7 2007/10/17 19:53:44 garbled Exp $ */
2 /* $OpenBSD: pxa2x0_i2s.c,v 1.7 2006/04/04 11:45:40 pascoe Exp $ */
5 * Copyright (c) 2005 Christopher Pascoe <pascoe@openbsd.org>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/cdefs.h>
21 __KERNEL_RCSID(0, "$NetBSD: pxa2x0_i2s.c,v 1.7 2007/10/17 19:53:44 garbled Exp $");
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/malloc.h>
28 #include <machine/bus.h>
30 #include <arm/xscale/pxa2x0reg.h>
31 #include <arm/xscale/pxa2x0var.h>
32 #include <arm/xscale/pxa2x0_gpio.h>
33 #include <arm/xscale/pxa2x0_i2s.h>
34 #include <arm/xscale/pxa2x0_dmac.h>
36 struct pxa2x0_i2s_dma
{
37 struct pxa2x0_i2s_dma
*next
;
42 bus_dma_segment_t segs
[I2S_N_SEGS
];
47 static void pxa2x0_i2s_dmac_ointr(struct dmac_xfer
*, int);
48 static void pxa2x0_i2s_dmac_iintr(struct dmac_xfer
*, int);
51 pxa2x0_i2s_init(struct pxa2x0_i2s_softc
*sc
)
54 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SACR0
, SACR0_RST
);
56 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SACR0
,
57 SACR0_BCKD
| SACR0_SET_TFTH(7) | SACR0_SET_RFTH(7));
58 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SACR1
, 0);
59 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SADR
, 0);
60 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SADIV
, sc
->sc_sadiv
);
61 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SACR0
,
62 SACR0_BCKD
| SACR0_SET_TFTH(7) | SACR0_SET_RFTH(7) | SACR0_ENB
);
66 pxa2x0_i2s_attach_sub(struct pxa2x0_i2s_softc
*sc
)
70 rv
= bus_space_map(sc
->sc_iot
, PXA2X0_I2S_BASE
, PXA2X0_I2S_SIZE
, 0,
77 sc
->sc_dr
.ds_addr
= PXA2X0_I2S_BASE
+ I2S_SADR
;
80 sc
->sc_sadiv
= SADIV_3_058MHz
;
82 bus_space_barrier(sc
->sc_iot
, sc
->sc_ioh
, 0, sc
->sc_size
,
83 BUS_SPACE_BARRIER_READ
|BUS_SPACE_BARRIER_WRITE
);
91 pxa2x0_i2s_open(struct pxa2x0_i2s_softc
*sc
)
94 if (sc
->sc_open
++ == 0) {
95 pxa2x0_clkman_config(CKEN_I2S
, 1);
100 pxa2x0_i2s_close(struct pxa2x0_i2s_softc
*sc
)
103 if (--sc
->sc_open
== 0) {
104 pxa2x0_clkman_config(CKEN_I2S
, 0);
109 pxa2x0_i2s_detach_sub(struct pxa2x0_i2s_softc
*sc
)
112 if (sc
->sc_size
> 0) {
113 bus_space_unmap(sc
->sc_iot
, sc
->sc_ioh
, sc
->sc_size
);
116 pxa2x0_clkman_config(CKEN_I2S
, 0);
122 pxa2x0_i2s_write(struct pxa2x0_i2s_softc
*sc
, uint32_t data
)
125 if (sc
->sc_open
== 0)
128 /* Clear intr and underrun bit if set. */
129 if (bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SASR0
) & SASR0_TUR
)
130 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SAICR
, SAICR_TUR
);
132 /* Wait for transmit fifo to have space. */
133 while ((bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SASR0
) & SASR0_TNF
)
135 continue; /* nothing */
138 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SADR
, data
);
142 pxa2x0_i2s_setspeed(struct pxa2x0_i2s_softc
*sc
, u_int
*argp
)
145 * The available speeds are in the following table.
146 * Keep the speeds in increasing order.
148 static const struct speed_struct
{
152 {8000, SADIV_513_25kHz
},
153 {11025, SADIV_702_75kHz
},
154 {16000, SADIV_1_026MHz
},
155 {22050, SADIV_1_405MHz
},
156 {44100, SADIV_2_836MHz
},
157 {48000, SADIV_3_058MHz
},
159 const int n
= (int)__arraycount(speed_table
);
160 u_int arg
= (u_int
)*argp
;
164 if (arg
< speed_table
[0].speed
)
166 if (arg
> speed_table
[n
- 1].speed
)
169 for (i
= 1; selected
== -1 && i
< n
; i
++) {
170 if (speed_table
[i
].speed
== arg
)
172 else if (speed_table
[i
].speed
> arg
) {
175 diff1
= arg
- speed_table
[i
- 1].speed
;
176 diff2
= speed_table
[i
].speed
- arg
;
187 *argp
= speed_table
[selected
].speed
;
189 sc
->sc_sadiv
= speed_table
[selected
].div
;
190 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, I2S_SADIV
, sc
->sc_sadiv
);
194 pxa2x0_i2s_allocm(void *hdl
, int direction
, size_t size
,
195 struct malloc_type
*type
, int flags
)
197 struct pxa2x0_i2s_softc
*sc
= hdl
;
198 struct pxa2x0_i2s_dma
*p
;
199 struct dmac_xfer
*dx
;
202 p
= malloc(sizeof(*p
), type
, flags
);
206 dx
= pxa2x0_dmac_allocate_xfer(M_NOWAIT
);
213 if ((error
= bus_dmamem_alloc(sc
->sc_dmat
, size
, NBPG
, 0, p
->segs
,
214 I2S_N_SEGS
, &p
->nsegs
, BUS_DMA_NOWAIT
)) != 0) {
218 if ((error
= bus_dmamem_map(sc
->sc_dmat
, p
->segs
, p
->nsegs
, size
,
219 &p
->addr
, BUS_DMA_NOWAIT
| BUS_DMA_COHERENT
)) != 0) {
223 if ((error
= bus_dmamap_create(sc
->sc_dmat
, size
, 1, size
, 0,
224 BUS_DMA_NOWAIT
, &p
->map
)) != 0) {
228 if ((error
= bus_dmamap_load(sc
->sc_dmat
, p
->map
, p
->addr
, size
, NULL
,
229 BUS_DMA_NOWAIT
)) != 0) {
234 dx
->dx_priority
= DMAC_PRIORITY_NORMAL
;
235 dx
->dx_dev_width
= DMAC_DEV_WIDTH_4
;
236 dx
->dx_burst_size
= DMAC_BURST_SIZE_32
;
238 p
->next
= sc
->sc_dmas
;
244 bus_dmamap_destroy(sc
->sc_dmat
, p
->map
);
246 bus_dmamem_unmap(sc
->sc_dmat
, p
->addr
, size
);
248 bus_dmamem_free(sc
->sc_dmat
, p
->segs
, p
->nsegs
);
250 pxa2x0_dmac_free_xfer(dx
);
257 pxa2x0_i2s_freem(void *hdl
, void *ptr
, struct malloc_type
*type
)
259 struct pxa2x0_i2s_softc
*sc
= hdl
;
260 struct pxa2x0_i2s_dma
**pp
, *p
;
262 for (pp
= &sc
->sc_dmas
; (p
= *pp
) != NULL
; pp
= &p
->next
) {
263 if (p
->addr
== ptr
) {
264 pxa2x0_dmac_abort_xfer(p
->dx
);
265 pxa2x0_dmac_free_xfer(p
->dx
);
266 p
->segs
[0].ds_len
= p
->size
; /* XXX */
267 bus_dmamap_unload(sc
->sc_dmat
, p
->map
);
268 bus_dmamap_destroy(sc
->sc_dmat
, p
->map
);
269 bus_dmamem_unmap(sc
->sc_dmat
, p
->addr
, p
->size
);
270 bus_dmamem_free(sc
->sc_dmat
, p
->segs
, p
->nsegs
);
277 panic("pxa2x0_i2s_freem: trying to free unallocated memory");
281 pxa2x0_i2s_mappage(void *hdl
, void *mem
, off_t off
, int prot
)
283 struct pxa2x0_i2s_softc
*sc
= hdl
;
284 struct pxa2x0_i2s_dma
*p
;
289 for (p
= sc
->sc_dmas
; p
&& p
->addr
!= mem
; p
= p
->next
)
297 return bus_dmamem_mmap(sc
->sc_dmat
, p
->segs
, p
->nsegs
, off
, prot
,
302 pxa2x0_i2s_round_blocksize(void *hdl
, int bs
, int mode
,
303 const struct audio_params
*param
)
306 /* Enforce individual DMA block size limit */
307 if (bs
> DCMD_LENGTH_MASK
)
308 return (DCMD_LENGTH_MASK
& ~0x07);
310 return (bs
+ 0x07) & ~0x07; /* XXX: 64-bit multiples */
314 pxa2x0_i2s_round_buffersize(void *hdl
, int direction
, size_t bufsize
)
321 pxa2x0_i2s_halt_output(void *hdl
)
323 struct pxa2x0_i2s_softc
*sc
= hdl
;
328 pxa2x0_dmac_abort_xfer(sc
->sc_txdma
->dx
);
337 pxa2x0_i2s_halt_input(void *hdl
)
339 struct pxa2x0_i2s_softc
*sc
= hdl
;
344 pxa2x0_dmac_abort_xfer(sc
->sc_rxdma
->dx
);
353 pxa2x0_i2s_start_output(void *hdl
, void *block
, int bsize
,
354 void (*tx_func
)(void *), void *tx_arg
)
356 struct pxa2x0_i2s_softc
*sc
= hdl
;
357 struct pxa2x0_i2s_dma
*p
;
358 struct dmac_xfer
*dx
;
363 /* Find mapping which contains block completely */
364 for (p
= sc
->sc_dmas
;
366 (((char*)block
< (char *)p
->addr
) ||
367 ((char *)block
+ bsize
> (char *)p
->addr
+ p
->size
));
369 continue; /* Nothing */
372 aprint_error("pxa2x0_i2s_start_output: "
373 "request with bad start address: %p, size: %d)\n",
379 p
->segs
[0].ds_addr
= p
->map
->dm_segs
[0].ds_addr
+
380 ((char *)block
- (char *)p
->addr
);
381 p
->segs
[0].ds_len
= bsize
;
384 dx
->dx_done
= pxa2x0_i2s_dmac_ointr
;
385 dx
->dx_peripheral
= DMAC_PERIPH_I2STX
;
386 dx
->dx_flow
= DMAC_FLOW_CTRL_DEST
;
387 dx
->dx_loop_notify
= DMAC_DONT_LOOP
;
388 dx
->dx_desc
[DMAC_DESC_SRC
].xd_addr_hold
= false;
389 dx
->dx_desc
[DMAC_DESC_SRC
].xd_nsegs
= p
->nsegs
;
390 dx
->dx_desc
[DMAC_DESC_SRC
].xd_dma_segs
= p
->segs
;
391 dx
->dx_desc
[DMAC_DESC_DST
].xd_addr_hold
= true;
392 dx
->dx_desc
[DMAC_DESC_DST
].xd_nsegs
= 1;
393 dx
->dx_desc
[DMAC_DESC_DST
].xd_dma_segs
= &sc
->sc_dr
;
395 sc
->sc_txfunc
= tx_func
;
396 sc
->sc_txarg
= tx_arg
;
399 return pxa2x0_dmac_start_xfer(dx
);
403 pxa2x0_i2s_start_input(void *hdl
, void *block
, int bsize
,
404 void (*rx_func
)(void *), void *rx_arg
)
406 struct pxa2x0_i2s_softc
*sc
= hdl
;
407 struct pxa2x0_i2s_dma
*p
;
408 struct dmac_xfer
*dx
;
413 /* Find mapping which contains block completely */
414 for (p
= sc
->sc_dmas
;
416 (((char*)block
< (char *)p
->addr
) ||
417 ((char *)block
+ bsize
> (char *)p
->addr
+ p
->size
));
419 continue; /* Nothing */
422 aprint_error("pxa2x0_i2s_start_input: "
423 "request with bad start address: %p, size: %d)\n",
429 p
->segs
[0].ds_addr
= p
->map
->dm_segs
[0].ds_addr
+
430 ((char *)block
- (char *)p
->addr
);
431 p
->segs
[0].ds_len
= bsize
;
434 dx
->dx_done
= pxa2x0_i2s_dmac_iintr
;
435 dx
->dx_peripheral
= DMAC_PERIPH_I2SRX
;
436 dx
->dx_flow
= DMAC_FLOW_CTRL_SRC
;
437 dx
->dx_loop_notify
= DMAC_DONT_LOOP
;
438 dx
->dx_desc
[DMAC_DESC_SRC
].xd_addr_hold
= true;
439 dx
->dx_desc
[DMAC_DESC_SRC
].xd_nsegs
= 1;
440 dx
->dx_desc
[DMAC_DESC_SRC
].xd_dma_segs
= &sc
->sc_dr
;
441 dx
->dx_desc
[DMAC_DESC_DST
].xd_addr_hold
= false;
442 dx
->dx_desc
[DMAC_DESC_DST
].xd_nsegs
= p
->nsegs
;
443 dx
->dx_desc
[DMAC_DESC_DST
].xd_dma_segs
= p
->segs
;
445 sc
->sc_rxfunc
= rx_func
;
446 sc
->sc_rxarg
= rx_arg
;
449 return pxa2x0_dmac_start_xfer(dx
);
453 pxa2x0_i2s_dmac_ointr(struct dmac_xfer
*dx
, int status
)
455 struct pxa2x0_i2s_softc
*sc
= dx
->dx_cookie
;
458 if (sc
->sc_txdma
== NULL
) {
459 panic("pxa2x_i2s_dmac_ointr: bad TX DMA descriptor!");
461 if (sc
->sc_txdma
->dx
!= dx
) {
462 panic("pxa2x_i2s_dmac_ointr: xfer mismatch!");
467 aprint_error("pxa2x0_i2s_dmac_ointr: "
468 "non-zero completion status %d\n", status
);
472 (sc
->sc_txfunc
)(sc
->sc_txarg
);
477 pxa2x0_i2s_dmac_iintr(struct dmac_xfer
*dx
, int status
)
479 struct pxa2x0_i2s_softc
*sc
= dx
->dx_cookie
;
482 if (sc
->sc_rxdma
== NULL
) {
483 panic("pxa2x_i2s_dmac_iintr: bad RX DMA descriptor!");
485 if (sc
->sc_rxdma
->dx
!= dx
) {
486 panic("pxa2x_i2s_dmac_iintr: xfer mismatch!");
491 aprint_error("pxa2x0_i2s_dmac_iintr: "
492 "non-zero completion status %d\n", status
);
497 (sc
->sc_rxfunc
)(sc
->sc_rxarg
);