summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2005-04-15 00:09:07 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2005-04-15 00:09:07 +0000
commit79d7bee3b1867132d15ad766bb1b4fa83002ef1b (patch)
tree15e1801c812c681e8c50cb807fe2bb85cb3d8e55
parent5d250d2e82db14de223feb361148e5a3d4f9bfd9 (diff)
Functions for accessing the pxa2x0 I2S controller.
ok dlg@ drahn@ uwe@ deraadt@
-rw-r--r--sys/arch/arm/xscale/pxa2x0_i2s.c328
-rw-r--r--sys/arch/arm/xscale/pxa2x0_i2s.h55
-rw-r--r--sys/arch/arm/xscale/pxa2x0reg.h6
3 files changed, 386 insertions, 3 deletions
diff --git a/sys/arch/arm/xscale/pxa2x0_i2s.c b/sys/arch/arm/xscale/pxa2x0_i2s.c
new file mode 100644
index 00000000000..ef4e5bbd4af
--- /dev/null
+++ b/sys/arch/arm/xscale/pxa2x0_i2s.c
@@ -0,0 +1,328 @@
+/* $OpenBSD: pxa2x0_i2s.c,v 1.1 2005/04/15 00:09:06 pascoe Exp $ */
+
+/*
+ * Copyright (c) 2005 Christopher Pascoe <pascoe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+
+#include <arm/xscale/pxa2x0reg.h>
+#include <arm/xscale/pxa2x0var.h>
+#include <arm/xscale/pxa2x0_gpio.h>
+#include <arm/xscale/pxa2x0_i2s.h>
+#include <arm/xscale/pxa2x0_dmac.h>
+
+struct pxa2x0_i2s_dma {
+ struct pxa2x0_i2s_dma *next;
+ caddr_t addr;
+ size_t size;
+ bus_dmamap_t map;
+ bus_dma_segment_t seg;
+};
+
+static void pxa2x0_i2s_init(struct pxa2x0_i2s_softc *sc);
+
+static void
+pxa2x0_i2s_init(struct pxa2x0_i2s_softc *sc)
+{
+ pxa2x0_clkman_config(CKEN_I2S, 1);
+
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR0, SACR0_RST);
+ delay(100);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR0,
+ SACR0_BCKD | SACR0_SET_TFTH(7) | SACR0_SET_RFTH(7));
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR1, 0);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADR, 0);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADIV, sc->sc_sadiv);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR0,
+ SACR0_BCKD | SACR0_SET_TFTH(7) | SACR0_SET_RFTH(7) | SACR0_ENB);
+
+ pxa2x0_clkman_config(CKEN_I2S, 0);
+}
+
+int
+pxa2x0_i2s_attach_sub(struct pxa2x0_i2s_softc *sc)
+{
+ if (bus_space_map(sc->sc_iot, PXA2X0_I2S_BASE, PXA2X0_I2S_SIZE, 0,
+ &sc->sc_ioh)) {
+ sc->sc_size = 0;
+ return 1;
+ }
+ sc->sc_sadiv = SADIV_3_058MHz;
+
+ bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, sc->sc_size,
+ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
+
+ pxa2x0_gpio_set_function(35, GPIO_ALT_FN_2_IN); /* I2S_SYSCLK */
+ pxa2x0_gpio_set_function(37, GPIO_ALT_FN_1_OUT); /* I2S_BITCLK */
+ pxa2x0_gpio_set_function(41, GPIO_ALT_FN_2_IN); /* I2S_SYNC */
+ pxa2x0_gpio_set_function(89, GPIO_ALT_FN_2_OUT); /* I2S_SDATAOUT */
+ pxa2x0_gpio_set_function(120, GPIO_ALT_FN_2_OUT); /* I2S_SDATAIN */
+
+ pxa2x0_i2s_init(sc);
+
+ return 0;
+}
+
+void pxa2x0_i2s_open(struct pxa2x0_i2s_softc *sc)
+{
+ sc->sc_open++;
+ pxa2x0_clkman_config(CKEN_I2S, 1);
+}
+
+void pxa2x0_i2s_close(struct pxa2x0_i2s_softc *sc)
+{
+ pxa2x0_clkman_config(CKEN_I2S, 0);
+ sc->sc_open--;
+}
+
+int
+pxa2x0_i2s_detach_sub(struct pxa2x0_i2s_softc *sc)
+{
+ if (sc->sc_size > 0) {
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
+ sc->sc_size = 0;
+ }
+ pxa2x0_clkman_config(CKEN_I2S, 0);
+
+ return (0);
+}
+
+void pxa2x0_i2s_write(struct pxa2x0_i2s_softc *sc, u_int32_t data)
+{
+ if (! sc->sc_open)
+ return;
+
+ /* Clear intr and underrun bit if set. */
+ if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, I2S_SASR0) & SASR0_TUR)
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SAICR, SAICR_TUR);
+
+ /* Wait for transmit fifo to have space. */
+ while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, I2S_SASR0) & SASR0_TNF)
+ == 0)
+ ; /* nothing */
+
+ /* Queue data */
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADR, data);
+}
+
+void
+pxa2x0_i2s_setspeed(struct pxa2x0_i2s_softc *sc, u_long *argp)
+{
+ /*
+ * The available speeds are in the following table.
+ * Keep the speeds in increasing order.
+ */
+ typedef struct {
+ int speed;
+ int div;
+ } speed_struct;
+ u_long arg = *argp;
+
+ static speed_struct speed_table[] = {
+ {8000, SADIV_513_25kHz},
+ {11025, SADIV_702_75kHz},
+ {16000, SADIV_1_026MHz},
+ {22050, SADIV_1_405MHz},
+ {44100, SADIV_2_836MHz},
+ {48000, SADIV_3_058MHz},
+ };
+
+ int i, n, selected = -1;
+
+ n = sizeof(speed_table) / sizeof(speed_struct);
+
+ if (arg < speed_table[0].speed)
+ selected = 0;
+ if (arg > speed_table[n - 1].speed)
+ selected = n - 1;
+
+ for (i = 1; selected == -1 && i < n; i++) {
+ if (speed_table[i].speed == arg)
+ selected = i;
+ else if (speed_table[i].speed > arg) {
+ int diff1, diff2;
+
+ diff1 = arg - speed_table[i - 1].speed;
+ diff2 = speed_table[i].speed - arg;
+ if (diff1 < diff2)
+ selected = i - 1;
+ else
+ selected = i;
+ }
+ }
+
+ if (selected == -1)
+ selected = 0;
+
+ *argp = speed_table[selected].speed;
+
+ sc->sc_sadiv = speed_table[selected].div;
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADIV, sc->sc_sadiv);
+}
+
+void *
+pxa2x0_i2s_allocm(void *hdl, int direction, size_t size, int type, int flags)
+{
+ struct device *sc_dev = hdl;
+ struct pxa2x0_i2s_softc *sc =
+ (struct pxa2x0_i2s_softc *)((struct device *)hdl + 1);
+ struct pxa2x0_i2s_dma *p;
+ int error;
+ int rseg;
+
+ p = malloc(sizeof(*p), type, flags);
+ if (!p)
+ return 0;
+
+ p->size = size;
+ if ((error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, &p->seg, 1,
+ &rseg, BUS_DMA_NOWAIT)) != 0) {
+ printf("%s: unable to allocate dma, error = %d\n",
+ sc_dev->dv_xname, error);
+ goto fail_alloc;
+ }
+
+ if ((error = bus_dmamem_map(sc->sc_dmat, &p->seg, rseg, size, &p->addr,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
+ printf("%s: unable to map dma, error = %d\n",
+ sc_dev->dv_xname, error);
+ goto fail_map;
+ }
+
+ if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
+ BUS_DMA_NOWAIT, &p->map)) != 0) {
+ printf("%s: unable to create dma map, error = %d\n",
+ sc_dev->dv_xname, error);
+ goto fail_create;
+ }
+
+ if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, size, NULL,
+ BUS_DMA_NOWAIT)) != 0) {
+ printf("%s: unable to load dma map, error = %d\n",
+ sc_dev->dv_xname, error);
+ goto fail_load;
+ }
+
+ p->next = sc->sc_dmas;
+ sc->sc_dmas = p;
+
+ return p->addr;
+
+fail_load:
+ bus_dmamap_destroy(sc->sc_dmat, p->map);
+fail_create:
+ bus_dmamem_unmap(sc->sc_dmat, p->addr, size);
+fail_map:
+ bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
+fail_alloc:
+ free(p, type);
+ return 0;
+}
+
+void
+pxa2x0_i2s_freem(void *hdl, void *ptr, int type)
+{
+ struct pxa2x0_i2s_softc *sc =
+ (struct pxa2x0_i2s_softc *)((struct device *)hdl + 1);
+ struct pxa2x0_i2s_dma **pp, *p;
+
+ for (pp = &(sc->sc_dmas); (p = *pp) != NULL; pp = &p->next)
+ if (p->addr == ptr) {
+ bus_dmamap_unload(sc->sc_dmat, p->map);
+ bus_dmamap_destroy(sc->sc_dmat, p->map);
+ bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
+ bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
+
+ *pp = p->next;
+ free(p, type);
+ return;
+ }
+
+ panic("pxa2x0_i2s_freem: trying to free unallocated memory");
+}
+
+paddr_t
+pxa2x0_i2s_mappage(void *hdl, void *mem, off_t off, int prot)
+{
+ struct pxa2x0_i2s_softc *sc =
+ (struct pxa2x0_i2s_softc *)((struct device *)hdl + 1);
+ struct pxa2x0_i2s_dma *p;
+
+ if (off < 0)
+ return -1;
+
+ for (p = sc->sc_dmas; p && p->addr != mem; p = p->next)
+ ;
+ if (!p)
+ return -1;
+
+ if (off > p->size)
+ return -1;
+
+ return bus_dmamem_mmap(sc->sc_dmat, &p->seg, 1, off, prot,
+ BUS_DMA_WAITOK);
+}
+
+int
+pxa2x0_i2s_round_blocksize(void *hdl, int bs)
+{
+ return (bs & ~0x03); /* 32-bit multiples */
+}
+
+size_t
+pxa2x0_i2s_round_buffersize(void *hdl, int direction, size_t bufsize)
+{
+ /*
+ * PXA DMA Controller can do up to (8kB-1) DMA blocks.
+ * The block gets split into two when using start_output, so
+ * use 2 * the maximum DMA controller length.
+ */
+ if (bufsize > (2 * DCMD_LENGTH_MASK))
+ bufsize = (2 * DCMD_LENGTH_MASK);
+
+ return pxa2x0_i2s_round_blocksize(hdl, bufsize);
+}
+
+int
+pxa2x0_i2s_start_output(struct pxa2x0_i2s_softc *sc, void *block, int bsize,
+ void (*intr)(void *), void *intrarg)
+{
+ struct pxa2x0_i2s_dma *p;
+ int offset;
+
+ /* Find mapping which contains block completely */
+ for (p = sc->sc_dmas; p && (((caddr_t)block < p->addr) ||
+ ((caddr_t)block + bsize > p->addr + p->size)); p = p->next)
+ ; /* Nothing */
+
+ /* Offset into block to use in mapped block */
+ offset = (caddr_t)block - p->addr;
+
+ if (!p) {
+ printf("pxa2x0_i2s_start_output: request with bad start "
+ "address: %p, size: %d)", block, bsize);
+ return ENXIO;
+ }
+
+ /* Start DMA */
+ pxa2x0_dma_to_fifo(3, 1, 0x40400080, 4, 32,
+ p->map->dm_segs[0].ds_addr + offset, bsize, intr, intrarg);
+
+ return 0;
+}
diff --git a/sys/arch/arm/xscale/pxa2x0_i2s.h b/sys/arch/arm/xscale/pxa2x0_i2s.h
new file mode 100644
index 00000000000..befd611429a
--- /dev/null
+++ b/sys/arch/arm/xscale/pxa2x0_i2s.h
@@ -0,0 +1,55 @@
+/* $OpenBSD: pxa2x0_i2s.h,v 1.1 2005/04/15 00:09:06 pascoe Exp $ */
+
+/*
+ * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _PXA2X0_I2S_H_
+#define _PXA2X0_I2S_H_
+
+#include <machine/bus.h>
+
+struct pxa2x0_i2s_dma;
+
+struct pxa2x0_i2s_softc {
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ bus_size_t sc_size;
+ bus_dma_tag_t sc_dmat;
+
+ int sc_open;
+ u_int32_t sc_sadiv;
+
+ struct pxa2x0_i2s_dma *sc_dmas;
+};
+
+int pxa2x0_i2s_attach_sub(struct pxa2x0_i2s_softc *);
+int pxa2x0_i2s_detach_sub(struct pxa2x0_i2s_softc *);
+void pxa2x0_i2s_open(struct pxa2x0_i2s_softc *);
+void pxa2x0_i2s_close(struct pxa2x0_i2s_softc *);
+void pxa2x0_i2s_write(struct pxa2x0_i2s_softc *, u_int32_t);
+
+void pxa2x0_i2s_setspeed(struct pxa2x0_i2s_softc *, u_long *);
+
+void * pxa2x0_i2s_allocm(void *, int, size_t, int, int);
+void pxa2x0_i2s_freem(void *, void *, int);
+paddr_t pxa2x0_i2s_mappage(void *, void *, off_t, int);
+int pxa2x0_i2s_round_blocksize(void *, int);
+size_t pxa2x0_i2s_round_buffersize(void *, int, size_t);
+
+int pxa2x0_i2s_start_output(struct pxa2x0_i2s_softc *, void *, int,
+ void (*)(void *), void *);
+
+#endif
diff --git a/sys/arch/arm/xscale/pxa2x0reg.h b/sys/arch/arm/xscale/pxa2x0reg.h
index efe26994302..5f30542db99 100644
--- a/sys/arch/arm/xscale/pxa2x0reg.h
+++ b/sys/arch/arm/xscale/pxa2x0reg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pxa2x0reg.h,v 1.21 2005/04/15 00:06:21 pascoe Exp $ */
+/* $OpenBSD: pxa2x0reg.h,v 1.22 2005/04/15 00:09:06 pascoe Exp $ */
/* $NetBSD: pxa2x0reg.h,v 1.4 2003/06/11 20:43:01 scw Exp $ */
/*
@@ -676,8 +676,8 @@ struct pxa2x0_dma_desc {
#define SADIV_2_836MHz 0x0d /* 2.836 MHz */
#define SADIV_1_405MHz 0x1a /* 1.405 MHz */
#define SADIV_1_026MHz 0x24 /* 1.026 MHz */
-#define SADIV_702_75KHz 0x34 /* 702.75 kHz */
-#define SADIV_513_25KHz 0x48 /* 513.25 kHz */
+#define SADIV_702_75kHz 0x34 /* 702.75 kHz */
+#define SADIV_513_25kHz 0x48 /* 513.25 kHz */
#define I2S_SADR 0x0080 /* Serial Audio Data Register */
#define SADR_DTL (0xffff<<0) /* Left Data Sample */
#define SADR_DTH (0xffff<<16) /* Right Data Sample */