1 /* $NetBSD: vr4181aiu.c,v 1.5 2008/04/28 20:23:22 martin Exp $ */
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Naoto Shimazaki of YOKOGAWA Electric Corporation.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: vr4181aiu.c,v 1.5 2008/04/28 20:23:22 martin Exp $");
35 #include <sys/param.h>
37 #include <sys/device.h>
38 #include <sys/errno.h>
39 #include <sys/malloc.h>
41 #include <sys/systm.h>
43 #include <mips/cpuregs.h>
45 #include <machine/bus.h>
47 #include <hpcmips/vr/vripif.h>
48 #include <hpcmips/vr/vr4181aiureg.h>
49 #include <hpcmips/vr/vr4181dcureg.h>
51 #define INBUFLEN 1024 /* length in u_int16_t */
53 #define SAMPLEFREQ 1000
54 #define PICKUPFREQ 100
55 #define PICKUPCOUNT (SAMPLEFREQ / PICKUPFREQ)
58 #define ST_OVERRUN 0x02
60 #define INBUF_MASK 0x3ff /* 2Kbyte */
61 #define INBUF_RAW_SIZE (INBUFLEN * 4 + (INBUF_MASK + 1))
63 #ifdef VR4181AIU_DEBUG
64 int vr4181aiu_debug
= 0;
65 #define DPRINTF(x) if (vr4181aiu_debug) printf x
71 struct vr4181aiu_softc
{
73 bus_space_tag_t sc_iot
;
74 bus_space_handle_t sc_dcu1_ioh
;
75 bus_space_handle_t sc_dcu2_ioh
;
76 bus_space_handle_t sc_aiu_ioh
;
77 u_int16_t
*sc_inbuf_head
;
78 u_int16_t
*sc_inbuf_tail
;
79 u_int16_t
*sc_inbuf_which
;
82 u_int16_t
*sc_inbuf_raw
;
86 static int vr4181aiu_match(struct device
*, struct cfdata
*, void *);
87 static void vr4181aiu_attach(struct device
*, struct device
*, void *);
88 static int vr4181aiu_intr(void *);
90 extern struct cfdriver vr4181aiu_cd
;
92 CFATTACH_DECL(vr4181aiu
, sizeof(struct vr4181aiu_softc
),
93 vr4181aiu_match
, vr4181aiu_attach
, NULL
, NULL
);
95 dev_type_open(vr4181aiuopen
);
96 dev_type_close(vr4181aiuclose
);
97 dev_type_read(vr4181aiuread
);
98 dev_type_write(vr4181aiuwrite
);
100 const struct cdevsw vr4181aiu_cdevsw
= {
101 vr4181aiuopen
, vr4181aiuclose
, vr4181aiuread
, vr4181aiuwrite
, noioctl
,
102 nostop
, notty
, nopoll
, nommap
, nokqfilter
,
106 vr4181aiu_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
112 vr4181aiu_init_inbuf(struct vr4181aiu_softc
*sc
)
117 * this is just a quick and dirty hack to locate the buffer
118 * in KSEG0 space. the only reason is that i want the physical
119 * address of the buffer.
121 * bus_dma framework should be used.
123 static char inbufbase
[INBUF_RAW_SIZE
];
125 sc
->sc_inbuf_raw
= (u_int16_t
*) inbufbase
;
127 sc
->sc_inbuf1
= (u_int16_t
*) ((((u_int32_t
) sc
->sc_inbuf_raw
)
130 sc
->sc_inbuf2
= sc
->sc_inbuf1
+ INBUFLEN
;
134 vr4181aiu_disable(struct vr4181aiu_softc
*sc
)
137 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu2_ioh
,
138 DCU_DMAITRQ_REG_W
, DCU_MICEOP
);
139 bus_space_write_2(sc
->sc_iot
, sc
->sc_aiu_ioh
,
143 | VR4181AIU_SIDLEINTR
);
145 /* disable microphone */
146 bus_space_write_2(sc
->sc_iot
, sc
->sc_aiu_ioh
,
147 VR4181AIU_SEQ_REG_W
, 0);
150 bus_space_write_2(sc
->sc_iot
, sc
->sc_aiu_ioh
,
151 VR4181AIU_MCNT_REG_W
, 0);
154 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu1_ioh
,
155 DCU_AIUDMAMSK_REG_W
, 0);
156 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu2_ioh
,
157 DCU_DMAITMK_REG_W
, 0);
163 vr4181aiu_attach(struct device
*parent
, struct device
*self
, void *aux
)
165 struct vrip_attach_args
*va
= aux
;
166 struct vr4181aiu_softc
*sc
= (void *) self
;
168 vr4181aiu_init_inbuf(sc
);
169 memset(sc
->sc_inbuf1
, 0x55, INBUFLEN
* 2);
170 memset(sc
->sc_inbuf2
, 0xaa, INBUFLEN
* 2);
173 sc
->sc_iot
= va
->va_iot
;
175 if (bus_space_map(sc
->sc_iot
,
176 VR4181AIU_DCU1_BASE
, VR4181AIU_DCU1_SIZE
,
177 0, &sc
->sc_dcu1_ioh
))
179 if (bus_space_map(sc
->sc_iot
,
180 VR4181AIU_DCU2_BASE
, VR4181AIU_DCU2_SIZE
,
181 0, &sc
->sc_dcu2_ioh
))
183 if (bus_space_map(sc
->sc_iot
,
184 VR4181AIU_AIU_BASE
, VR4181AIU_AIU_SIZE
,
191 bus_space_write_2(sc
->sc_iot
, sc
->sc_aiu_ioh
,
192 VR4181AIU_SEQ_REG_W
, VR4181AIU_AIURST
);
193 bus_space_write_2(sc
->sc_iot
, sc
->sc_aiu_ioh
,
194 VR4181AIU_SEQ_REG_W
, 0);
197 * set sample rate (1kHz fixed)
199 * assume to PCLK is 32.768MHz
201 bus_space_write_2(sc
->sc_iot
, sc
->sc_aiu_ioh
,
203 32768000 / SAMPLEFREQ
);
207 * assume to PCLK is 32.768MHz
208 * DAVREF_SETUP = 5usec * PCLK = 163.84
210 bus_space_write_2(sc
->sc_iot
, sc
->sc_aiu_ioh
,
211 VR4181AIU_DAVREF_SETUP_REG_W
, 164);
213 vr4181aiu_disable(sc
);
215 if (vrip_intr_establish(va
->va_vc
, va
->va_unit
, 0,
216 IPL_BIO
, vr4181aiu_intr
, sc
) == NULL
) {
217 printf("%s: can't establish interrupt\n",
218 sc
->sc_dev
.dv_xname
);
226 bus_space_unmap(sc
->sc_iot
, sc
->sc_dcu2_ioh
, VR4181AIU_DCU2_SIZE
);
228 bus_space_unmap(sc
->sc_iot
, sc
->sc_dcu1_ioh
, VR4181AIU_DCU1_SIZE
);
230 printf(": can't map i/o space\n");
234 vr4181aiuopen(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
236 struct vr4181aiu_softc
*sc
;
238 sc
= device_lookup_private(&vr4181aiu_cd
, minor(dev
);
242 if (sc
->sc_status
& ST_BUSY
)
245 sc
->sc_inbuf_head
= sc
->sc_inbuf_tail
246 = sc
->sc_inbuf_which
= sc
->sc_inbuf1
;
247 sc
->sc_status
&= ~ST_OVERRUN
;
251 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu1_ioh
,
252 DCU_DMARST_REG_W
, 0);
253 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu1_ioh
,
254 DCU_DMARST_REG_W
, DCU_DMARST
);
255 /* dest1 <- sc_inbuf1 */
256 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu1_ioh
,
258 MIPS_KSEG0_TO_PHYS(sc
->sc_inbuf1
) & 0xffff);
259 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu1_ioh
,
261 MIPS_KSEG0_TO_PHYS(sc
->sc_inbuf1
) >> 16);
262 /* dest2 <- sc_inbuf2 */
263 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu1_ioh
,
265 MIPS_KSEG0_TO_PHYS(sc
->sc_inbuf2
) & 0xffff);
266 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu1_ioh
,
268 MIPS_KSEG0_TO_PHYS(sc
->sc_inbuf2
) >> 16);
269 /* record length <- INPUTLEN */
270 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu2_ioh
,
271 DCU_MICRCLEN_REG_W
, INPUTLEN
);
272 /* config <- auto load */
273 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu2_ioh
,
274 DCU_MICDMACFG_REG_W
, DCU_MICLOAD
);
275 /* irq <- irq clear */
276 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu2_ioh
,
277 DCU_DMAITRQ_REG_W
, DCU_MICEOP
);
279 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu2_ioh
,
280 DCU_DMACTL_REG_W
, DCU_MICCNT_INC
);
281 /* irq mask <- microphone end of process */
282 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu2_ioh
,
283 DCU_DMAITMK_REG_W
, DCU_MICEOP_ENABLE
);
286 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu1_ioh
,
287 DCU_AIUDMAMSK_REG_W
, DCU_ENABLE_MIC
);
290 bus_space_write_2(sc
->sc_iot
, sc
->sc_aiu_ioh
,
291 VR4181AIU_MCNT_REG_W
, VR4181AIU_ADENAIU
);
293 /* enable microphone */
294 bus_space_write_2(sc
->sc_iot
, sc
->sc_aiu_ioh
,
295 VR4181AIU_SEQ_REG_W
, VR4181AIU_AIUMEN
);
297 sc
->sc_status
|= ST_BUSY
;
303 vr4181aiuclose(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
305 vr4181aiu_disable(device_lookup_private(&vr4181aiu_cd
, minor(dev
)));
310 vr4181aiuread(dev_t dev
, struct uio
*uio
, int flag
)
312 struct vr4181aiu_softc
*sc
;
317 u_int8_t tmp
[INPUTLEN
/ PICKUPCOUNT
];
321 sc
= device_lookup_private(&vr4181aiu_cd
, minor(dev
));
323 src
= sc
->sc_inbuf_tail
;
325 if (src
== sc
->sc_inbuf_head
) {
326 /* wait for DMA to complete writing */
327 tsleep(sc
, PRIBIO
, "aiu read", 0);
328 /* now sc_inbuf_head points alternate buffer */
332 fence
= sc
->sc_inbuf_which
== sc
->sc_inbuf1
333 ? &sc
->sc_inbuf1
[INPUTLEN
]
334 : &sc
->sc_inbuf2
[INPUTLEN
];
335 avail
= (fence
- src
) / PICKUPCOUNT
;
336 count
= min(avail
, uio
->uio_resid
);
339 *dst
++ = (u_int8_t
) (*src
>> 2);
345 sc
->sc_inbuf_tail
= src
;
347 /* alter the buffer */
350 = sc
->sc_inbuf_which
== sc
->sc_inbuf1
351 ? sc
->sc_inbuf2
: sc
->sc_inbuf1
;
354 return uiomove(tmp
, dst
- tmp
, uio
);
358 vr4181aiuwrite(dev_t dev
, struct uio
*uio
, int flag
)
367 vr4181aiu_intr(void *arg
)
369 struct vr4181aiu_softc
*sc
= arg
;
371 if (!(sc
->sc_status
& ST_BUSY
)) {
372 printf("vr4181aiu_intr: stray interrupt\n");
373 vr4181aiu_disable(sc
);
378 bus_space_write_2(sc
->sc_iot
, sc
->sc_dcu2_ioh
,
379 DCU_DMAITRQ_REG_W
, DCU_MICEOP
);
381 if (sc
->sc_inbuf_head
== sc
->sc_inbuf1
) {
382 if (sc
->sc_inbuf_tail
!= sc
->sc_inbuf1
)
383 sc
->sc_status
|= ST_OVERRUN
;
384 sc
->sc_inbuf_head
= sc
->sc_inbuf2
;
386 if (sc
->sc_inbuf_tail
!= sc
->sc_inbuf2
)
387 sc
->sc_status
|= ST_OVERRUN
;
388 sc
->sc_inbuf_head
= sc
->sc_inbuf1
;
391 if (sc
->sc_status
& ST_OVERRUN
) {
392 printf("vr4181aiu_intr: overrun\n");
395 DPRINTF(("vr4181aiu_intr: sc_inbuf1 = %04x, sc_inbuf2 = %04x\n",
396 sc
->sc_inbuf1
[0], sc
->sc_inbuf2
[0]));